summaryrefslogtreecommitdiff
path: root/toxav/msi.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxav/msi.c')
-rw-r--r--toxav/msi.c2083
1 files changed, 633 insertions, 1450 deletions
diff --git a/toxav/msi.c b/toxav/msi.c
index dcb7b62a..d3559160 100644
--- a/toxav/msi.c
+++ b/toxav/msi.c
@@ -1,6 +1,6 @@
1/** msi.c 1/** msi.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -33,163 +33,336 @@
33#include <string.h> 33#include <string.h>
34#include <stdlib.h> 34#include <stdlib.h>
35#include <stdbool.h> 35#include <stdbool.h>
36#include <assert.h>
36 37
37#define MSI_MAXMSG_SIZE 256 38#define MSI_MAXMSG_SIZE 256
38 39
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/** 40/**
46 * Protocol: 41 * Protocol:
47 * 42 *
48 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}| 43 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|
49 */ 44 */
50 45
51typedef uint8_t MSIRawCSettingsType[23];
52
53typedef enum { 46typedef enum {
54 IDRequest = 1, 47 IDRequest = 1,
55 IDResponse, 48 IDError,
56 IDReason, 49 IDCapabilities,
57 IDCallId, 50 IDVFPSZ,
58 IDCSettings,
59 51
60} MSIHeaderID; 52} MSIHeaderID;
61 53
62typedef enum {
63 TypeRequest,
64 TypeResponse,
65
66} MSIMessageType;
67 54
68typedef enum { 55typedef enum {
69 invite, 56 requ_push,
70 start, 57 requ_pop,
71 cancel,
72 reject,
73 end,
74
75} MSIRequest; 58} MSIRequest;
76 59
77typedef enum {
78 ringing,
79 starting,
80 ending,
81 error
82
83} MSIResponse;
84
85 60
86#define GENERIC_HEADER(header, val_type) \ 61#define GENERIC_HEADER(header, val_type) \
87typedef struct _MSIHeader##header { \ 62typedef struct { \
88val_type value; \ 63 val_type value; \
89_Bool exists; \ 64 bool exists; \
90} MSIHeader##header; 65} MSIHeader##header
91
92
93GENERIC_HEADER ( Request, MSIRequest )
94GENERIC_HEADER ( Response, MSIResponse )
95GENERIC_HEADER ( CallId, MSICallIDType )
96GENERIC_HEADER ( Reason, MSIReasonStrType )
97GENERIC_HEADER ( CSettings, MSIRawCSettingsType )
98
99 66
100typedef struct _MSIMessage {
101 67
102 MSIHeaderRequest request; 68GENERIC_HEADER ( Request, MSIRequest );
103 MSIHeaderResponse response; 69GENERIC_HEADER ( Error, MSIError );
104 MSIHeaderReason reason; 70GENERIC_HEADER ( Capabilities, uint8_t );
105 MSIHeaderCallId callid; 71GENERIC_HEADER ( VFPSZ, uint16_t );
106 MSIHeaderCSettings csettings;
107 72
108 int friend_id;
109 73
74typedef struct {
75 MSIHeaderRequest request;
76 MSIHeaderError error;
77 MSIHeaderCapabilities capabilities;
78 MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order TODO: get rid of this eventually */
110} MSIMessage; 79} MSIMessage;
111 80
112 81
113static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) 82void msg_init (MSIMessage *dest, MSIRequest request);
114{ 83int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length );
115 if ( s->callbacks[i].first ) { 84uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length );
116 LOGGER_DEBUG("Invoking callback function: %d", i); 85int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg );
86int send_error ( Messenger* m, uint32_t friend_number, MSIError error );
87static int invoke_callback(MSICall* call, MSICallbackID cb);
88static MSICall *get_call ( MSISession *session, uint32_t friend_number );
89MSICall *new_call ( MSISession *session, uint32_t friend_number );
90void kill_call ( MSICall *call );
91void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
92void handle_push ( MSICall *call, const MSIMessage *msg );
93void handle_pop ( MSICall *call, const MSIMessage *msg );
94void handle_msi_packet ( Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object );
117 95
118 s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second );
119 }
120}
121 96
122/** 97/**
123 * Parse raw 'data' received from socket into MSIMessage struct. 98 * Public functions
124 * Every message has to have end value of 'end_byte' or _undefined_ behavior
125 * occures. The best practice is to check the end of the message at the handle_packet.
126 */ 99 */
127static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) 100void msi_register_callback ( MSISession* session, msi_action_cb* callback, MSICallbackID id)
128{ 101{
129 102 pthread_mutex_lock(session->mutex);
130#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; } 103 session->callbacks[id] = callback;
131#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; } 104 pthread_mutex_unlock(session->mutex);
132#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; } 105}
133 106MSISession *msi_new ( Messenger *m )
134 if ( msg == NULL ) { 107{
135 LOGGER_ERROR("Could not parse message: no storage!"); 108 if (m == NULL) {
109 LOGGER_ERROR("Could not init session on empty messenger!");
110 return NULL;
111 }
112
113 MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
114
115 if (retu == NULL) {
116 LOGGER_ERROR("Allocation failed! Program might misbehave!");
117 return NULL;
118 }
119
120 if (create_recursive_mutex(retu->mutex) != 0) {
121 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
122 free(retu);
123 return NULL;
124 }
125
126 retu->messenger = m;
127
128 m_callback_msi_packet(m, handle_msi_packet, retu );
129
130 /* This is called when remote terminates session */
131 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
132
133 LOGGER_DEBUG("New msi session: %p ", retu);
134 return retu;
135}
136int msi_kill ( MSISession *session )
137{
138 if (session == NULL) {
139 LOGGER_ERROR("Tried to terminate non-existing session");
140 return -1;
141 }
142
143 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
144 pthread_mutex_lock(session->mutex);
145
146 if (session->calls) {
147 MSIMessage msg;
148 msg_init(&msg, requ_pop);
149
150 MSICall* it = get_call(session, session->calls_head);
151 for (; it; it = it->next) {
152 send_message(session->messenger, it->friend_number, &msg);
153 kill_call(it); /* This will eventually free session->calls */
154 }
155 }
156
157 pthread_mutex_unlock(session->mutex);
158 pthread_mutex_destroy(session->mutex);
159
160 LOGGER_DEBUG("Terminated session: %p", session);
161 free ( session );
162 return 0;
163}
164int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities )
165{
166 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
167
168 pthread_mutex_lock(session->mutex);
169 if (get_call(session, friend_number) != NULL) {
170 LOGGER_ERROR("Already in a call");
171 pthread_mutex_unlock(session->mutex);
172 return -1;
173 }
174
175 (*call) = new_call ( session, friend_number );
176
177 if ( *call == NULL ) {
178 pthread_mutex_unlock(session->mutex);
179 return -1;
180 }
181
182 (*call)->self_capabilities = capabilities;
183
184 MSIMessage msg;
185 msg_init(&msg, requ_push);
186
187 msg.capabilities.exists = true;
188 msg.capabilities.value = capabilities;
189
190 msg.vfpsz.exists = true;
191 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
192
193 send_message ( (*call)->session->messenger, (*call)->friend_number, &msg );
194
195 (*call)->state = msi_CallRequesting;
196
197 LOGGER_DEBUG("Invite sent");
198 pthread_mutex_unlock(session->mutex);
199 return 0;
200}
201int msi_hangup ( MSICall* call )
202{
203 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number);
204
205 MSISession* session = call->session;
206 pthread_mutex_lock(session->mutex);
207
208 if ( call->state == msi_CallInactive ) {
209 LOGGER_ERROR("Call is in invalid state!");
210 pthread_mutex_unlock(session->mutex);
136 return -1; 211 return -1;
137 } 212 }
213
214 MSIMessage msg;
215 msg_init(&msg, requ_pop);
216
217 send_message ( session->messenger, call->friend_number, &msg );
218
219 kill_call(call);
220 pthread_mutex_unlock(session->mutex);
221 return 0;
222}
223int msi_answer ( MSICall* call, uint8_t capabilities )
224{
225 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
226
227 MSISession* session = call->session;
228 pthread_mutex_lock(session->mutex);
229
230 if ( call->state != msi_CallRequested ) {
231 /* Though sending in invalid state will not cause anything wierd
232 * Its better to not do it like a maniac */
233 LOGGER_ERROR("Call is in invalid state!");
234 pthread_mutex_unlock(session->mutex);
235 return -1;
236 }
237
238 call->self_capabilities = capabilities;
239
240 MSIMessage msg;
241 msg_init(&msg, requ_push);
242
243 msg.capabilities.exists = true;
244 msg.capabilities.value = capabilities;
245
246 msg.vfpsz.exists = true;
247 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
248
249 send_message ( session->messenger, call->friend_number, &msg );
250
251 call->state = msi_CallActive;
252 pthread_mutex_unlock(session->mutex);
253
254 return 0;
255}
256int msi_change_capabilities( MSICall* call, uint8_t capabilities )
257{
258 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
259
260 MSISession* session = call->session;
261 pthread_mutex_lock(session->mutex);
262
263 if ( call->state != msi_CallActive ) {
264 /* Sending capabilities change can cause error on other side if
265 * the call is not active since we don't send header 'vfpsz'.
266 * If we were to send 'vfpsz' while call is active it would be
267 * ignored. However, if call is not active peer will expect
268 * the said header on 'push' so that it could handle the call
269 * like new. TODO: explain this better
270 */
271 LOGGER_ERROR("Call is in invalid state!");
272 pthread_mutex_unlock(session->mutex);
273 return -1;
274 }
275
276 call->self_capabilities = capabilities;
277
278 MSIMessage msg;
279 msg_init(&msg, requ_push);
280
281 msg.capabilities.exists = true;
282 msg.capabilities.value = capabilities;
283
284 send_message ( call->session->messenger, call->friend_number, &msg );
285
286 pthread_mutex_unlock(session->mutex);
287 return 0;
288}
289
138 290
139 if ( data[length - 1] ) { /* End byte must have value 0 */ 291/**
292 * Private functions
293 */
294void msg_init(MSIMessage* dest, MSIRequest request)
295{
296 memset(dest, 0, sizeof(*dest));
297 dest->request.exists = true;
298 dest->request.value = request;
299}
300int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
301{
302 /* Parse raw data received from socket into MSIMessage struct */
303
304#define CHECK_SIZE(bytes, constraint, size) \
305 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
306 if ( bytes[1] != size ) { LOGGER_ERROR("Invalid data size!"); return -1; }
307
308#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
309 if ( bytes[2] > enum_high ) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
310
311#define SET_UINT8(bytes, header) do { \
312 header.value = bytes[2]; \
313 header.exists = true; \
314 bytes += 3; \
315 } while(0)
316
317#define SET_UINT16(bytes, header) do { \
318 memcpy(&header.value, bytes + 2, 2);\
319 header.exists = true; \
320 bytes += 4; \
321 } while(0)
322
323
324 assert(dest);
325
326 if ( length == 0 || data[length - 1] ) { /* End byte must have value 0 */
140 LOGGER_ERROR("Invalid end byte"); 327 LOGGER_ERROR("Invalid end byte");
141 return -1; 328 return -1;
142 } 329 }
143 330
331 memset(dest, 0, sizeof(*dest));
332
144 const uint8_t *it = data; 333 const uint8_t *it = data;
145 int size_constraint = length; 334 int size_constraint = length;
146 335
147 while ( *it ) {/* until end byte is hit */ 336 while ( *it ) {/* until end byte is hit */
148 switch (*it) { 337 switch (*it) {
149 case IDRequest: 338 case IDRequest:
150 FAIL_CONSTRAINT(size_constraint, 3); 339 CHECK_SIZE(it, size_constraint, 1);
151 FAIL_SIZE(it[1], 1); 340 CHECK_ENUM_HIGH(it, requ_pop);
152// FAIL_LIMITS(it[2], invite, end); 341 SET_UINT8(it, dest->request);
153 FAIL_LIMITS(it[2], end);
154 msg->request.value = it[2];
155 it += 3;
156 msg->request.exists = 1;
157 break; 342 break;
158 343
159 case IDResponse: 344 case IDError:
160 FAIL_CONSTRAINT(size_constraint, 3); 345 CHECK_SIZE(it, size_constraint, 1);
161 FAIL_SIZE(it[1], 1); 346 CHECK_ENUM_HIGH(it, msi_EUndisclosed);
162// FAIL_LIMITS(it[2], ringing, error); 347 SET_UINT8(it, dest->error);
163 FAIL_LIMITS(it[2], error);
164 msg->response.value = it[2];
165 it += 3;
166 msg->response.exists = 1;
167 break;
168
169 case IDCallId:
170 FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2);
171 FAIL_SIZE(it[1], sizeof(MSICallIDType));
172 memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType));
173 it += sizeof(MSICallIDType) + 2;
174 msg->callid.exists = 1;
175 break; 348 break;
176 349
177 case IDReason: 350 case IDCapabilities:
178 FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2); 351 CHECK_SIZE(it, size_constraint, 1);
179 FAIL_SIZE(it[1], sizeof(MSIReasonStrType)); 352 SET_UINT8(it, dest->capabilities);
180 memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType));
181 it += sizeof(MSIReasonStrType) + 2;
182 msg->reason.exists = 1;
183 break; 353 break;
184 354
185 case IDCSettings: 355 case IDVFPSZ:
186 FAIL_CONSTRAINT(size_constraint, sizeof(MSIRawCSettingsType) + 2); 356 CHECK_SIZE(it, size_constraint, 2);
187 FAIL_SIZE(it[1], sizeof(MSIRawCSettingsType)); 357 SET_UINT16(it, dest->vfpsz);
188 memcpy(msg->csettings.value, it + 2, sizeof(MSIRawCSettingsType)); 358 dest->vfpsz.value = ntohs(dest->vfpsz.value);
189 it += sizeof(MSIRawCSettingsType) + 2; 359
190 msg->csettings.exists = 1; 360 if (dest->vfpsz.value > 1200) {
361 LOGGER_ERROR("Invalid vfpsz param");
362 return -1;
363 }
191 break; 364 break;
192 365
193 default: 366 default:
194 LOGGER_ERROR("Invalid id byte"); 367 LOGGER_ERROR("Invalid id byte");
195 return -1; 368 return -1;
@@ -197,80 +370,25 @@ static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t lengt
197 } 370 }
198 } 371 }
199 372
200 return 0; 373 if (dest->request.exists == false) {
201} 374 LOGGER_ERROR("Invalid request field!");
202 375 return -1;
203/**
204 * Create the message.
205 */
206MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value )
207{
208 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
209
210 if ( retu == NULL ) {
211 LOGGER_WARNING("Allocation failed! Program might misbehave!");
212 return NULL;
213 }
214
215 if ( type == TypeRequest ) {
216 retu->request.exists = 1;
217 retu->request.value = type_value;
218
219 } else {
220 retu->response.exists = 1;
221 retu->response.value = type_value;
222 }
223
224 return retu;
225}
226
227
228/**
229 * Parse data from handle_packet.
230 */
231MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
232{
233 if ( data == NULL ) {
234 LOGGER_WARNING("Tried to parse empty message!");
235 return NULL;
236 }
237
238 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
239
240 if ( retu == NULL ) {
241 LOGGER_WARNING("Allocation failed! Program might misbehave!");
242 return NULL;
243 }
244
245 if ( parse_raw_data ( retu, data, length ) == -1 ) {
246
247 free ( retu );
248 return NULL;
249 } 376 }
377
378 return 0;
250 379
251 return retu; 380#undef CHECK_SIZE
381#undef CHECK_ENUM_HIGH
382#undef SET_UINT8
383#undef SET_UINT16
252} 384}
253 385uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length )
254
255/**
256 * Speaks for itself.
257 */
258uint8_t *format_output ( uint8_t *dest,
259 MSIHeaderID id,
260 const void *value,
261 uint8_t value_len,
262 uint16_t *length )
263{ 386{
264 if ( dest == NULL ) { 387 /* Parse a single header for sending */
265 LOGGER_ERROR("No destination space!"); 388 assert(dest);
266 return NULL; 389 assert(value);
267 } 390 assert(value_len);
268 391
269 if (value == NULL || value_len == 0) {
270 LOGGER_ERROR("Empty header value");
271 return NULL;
272 }
273
274 *dest = id; 392 *dest = id;
275 dest ++; 393 dest ++;
276 *dest = value_len; 394 *dest = value_len;
@@ -282,521 +400,211 @@ uint8_t *format_output ( uint8_t *dest,
282 400
283 return dest + value_len; /* Set to next position ready to be written */ 401 return dest + value_len; /* Set to next position ready to be written */
284} 402}
285 403int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg )
286
287/**
288 * Parse MSIMessage to send.
289 */
290uint16_t parse_send ( MSIMessage *msg, uint8_t *dest )
291{ 404{
292 if (msg == NULL) { 405 /* Parse and send message */
293 LOGGER_ERROR("No message!"); 406 assert(m);
294 return 0; 407
295 } 408 uint8_t parsed [MSI_MAXMSG_SIZE];
296
297 if (dest == NULL ) {
298 LOGGER_ERROR("No destination!");
299 return 0;
300 }
301 409
302 uint8_t *it = dest; 410 uint8_t *it = parsed;
303 uint16_t size = 0; 411 uint16_t size = 0;
304 412
305 if (msg->request.exists) { 413 if (msg->request.exists) {
306 uint8_t cast = msg->request.value; 414 uint8_t cast = msg->request.value;
307 it = format_output(it, IDRequest, &cast, 1, &size); 415 it = msg_parse_header_out(IDRequest, it, &cast,
308 } 416 sizeof(cast), &size);
309 417 } else {
310 if (msg->response.exists) { 418 LOGGER_DEBUG("Must have request field");
311 uint8_t cast = msg->response.value;
312 it = format_output(it, IDResponse, &cast, 1, &size);
313 }
314
315 if (msg->callid.exists) {
316 it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size);
317 }
318
319 if (msg->reason.exists) {
320 it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
321 }
322
323 if (msg->csettings.exists) {
324 it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size);
325 }
326
327 *it = 0;
328 size ++;
329
330 return size;
331}
332
333void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
334{
335 if ( !msg ) return;
336
337 msg->reason.exists = 1;
338 memcpy(msg->reason.value, value, sizeof(MSIReasonStrType));
339}
340
341void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
342{
343 if ( !msg ) return;
344
345 msg->callid.exists = 1;
346 memcpy(msg->callid.value, value, sizeof(MSICallIDType));
347}
348
349void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value )
350{
351 if ( !msg ) return;
352
353 msg->csettings.exists = 1;
354
355 msg->csettings.value[0] = value->call_type;
356 uint8_t *iter = msg->csettings.value + 1;
357
358 /* Video bitrate */
359 uint32_t lval = htonl(value->video_bitrate);
360 memcpy(iter, &lval, 4);
361 iter += 4;
362
363 /* Video max width */
364 uint16_t sval = htons(value->max_video_width);
365 memcpy(iter, &sval, 2);
366 iter += 2;
367
368 /* Video max height */
369 sval = htons(value->max_video_height);
370 memcpy(iter, &sval, 2);
371 iter += 2;
372
373 /* Audio bitrate */
374 lval = htonl(value->audio_bitrate);
375 memcpy(iter, &lval, 4);
376 iter += 4;
377
378 /* Audio frame duration */
379 sval = htons(value->audio_frame_duration);
380 memcpy(iter, &sval, 2);
381 iter += 2;
382
383 /* Audio sample rate */
384 lval = htonl(value->audio_sample_rate);
385 memcpy(iter, &lval, 4);
386 iter += 4;
387
388 /* Audio channels */
389 lval = htonl(value->audio_channels);
390 memcpy(iter, &lval, 4);
391}
392
393void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest )
394{
395 if ( !msg || !dest || !msg->csettings.exists ) return;
396
397 dest->call_type = msg->csettings.value[0];
398 uint8_t *iter = msg->csettings.value + 1;
399
400 memcpy(&dest->video_bitrate, iter, 4);
401 iter += 4;
402 dest->video_bitrate = ntohl(dest->video_bitrate);
403
404 memcpy(&dest->max_video_width, iter, 2);
405 iter += 2;
406 dest->max_video_width = ntohs(dest->max_video_width);
407
408 memcpy(&dest->max_video_height, iter, 2);
409 iter += 2;
410 dest->max_video_height = ntohs(dest->max_video_height);
411
412 memcpy(&dest->audio_bitrate, iter, 4);
413 iter += 4;
414 dest->audio_bitrate = ntohl(dest->audio_bitrate);
415
416 memcpy(&dest->audio_frame_duration, iter, 2);
417 iter += 2;
418 dest->audio_frame_duration = ntohs(dest->audio_frame_duration);
419
420 memcpy(&dest->audio_sample_rate, iter, 4);
421 iter += 4;
422 dest->audio_sample_rate = ntohl(dest->audio_sample_rate);
423
424 memcpy(&dest->audio_channels, iter, 4);
425 dest->audio_channels = ntohl(dest->audio_channels);
426}
427
428typedef struct _Timer {
429 void (*func)(struct _Timer *);
430 uint64_t timeout;
431 MSISession *session;
432 int call_idx;
433 int id;
434
435} Timer;
436
437typedef struct _TimerHandler {
438 Timer **timers;
439
440 uint32_t max_capacity;
441 uint32_t size;
442} TimerHandler;
443
444
445static int timer_alloc (MSISession *session , void (*func)(Timer *), int call_idx, uint32_t timeout)
446{
447 static int timer_id;
448 TimerHandler *timer_handler = session->timer_handler;
449
450 uint32_t i = 0;
451
452 for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++);
453
454 if (i == timer_handler->max_capacity) {
455 LOGGER_WARNING("Maximum capacity reached!");
456 return -1;
457 }
458
459 Timer *timer = timer_handler->timers[i] = calloc(sizeof(Timer), 1);
460
461 if (timer == NULL) {
462 LOGGER_ERROR("Failed to allocate timer!");
463 return -1;
464 }
465
466 timer_handler->size ++;
467
468 timer->func = func;
469 timer->session = session;
470 timer->call_idx = call_idx;
471 timer->timeout = timeout + current_time_monotonic(); /* In ms */
472 ++timer_id;
473 timer->id = timer_id;
474
475 /* reorder */
476 if (i) {
477 int64_t j = i - 1;
478
479 for (; j >= 0 && timeout < timer_handler->timers[j]->timeout; j--) {
480 Timer *tmp = timer_handler->timers[j];
481 timer_handler->timers[j] = timer;
482 timer_handler->timers[j + 1] = tmp;
483 }
484 }
485
486 LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timer_handler->size);
487 return timer->id;
488}
489
490static int timer_release ( TimerHandler *timers_container, int id)
491{
492 Timer **timed_events = timers_container->timers;
493
494 uint32_t i;
495 int rc = -1;
496
497 for (i = 0; i < timers_container->max_capacity; ++i) {
498 if (timed_events[i] && timed_events[i]->id == id) {
499 rc = i;
500 break;
501 }
502 }
503
504 if (rc == -1) {
505 LOGGER_WARNING("No event with id: %d", id);
506 return -1; 419 return -1;
507 } 420 }
508 421
509 free(timed_events[rc]); 422 if (msg->error.exists) {
510 423 uint8_t cast = msg->error.value;
511 timed_events[rc] = NULL; 424 it = msg_parse_header_out(IDError, it, &cast,
512 425 sizeof(cast), &size);
513 i = rc + 1; 426 }
514 427
515 for (; i < timers_container->max_capacity && timed_events[i]; i ++) { 428 if (msg->capabilities.exists) {
516 timed_events[i - 1] = timed_events[i]; 429 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
517 timed_events[i] = NULL; 430 sizeof(msg->capabilities.value), &size);
518 } 431 }
519 432
520 timers_container->size--; 433 if (msg->vfpsz.exists) {
521 434 uint16_t nb_vfpsz = htons(msg->vfpsz.value);
522 LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size); 435 it = msg_parse_header_out(IDVFPSZ, it, &nb_vfpsz,
523 return 0; 436 sizeof(nb_vfpsz), &size);
524} 437 }
525 438
526/** 439 if ( it == parsed ) {
527 * Generate _random_ alphanumerical string. 440 LOGGER_WARNING("Parsing message failed; empty message");
528 */
529static void t_randomstr ( uint8_t *str, uint32_t size )
530{
531 if (str == NULL) {
532 LOGGER_DEBUG("Empty destination!");
533 return;
534 }
535
536 static const uint8_t _bytes[] =
537 "0123456789"
538 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
539 "abcdefghijklmnopqrstuvwxyz";
540
541 uint32_t _it = 0;
542
543 for ( ; _it < size; _it++ ) {
544 str[_it] = _bytes[ random_int() % 61 ];
545 }
546}
547
548/* TODO: it would be nice to actually have some sane error codes */
549typedef enum {
550 error_none,
551 error_deadcall, /* has call id but it's from old call */
552 error_id_mismatch, /* non-existing call */
553
554 error_no_callid, /* not having call id */
555 error_no_call, /* no call in session */
556 error_no_crypto_key, /* no crypto key */
557
558 error_busy
559
560} MSICallError; /* Error codes */
561
562
563/**
564 * Stringify error code.
565 */
566static const uint8_t *stringify_error ( MSICallError error_code )
567{
568 static const uint8_t *strings[] = {
569 ( uint8_t *) "",
570 ( uint8_t *) "Using dead call",
571 ( uint8_t *) "Call id not set to any call",
572 ( uint8_t *) "Call id not available",
573 ( uint8_t *) "No active call in session",
574 ( uint8_t *) "No Crypto-key set",
575 ( uint8_t *) "Callee busy"
576 };
577
578 return strings[error_code];
579}
580
581static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to )
582{
583 msi_msg_set_callid ( msg, call->id );
584
585 uint8_t msg_string_final [MSI_MAXMSG_SIZE];
586 uint16_t length = parse_send ( msg, msg_string_final );
587
588 if (!length) {
589 LOGGER_WARNING("Parsing message failed; nothing sent!");
590 return -1; 441 return -1;
591 } 442 }
592 443
593 if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) { 444 *it = 0;
445 size ++;
446
447 if ( m_msi_packet(m, friend_number, parsed, size) ) {
594 LOGGER_DEBUG("Sent message"); 448 LOGGER_DEBUG("Sent message");
595 return 0; 449 return 0;
596 } 450 }
597 451
598 return -1; 452 return -1;
599} 453}
600 454int send_error ( Messenger* m, uint32_t friend_number, MSIError error )
601static int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to )
602{ 455{
603 MSIMessage *msg = msi_new_message ( TypeResponse, response ); 456 /* Send error message */
604 int ret = send_message ( session, call, msg, to ); 457 assert(m);
605 free ( msg ); 458
606 return ret; 459 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
607}
608
609static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to )
610{
611 if (!call) {
612 LOGGER_WARNING("Cannot handle error on 'null' call");
613 return -1;
614 }
615
616 LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id);
617
618 MSIMessage *msg_error = msi_new_message ( TypeResponse, error );
619
620 msi_msg_set_reason ( msg_error, stringify_error(errid) );
621 send_message ( session, call, msg_error, to );
622 free ( msg_error );
623 460
461 MSIMessage msg;
462 msg_init(&msg, requ_pop);
463
464 msg.error.exists = true;
465 msg.error.value = error;
466
467 send_message ( m, friend_number, &msg );
624 return 0; 468 return 0;
625} 469}
626 470int invoke_callback(MSICall* call, MSICallbackID cb)
627/**
628 * Determine 'bigger' call id
629 */
630static int call_id_bigger( const uint8_t *first, const uint8_t *second)
631{
632 return (memcmp(first, second, sizeof(MSICallIDType)) < 0);
633}
634
635
636/**
637 * Set/change peer csettings
638 */
639static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id )
640{ 471{
641 if ( msg->csettings.exists ) { 472 assert(call);
642 msi_msg_get_csettings(msg, &call->csettings_peer[peer_id]); 473
643 474 if ( call->session->callbacks[cb] ) {
644 LOGGER_DEBUG("Peer: %d \n" 475 LOGGER_DEBUG("Invoking callback function: %d", cb);
645 "Type: %u \n" 476 if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) {
646 "Video bitrate: %u \n" 477 LOGGER_WARNING("Callback state handling failed, sending error");
647 "Video height: %u \n" 478 goto FAILURE;
648 "Video width: %u \n" 479 }
649 "Audio bitrate: %u \n" 480
650 "Audio framedur: %u \n"
651 "Audio sample rate: %u \n"
652 "Audio channels: %u \n", peer_id,
653 call->csettings_peer[peer_id].call_type,
654 call->csettings_peer[peer_id].video_bitrate,
655 call->csettings_peer[peer_id].max_video_height,
656 call->csettings_peer[peer_id].max_video_width,
657 call->csettings_peer[peer_id].audio_bitrate,
658 call->csettings_peer[peer_id].audio_frame_duration,
659 call->csettings_peer[peer_id].audio_sample_rate,
660 call->csettings_peer[peer_id].audio_channels );
661
662 return 0; 481 return 0;
663 } 482 }
664 483
665 LOGGER_WARNING("No csettings header!"); 484FAILURE:
485 /* If no callback present or error happened while handling,
486 * an error message will be sent to friend
487 */
488
489 if (call->error == msi_ENone)
490 call->error = msi_EHandle;
666 return -1; 491 return -1;
667} 492}
668 493static MSICall *get_call ( MSISession *session, uint32_t friend_number )
669
670/**
671 * Add peer to peer list.
672 */
673static void add_peer( MSICall *call, int peer_id )
674{ 494{
675 uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : 495 assert(session);
676 realloc( call->peers, sizeof(uint32_t) * call->peer_count); 496
677 497 if (session->calls == NULL || session->calls_tail < friend_number)
678 if (!peers) { 498 return NULL;
679 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 499
680 return; 500 return session->calls[friend_number];
681 }
682
683 call->peer_count ++;
684 call->peers = peers;
685 call->peers[call->peer_count - 1] = peer_id;
686
687 LOGGER_DEBUG("Added peer: %d", peer_id);
688}
689
690
691static MSICall *find_call ( MSISession *session, uint8_t *call_id )
692{
693 if ( call_id == NULL ) return NULL;
694
695 int32_t i = 0;
696
697 for (; i < session->max_calls; i ++ )
698 if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) {
699 return session->calls[i];
700 }
701
702 return NULL;
703} 501}
704 502MSICall *new_call ( MSISession *session, uint32_t friend_number )
705static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
706{ 503{
707 504 assert(session);
708 if (peers == 0) { 505
709 LOGGER_ERROR("No peers!"); 506 MSICall *rc = calloc(sizeof(MSICall), 1);
507
508 if (rc == NULL)
710 return NULL; 509 return NULL;
711 } 510
712 511 rc->session = session;
713 int32_t call_idx = 0; 512 rc->friend_number = friend_number;
714 513
715 for (; call_idx < session->max_calls; call_idx ++) { 514 if (session->calls == NULL) { /* Creating */
716 if ( !session->calls[call_idx] ) { 515 session->calls = calloc (sizeof(MSICall*), friend_number + 1);
717 516
718 if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) { 517 if (session->calls == NULL) {
719 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 518 free(rc);
720 return NULL; 519 return NULL;
721 }
722
723 break;
724 } 520 }
725 } 521
726 522 session->calls_tail = session->calls_head = friend_number;
727 if ( call_idx == session->max_calls ) { 523
728 LOGGER_WARNING("Reached maximum amount of calls!"); 524 } else if (session->calls_tail < friend_number) { /* Appending */
729 return NULL; 525 void* tmp = realloc(session->calls, sizeof(MSICall*) * friend_number + 1);
730 } 526
731 527 if (tmp == NULL) {
732 528 free(rc);
733 MSICall *call = session->calls[call_idx]; 529 return NULL;
734 530 }
735 call->call_idx = call_idx; 531
736 532 session->calls = tmp;
737 if ( !(call->csettings_peer = calloc ( sizeof ( MSICSettings ), peers )) ) { 533
738 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 534 /* Set fields in between to null */
739 free(call); 535 uint32_t i = session->calls_tail + 1;
740 return NULL; 536 for (; i < friend_number; i ++)
741 } 537 session->calls[i] = NULL;
742 538
743 call->session = session; 539 rc->prev = session->calls[session->calls_tail];
744 540 session->calls[session->calls_tail]->next = rc;
745 call->request_timer_id = 0; 541
746 call->ringing_timer_id = 0; 542 session->calls_tail = friend_number;
747 543
748 call->ringing_tout_ms = ringing_timeout; 544 } else if (session->calls_head > friend_number) { /* Inserting at front */
749 545 rc->next = session->calls[session->calls_head];
750 LOGGER_DEBUG("Started new call with index: %u", call_idx); 546 session->calls[session->calls_head]->prev = rc;
751 return call; 547 session->calls_head = friend_number;
752} 548 }
753 549
754static int terminate_call ( MSISession *session, MSICall *call ) 550 session->calls[friend_number] = rc;
755{ 551 return rc;
756 if ( !call ) { 552}
757 LOGGER_WARNING("Tried to terminate non-existing call!"); 553void kill_call ( MSICall *call )
758 return -1; 554{
759 } 555 /* Assume that session mutex is locked */
760 556 if ( call == NULL )
761 /* Check event loop and cancel timed events if there are any 557 return;
762 */ 558
763 timer_release ( session->timer_handler, call->request_timer_id); 559 LOGGER_DEBUG("Killing call: %p", call);
764 timer_release ( session->timer_handler, call->ringing_timer_id); 560
765 561 MSISession* session = call->session;
766 session->calls[call->call_idx] = NULL; 562
767 563 MSICall* prev = call->prev;
768 LOGGER_DEBUG("Terminated call id: %d", call->call_idx); 564 MSICall* next = call->next;
769 565
770 free ( call->csettings_peer ); 566 if (prev)
771 free ( call->peers ); 567 prev->next = next;
772 free ( call ); 568 else if (next)
773 569 session->calls_head = next->friend_number;
774 return 0; 570 else goto CLEAR_CONTAINER;
775} 571
776 572 if (next)
777static void handle_remote_connection_change(Messenger *messenger, uint32_t friend_num, uint8_t status, void *session_p) 573 next->prev = prev;
778{ 574 else if (prev)
779 (void)messenger; 575 session->calls_tail = prev->friend_number;
780 MSISession *session = session_p; 576 else goto CLEAR_CONTAINER;
577
578 session->calls[call->friend_number] = NULL;
579 free(call);
580 return;
581
582CLEAR_CONTAINER:
583 session->calls_head = session->calls_tail = 0;
584 free(session->calls);
585 free(call);
586 session->calls = NULL;
587}
588void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* data)
589{
590 (void)m;
591 MSISession *session = data;
781 592
782 switch ( status ) { 593 switch ( status ) {
783 case 0: { /* Went offline */ 594 case 0: { /* Friend is now offline */
784 int32_t j = 0; 595 LOGGER_DEBUG("Friend %d is now offline", friend_number);
785 596
786 for ( ; j < session->max_calls; j ++ ) { 597 pthread_mutex_lock(session->mutex);
787 598 MSICall* call = get_call(session, friend_number);
788 if ( !session->calls[j] ) continue; 599
789 600 if (call == NULL) {
790 uint16_t i = 0; 601 pthread_mutex_unlock(session->mutex);
791 602 return;
792 for ( ; i < session->calls[j]->peer_count; i ++ )
793 if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) {
794 invoke_callback(session, j, msi_OnPeerTimeout);
795 terminate_call(session, session->calls[j]);
796 LOGGER_DEBUG("Remote: %d timed out!", friend_num);
797 return; /* TODO: On group calls change behaviour */
798 }
799 } 603 }
604
605 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
606 kill_call(call);
607 pthread_mutex_unlock(session->mutex);
800 } 608 }
801 break; 609 break;
802 610
@@ -804,811 +612,186 @@ static void handle_remote_connection_change(Messenger *messenger, uint32_t frien
804 break; 612 break;
805 } 613 }
806} 614}
807 615void handle_push ( MSICall *call, const MSIMessage *msg )
808/**
809 * Function called at request timeout
810 */
811static void handle_timeout ( Timer *timer )
812{ 616{
813 /* TODO: Cancel might not arrive there; set up 617 assert(call);
814 * timers on these cancels and terminate call on 618
815 * their timeout 619 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
816 */
817 MSICall *call = timer->session->calls[timer->call_idx];
818
819
820 if (call) {
821 LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx);
822 620
823 invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout); 621 if (!msg->capabilities.exists) {
824 msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out"); 622 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
623 call->error = msi_EInvalidMessage;
624 goto FAILURE;
825 } 625 }
826} 626
827 627 if (call->state != msi_CallActive) {
828 628 if (!msg->vfpsz.exists) {
829/********** Request handlers **********/ 629 LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'");
830static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) 630 call->error = msi_EInvalidMessage;
831{ 631 goto FAILURE;
832 LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); 632 }
833 633
834 634 call->peer_vfpsz = msg->vfpsz.value;
835 if (!msg->csettings.exists) {/**/ 635 }
836 LOGGER_WARNING("Peer sent invalid codec settings!"); 636
837 send_error ( session, call, error_no_callid, msg->friend_id ); 637
838 return 0; 638 switch (call->state) {
839 } 639 case msi_CallInactive: {
840 640 LOGGER_INFO("Friend is calling us");
841 if ( call ) { 641
842 if ( call->peers[0] == (uint32_t)msg->friend_id ) { 642 /* Call requested */
843 if (call->state == msi_CallInviting) { 643 call->peer_capabilities = msg->capabilities.value;
844 /* The glare case. A calls B when at the same time 644 call->state = msi_CallRequested;
845 * B calls A. Who has advantage is set bey calculating 645
846 * 'bigger' Call id and then that call id is being used in 646 if ( invoke_callback(call, msi_OnInvite) == -1 )
847 * future. User with 'bigger' Call id has the advantage 647 goto FAILURE;
848 * as in he will wait the response from the other. 648
649 } break;
650
651 case msi_CallActive: {
652 if (msg->vfpsz.exists) {
653 /* If peer sended video frame piece size
654 * while the call is already active it's probable
655 * that he is trying to re-call us while the call
656 * is not terminated on our side. We can assume that
657 * in this case we can automatically answer the re-call.
849 */ 658 */
850 LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]); 659 if (call->peer_vfpsz != msg->vfpsz.value) {
851 660 LOGGER_WARNING("Friend sent invalid parameters for re-call");
852 if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */ 661 call->error = msi_EInvalidParam;
853 662 invoke_callback(call, msi_OnError);
854 /* Terminate call; peer will timeout(call) if call initialization fails */ 663 goto FAILURE;
855 terminate_call(session, call);
856
857 call = init_call ( session, 1, 0 );
858
859 if ( !call ) {
860 LOGGER_ERROR("Starting call");
861 return 0;
862 }
863
864 } else {
865 return 0; /* Wait for ringing from peer */
866 }
867 } else if (call->state == msi_CallActive) {
868 /* Request for media change; call callback and send starting response */
869 if (flush_peer_csettings(call, msg, 0) != 0) { /**/
870 LOGGER_WARNING("Peer sent invalid csetting!");
871 send_error ( session, call, error_no_callid, msg->friend_id );
872 return 0;
873 } 664 }
874 665
875 LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == msi_TypeAudio ? "audio" : "video"); 666 LOGGER_INFO("Friend is recalling us");
876 send_reponse(session, call, starting, msg->friend_id); 667
877 invoke_callback(session, call->call_idx, msi_OnPeerCSChange); 668 MSIMessage msg;
878 return 1; 669 msg_init(&msg, requ_push);
670
671 msg.capabilities.exists = true;
672 msg.capabilities.value = call->self_capabilities;
673
674 msg.vfpsz.exists = true;
675 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
676
677 send_message ( call->session->messenger, call->friend_number, &msg );
678
679 /* If peer changed capabilities during re-call they will
680 * be handled accordingly during the next step
681 */
879 } 682 }
880 } else { 683
881 send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/ 684 /* Only act if capabilities changed */
882 terminate_call(session, call); 685 if ( call->peer_capabilities != msg->capabilities.value) {
883 return 0; 686 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
884 } 687
885 } else { 688 call->peer_capabilities = msg->capabilities.value;
886 call = init_call ( session, 1, 0 ); 689 if ( invoke_callback(call, msi_OnCapabilities) == -1 )
887 690 goto FAILURE;
888 if ( !call ) { 691 }
889 LOGGER_ERROR("Starting call"); 692 } break;
890 return 0; 693
891 } 694 case msi_CallRequesting: {
892 } 695 LOGGER_INFO("Friend answered our call");
893 696
894 if ( !msg->callid.exists ) { 697 /* Call started */
895 send_error ( session, call, error_no_callid, msg->friend_id ); 698 call->peer_capabilities = msg->capabilities.value;
896 terminate_call(session, call); 699 call->state = msi_CallActive;
897 return 0; 700
898 } 701 if ( invoke_callback(call, msi_OnStart) == -1 )
899 702 goto FAILURE;
900 memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); 703
901 call->state = msi_CallStarting; 704 } break;
902 705
903 add_peer( call, msg->friend_id); 706 case msi_CallRequested: {
904 flush_peer_csettings ( call, msg, 0 ); 707 /* Consecutive pushes during initialization state are ignored */
905 send_reponse(session, call, ringing, msg->friend_id); 708 LOGGER_WARNING("Consecutive push");
906 invoke_callback(session, call->call_idx, msi_OnInvite); 709 } break;
907 710 }
908 return 1; 711
909} 712 return;
910 713
911static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg ) 714FAILURE:
912{ 715 send_error(call->session->messenger, call->friend_number, call->error);
913 if ( !call ) { 716 kill_call(call);
914 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 717}
915 return 0; 718void handle_pop ( MSICall *call, const MSIMessage *msg )
916 } 719{
917 720 assert(call);
918 (void)msg; 721
919 722 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
920 LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); 723
921 724 /* callback errors are ignored */
922 call->state = msi_CallActive; 725
923 invoke_callback(session, call->call_idx, msi_OnStart); 726 if (msg->error.exists) {
924 return 1; 727 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
925} 728 call->error = msg->error.value;
926 729 invoke_callback(call, msi_OnError);
927static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg ) 730
928{ 731 } else switch (call->state) {
929 if ( !call ) { 732 case msi_CallInactive: {
930 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 733 LOGGER_ERROR("Handling what should be impossible case");
931 return 0; 734 abort();
932 } 735 } break;
933 736
934 LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); 737 case msi_CallActive: {
935 738 /* Hangup */
936 invoke_callback(session, call->call_idx, msi_OnReject); 739 LOGGER_INFO("Friend hung up on us");
937 740 invoke_callback(call, msi_OnEnd);
938 send_reponse(session, call, ending, msg->friend_id); 741 } break;
939 terminate_call(session, call); 742
940 743 case msi_CallRequesting: {
941 return 1; 744 /* Reject */
942} 745 LOGGER_INFO("Friend rejected our call");
943 746 invoke_callback(call, msi_OnEnd);
944static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) 747 } break;
945{ 748
946 if ( !call ) { 749 case msi_CallRequested: {
947 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 750 /* Cancel */
948 return 0; 751 LOGGER_INFO("Friend canceled call invite");
949 } 752 invoke_callback(call, msi_OnEnd);
950 753 } break;
951 (void)msg; 754 }
952 755
953 LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); 756 kill_call ( call );
954 757}
955 invoke_callback(session, call->call_idx, msi_OnCancel); 758void handle_msi_packet ( Messenger* m, uint32_t friend_number, const uint8_t* data, uint16_t length, void* object )
956 terminate_call ( session, call );
957
958 return 1;
959}
960
961static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
962{
963 if ( !call ) {
964 LOGGER_WARNING("Session: %p Handling 'start' on no call");
965 return 0;
966 }
967
968 LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx);
969
970 invoke_callback(session, call->call_idx, msi_OnEnd);
971 send_reponse(session, call, ending, msg->friend_id);
972 terminate_call ( session, call );
973
974 return 1;
975}
976
977/********** Response handlers **********/
978static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
979{
980 if ( !call ) {
981 LOGGER_WARNING("Session: %p Handling 'start' on no call");
982 return 0;
983 }
984
985 (void)msg;
986
987 if ( call->ringing_timer_id ) {
988 LOGGER_WARNING("Call already ringing");
989 return 0;
990 }
991
992 LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
993
994 call->ringing_timer_id = timer_alloc
995 ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
996 invoke_callback(session, call->call_idx, msi_OnRinging);
997 return 1;
998}
999static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg )
1000{
1001 if ( !call ) {
1002 LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call");
1003 return 0;
1004 }
1005
1006 if ( call->state == msi_CallActive ) { /* Change media */
1007
1008 LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx );
1009
1010 invoke_callback(session, call->call_idx, msi_OnSelfCSChange);
1011
1012 } else if ( call->state == msi_CallInviting ) {
1013 LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx );
1014
1015 call->state = msi_CallActive;
1016
1017 MSIMessage *msg_start = msi_new_message ( TypeRequest, start );
1018 send_message ( session, call, msg_start, msg->friend_id );
1019 free ( msg_start );
1020
1021
1022 flush_peer_csettings ( call, msg, 0 );
1023
1024 /* This is here in case of glare */
1025 timer_release(session->timer_handler, call->ringing_timer_id);
1026 invoke_callback(session, call->call_idx, msi_OnStart);
1027 } else {
1028 LOGGER_ERROR("Invalid call state");
1029 terminate_call(session, call );
1030 return 0;
1031 }
1032
1033 return 1;
1034}
1035static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg )
1036{
1037 if ( !call ) {
1038 LOGGER_WARNING("Session: %p Handling 'start' on no call");
1039 return 0;
1040 }
1041
1042 (void)msg;
1043
1044 LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx );
1045
1046 invoke_callback(session, call->call_idx, msi_OnEnd);
1047 terminate_call ( session, call );
1048
1049 return 1;
1050}
1051static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg )
1052{
1053 if ( !call ) {
1054 LOGGER_WARNING("Handling 'error' on non-existing call!");
1055 return -1;
1056 }
1057
1058 LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx );
1059
1060 invoke_callback(session, call->call_idx, msi_OnEnd);
1061
1062 /* Handle error accordingly */
1063 if ( msg->reason.exists ) {
1064 /* TODO */
1065 }
1066
1067 terminate_call ( session, call );
1068
1069 return 1;
1070}
1071
1072/**
1073 * BASIC call flow:
1074 *
1075 * ALICE BOB
1076 * | invite --> |
1077 * | |
1078 * | <-- ringing |
1079 * | |
1080 * | <-- starting |
1081 * | |
1082 * | start --> |
1083 * | |
1084 * | <-- MEDIA TRANS --> |
1085 * | |
1086 * | end --> |
1087 * | |
1088 * | <-- ending |
1089 *
1090 * Alice calls Bob by sending invite packet.
1091 * Bob recvs the packet and sends an ringing packet;
1092 * which notifies Alice that her invite is acknowledged.
1093 * Ringing screen shown on both sides.
1094 * Bob accepts the invite for a call by sending starting packet.
1095 * Alice recvs the starting packet and sends the started packet to
1096 * inform Bob that she recved the starting packet.
1097 * Now the media transmission is established ( i.e. RTP transmission ).
1098 * Alice hangs up and sends end packet.
1099 * Bob recves the end packet and sends ending packet
1100 * as the acknowledgement that the call is ending.
1101 *
1102 *
1103 */
1104static void msi_handle_packet ( Messenger *messenger, uint32_t source, const uint8_t *data, uint16_t length,
1105 void *object )
1106{ 759{
1107 LOGGER_DEBUG("Got msi message"); 760 LOGGER_DEBUG("Got msi message");
1108 /* Unused */ 761
1109 (void)messenger;
1110
1111 MSISession *session = object; 762 MSISession *session = object;
1112 MSIMessage *msg; 763 MSIMessage msg;
1113 764
1114 if ( !length ) { 765 if ( msg_parse_in ( &msg, data, length ) == -1 ) {
1115 LOGGER_WARNING("Length param negative");
1116 return;
1117 }
1118
1119 msg = parse_recv ( data, length );
1120
1121 if ( !msg ) {
1122 LOGGER_WARNING("Error parsing message"); 766 LOGGER_WARNING("Error parsing message");
767 send_error(m, friend_number, msi_EInvalidMessage);
1123 return; 768 return;
1124 } else { 769 } else {
1125 LOGGER_DEBUG("Successfully parsed message"); 770 LOGGER_DEBUG("Successfully parsed message");
1126 } 771 }
1127 772
1128 msg->friend_id = source;
1129
1130 pthread_mutex_lock(session->mutex);
1131
1132 /* Find what call */
1133 MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL;
1134
1135 /* Now handle message */
1136
1137 if ( msg->request.exists ) { /* Handle request */
1138
1139 switch (msg->request.value) {
1140 case invite:
1141 handle_recv_invite ( session, call, msg );
1142 break;
1143
1144 case start:
1145 handle_recv_start ( session, call, msg );
1146 break;
1147
1148 case cancel:
1149 handle_recv_cancel ( session, call, msg );
1150 break;
1151
1152 case reject:
1153 handle_recv_reject ( session, call, msg );
1154 break;
1155
1156 case end:
1157 handle_recv_end ( session, call, msg );
1158 break;
1159 }
1160
1161 } else if ( msg->response.exists ) { /* Handle response */
1162
1163 /* Got response so cancel timer */
1164 if ( call ) timer_release(session->timer_handler, call->request_timer_id);
1165
1166 switch (msg->response.value) {
1167 case ringing:
1168 handle_recv_ringing ( session, call, msg );
1169 break;
1170
1171 case starting:
1172 handle_recv_starting ( session, call, msg );
1173 break;
1174
1175 case ending:
1176 handle_recv_ending ( session, call, msg );
1177 break;
1178
1179 case error:
1180 handle_recv_error ( session, call, msg );
1181 break;
1182 }
1183
1184 } else {
1185 LOGGER_WARNING("Invalid message: no resp nor requ headers");
1186 }
1187
1188 free ( msg );
1189
1190 pthread_mutex_unlock(session->mutex);
1191}
1192
1193
1194
1195/********** User functions **********/
1196void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1197{
1198 session->callbacks[id].first = callback;
1199 session->callbacks[id].second = userdata;
1200}
1201
1202
1203MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
1204{
1205 if (messenger == NULL) {
1206 LOGGER_ERROR("Could not init session on empty messenger!");
1207 return NULL;
1208 }
1209
1210 if ( !max_calls ) {
1211 LOGGER_WARNING("Invalid max call treshold!");
1212 return NULL;
1213 }
1214
1215 MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
1216
1217 if (retu == NULL) {
1218 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1219 return NULL;
1220 }
1221
1222 if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) {
1223 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1224 goto error;
1225 }
1226
1227 retu->timer_handler = calloc(1, sizeof(TimerHandler));
1228
1229 if (retu->timer_handler == NULL) {
1230 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1231 goto error;
1232 }
1233
1234 /* Allocate space for timers */
1235 ((TimerHandler *)retu->timer_handler)->max_capacity = max_calls * 10;
1236
1237 if (!(((TimerHandler *)retu->timer_handler)->timers = calloc(max_calls * 10, sizeof(Timer *)))) {
1238 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1239 goto error;
1240 }
1241
1242 if (create_recursive_mutex(retu->mutex) != 0) {
1243 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
1244 goto error;
1245 }
1246
1247 retu->messenger_handle = messenger;
1248 retu->agent_handler = NULL;
1249 retu->max_calls = max_calls;
1250 retu->frequ = 10000; /* default value? */
1251 retu->call_timeout = 30000; /* default value? */
1252
1253 m_callback_msi_packet(messenger, msi_handle_packet, retu );
1254
1255 /* This is called when remote terminates session */
1256 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu);
1257
1258 LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls);
1259 return retu;
1260
1261error:
1262
1263 if (retu->timer_handler) {
1264 free(((TimerHandler *)retu->timer_handler)->timers);
1265 free(retu->timer_handler);
1266 }
1267
1268 free(retu->calls);
1269 free(retu);
1270 return NULL;
1271}
1272
1273
1274int msi_kill ( MSISession *session )
1275{
1276 if (session == NULL) {
1277 LOGGER_ERROR("Tried to terminate non-existing session");
1278 return -1;
1279 }
1280
1281 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL);
1282 pthread_mutex_lock(session->mutex); 773 pthread_mutex_lock(session->mutex);
1283 774 MSICall *call = get_call(session, friend_number);
1284 /* Cancel active calls */ 775
1285 int32_t idx = 0; 776 if (call == NULL) {
1286 777 if (msg.request.value != requ_push) {
1287 for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) { 778 send_error(m, friend_number, msi_EStrayMessage);
1288 /* Cancel all? */ 779 pthread_mutex_unlock(session->mutex);
1289 uint16_t _it = 0; 780 return;
1290 /*for ( ; _it < session->calls[idx]->peer_count; _it++ )
1291 * FIXME: will not work on multiple peers, must cancel call for all peers
1292 */
1293 MSICallState state = session->calls[idx]->state;
1294
1295 if (state == msi_CallInviting) {
1296 msi_cancel( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" );
1297 } else {
1298 msi_stopcall(session, idx);
1299 }
1300 } 781 }
1301 782
1302 free(((TimerHandler *)session->timer_handler)->timers); 783 call = new_call(session, friend_number);
1303 free(session->timer_handler); 784 if (call == NULL) {
1304 785 send_error(m, friend_number, msi_ESystem);
1305 free ( session->calls );
1306 pthread_mutex_unlock(session->mutex);
1307 pthread_mutex_destroy(session->mutex);
1308
1309 LOGGER_DEBUG("Terminated session: %p", session);
1310 free ( session );
1311 return 0;
1312}
1313
1314int msi_invite ( MSISession *session,
1315 int32_t *call_index,
1316 const MSICSettings *csettings,
1317 uint32_t rngsec,
1318 uint32_t friend_id )
1319{
1320 pthread_mutex_lock(session->mutex);
1321
1322 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
1323
1324
1325 int i = 0;
1326
1327 for (; i < session->max_calls; i ++)
1328 if (session->calls[i] && session->calls[i]->peers[0] == friend_id) {
1329 LOGGER_ERROR("Already in a call with friend %d", friend_id);
1330 pthread_mutex_unlock(session->mutex); 786 pthread_mutex_unlock(session->mutex);
1331 return msi_ErrorAlreadyInCallWithPeer; 787 return;
1332 } 788 }
1333
1334
1335 MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */
1336
1337 if ( !call ) {
1338 pthread_mutex_unlock(session->mutex);
1339 LOGGER_ERROR("Cannot handle more calls");
1340 return msi_ErrorReachedCallLimit;
1341 }
1342
1343 *call_index = call->call_idx;
1344
1345 t_randomstr ( call->id, sizeof(call->id) );
1346
1347 add_peer ( call, friend_id );
1348
1349 call->csettings_local = *csettings;
1350
1351 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1352
1353 msi_msg_set_csettings(msg_invite, csettings);
1354 send_message ( session, call, msg_invite, friend_id );
1355 free( msg_invite );
1356
1357 call->state = msi_CallInviting;
1358
1359 call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout );
1360
1361 LOGGER_DEBUG("Invite sent");
1362
1363 pthread_mutex_unlock(session->mutex);
1364
1365 return 0;
1366}
1367
1368int msi_hangup ( MSISession *session, int32_t call_index )
1369{
1370 pthread_mutex_lock(session->mutex);
1371 LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index);
1372
1373 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1374 LOGGER_ERROR("Invalid call index!");
1375 pthread_mutex_unlock(session->mutex);
1376 return msi_ErrorNoCall;
1377 }
1378
1379 if ( session->calls[call_index]->state != msi_CallActive ) {
1380 LOGGER_ERROR("Call is not active!");
1381 pthread_mutex_unlock(session->mutex);
1382 return msi_ErrorInvalidState;
1383 } 789 }
1384 790
1385 MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); 791 if (msg.request.value == requ_push)
1386 792 handle_push(call, &msg);
1387 /* hangup for each peer */ 793 else
1388 int it = 0; 794 handle_pop(call, &msg); /* always kills the call */
1389 795
1390 for ( ; it < session->calls[call_index]->peer_count; it ++ )
1391 send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1392
1393 session->calls[call_index]->state = msi_CallOver;
1394
1395 free ( msg_end );
1396
1397 session->calls[call_index]->request_timer_id =
1398 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1399
1400 pthread_mutex_unlock(session->mutex);
1401 return 0;
1402}
1403
1404int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings )
1405{
1406 pthread_mutex_lock(session->mutex);
1407 LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1408
1409 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1410 LOGGER_ERROR("Invalid call index!");
1411 pthread_mutex_unlock(session->mutex);
1412 return msi_ErrorNoCall;
1413 }
1414
1415 if ( session->calls[call_index]->state != msi_CallStarting ) {
1416 LOGGER_ERROR("Call is in invalid state!");
1417 pthread_mutex_unlock(session->mutex);
1418 return msi_ErrorInvalidState;
1419 }
1420
1421 MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting );
1422
1423 session->calls[call_index]->csettings_local = *csettings;
1424
1425 msi_msg_set_csettings(msg_starting, csettings);
1426
1427 send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] );
1428 free ( msg_starting );
1429
1430 session->calls[call_index]->state = msi_CallActive;
1431
1432 pthread_mutex_unlock(session->mutex);
1433 return 0;
1434}
1435
1436int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason )
1437{
1438 pthread_mutex_lock(session->mutex);
1439 LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1440
1441 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1442 LOGGER_ERROR("Invalid call index!");
1443 pthread_mutex_unlock(session->mutex);
1444 return msi_ErrorNoCall;
1445 }
1446
1447 if ( session->calls[call_index]->state != msi_CallInviting ) {
1448 LOGGER_ERROR("Call is in invalid state: %u", session->calls[call_index]->state);
1449 pthread_mutex_unlock(session->mutex);
1450 return msi_ErrorInvalidState;
1451 }
1452
1453 MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel );
1454
1455 /* FIXME */
1456#if 0
1457
1458 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1459 MSIReasonStrType reason_cast;
1460 memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1461 memcpy(reason_cast, reason, strlen(reason));
1462 msi_msg_set_reason(msg_cancel, reason_cast);
1463 }
1464
1465#else
1466 (void)reason;
1467
1468#endif
1469
1470 send_message ( session, session->calls[call_index], msg_cancel, peer );
1471 free ( msg_cancel );
1472
1473 terminate_call ( session, session->calls[call_index] );
1474 pthread_mutex_unlock(session->mutex);
1475
1476 return 0;
1477}
1478
1479int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1480{
1481 pthread_mutex_lock(session->mutex);
1482 LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1483
1484 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1485 LOGGER_ERROR("Invalid call index!");
1486 pthread_mutex_unlock(session->mutex);
1487 return msi_ErrorNoCall;
1488 }
1489
1490 if ( session->calls[call_index]->state != msi_CallStarting ) {
1491 LOGGER_ERROR("Call is in invalid state!");
1492 pthread_mutex_unlock(session->mutex);
1493 return msi_ErrorInvalidState;
1494 }
1495
1496 MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject );
1497
1498 /* FIXME */
1499#if 0
1500
1501 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1502 MSIReasonStrType reason_cast;
1503 memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1504 memcpy(reason_cast, reason, strlen(reason));
1505 msi_msg_set_reason(msg_reject, reason_cast);
1506 }
1507
1508#else
1509 (void)reason;
1510
1511#endif
1512
1513 send_message ( session, session->calls[call_index], msg_reject,
1514 session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] );
1515 free ( msg_reject );
1516
1517 session->calls[call_index]->state = msi_CallOver;
1518 session->calls[call_index]->request_timer_id =
1519 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1520
1521 pthread_mutex_unlock(session->mutex);
1522 return 0;
1523}
1524
1525int msi_stopcall ( MSISession *session, int32_t call_index )
1526{
1527 pthread_mutex_lock(session->mutex);
1528 LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index);
1529
1530 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1531 pthread_mutex_unlock(session->mutex);
1532 return msi_ErrorNoCall;
1533 }
1534
1535 /* just terminate it */
1536
1537 terminate_call ( session, session->calls[call_index] );
1538
1539 pthread_mutex_unlock(session->mutex);
1540 return 0;
1541}
1542
1543int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
1544{
1545 pthread_mutex_lock(session->mutex);
1546
1547 LOGGER_DEBUG("Changing media on call: %d", call_index);
1548
1549 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1550 LOGGER_ERROR("Invalid call index!");
1551 pthread_mutex_unlock(session->mutex);
1552 return msi_ErrorNoCall;
1553 }
1554
1555 MSICall *call = session->calls[call_index];
1556
1557 if ( call->state != msi_CallActive ) {
1558 LOGGER_ERROR("Call is not active!");
1559 pthread_mutex_unlock(session->mutex);
1560 return msi_ErrorInvalidState;
1561 }
1562
1563 MSICSettings *local = &call->csettings_local;
1564
1565 if (
1566 local->call_type == csettings->call_type &&
1567 local->video_bitrate == csettings->video_bitrate &&
1568 local->max_video_width == csettings->max_video_width &&
1569 local->max_video_height == csettings->max_video_height &&
1570 local->audio_bitrate == csettings->audio_bitrate &&
1571 local->audio_frame_duration == csettings->audio_frame_duration &&
1572 local->audio_sample_rate == csettings->audio_sample_rate &&
1573 local->audio_channels == csettings->audio_channels ) {
1574 LOGGER_ERROR("Call is already set accordingly!");
1575 pthread_mutex_unlock(session->mutex);
1576 return -1;
1577 }
1578
1579 *local = *csettings;
1580
1581 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1582
1583 msi_msg_set_csettings ( msg_invite, local );
1584 send_message ( session, call, msg_invite, call->peers[0] );
1585 free ( msg_invite );
1586
1587 LOGGER_DEBUG("Request for media change sent");
1588
1589 pthread_mutex_unlock(session->mutex);
1590
1591 return 0;
1592}
1593
1594void msi_do(MSISession *session)
1595{
1596 pthread_mutex_lock(session->mutex);
1597
1598 TimerHandler *timer = session->timer_handler;
1599
1600 uint64_t time = current_time_monotonic();
1601
1602 while ( timer->timers[0] && timer->timers[0]->timeout < time ) {
1603 LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout);
1604
1605 int id = timer->timers[0]->id;
1606 timer->timers[0]->func(timer->timers[0]);
1607
1608 /* In case function has released timer */
1609 if (timer->timers[0] && timer->timers[0]->id == id)
1610 timer_release(timer, id);
1611 }
1612
1613 pthread_mutex_unlock(session->mutex); 796 pthread_mutex_unlock(session->mutex);
1614} 797}