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