ffi-tox 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/ProjectTox-Core/AUTHORS +0 -0
  3. data/ProjectTox-Core/ChangeLog +0 -0
  4. data/ProjectTox-Core/INSTALL +370 -0
  5. data/ProjectTox-Core/INSTALL.md +455 -56
  6. data/ProjectTox-Core/Makefile.am +35 -0
  7. data/ProjectTox-Core/NEWS +0 -0
  8. data/ProjectTox-Core/README +43 -0
  9. data/ProjectTox-Core/README.md +34 -44
  10. data/ProjectTox-Core/auto_tests/Makefile.inc +110 -0
  11. data/ProjectTox-Core/auto_tests/TCP_test.c +519 -0
  12. data/ProjectTox-Core/auto_tests/assoc_test.c +160 -0
  13. data/ProjectTox-Core/auto_tests/crypto_test.c +302 -0
  14. data/ProjectTox-Core/auto_tests/dht_test.c +362 -0
  15. data/ProjectTox-Core/auto_tests/encryptsave_test.c +104 -0
  16. data/ProjectTox-Core/auto_tests/friends_test.c +238 -0
  17. data/ProjectTox-Core/auto_tests/helpers.h +15 -0
  18. data/ProjectTox-Core/auto_tests/messenger_test.c +365 -0
  19. data/ProjectTox-Core/auto_tests/network_test.c +171 -0
  20. data/ProjectTox-Core/auto_tests/onion_test.c +363 -0
  21. data/ProjectTox-Core/auto_tests/skeleton_test.c +49 -0
  22. data/ProjectTox-Core/auto_tests/tox_test.c +454 -0
  23. data/ProjectTox-Core/auto_tests/toxav_basic_test.c +597 -0
  24. data/ProjectTox-Core/auto_tests/toxav_many_test.c +402 -0
  25. data/ProjectTox-Core/autogen.sh +6 -0
  26. data/ProjectTox-Core/build/Makefile.am +14 -0
  27. data/ProjectTox-Core/configure.ac +694 -0
  28. data/ProjectTox-Core/dist-build/android-arm.sh +3 -0
  29. data/ProjectTox-Core/dist-build/android-armv7.sh +3 -0
  30. data/ProjectTox-Core/dist-build/android-build.sh +59 -0
  31. data/ProjectTox-Core/dist-build/android-mips.sh +3 -0
  32. data/ProjectTox-Core/dist-build/android-x86.sh +3 -0
  33. data/ProjectTox-Core/docs/Group-Chats.md +71 -0
  34. data/ProjectTox-Core/docs/Hardening.txt +60 -0
  35. data/ProjectTox-Core/docs/Hardening_docs.txt +30 -0
  36. data/ProjectTox-Core/docs/Prevent_Tracking.txt +160 -0
  37. data/ProjectTox-Core/docs/TCP_Network.txt +154 -0
  38. data/ProjectTox-Core/docs/TODO +62 -0
  39. data/ProjectTox-Core/docs/Tox_middle_level_network_protocol.txt +120 -0
  40. data/ProjectTox-Core/docs/av_api.md +194 -0
  41. data/ProjectTox-Core/libtoxav.pc.in +11 -0
  42. data/ProjectTox-Core/libtoxcore.pc.in +11 -0
  43. data/ProjectTox-Core/m4/ax_have_epoll.m4 +104 -0
  44. data/ProjectTox-Core/m4/ax_pthread.m4 +317 -0
  45. data/ProjectTox-Core/m4/pkg.m4 +199 -0
  46. data/ProjectTox-Core/other/DHT_bootstrap.c +121 -58
  47. data/ProjectTox-Core/other/DHTnodes +3 -0
  48. data/ProjectTox-Core/other/Makefile.inc +20 -0
  49. data/ProjectTox-Core/other/bootstrap_node_packets.c +65 -0
  50. data/ProjectTox-Core/other/tox.png +0 -0
  51. data/ProjectTox-Core/testing/DHT_test.c +170 -98
  52. data/ProjectTox-Core/testing/Makefile.inc +112 -0
  53. data/ProjectTox-Core/testing/Messenger_test.c +133 -69
  54. data/ProjectTox-Core/testing/dns3_test.c +115 -0
  55. data/ProjectTox-Core/testing/misc_tools.c +59 -13
  56. data/ProjectTox-Core/testing/nTox.c +1127 -264
  57. data/ProjectTox-Core/testing/nTox.h +10 -19
  58. data/ProjectTox-Core/testing/tox_shell.c +159 -0
  59. data/ProjectTox-Core/testing/tox_sync.c +299 -0
  60. data/ProjectTox-Core/tools/README +11 -0
  61. data/ProjectTox-Core/tools/astylerc +11 -0
  62. data/ProjectTox-Core/tools/pre-commit +17 -0
  63. data/ProjectTox-Core/toxav/Makefile.inc +36 -0
  64. data/ProjectTox-Core/toxav/codec.c +357 -0
  65. data/ProjectTox-Core/toxav/codec.h +116 -0
  66. data/ProjectTox-Core/toxav/msi.c +1949 -0
  67. data/ProjectTox-Core/toxav/msi.h +267 -0
  68. data/ProjectTox-Core/toxav/rtp.c +600 -0
  69. data/ProjectTox-Core/toxav/rtp.h +196 -0
  70. data/ProjectTox-Core/toxav/toxav.c +1148 -0
  71. data/ProjectTox-Core/toxav/toxav.h +389 -0
  72. data/ProjectTox-Core/toxcore/DHT.c +2521 -0
  73. data/ProjectTox-Core/toxcore/DHT.h +412 -0
  74. data/ProjectTox-Core/toxcore/LAN_discovery.c +322 -0
  75. data/ProjectTox-Core/{core → toxcore}/LAN_discovery.h +17 -12
  76. data/ProjectTox-Core/toxcore/Makefile.inc +67 -0
  77. data/ProjectTox-Core/toxcore/Messenger.c +3006 -0
  78. data/ProjectTox-Core/toxcore/Messenger.h +818 -0
  79. data/ProjectTox-Core/toxcore/TCP_client.c +858 -0
  80. data/ProjectTox-Core/toxcore/TCP_client.h +156 -0
  81. data/ProjectTox-Core/toxcore/TCP_server.c +1332 -0
  82. data/ProjectTox-Core/toxcore/TCP_server.h +181 -0
  83. data/ProjectTox-Core/toxcore/assoc.c +1033 -0
  84. data/ProjectTox-Core/toxcore/assoc.h +104 -0
  85. data/ProjectTox-Core/toxcore/crypto_core.c +278 -0
  86. data/ProjectTox-Core/toxcore/crypto_core.h +151 -0
  87. data/ProjectTox-Core/toxcore/friend_requests.c +175 -0
  88. data/ProjectTox-Core/toxcore/friend_requests.h +83 -0
  89. data/ProjectTox-Core/toxcore/group_chats.c +837 -0
  90. data/ProjectTox-Core/toxcore/group_chats.h +199 -0
  91. data/ProjectTox-Core/toxcore/list.c +256 -0
  92. data/ProjectTox-Core/toxcore/list.h +85 -0
  93. data/ProjectTox-Core/toxcore/logger.c +153 -0
  94. data/ProjectTox-Core/toxcore/logger.h +84 -0
  95. data/ProjectTox-Core/toxcore/misc_tools.h +70 -0
  96. data/ProjectTox-Core/toxcore/net_crypto.c +2753 -0
  97. data/ProjectTox-Core/toxcore/net_crypto.h +410 -0
  98. data/ProjectTox-Core/toxcore/network.c +979 -0
  99. data/ProjectTox-Core/toxcore/network.h +367 -0
  100. data/ProjectTox-Core/toxcore/onion.c +540 -0
  101. data/ProjectTox-Core/toxcore/onion.h +150 -0
  102. data/ProjectTox-Core/toxcore/onion_announce.c +433 -0
  103. data/ProjectTox-Core/toxcore/onion_announce.h +139 -0
  104. data/ProjectTox-Core/toxcore/onion_client.c +1347 -0
  105. data/ProjectTox-Core/toxcore/onion_client.h +253 -0
  106. data/ProjectTox-Core/toxcore/ping.c +346 -0
  107. data/ProjectTox-Core/toxcore/ping.h +47 -0
  108. data/ProjectTox-Core/toxcore/ping_array.c +162 -0
  109. data/ProjectTox-Core/toxcore/ping_array.h +75 -0
  110. data/ProjectTox-Core/toxcore/tox.c +940 -0
  111. data/ProjectTox-Core/toxcore/tox.h +734 -0
  112. data/ProjectTox-Core/toxcore/util.c +193 -0
  113. data/ProjectTox-Core/toxcore/util.h +63 -0
  114. data/ProjectTox-Core/toxdns/Makefile.inc +29 -0
  115. data/ProjectTox-Core/toxdns/toxdns.c +238 -0
  116. data/ProjectTox-Core/toxdns/toxdns.h +88 -0
  117. data/ProjectTox-Core/toxencryptsave/Makefile.inc +45 -0
  118. data/ProjectTox-Core/toxencryptsave/toxencryptsave.c +179 -0
  119. data/ProjectTox-Core/toxencryptsave/toxencryptsave.h +74 -0
  120. data/interfaces/libtox.i +2 -6
  121. data/lib/ffi-tox/libtox.rb +406 -28
  122. metadata +124 -46
  123. data/ProjectTox-Core/CMakeLists.txt +0 -50
  124. data/ProjectTox-Core/cmake/FindLIBCONFIG.cmake +0 -15
  125. data/ProjectTox-Core/cmake/FindNaCl.cmake +0 -17
  126. data/ProjectTox-Core/cmake/FindSODIUM.cmake +0 -15
  127. data/ProjectTox-Core/core/CMakeLists.txt +0 -19
  128. data/ProjectTox-Core/core/DHT.c +0 -1104
  129. data/ProjectTox-Core/core/DHT.h +0 -111
  130. data/ProjectTox-Core/core/LAN_discovery.c +0 -79
  131. data/ProjectTox-Core/core/Lossless_UDP.c +0 -755
  132. data/ProjectTox-Core/core/Lossless_UDP.h +0 -106
  133. data/ProjectTox-Core/core/Messenger.c +0 -596
  134. data/ProjectTox-Core/core/Messenger.h +0 -165
  135. data/ProjectTox-Core/core/friend_requests.c +0 -131
  136. data/ProjectTox-Core/core/friend_requests.h +0 -51
  137. data/ProjectTox-Core/core/net_crypto.c +0 -575
  138. data/ProjectTox-Core/core/net_crypto.h +0 -134
  139. data/ProjectTox-Core/core/network.c +0 -205
  140. data/ProjectTox-Core/core/network.h +0 -134
  141. data/ProjectTox-Core/docs/commands.md +0 -25
  142. data/ProjectTox-Core/docs/start_guide.de.md +0 -40
  143. data/ProjectTox-Core/docs/start_guide.md +0 -38
  144. data/ProjectTox-Core/other/CMakeLists.txt +0 -9
  145. data/ProjectTox-Core/testing/CMakeLists.txt +0 -18
  146. data/ProjectTox-Core/testing/DHT_cryptosendfiletest.c +0 -228
  147. data/ProjectTox-Core/testing/DHT_sendfiletest.c +0 -176
  148. data/ProjectTox-Core/testing/Lossless_UDP_testclient.c +0 -214
  149. data/ProjectTox-Core/testing/Lossless_UDP_testserver.c +0 -201
  150. data/ProjectTox-Core/testing/misc_tools.h +0 -29
  151. data/ProjectTox-Core/testing/nTox_win32.c +0 -387
  152. data/ProjectTox-Core/testing/nTox_win32.h +0 -40
  153. data/ProjectTox-Core/testing/rect.py +0 -45
@@ -0,0 +1,1949 @@
1
+ /** msi.c
2
+ *
3
+ * Copyright (C) 2013 Tox project All Rights Reserved.
4
+ *
5
+ * This file is part of Tox.
6
+ *
7
+ * Tox is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * Tox is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19
+ *
20
+ */
21
+
22
+
23
+ #ifdef HAVE_CONFIG_H
24
+ #include "config.h"
25
+ #endif /* HAVE_CONFIG_H */
26
+
27
+ #include "../toxcore/logger.h"
28
+ #include "../toxcore/util.h"
29
+
30
+ #include "msi.h"
31
+
32
+ #include <unistd.h>
33
+ #include <string.h>
34
+ #include <stdlib.h>
35
+ #include <stdbool.h>
36
+
37
+ #define MSI_MAXMSG_SIZE 256
38
+
39
+ /* Define default timeout for a request.
40
+ * There is no behavior specified by the msi on what will
41
+ * client do on timeout, but to call timeout callback.
42
+ */
43
+ #define m_deftout 10000 /* in milliseconds */
44
+
45
+ /**
46
+ * Protocol:
47
+ *
48
+ * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|
49
+ */
50
+
51
+ typedef uint8_t MSIRawCSettingsType[23];
52
+
53
+ typedef enum {
54
+ IDRequest = 1,
55
+ IDResponse,
56
+ IDReason,
57
+ IDCallId,
58
+ IDCSettings,
59
+
60
+ } MSIHeaderID;
61
+
62
+ typedef enum {
63
+ TypeRequest,
64
+ TypeResponse,
65
+
66
+ } MSIMessageType;
67
+
68
+ typedef enum {
69
+ invite,
70
+ start,
71
+ cancel,
72
+ reject,
73
+ end,
74
+
75
+ } MSIRequest;
76
+
77
+ typedef enum {
78
+ ringing,
79
+ starting,
80
+ ending,
81
+ error
82
+
83
+ } MSIResponse;
84
+
85
+
86
+ #define GENERIC_HEADER(header, val_type) \
87
+ typedef struct _MSIHeader##header { \
88
+ val_type value; \
89
+ _Bool exists; \
90
+ } MSIHeader##header;
91
+
92
+
93
+ GENERIC_HEADER ( Request, MSIRequest )
94
+ GENERIC_HEADER ( Response, MSIResponse )
95
+ GENERIC_HEADER ( CallId, MSICallIDType )
96
+ GENERIC_HEADER ( Reason, MSIReasonStrType )
97
+ GENERIC_HEADER ( CSettings, MSIRawCSettingsType )
98
+
99
+
100
+ /**
101
+ * @brief This is the message structure. It contains all of the headers and
102
+ * destination/source of the message stored in friend_id.
103
+ *
104
+ */
105
+ typedef struct _MSIMessage {
106
+
107
+ MSIHeaderRequest request;
108
+ MSIHeaderResponse response;
109
+ MSIHeaderReason reason;
110
+ MSIHeaderCallId callid;
111
+ MSIHeaderCSettings csettings;
112
+
113
+ int friend_id;
114
+
115
+ } MSIMessage;
116
+
117
+
118
+ inline__ void invoke_callback(MSISession *session, int32_t call_index, MSICallbackID id)
119
+ {
120
+ if ( session->callbacks[id].function ) {
121
+ LOGGER_DEBUG("Invoking callback function: %d", id);
122
+ session->callbacks[id].function ( session->agent_handler, call_index, session->callbacks[id].data );
123
+ }
124
+ }
125
+
126
+ /**
127
+ * @brief Parse raw 'data' received from socket into MSIMessage struct.
128
+ * Every message has to have end value of 'end_byte' or _undefined_ behavior
129
+ * occures. The best practice is to check the end of the message at the handle_packet.
130
+ *
131
+ * @param msg Container.
132
+ * @param data The data.
133
+ * @return int
134
+ * @retval -1 Error occurred.
135
+ * @retval 0 Success.
136
+ */
137
+ static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length )
138
+ {
139
+
140
+ #define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; }
141
+ #define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; }
142
+ #define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; }
143
+
144
+ if ( msg == NULL ) {
145
+ LOGGER_ERROR("Could not parse message: no storage!");
146
+ return -1;
147
+ }
148
+
149
+ if ( data[length - 1] ) { /* End byte must have value 0 */
150
+ LOGGER_ERROR("Invalid end byte");
151
+ return -1;
152
+ }
153
+
154
+ const uint8_t *it = data;
155
+ int size_constraint = length;
156
+
157
+ while ( *it ) {/* until end byte is hit */
158
+ switch (*it) {
159
+ case IDRequest:
160
+ FAIL_CONSTRAINT(size_constraint, 3);
161
+ FAIL_SIZE(it[1], 1);
162
+ // FAIL_LIMITS(it[2], invite, end);
163
+ FAIL_LIMITS(it[2], end);
164
+ msg->request.value = it[2];
165
+ it += 3;
166
+ msg->request.exists = 1;
167
+ break;
168
+
169
+ case IDResponse:
170
+ FAIL_CONSTRAINT(size_constraint, 3);
171
+ FAIL_SIZE(it[1], 1);
172
+ // FAIL_LIMITS(it[2], ringing, error);
173
+ FAIL_LIMITS(it[2], error);
174
+ msg->response.value = it[2];
175
+ it += 3;
176
+ msg->response.exists = 1;
177
+ break;
178
+
179
+ case IDCallId:
180
+ FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2);
181
+ FAIL_SIZE(it[1], sizeof(MSICallIDType));
182
+ memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType));
183
+ it += sizeof(MSICallIDType) + 2;
184
+ msg->callid.exists = 1;
185
+ break;
186
+
187
+ case IDReason:
188
+ FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2);
189
+ FAIL_SIZE(it[1], sizeof(MSIReasonStrType));
190
+ memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType));
191
+ it += sizeof(MSIReasonStrType) + 2;
192
+ msg->reason.exists = 1;
193
+ break;
194
+
195
+ case IDCSettings:
196
+ FAIL_CONSTRAINT(size_constraint, sizeof(MSIRawCSettingsType) + 2);
197
+ FAIL_SIZE(it[1], sizeof(MSIRawCSettingsType));
198
+ memcpy(msg->csettings.value, it + 2, sizeof(MSIRawCSettingsType));
199
+ it += sizeof(MSIRawCSettingsType) + 2;
200
+ msg->csettings.exists = 1;
201
+ break;
202
+
203
+ default:
204
+ LOGGER_ERROR("Invalid id byte");
205
+ return -1;
206
+ break;
207
+ }
208
+ }
209
+
210
+ return 0;
211
+ }
212
+
213
+ /**
214
+ * @brief Create the message.
215
+ *
216
+ * @param type Request or response.
217
+ * @param type_id Type of request/response.
218
+ * @return MSIMessage* Created message.
219
+ * @retval NULL Error occurred.
220
+ */
221
+ MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value )
222
+ {
223
+ MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
224
+
225
+ if ( retu == NULL ) {
226
+ LOGGER_WARNING("Allocation failed! Program might misbehave!");
227
+ return NULL;
228
+ }
229
+
230
+ if ( type == TypeRequest ) {
231
+ retu->request.exists = 1;
232
+ retu->request.value = type_value;
233
+
234
+ } else {
235
+ retu->response.exists = 1;
236
+ retu->response.value = type_value;
237
+ }
238
+
239
+ return retu;
240
+ }
241
+
242
+
243
+ /**
244
+ * @brief Parse data from handle_packet.
245
+ *
246
+ * @param data The data.
247
+ * @return MSIMessage* Parsed message.
248
+ * @retval NULL Error occurred.
249
+ */
250
+ MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
251
+ {
252
+ if ( data == NULL ) {
253
+ LOGGER_WARNING("Tried to parse empty message!");
254
+ return NULL;
255
+ }
256
+
257
+ MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
258
+
259
+ if ( retu == NULL ) {
260
+ LOGGER_WARNING("Allocation failed! Program might misbehave!");
261
+ return NULL;
262
+ }
263
+
264
+ if ( parse_raw_data ( retu, data, length ) == -1 ) {
265
+
266
+ free ( retu );
267
+ return NULL;
268
+ }
269
+
270
+ return retu;
271
+ }
272
+
273
+
274
+ /**
275
+ * @brief Speaks for it self.
276
+ *
277
+ * @param dest Container.
278
+ * @param header_field Field.
279
+ * @param header_value Field value.
280
+ * @param value_len Length of field value.
281
+ * @param length Pointer to container length.
282
+ * @return uint8_t* Iterated container.
283
+ */
284
+ uint8_t *format_output ( uint8_t *dest, MSIHeaderID id, const void *value, uint8_t value_len, uint16_t *length )
285
+ {
286
+ if ( dest == NULL ) {
287
+ LOGGER_ERROR("No destination space!");
288
+ return NULL;
289
+ }
290
+
291
+ if (value == NULL || value_len == 0) {
292
+ LOGGER_ERROR("Empty header value");
293
+ return NULL;
294
+ }
295
+
296
+ *dest = id;
297
+ dest ++;
298
+ *dest = value_len;
299
+ dest ++;
300
+
301
+ memcpy(dest, value, value_len);
302
+
303
+ *length += (2 + value_len);
304
+
305
+ return dest + value_len; /* Set to next position ready to be written */
306
+ }
307
+
308
+
309
+ /**
310
+ * @brief Parse MSIMessage to send.
311
+ *
312
+ * @param msg The message.
313
+ * @param dest Destination.
314
+ * @return uint16_t Its final size.
315
+ */
316
+ uint16_t parse_send ( MSIMessage *msg, uint8_t *dest )
317
+ {
318
+ if (msg == NULL) {
319
+ LOGGER_ERROR("No message!");
320
+ return 0;
321
+ }
322
+
323
+ if (dest == NULL ) {
324
+ LOGGER_ERROR("No destination!");
325
+ return 0;
326
+ }
327
+
328
+ uint8_t *it = dest;
329
+ uint16_t size = 0;
330
+
331
+ if (msg->request.exists) {
332
+ uint8_t cast = msg->request.value;
333
+ it = format_output(it, IDRequest, &cast, 1, &size);
334
+ }
335
+
336
+ if (msg->response.exists) {
337
+ uint8_t cast = msg->response.value;
338
+ it = format_output(it, IDResponse, &cast, 1, &size);
339
+ }
340
+
341
+ if (msg->callid.exists) {
342
+ it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size);
343
+ }
344
+
345
+ if (msg->reason.exists) {
346
+ it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
347
+ }
348
+
349
+ if (msg->csettings.exists) {
350
+ it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size);
351
+ }
352
+
353
+ *it = 0;
354
+ size ++;
355
+
356
+ return size;
357
+ }
358
+
359
+ void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
360
+ {
361
+ if ( !msg ) return;
362
+
363
+ msg->reason.exists = 1;
364
+ memcpy(msg->reason.value, value, sizeof(MSIReasonStrType));
365
+ }
366
+
367
+ void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
368
+ {
369
+ if ( !msg ) return;
370
+
371
+ msg->callid.exists = 1;
372
+ memcpy(msg->callid.value, value, sizeof(MSICallIDType));
373
+ }
374
+
375
+ void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value )
376
+ {
377
+ if ( !msg ) return;
378
+
379
+ msg->csettings.exists = 1;
380
+
381
+ msg->csettings.value[0] = value->call_type;
382
+ uint8_t *iter = msg->csettings.value + 1;
383
+
384
+ /* Video bitrate */
385
+ uint32_t lval = htonl(value->video_bitrate);
386
+ memcpy(iter, &lval, 4);
387
+ iter += 4;
388
+
389
+ /* Video max width */
390
+ uint16_t sval = htons(value->max_video_width);
391
+ memcpy(iter, &sval, 2);
392
+ iter += 2;
393
+
394
+ /* Video max height */
395
+ sval = htons(value->max_video_height);
396
+ memcpy(iter, &sval, 2);
397
+ iter += 2;
398
+
399
+ /* Audio bitrate */
400
+ lval = htonl(value->audio_bitrate);
401
+ memcpy(iter, &lval, 4);
402
+ iter += 4;
403
+
404
+ /* Audio frame duration */
405
+ sval = htons(value->audio_frame_duration);
406
+ memcpy(iter, &sval, 2);
407
+ iter += 2;
408
+
409
+ /* Audio sample rate */
410
+ lval = htonl(value->audio_sample_rate);
411
+ memcpy(iter, &lval, 4);
412
+ iter += 4;
413
+
414
+ /* Audio channels */
415
+ lval = htonl(value->audio_channels);
416
+ memcpy(iter, &lval, 4);
417
+ }
418
+
419
+ void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest )
420
+ {
421
+ if ( !msg || !dest || !msg->csettings.exists ) return;
422
+
423
+ dest->call_type = msg->csettings.value[0];
424
+ uint8_t *iter = msg->csettings.value + 1;
425
+
426
+ memcpy(&dest->video_bitrate, iter, 4);
427
+ iter += 4;
428
+ dest->video_bitrate = ntohl(dest->video_bitrate);
429
+
430
+ memcpy(&dest->max_video_width, iter, 2);
431
+ iter += 2;
432
+ dest->max_video_width = ntohs(dest->max_video_width);
433
+
434
+ memcpy(&dest->max_video_height, iter, 2);
435
+ iter += 2;
436
+ dest->max_video_height = ntohs(dest->max_video_height);
437
+
438
+ memcpy(&dest->audio_bitrate, iter, 4);
439
+ iter += 4;
440
+ dest->audio_bitrate = ntohl(dest->audio_bitrate);
441
+
442
+ memcpy(&dest->audio_frame_duration, iter, 2);
443
+ iter += 2;
444
+ dest->audio_frame_duration = ntohs(dest->audio_frame_duration);
445
+
446
+ memcpy(&dest->audio_sample_rate, iter, 4);
447
+ iter += 4;
448
+ dest->audio_sample_rate = ntohl(dest->audio_sample_rate);
449
+
450
+ memcpy(&dest->audio_channels, iter, 4);
451
+ dest->audio_channels = ntohl(dest->audio_channels);
452
+ }
453
+
454
+ typedef struct _Timer {
455
+ void *(*func)(void *);
456
+ void *func_arg1;
457
+ int func_arg2;
458
+ uint64_t timeout;
459
+ int idx;
460
+
461
+ } Timer;
462
+
463
+ typedef struct _TimerHandler {
464
+ Timer **timers;
465
+ pthread_mutex_t mutex;
466
+
467
+ uint32_t max_capacity;
468
+ uint32_t size;
469
+ uint64_t resolution;
470
+
471
+ _Bool running;
472
+
473
+ } TimerHandler;
474
+
475
+ struct timer_function_args {
476
+ void *arg1;
477
+ int arg2;
478
+ };
479
+
480
+ /**
481
+ * @brief Allocate timer in array
482
+ *
483
+ * @param timers_container Handler
484
+ * @param func Function to be executed
485
+ * @param arg Its args
486
+ * @param timeout Timeout in ms
487
+ * @return int
488
+ */
489
+ static int timer_alloc ( TimerHandler *timers_container, void *(func)(void *), void *arg1, int arg2, uint32_t timeout)
490
+ {
491
+ static int timer_id;
492
+ pthread_mutex_lock(&timers_container->mutex);
493
+
494
+ uint32_t i = 0;
495
+
496
+ for (; i < timers_container->max_capacity && timers_container->timers[i]; i ++);
497
+
498
+ if (i == timers_container->max_capacity) {
499
+ LOGGER_WARNING("Maximum capacity reached!");
500
+ pthread_mutex_unlock(&timers_container->mutex);
501
+ return -1;
502
+ }
503
+
504
+ Timer *timer = timers_container->timers[i] = calloc(sizeof(Timer), 1);
505
+
506
+ if (timer == NULL) {
507
+ LOGGER_ERROR("Failed to allocate timer!");
508
+ pthread_mutex_unlock(&timers_container->mutex);
509
+ return -1;
510
+ }
511
+
512
+ timers_container->size ++;
513
+
514
+ timer->func = func;
515
+ timer->func_arg1 = arg1;
516
+ timer->func_arg2 = arg2;
517
+ timer->timeout = timeout + current_time_monotonic(); /* In ms */
518
+ ++timer_id;
519
+ timer->idx = timer_id;
520
+
521
+ /* reorder */
522
+ if (i) {
523
+ int64_t j = i - 1;
524
+
525
+ for (; j >= 0 && timeout < timers_container->timers[j]->timeout; j--) {
526
+ Timer *tmp = timers_container->timers[j];
527
+ timers_container->timers[j] = timer;
528
+ timers_container->timers[j + 1] = tmp;
529
+ }
530
+ }
531
+
532
+ pthread_mutex_unlock(&timers_container->mutex);
533
+
534
+ LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timers_container->size);
535
+ return timer->idx;
536
+ }
537
+
538
+ /**
539
+ * @brief Remove timer from array
540
+ *
541
+ * @param timers_container handler
542
+ * @param idx timer id
543
+ * @param lock_mutex (does the mutex need to be locked)
544
+ * @return int
545
+ */
546
+ static int timer_release ( TimerHandler *timers_container, int idx , int lock_mutex)
547
+ {
548
+ if (lock_mutex)
549
+ pthread_mutex_lock(&timers_container->mutex);
550
+
551
+ Timer **timed_events = timers_container->timers;
552
+
553
+ size_t i;
554
+ int rc = -1;
555
+
556
+ for (i = 0; i < timers_container->max_capacity; ++i) {
557
+ if (timed_events[i] && timed_events[i]->idx == idx) {
558
+ rc = i;
559
+ break;
560
+ }
561
+ }
562
+
563
+ if (rc == -1) {
564
+ LOGGER_WARNING("No event with id: %d", idx);
565
+
566
+ if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex);
567
+
568
+ return -1;
569
+ }
570
+
571
+ free(timed_events[rc]);
572
+
573
+ timed_events[rc] = NULL;
574
+
575
+ i = rc + 1;
576
+
577
+ for (; i < timers_container->max_capacity && timed_events[i]; i ++) {
578
+ timed_events[i - 1] = timed_events[i];
579
+ timed_events[i] = NULL;
580
+ }
581
+
582
+ timers_container->size--;
583
+
584
+ LOGGER_DEBUG("Popped id: %d, current size: %ull ", idx, timers_container->size);
585
+
586
+ if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex);
587
+
588
+ return 0;
589
+ }
590
+
591
+ /**
592
+ * @brief Main poll for timer execution
593
+ *
594
+ * @param arg ...
595
+ * @return void*
596
+ */
597
+ static void *timer_poll( void *arg )
598
+ {
599
+ TimerHandler *handler = arg;
600
+
601
+ while ( handler->running ) {
602
+
603
+ pthread_mutex_lock(&handler->mutex);
604
+
605
+ if ( handler->running ) {
606
+
607
+ uint64_t time = current_time_monotonic();
608
+
609
+ while ( handler->timers[0] && handler->timers[0]->timeout < time ) {
610
+ pthread_t tid;
611
+
612
+ struct timer_function_args *args = malloc(sizeof(struct timer_function_args));
613
+ args->arg1 = handler->timers[0]->func_arg1;
614
+ args->arg2 = handler->timers[0]->func_arg2;
615
+
616
+ if ( 0 != pthread_create(&tid, NULL, handler->timers[0]->func, args) ||
617
+ 0 != pthread_detach(tid) ) {
618
+ LOGGER_ERROR("Failed to execute timer at: %d!", handler->timers[0]->timeout);
619
+ free(args);
620
+ } else {
621
+ LOGGER_DEBUG("Executed timer assigned at: %d", handler->timers[0]->timeout);
622
+ }
623
+
624
+ timer_release(handler, handler->timers[0]->idx, 0);
625
+ }
626
+
627
+ }
628
+
629
+ pthread_mutex_unlock(&handler->mutex);
630
+
631
+ usleep(handler->resolution);
632
+ }
633
+
634
+ pthread_exit(NULL);
635
+ }
636
+
637
+ /**
638
+ * @brief Start timer poll and return handler
639
+ *
640
+ * @param max_capacity capacity
641
+ * @param resolution ...
642
+ * @return TimerHandler*
643
+ */
644
+ static TimerHandler *timer_init_session (int max_capacity, int resolution)
645
+ {
646
+ TimerHandler *handler = calloc(1, sizeof(TimerHandler));
647
+
648
+ if (handler == NULL) {
649
+ LOGGER_ERROR("Failed to allocate memory, program might misbehave!");
650
+ return NULL;
651
+ }
652
+
653
+ handler->timers = calloc(max_capacity, sizeof(Timer *));
654
+
655
+ if (handler->timers == NULL) {
656
+ LOGGER_ERROR("Failed to allocate %d timed events!", max_capacity);
657
+ free(handler);
658
+ return NULL;
659
+ }
660
+
661
+ handler->max_capacity = max_capacity;
662
+ handler->running = 1;
663
+ handler->resolution = resolution;
664
+
665
+ pthread_mutex_init(&handler->mutex, NULL);
666
+
667
+
668
+ pthread_t _tid;
669
+
670
+ if ( 0 != pthread_create(&_tid, NULL, timer_poll, handler) || 0 != pthread_detach(_tid) ) {
671
+ LOGGER_ERROR("Failed to start timer poll thread!");
672
+ free(handler->timers);
673
+ free(handler);
674
+ return NULL;
675
+ }
676
+
677
+ return handler;
678
+ }
679
+
680
+ /**
681
+ * @brief Terminate timer session
682
+ *
683
+ * @param handler The timer handler
684
+ * @return void
685
+ */
686
+ static void timer_terminate_session(TimerHandler *handler)
687
+ {
688
+ pthread_mutex_lock(&handler->mutex);
689
+
690
+ handler->running = 0;
691
+
692
+ pthread_mutex_unlock(&handler->mutex);
693
+
694
+ size_t i = 0;
695
+
696
+ for (; i < handler->max_capacity; i ++)
697
+ free(handler->timers[i]);
698
+
699
+ free(handler->timers);
700
+
701
+ pthread_mutex_destroy( &handler->mutex );
702
+ }
703
+
704
+ /**
705
+ * @brief Generate _random_ alphanumerical string.
706
+ *
707
+ * @param str Destination.
708
+ * @param size Size of string.
709
+ * @return void
710
+ */
711
+ static void t_randomstr ( uint8_t *str, uint32_t size )
712
+ {
713
+ if (str == NULL) {
714
+ LOGGER_DEBUG("Empty destination!");
715
+ return;
716
+ }
717
+
718
+ static const uint8_t _bytes[] =
719
+ "0123456789"
720
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
721
+ "abcdefghijklmnopqrstuvwxyz";
722
+
723
+ uint32_t _it = 0;
724
+
725
+ for ( ; _it < size; _it++ ) {
726
+ str[_it] = _bytes[ random_int() % 61 ];
727
+ }
728
+ }
729
+
730
+
731
+ typedef enum {
732
+ error_none,
733
+ error_deadcall, /* has call id but it's from old call */
734
+ error_id_mismatch, /* non-existing call */
735
+
736
+ error_no_callid, /* not having call id */
737
+ error_no_call, /* no call in session */
738
+ error_no_crypto_key, /* no crypto key */
739
+
740
+ error_busy
741
+
742
+ } MSICallError; /* Error codes */
743
+
744
+
745
+ /**
746
+ * @brief Stringify error code.
747
+ *
748
+ * @param error_code The code.
749
+ * @return const uint8_t* The string.
750
+ */
751
+ static inline__ const uint8_t *stringify_error ( MSICallError error_code )
752
+ {
753
+ static const uint8_t *strings[] = {
754
+ ( uint8_t *) "",
755
+ ( uint8_t *) "Using dead call",
756
+ ( uint8_t *) "Call id not set to any call",
757
+ ( uint8_t *) "Call id not available",
758
+ ( uint8_t *) "No active call in session",
759
+ ( uint8_t *) "No Crypto-key set",
760
+ ( uint8_t *) "Callee busy"
761
+ };
762
+
763
+ return strings[error_code];
764
+ }
765
+
766
+ /**
767
+ * @brief Speaks for it self.
768
+ *
769
+ * @param session Control session.
770
+ * @param msg The message.
771
+ * @param to Where to.
772
+ * @return int
773
+ * @retval -1 Error occurred.
774
+ * @retval 0 Success.
775
+ */
776
+ static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to )
777
+ {
778
+ msi_msg_set_callid ( msg, call->id );
779
+
780
+ uint8_t msg_string_final [MSI_MAXMSG_SIZE];
781
+ uint16_t length = parse_send ( msg, msg_string_final );
782
+
783
+ if (!length) {
784
+ LOGGER_WARNING("Parsing message failed; nothing sent!");
785
+ return -1;
786
+ }
787
+
788
+ if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) {
789
+ LOGGER_DEBUG("Sent message");
790
+ return 0;
791
+ }
792
+
793
+ return -1;
794
+ }
795
+
796
+ inline__ int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to )
797
+ {
798
+ MSIMessage *msg = msi_new_message ( TypeResponse, response );
799
+ int ret = send_message ( session, call, msg, to );
800
+ free ( msg );
801
+ return ret;
802
+ }
803
+
804
+ /**
805
+ * @brief Determine 'bigger' call id
806
+ *
807
+ * @param first duh
808
+ * @param second duh
809
+ * @return int
810
+ * @retval 0 it's first
811
+ * @retval 1 it's second
812
+ */
813
+ static int call_id_bigger( const uint8_t *first, const uint8_t *second)
814
+ {
815
+ return (memcmp(first, second, sizeof(MSICallIDType)) < 0);
816
+ }
817
+
818
+
819
+ /**
820
+ * @brief Speaks for it self.
821
+ *
822
+ * @param session Control session.
823
+ * @param msg The message.
824
+ * @param peer_id The peer.
825
+ * @return -1, 0
826
+ */
827
+ static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id )
828
+ {
829
+ if ( msg->csettings.exists ) {
830
+ msi_msg_get_csettings(msg, &call->csettings_peer[peer_id]);
831
+
832
+ LOGGER_DEBUG("Peer: %d \n"
833
+ "Type: %u \n"
834
+ "Video bitrate: %u \n"
835
+ "Video height: %u \n"
836
+ "Video width: %u \n"
837
+ "Audio bitrate: %u \n"
838
+ "Audio framedur: %u \n"
839
+ "Audio sample rate: %u \n"
840
+ "Audio channels: %u \n", peer_id,
841
+ call->csettings_peer[peer_id].call_type,
842
+ call->csettings_peer[peer_id].video_bitrate,
843
+ call->csettings_peer[peer_id].max_video_height,
844
+ call->csettings_peer[peer_id].max_video_width,
845
+ call->csettings_peer[peer_id].audio_bitrate,
846
+ call->csettings_peer[peer_id].audio_frame_duration,
847
+ call->csettings_peer[peer_id].audio_sample_rate,
848
+ call->csettings_peer[peer_id].audio_channels );
849
+
850
+ return 0;
851
+ }
852
+
853
+ LOGGER_WARNING("No csettings header!");
854
+ return -1;
855
+ }
856
+
857
+ static int terminate_call ( MSISession *session, MSICall *call );
858
+
859
+ static void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p)
860
+ {
861
+ (void)messenger;
862
+ MSISession *session = session_p;
863
+
864
+ switch ( status ) {
865
+ case 0: { /* Went offline */
866
+ int32_t j = 0;
867
+
868
+ for ( ; j < session->max_calls; j ++ ) {
869
+
870
+ if ( !session->calls[j] ) continue;
871
+
872
+ uint16_t i = 0;
873
+
874
+ for ( ; i < session->calls[j]->peer_count; i ++ )
875
+ if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) {
876
+ invoke_callback(session, j, MSI_OnPeerTimeout);
877
+ terminate_call(session, session->calls[j]);
878
+ LOGGER_DEBUG("Remote: %d timed out!", friend_num);
879
+ return; /* TODO: On group calls change behaviour */
880
+ }
881
+ }
882
+ }
883
+ break;
884
+
885
+ default:
886
+ break;
887
+ }
888
+ }
889
+
890
+ static MSICall *find_call ( MSISession *session, uint8_t *call_id )
891
+ {
892
+ if ( call_id == NULL ) return NULL;
893
+
894
+ int32_t i = 0;
895
+
896
+ for (; i < session->max_calls; i ++ )
897
+ if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) {
898
+ return session->calls[i];
899
+ }
900
+
901
+ return NULL;
902
+ }
903
+
904
+ /**
905
+ * @brief Sends error response to peer.
906
+ *
907
+ * @param session The session.
908
+ * @param errid The id.
909
+ * @param to Where to?
910
+ * @return int
911
+ * @retval -1/0 It's usually always success.
912
+ */
913
+ static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to )
914
+ {
915
+ if (!call) {
916
+ LOGGER_WARNING("Cannot handle error on 'null' call");
917
+ return -1;
918
+ }
919
+
920
+ LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id);
921
+
922
+ MSIMessage *msg_error = msi_new_message ( TypeResponse, error );
923
+
924
+ msi_msg_set_reason ( msg_error, stringify_error(errid) );
925
+ send_message ( session, call, msg_error, to );
926
+ free ( msg_error );
927
+
928
+ return 0;
929
+ }
930
+
931
+
932
+
933
+ /**
934
+ * @brief Add peer to peer list.
935
+ *
936
+ * @param call What call.
937
+ * @param peer_id Its id.
938
+ * @return void
939
+ */
940
+ static void add_peer( MSICall *call, int peer_id )
941
+ {
942
+ uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) :
943
+ realloc( call->peers, sizeof(uint32_t) * call->peer_count);
944
+
945
+ if (!peers) {
946
+ LOGGER_WARNING("Allocation failed! Program might misbehave!");
947
+ return;
948
+ }
949
+
950
+ call->peer_count ++;
951
+ call->peers = peers;
952
+ call->peers[call->peer_count - 1] = peer_id;
953
+
954
+ LOGGER_DEBUG("Added peer: %d", peer_id);
955
+ }
956
+
957
+
958
+ /**
959
+ * @brief Speaks for it self.
960
+ *
961
+ * @param session Control session.
962
+ * @param peers Amount of peers. (Currently it only supports 1)
963
+ * @param ringing_timeout Ringing timeout.
964
+ * @return MSICall* The created call.
965
+ */
966
+ static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
967
+ {
968
+
969
+ if (peers == 0) {
970
+ LOGGER_ERROR("No peers!");
971
+ return NULL;
972
+ }
973
+
974
+ int32_t call_idx = 0;
975
+
976
+ for (; call_idx < session->max_calls; call_idx ++) {
977
+ if ( !session->calls[call_idx] ) {
978
+
979
+ if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) {
980
+ LOGGER_WARNING("Allocation failed! Program might misbehave!");
981
+ return NULL;
982
+ }
983
+
984
+ break;
985
+ }
986
+ }
987
+
988
+ if ( call_idx == session->max_calls ) {
989
+ LOGGER_WARNING("Reached maximum amount of calls!");
990
+ return NULL;
991
+ }
992
+
993
+
994
+ MSICall *call = session->calls[call_idx];
995
+
996
+ call->call_idx = call_idx;
997
+
998
+ if ( !(call->csettings_peer = calloc ( sizeof ( MSICSettings ), peers )) ) {
999
+ LOGGER_WARNING("Allocation failed! Program might misbehave!");
1000
+ free(call);
1001
+ return NULL;
1002
+ }
1003
+
1004
+ call->session = session;
1005
+
1006
+ call->request_timer_id = 0;
1007
+ call->ringing_timer_id = 0;
1008
+
1009
+ call->ringing_tout_ms = ringing_timeout;
1010
+
1011
+ pthread_mutex_init ( &call->mutex, NULL );
1012
+
1013
+ LOGGER_DEBUG("Started new call with index: %u", call_idx);
1014
+ return call;
1015
+ }
1016
+
1017
+
1018
+ /**
1019
+ * @brief Terminate the call.
1020
+ *
1021
+ * @param session Control session.
1022
+ * @return int
1023
+ * @retval -1 Error occurred.
1024
+ * @retval 0 Success.
1025
+ */
1026
+ static int terminate_call ( MSISession *session, MSICall *call )
1027
+ {
1028
+ if ( !call ) {
1029
+ LOGGER_WARNING("Tried to terminate non-existing call!");
1030
+ return -1;
1031
+ }
1032
+
1033
+ LOGGER_DEBUG("Terminated call id: %d", call->call_idx);
1034
+ /* Check event loop and cancel timed events if there are any
1035
+ * NOTE: This has to be done before possibly
1036
+ * locking the mutex the second time
1037
+ */
1038
+ timer_release ( session->timer_handler, call->request_timer_id, 1);
1039
+ timer_release ( session->timer_handler, call->ringing_timer_id, 1);
1040
+
1041
+ /* Get a handle */
1042
+ pthread_mutex_lock ( &call->mutex );
1043
+
1044
+ session->calls[call->call_idx] = NULL;
1045
+
1046
+ free ( call->csettings_peer );
1047
+ free ( call->peers);
1048
+
1049
+ /* Release handle */
1050
+ pthread_mutex_unlock ( &call->mutex );
1051
+
1052
+ pthread_mutex_destroy ( &call->mutex );
1053
+
1054
+ free ( call );
1055
+
1056
+ return 0;
1057
+ }
1058
+
1059
+
1060
+ /**
1061
+ * @brief Function called at request timeout. If not called in thread it might cause trouble
1062
+ *
1063
+ * @param arg Control session
1064
+ * @return void*
1065
+ */
1066
+ static void *handle_timeout ( void *arg )
1067
+ {
1068
+ /* TODO: Cancel might not arrive there; set up
1069
+ * timers on these cancels and terminate call on
1070
+ * their timeout
1071
+ */
1072
+ struct timer_function_args *args = arg;
1073
+ int call_index = args->arg2;
1074
+ MSISession *session = args->arg1;
1075
+ MSICall *call = session->calls[call_index];
1076
+
1077
+ if (call) {
1078
+ LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx);
1079
+
1080
+ invoke_callback(session, call_index, MSI_OnRequestTimeout);
1081
+ }
1082
+
1083
+ if ( call && call->session ) {
1084
+
1085
+ /* TODO: Cancel all? */
1086
+ /* uint16_t _it = 0;
1087
+ * for ( ; _it < _session->call->peer_count; _it++ ) */
1088
+ msi_cancel ( call->session, call->call_idx, call->peers [0], "Request timed out" );
1089
+ /*terminate_call(call->session, call);*/
1090
+ }
1091
+
1092
+ free(arg);
1093
+ pthread_exit(NULL);
1094
+ }
1095
+
1096
+
1097
+ /********** Request handlers **********/
1098
+ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg )
1099
+ {
1100
+ LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1);
1101
+
1102
+ pthread_mutex_lock(&session->mutex);
1103
+
1104
+ if (!msg->csettings.exists) {/**/
1105
+ LOGGER_WARNING("Peer sent invalid codec settings!");
1106
+ send_error ( session, call, error_no_callid, msg->friend_id );
1107
+ pthread_mutex_unlock(&session->mutex);
1108
+ return 0;
1109
+ }
1110
+
1111
+ if ( call ) {
1112
+ if ( call->peers[0] == (uint32_t)msg->friend_id ) {
1113
+ if (call->state == call_inviting) {
1114
+ /* The glare case. A calls B when at the same time
1115
+ * B calls A. Who has advantage is set bey calculating
1116
+ * 'bigger' Call id and then that call id is being used in
1117
+ * future. User with 'bigger' Call id has the advantage
1118
+ * as in he will wait the response from the other.
1119
+ */
1120
+ LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]);
1121
+
1122
+ if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */
1123
+
1124
+ /* Terminate call; peer will timeout(call) if call initialization fails */
1125
+ terminate_call(session, call);
1126
+
1127
+ call = init_call ( session, 1, 0 );
1128
+
1129
+ if ( !call ) {
1130
+ pthread_mutex_unlock(&session->mutex);
1131
+ LOGGER_ERROR("Starting call");
1132
+ return 0;
1133
+ }
1134
+
1135
+ } else {
1136
+ pthread_mutex_unlock(&session->mutex);
1137
+ return 0; /* Wait for ringing from peer */
1138
+ }
1139
+ } else if (call->state == call_active) {
1140
+ /* Request for media change; call callback and send starting response */
1141
+ if (flush_peer_csettings(call, msg, 0) != 0) { /**/
1142
+ LOGGER_WARNING("Peer sent invalid csetting!");
1143
+ send_error ( session, call, error_no_callid, msg->friend_id );
1144
+ pthread_mutex_unlock(&session->mutex);
1145
+ return 0;
1146
+ }
1147
+
1148
+ LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == type_audio ? "audio" : "video");
1149
+ send_reponse(session, call, starting, msg->friend_id);
1150
+ pthread_mutex_unlock(&session->mutex);
1151
+ invoke_callback(session, call->call_idx, MSI_OnMediaChange);
1152
+ return 1;
1153
+ }
1154
+ } else {
1155
+ send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/
1156
+ terminate_call(session, call);
1157
+ pthread_mutex_unlock(&session->mutex);
1158
+ return 0;
1159
+ }
1160
+ } else {
1161
+ call = init_call ( session, 1, 0 );
1162
+
1163
+ if ( !call ) {
1164
+ pthread_mutex_unlock(&session->mutex);
1165
+ LOGGER_ERROR("Starting call");
1166
+ return 0;
1167
+ }
1168
+ }
1169
+
1170
+ if ( !msg->callid.exists ) {
1171
+ send_error ( session, call, error_no_callid, msg->friend_id );
1172
+ terminate_call(session, call);
1173
+ pthread_mutex_unlock(&session->mutex);
1174
+ return 0;
1175
+ }
1176
+
1177
+ memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) );
1178
+ call->state = call_starting;
1179
+
1180
+ add_peer( call, msg->friend_id);
1181
+
1182
+ flush_peer_csettings ( call, msg, 0 );
1183
+
1184
+ send_reponse(session, call, ringing, msg->friend_id);
1185
+
1186
+ pthread_mutex_unlock(&session->mutex);
1187
+
1188
+ invoke_callback(session, call->call_idx, MSI_OnInvite);
1189
+
1190
+ return 1;
1191
+ }
1192
+
1193
+ static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg )
1194
+ {
1195
+ if ( !call ) {
1196
+ LOGGER_WARNING("Session: %p Handling 'start' on no call");
1197
+ return 0;
1198
+ }
1199
+
1200
+ (void)msg;
1201
+
1202
+ LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id );
1203
+
1204
+ pthread_mutex_lock(&session->mutex);
1205
+
1206
+ call->state = call_active;
1207
+
1208
+ pthread_mutex_unlock(&session->mutex);
1209
+
1210
+ invoke_callback(session, call->call_idx, MSI_OnStart);
1211
+ return 1;
1212
+ }
1213
+
1214
+ static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg )
1215
+ {
1216
+ if ( !call ) {
1217
+ LOGGER_WARNING("Session: %p Handling 'start' on no call");
1218
+ return 0;
1219
+ }
1220
+
1221
+ LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx);
1222
+
1223
+ invoke_callback(session, call->call_idx, MSI_OnReject);
1224
+
1225
+ pthread_mutex_lock(&session->mutex);
1226
+
1227
+ send_reponse(session, call, ending, msg->friend_id);
1228
+ terminate_call(session, call);
1229
+
1230
+ pthread_mutex_unlock(&session->mutex);
1231
+
1232
+ return 1;
1233
+ }
1234
+
1235
+ static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg )
1236
+ {
1237
+ if ( !call ) {
1238
+ LOGGER_WARNING("Session: %p Handling 'start' on no call");
1239
+ return 0;
1240
+ }
1241
+
1242
+ (void)msg;
1243
+
1244
+ LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx);
1245
+
1246
+ invoke_callback(session, call->call_idx, MSI_OnCancel);
1247
+
1248
+ pthread_mutex_lock(&session->mutex);
1249
+
1250
+ terminate_call ( session, call );
1251
+
1252
+ pthread_mutex_unlock(&session->mutex);
1253
+
1254
+ return 1;
1255
+ }
1256
+
1257
+ static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
1258
+ {
1259
+ if ( !call ) {
1260
+ LOGGER_WARNING("Session: %p Handling 'start' on no call");
1261
+ return 0;
1262
+ }
1263
+
1264
+ LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx);
1265
+
1266
+ invoke_callback(session, call->call_idx, MSI_OnEnd);
1267
+ pthread_mutex_lock(&session->mutex);
1268
+
1269
+ send_reponse(session, call, ending, msg->friend_id);
1270
+ terminate_call ( session, call );
1271
+
1272
+ pthread_mutex_unlock(&session->mutex);
1273
+
1274
+
1275
+ return 1;
1276
+ }
1277
+
1278
+ /********** Response handlers **********/
1279
+ static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
1280
+ {
1281
+ if ( !call ) {
1282
+ LOGGER_WARNING("Session: %p Handling 'start' on no call");
1283
+ return 0;
1284
+ }
1285
+
1286
+ (void)msg;
1287
+
1288
+ pthread_mutex_lock(&session->mutex);
1289
+
1290
+ if ( call->ringing_timer_id ) {
1291
+ LOGGER_WARNING("Call already ringing");
1292
+ pthread_mutex_unlock(&session->mutex);
1293
+ return 0;
1294
+ }
1295
+
1296
+ LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
1297
+
1298
+ call->ringing_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx,
1299
+ call->ringing_tout_ms );
1300
+
1301
+ pthread_mutex_unlock(&session->mutex);
1302
+
1303
+ invoke_callback(session, call->call_idx, MSI_OnRinging);
1304
+ return 1;
1305
+ }
1306
+ static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg )
1307
+ {
1308
+ if ( !call ) {
1309
+ LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call");
1310
+ return 0;
1311
+ }
1312
+
1313
+ pthread_mutex_lock(&session->mutex);
1314
+
1315
+ if ( call->state == call_active ) { /* Change media */
1316
+
1317
+ LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx );
1318
+ pthread_mutex_unlock(&session->mutex);
1319
+
1320
+ invoke_callback(session, call->call_idx, MSI_OnMediaChange);
1321
+
1322
+ } else if ( call->state == call_inviting ) {
1323
+ LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx );
1324
+
1325
+ call->state = call_active;
1326
+
1327
+ MSIMessage *msg_start = msi_new_message ( TypeRequest, start );
1328
+ send_message ( session, call, msg_start, msg->friend_id );
1329
+ free ( msg_start );
1330
+
1331
+
1332
+ flush_peer_csettings ( call, msg, 0 );
1333
+
1334
+ /* This is here in case of glare */
1335
+ timer_release ( session->timer_handler, call->ringing_timer_id, 1 );
1336
+
1337
+ pthread_mutex_unlock(&session->mutex);
1338
+
1339
+ invoke_callback(session, call->call_idx, MSI_OnStarting);
1340
+ } else {
1341
+ LOGGER_ERROR("Invalid call state");
1342
+ terminate_call(session, call );
1343
+ pthread_mutex_unlock(&session->mutex);
1344
+ return 0;
1345
+ }
1346
+
1347
+ return 1;
1348
+ }
1349
+ static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg )
1350
+ {
1351
+ if ( !call ) {
1352
+ LOGGER_WARNING("Session: %p Handling 'start' on no call");
1353
+ return 0;
1354
+ }
1355
+
1356
+ (void)msg;
1357
+
1358
+ LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx );
1359
+
1360
+ invoke_callback(session, call->call_idx, MSI_OnEnding);
1361
+
1362
+ /* Terminate call */
1363
+ pthread_mutex_lock(&session->mutex);
1364
+ terminate_call ( session, call );
1365
+ pthread_mutex_unlock(&session->mutex);
1366
+
1367
+ return 1;
1368
+ }
1369
+ static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg )
1370
+ {
1371
+
1372
+ if ( !call ) {
1373
+ LOGGER_WARNING("Handling 'error' on non-existing call!");
1374
+ pthread_mutex_unlock(&session->mutex);
1375
+ return -1;
1376
+ }
1377
+
1378
+ LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx );
1379
+
1380
+ invoke_callback(session, call->call_idx, MSI_OnEnding);
1381
+
1382
+ pthread_mutex_lock(&session->mutex);
1383
+
1384
+ /* Handle error accordingly */
1385
+ if ( msg->reason.exists ) {
1386
+ /* TODO */
1387
+ }
1388
+
1389
+ terminate_call ( session, call );
1390
+
1391
+ pthread_mutex_unlock(&session->mutex);
1392
+
1393
+ return 1;
1394
+ }
1395
+
1396
+
1397
+ /**
1398
+ * @brief BASIC call flow:
1399
+ *
1400
+ * ALICE BOB
1401
+ * | invite --> |
1402
+ * | |
1403
+ * | <-- ringing |
1404
+ * | |
1405
+ * | <-- starting |
1406
+ * | |
1407
+ * | start --> |
1408
+ * | |
1409
+ * | <-- MEDIA TRANS --> |
1410
+ * | |
1411
+ * | end --> |
1412
+ * | |
1413
+ * | <-- ending |
1414
+ *
1415
+ * Alice calls Bob by sending invite packet.
1416
+ * Bob recvs the packet and sends an ringing packet;
1417
+ * which notifies Alice that her invite is acknowledged.
1418
+ * Ringing screen shown on both sides.
1419
+ * Bob accepts the invite for a call by sending starting packet.
1420
+ * Alice recvs the starting packet and sends the started packet to
1421
+ * inform Bob that she recved the starting packet.
1422
+ * Now the media transmission is established ( i.e. RTP transmission ).
1423
+ * Alice hangs up and sends end packet.
1424
+ * Bob recves the end packet and sends ending packet
1425
+ * as the acknowledgement that the call is ending.
1426
+ *
1427
+ *
1428
+ */
1429
+ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t *data, uint16_t length, void *object )
1430
+ {
1431
+ LOGGER_DEBUG("Got msi message");
1432
+ /* Unused */
1433
+ (void)messenger;
1434
+
1435
+ MSISession *session = object;
1436
+ MSIMessage *msg;
1437
+
1438
+ if ( !length ) {
1439
+ LOGGER_WARNING("Length param negative");
1440
+ return;
1441
+ }
1442
+
1443
+ msg = parse_recv ( data, length );
1444
+
1445
+ if ( !msg ) {
1446
+ LOGGER_WARNING("Error parsing message");
1447
+ return;
1448
+ } else {
1449
+ LOGGER_DEBUG("Successfully parsed message");
1450
+ }
1451
+
1452
+ msg->friend_id = source;
1453
+
1454
+
1455
+ /* Find what call */
1456
+ MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL;
1457
+
1458
+ /* Now handle message */
1459
+
1460
+ if ( msg->request.exists ) { /* Handle request */
1461
+
1462
+ switch (msg->request.value) {
1463
+ case invite:
1464
+ handle_recv_invite ( session, call, msg );
1465
+ break;
1466
+
1467
+ case start:
1468
+ handle_recv_start ( session, call, msg );
1469
+ break;
1470
+
1471
+ case cancel:
1472
+ handle_recv_cancel ( session, call, msg );
1473
+ break;
1474
+
1475
+ case reject:
1476
+ handle_recv_reject ( session, call, msg );
1477
+ break;
1478
+
1479
+ case end:
1480
+ handle_recv_end ( session, call, msg );
1481
+ break;
1482
+ }
1483
+
1484
+ } else if ( msg->response.exists ) { /* Handle response */
1485
+
1486
+ /* Got response so cancel timer */
1487
+ if ( call ) timer_release ( session->timer_handler, call->request_timer_id, 1 );
1488
+
1489
+ switch (msg->response.value) {
1490
+ case ringing:
1491
+ handle_recv_ringing ( session, call, msg );
1492
+ break;
1493
+
1494
+ case starting:
1495
+ handle_recv_starting ( session, call, msg );
1496
+ break;
1497
+
1498
+ case ending:
1499
+ handle_recv_ending ( session, call, msg );
1500
+ break;
1501
+
1502
+ case error:
1503
+ handle_recv_error ( session, call, msg );
1504
+ break;
1505
+ }
1506
+
1507
+ } else {
1508
+ LOGGER_WARNING("Invalid message: no resp nor requ headers");
1509
+ }
1510
+
1511
+ free ( msg );
1512
+ }
1513
+
1514
+
1515
+ /**
1516
+ * @brief Callback setter.
1517
+ *
1518
+ * @param callback The callback.
1519
+ * @param id The id.
1520
+ * @return void
1521
+ */
1522
+ void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1523
+ {
1524
+ session->callbacks[id].function = callback;
1525
+ session->callbacks[id].data = userdata;
1526
+ }
1527
+
1528
+
1529
+ /**
1530
+ * @brief Start the control session.
1531
+ *
1532
+ * @param messenger Tox* object.
1533
+ * @param max_calls Amount of calls possible
1534
+ * @return MSISession* The created session.
1535
+ * @retval NULL Error occurred.
1536
+ */
1537
+ MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls )
1538
+ {
1539
+ if (messenger == NULL) {
1540
+ LOGGER_ERROR("Could not init session on empty messenger!");
1541
+ return NULL;
1542
+ }
1543
+
1544
+ TimerHandler *handler = timer_init_session(max_calls * 10, 10000);
1545
+
1546
+ if ( !max_calls || !handler ) {
1547
+ LOGGER_WARNING("Invalid max call treshold or timer handler initialization failed!");
1548
+ return NULL;
1549
+ }
1550
+
1551
+ MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
1552
+
1553
+ if (retu == NULL) {
1554
+ LOGGER_ERROR("Allocation failed! Program might misbehave!");
1555
+ timer_terminate_session(handler);
1556
+ return NULL;
1557
+ }
1558
+
1559
+ retu->messenger_handle = messenger;
1560
+ retu->agent_handler = NULL;
1561
+ retu->timer_handler = handler;
1562
+
1563
+ if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) {
1564
+ LOGGER_ERROR("Allocation failed! Program might misbehave!");
1565
+ timer_terminate_session(handler);
1566
+ free(retu);
1567
+ return NULL;
1568
+ }
1569
+
1570
+ retu->max_calls = max_calls;
1571
+
1572
+ retu->frequ = 10000; /* default value? */
1573
+ retu->call_timeout = 30000; /* default value? */
1574
+
1575
+
1576
+ m_callback_msi_packet(messenger, msi_handle_packet, retu );
1577
+
1578
+ /* This is called when remote terminates session */
1579
+ m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu);
1580
+
1581
+ pthread_mutex_init(&retu->mutex, NULL);
1582
+
1583
+ LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls);
1584
+ return retu;
1585
+ }
1586
+
1587
+
1588
+ /**
1589
+ * @brief Terminate control session.
1590
+ *
1591
+ * @param session The session
1592
+ * @return int
1593
+ */
1594
+ int msi_terminate_session ( MSISession *session )
1595
+ {
1596
+ if (session == NULL) {
1597
+ LOGGER_ERROR("Tried to terminate non-existing session");
1598
+ return -1;
1599
+ }
1600
+
1601
+ pthread_mutex_lock(&session->mutex);
1602
+ m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL);
1603
+ pthread_mutex_unlock(&session->mutex);
1604
+
1605
+ int _status = 0;
1606
+
1607
+ /* If have calls, cancel them */
1608
+ int32_t idx = 0;
1609
+
1610
+ for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) {
1611
+ /* Cancel all? */
1612
+ uint16_t _it = 0;
1613
+ /*for ( ; _it < session->calls[idx]->peer_count; _it++ )
1614
+ * FIXME: will not work on multiple peers, must cancel call for all peers
1615
+ */
1616
+ msi_cancel ( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" );
1617
+ }
1618
+
1619
+ timer_terminate_session(session->timer_handler);
1620
+
1621
+ pthread_mutex_destroy(&session->mutex);
1622
+
1623
+ LOGGER_DEBUG("Terminated session: %p", session);
1624
+ free ( session );
1625
+ return _status;
1626
+ }
1627
+
1628
+
1629
+ /**
1630
+ * @brief Send invite request to friend_id.
1631
+ *
1632
+ * @param session Control session.
1633
+ * @param call_type Type of the call. Audio or Video(both audio and video)
1634
+ * @param rngsec Ringing timeout.
1635
+ * @param friend_id The friend.
1636
+ * @return int
1637
+ */
1638
+ int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec, uint32_t friend_id )
1639
+ {
1640
+ pthread_mutex_lock(&session->mutex);
1641
+
1642
+ LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
1643
+
1644
+
1645
+ int i = 0;
1646
+
1647
+ for (; i < session->max_calls; i ++)
1648
+ if (session->calls[i] && session->calls[i]->peers[0] == friend_id) {
1649
+ LOGGER_ERROR("Already in a call with friend %d", friend_id);
1650
+ pthread_mutex_unlock(&session->mutex);
1651
+ return -1;
1652
+ }
1653
+
1654
+
1655
+ MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */
1656
+
1657
+ if ( !call ) {
1658
+ pthread_mutex_unlock(&session->mutex);
1659
+ LOGGER_ERROR("Cannot handle more calls");
1660
+ return -1;
1661
+ }
1662
+
1663
+ *call_index = call->call_idx;
1664
+
1665
+ t_randomstr ( call->id, sizeof(call->id) );
1666
+
1667
+ add_peer ( call, friend_id );
1668
+
1669
+ call->csettings_local = csettings;
1670
+
1671
+ MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1672
+
1673
+ msi_msg_set_csettings(msg_invite, &csettings);
1674
+ send_message ( session, call, msg_invite, friend_id );
1675
+ free( msg_invite );
1676
+
1677
+ call->state = call_inviting;
1678
+
1679
+ call->request_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, m_deftout );
1680
+
1681
+ LOGGER_DEBUG("Invite sent");
1682
+
1683
+ pthread_mutex_unlock(&session->mutex);
1684
+
1685
+ return 0;
1686
+ }
1687
+
1688
+
1689
+ /**
1690
+ * @brief Hangup active call.
1691
+ *
1692
+ * @param session Control session.
1693
+ * @param call_id To which call is this action handled.
1694
+ * @return int
1695
+ * @retval -1 Error occurred.
1696
+ * @retval 0 Success.
1697
+ */
1698
+ int msi_hangup ( MSISession *session, int32_t call_index )
1699
+ {
1700
+ pthread_mutex_lock(&session->mutex);
1701
+ LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index);
1702
+
1703
+ if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1704
+ LOGGER_ERROR("Invalid call index!");
1705
+ pthread_mutex_unlock(&session->mutex);
1706
+ return -1;
1707
+ }
1708
+
1709
+ if ( session->calls[call_index]->state != call_active ) {
1710
+ LOGGER_ERROR("No call with such index or call is not active!");
1711
+ pthread_mutex_unlock(&session->mutex);
1712
+ return -1;
1713
+ }
1714
+
1715
+ MSIMessage *msg_end = msi_new_message ( TypeRequest, end );
1716
+
1717
+ /* hangup for each peer */
1718
+ int it = 0;
1719
+
1720
+ for ( ; it < session->calls[call_index]->peer_count; it ++ )
1721
+ send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1722
+
1723
+ session->calls[call_index]->state = call_hanged_up;
1724
+
1725
+ free ( msg_end );
1726
+
1727
+ session->calls[call_index]->request_timer_id =
1728
+ timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout );
1729
+
1730
+ pthread_mutex_unlock(&session->mutex);
1731
+ return 0;
1732
+ }
1733
+
1734
+
1735
+ /**
1736
+ * @brief Answer active call request.
1737
+ *
1738
+ * @param session Control session.
1739
+ * @param call_id To which call is this action handled.
1740
+ * @param call_type Answer with Audio or Video(both).
1741
+ * @return int
1742
+ */
1743
+ int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings )
1744
+ {
1745
+ pthread_mutex_lock(&session->mutex);
1746
+ LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1747
+
1748
+ if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1749
+ LOGGER_ERROR("Invalid call index!");
1750
+ pthread_mutex_unlock(&session->mutex);
1751
+ return -1;
1752
+ }
1753
+
1754
+ MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting );
1755
+
1756
+ session->calls[call_index]->csettings_local = csettings;
1757
+
1758
+ msi_msg_set_csettings(msg_starting, &csettings);
1759
+
1760
+ send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] );
1761
+ free ( msg_starting );
1762
+
1763
+ session->calls[call_index]->state = call_active;
1764
+
1765
+ pthread_mutex_unlock(&session->mutex);
1766
+ return 0;
1767
+ }
1768
+
1769
+
1770
+ /**
1771
+ * @brief Cancel request.
1772
+ *
1773
+ * @param session Control session.
1774
+ * @param call_id To which call is this action handled.
1775
+ * @param reason Set optional reason header. Pass NULL if none.
1776
+ * @return int
1777
+ */
1778
+ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason )
1779
+ {
1780
+ pthread_mutex_lock(&session->mutex);
1781
+ LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1782
+
1783
+ if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1784
+ LOGGER_ERROR("Invalid call index!");
1785
+ pthread_mutex_unlock(&session->mutex);
1786
+ return -1;
1787
+ }
1788
+
1789
+ MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel );
1790
+
1791
+ /* FIXME */
1792
+ #if 0
1793
+
1794
+ if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1795
+ MSIReasonStrType reason_cast;
1796
+ memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1797
+ memcpy(reason_cast, reason, strlen(reason));
1798
+ msi_msg_set_reason(msg_cancel, reason_cast);
1799
+ }
1800
+
1801
+ #else
1802
+ (void)reason;
1803
+
1804
+ #endif
1805
+
1806
+ send_message ( session, session->calls[call_index], msg_cancel, peer );
1807
+ free ( msg_cancel );
1808
+
1809
+ terminate_call ( session, session->calls[call_index] );
1810
+ pthread_mutex_unlock(&session->mutex);
1811
+
1812
+ return 0;
1813
+ }
1814
+
1815
+
1816
+ /**
1817
+ * @brief Reject request.
1818
+ *
1819
+ * @param session Control session.
1820
+ * @param call_id To which call is this action handled.
1821
+ * @return int
1822
+ */
1823
+ int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1824
+ {
1825
+ pthread_mutex_lock(&session->mutex);
1826
+ LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1827
+
1828
+ if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1829
+ LOGGER_ERROR("Invalid call index!");
1830
+ pthread_mutex_unlock(&session->mutex);
1831
+ return -1;
1832
+ }
1833
+
1834
+ MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject );
1835
+
1836
+ /* FIXME */
1837
+ #if 0
1838
+
1839
+ if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1840
+ MSIReasonStrType reason_cast;
1841
+ memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1842
+ memcpy(reason_cast, reason, strlen(reason));
1843
+ msi_msg_set_reason(msg_reject, reason_cast);
1844
+ }
1845
+
1846
+ #else
1847
+ (void)reason;
1848
+
1849
+ #endif
1850
+
1851
+ send_message ( session, session->calls[call_index], msg_reject,
1852
+ session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] );
1853
+ free ( msg_reject );
1854
+
1855
+ session->calls[call_index]->state = call_hanged_up;
1856
+ session->calls[call_index]->request_timer_id =
1857
+ timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout );
1858
+
1859
+ pthread_mutex_unlock(&session->mutex);
1860
+ return 0;
1861
+ }
1862
+
1863
+
1864
+ /**
1865
+ * @brief Send invite request to friend_id.
1866
+ *
1867
+ * @param session Control session.
1868
+ * @param call_index Call index.
1869
+ * @param call_type Type of the call. Audio or Video(both audio and video)
1870
+ * @param rngsec Ringing timeout.
1871
+ * @param friend_id The friend.
1872
+ * @return int
1873
+ */
1874
+ int msi_change_csettings(MSISession *session, int32_t call_index, MSICSettings csettings)
1875
+ {
1876
+ pthread_mutex_lock(&session->mutex);
1877
+
1878
+ LOGGER_DEBUG("Changing media on call: %d", call_index);
1879
+
1880
+ if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1881
+ LOGGER_ERROR("Invalid call index!");
1882
+ pthread_mutex_unlock(&session->mutex);
1883
+ return -1;
1884
+ }
1885
+
1886
+ MSICall *call = session->calls[call_index];
1887
+
1888
+ if ( call->state != call_active ) {
1889
+ LOGGER_ERROR("Call is not active!");
1890
+ pthread_mutex_unlock(&session->mutex);
1891
+ return -1;
1892
+ }
1893
+
1894
+ MSICSettings *local = &call->csettings_local;
1895
+
1896
+ if (
1897
+ local->call_type == csettings.call_type &&
1898
+ local->video_bitrate == csettings.video_bitrate &&
1899
+ local->max_video_width == csettings.max_video_width &&
1900
+ local->max_video_height == csettings.max_video_height &&
1901
+ local->audio_bitrate == csettings.audio_bitrate &&
1902
+ local->audio_frame_duration == csettings.audio_frame_duration &&
1903
+ local->audio_sample_rate == csettings.audio_sample_rate &&
1904
+ local->audio_channels == csettings.audio_channels ) {
1905
+ LOGGER_ERROR("Call is already set accordingly!");
1906
+ pthread_mutex_unlock(&session->mutex);
1907
+ return -1;
1908
+ }
1909
+
1910
+ *local = csettings;
1911
+
1912
+ MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1913
+
1914
+ msi_msg_set_csettings ( msg_invite, local );
1915
+ send_message ( session, call, msg_invite, call->peers[0] );
1916
+ free ( msg_invite );
1917
+
1918
+ LOGGER_DEBUG("Request for media change sent");
1919
+
1920
+ pthread_mutex_unlock(&session->mutex);
1921
+
1922
+ return 0;
1923
+ }
1924
+
1925
+
1926
+ /**
1927
+ * @brief Terminate the current call.
1928
+ *
1929
+ * @param session Control session.
1930
+ * @param call_id To which call is this action handled.
1931
+ * @return int
1932
+ */
1933
+ int msi_stopcall ( MSISession *session, int32_t call_index )
1934
+ {
1935
+ pthread_mutex_lock(&session->mutex);
1936
+ LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index);
1937
+
1938
+ if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1939
+ pthread_mutex_unlock(&session->mutex);
1940
+ return -1;
1941
+ }
1942
+
1943
+ /* just terminate it */
1944
+
1945
+ terminate_call ( session, session->calls[call_index] );
1946
+
1947
+ pthread_mutex_unlock(&session->mutex);
1948
+ return 0;
1949
+ }