summaryrefslogtreecommitdiff
path: root/toxav/msi.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxav/msi.c')
-rw-r--r--toxav/msi.c1808
1 files changed, 511 insertions, 1297 deletions
diff --git a/toxav/msi.c b/toxav/msi.c
index 97ba936c..c08c0135 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,795 @@
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);
116 83int send_error (Messenger *m, uint32_t friend_number, MSIError error);
117 s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second ); 84static int invoke_callback(MSICall *call, MSICallbackID cb);
118 } 85static MSICall *get_call (MSISession *session, uint32_t friend_number);
119} 86MSICall *new_call (MSISession *session, uint32_t friend_number);
120 87void kill_call (MSICall *call);
121/** 88void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
122 * Parse raw 'data' received from socket into MSIMessage struct. 89void handle_init (MSICall *call, const MSIMessage *msg);
123 * Every message has to have end value of 'end_byte' or _undefined_ behavior 90void handle_push (MSICall *call, const MSIMessage *msg);
124 * occures. The best practice is to check the end of the message at the handle_packet. 91void handle_pop (MSICall *call, const MSIMessage *msg);
125 */ 92void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
126static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length )
127{
128
129#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; }
130#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; }
131#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; }
132
133 if ( msg == NULL ) {
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 93
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}
201 94
202/** 95/**
203 * Create the message. 96 * Public functions
204 */ 97 */
205MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) 98void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICallbackID id)
206{ 99{
207 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); 100 if (!session)
208 101 return;
209 if ( retu == NULL ) {
210 LOGGER_WARNING("Allocation failed! Program might misbehave!");
211 return NULL;
212 }
213
214 if ( type == TypeRequest ) {
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 102
223 return retu; 103 pthread_mutex_lock(session->mutex);
104 session->callbacks[id] = callback;
105 pthread_mutex_unlock(session->mutex);
224} 106}
225 107MSISession *msi_new (Messenger *m)
226
227/**
228 * Parse data from handle_packet.
229 */
230MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
231{ 108{
232 if ( data == NULL ) { 109 if (m == NULL) {
233 LOGGER_WARNING("Tried to parse empty message!"); 110 LOGGER_ERROR("Could not init session on empty messenger!");
234 return NULL;
235 }
236
237 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
238
239 if ( retu == NULL ) {
240 LOGGER_WARNING("Allocation failed! Program might misbehave!");
241 return NULL; 111 return NULL;
242 } 112 }
243 113
244 if ( parse_raw_data ( retu, data, length ) == -1 ) { 114 MSISession *retu = calloc (sizeof (MSISession), 1);
245 115
246 free ( retu ); 116 if (retu == NULL) {
247 return NULL; 117 LOGGER_ERROR("Allocation failed! Program might misbehave!");
248 }
249
250 return retu;
251}
252
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{
263 if ( dest == NULL ) {
264 LOGGER_ERROR("No destination space!");
265 return NULL; 118 return NULL;
266 } 119 }
267 120
268 if (value == NULL || value_len == 0) { 121 if (create_recursive_mutex(retu->mutex) != 0) {
269 LOGGER_ERROR("Empty header value"); 122 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
123 free(retu);
270 return NULL; 124 return NULL;
271 } 125 }
272 126
273 *dest = id; 127 retu->messenger = m;
274 dest ++;
275 *dest = value_len;
276 dest ++;
277 128
278 memcpy(dest, value, value_len); 129 m_callback_msi_packet(m, handle_msi_packet, retu);
279 130
280 *length += (2 + value_len); 131 /* This is called when remote terminates session */
132 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
281 133
282 return dest + value_len; /* Set to next position ready to be written */ 134 LOGGER_DEBUG("New msi session: %p ", retu);
135 return retu;
283} 136}
284 137int msi_kill (MSISession *session)
285
286/**
287 * Parse MSIMessage to send.
288 */
289uint16_t parse_send ( MSIMessage *msg, uint8_t *dest )
290{ 138{
291 if (msg == NULL) { 139 if (session == NULL) {
292 LOGGER_ERROR("No message!"); 140 LOGGER_ERROR("Tried to terminate non-existing session");
293 return 0; 141 return -1;
294 }
295
296 if (dest == NULL ) {
297 LOGGER_ERROR("No destination!");
298 return 0;
299 } 142 }
300 143
301 uint8_t *it = dest; 144 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
302 uint16_t size = 0;
303 145
304 if (msg->request.exists) { 146 if (pthread_mutex_trylock(session->mutex) != 0) {
305 uint8_t cast = msg->request.value; 147 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
306 it = format_output(it, IDRequest, &cast, 1, &size); 148 return -1;
307 }
308
309 if (msg->response.exists) {
310 uint8_t cast = msg->response.value;
311 it = format_output(it, IDResponse, &cast, 1, &size);
312 } 149 }
313 150
314 if (msg->callid.exists) { 151 if (session->calls) {
315 it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size); 152 MSIMessage msg;
316 } 153 msg_init(&msg, requ_pop);
317 154
318 if (msg->reason.exists) { 155 MSICall *it = get_call(session, session->calls_head);
319 it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
320 }
321 156
322 if (msg->csettings.exists) { 157 for (; it; it = it->next) {
323 it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size); 158 send_message(session->messenger, it->friend_number, &msg);
159 kill_call(it); /* This will eventually free session->calls */
160 }
324 } 161 }
325 162
326 *it = 0; 163 pthread_mutex_unlock(session->mutex);
327 size ++; 164 pthread_mutex_destroy(session->mutex);
328
329 return size;
330}
331
332void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
333{
334 if ( !msg ) return;
335 165
336 msg->reason.exists = 1; 166 LOGGER_DEBUG("Terminated session: %p", session);
337 memcpy(msg->reason.value, value, sizeof(MSIReasonStrType)); 167 free (session);
168 return 0;
338} 169}
339 170int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
340void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
341{ 171{
342 if ( !msg ) return; 172 if (!session)
173 return -1;
343 174
344 msg->callid.exists = 1; 175 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
345 memcpy(msg->callid.value, value, sizeof(MSICallIDType));
346}
347 176
348void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value ) 177 if (pthread_mutex_trylock(session->mutex) != 0) {
349{ 178 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
350 if ( !msg ) return; 179 return -1;
351 180 }
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 181
392void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest ) 182 if (get_call(session, friend_number) != NULL) {
393{ 183 LOGGER_ERROR("Already in a call");
394 if ( !msg || !dest || !msg->csettings.exists ) return; 184 pthread_mutex_unlock(session->mutex);
185 return -1;
186 }
395 187
396 dest->call_type = msg->csettings.value[0]; 188 (*call) = new_call (session, friend_number);
397 uint8_t *iter = msg->csettings.value + 1;
398 189
399 memcpy(&dest->video_bitrate, iter, 4); 190 if (*call == NULL) {
400 iter += 4; 191 pthread_mutex_unlock(session->mutex);
401 dest->video_bitrate = ntohl(dest->video_bitrate); 192 return -1;
193 }
402 194
403 memcpy(&dest->max_video_width, iter, 2); 195 (*call)->self_capabilities = capabilities;
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 217
451 for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++); 218 MSISession *session = call->session;
452 219
453 if (i == timer_handler->max_capacity) { 220 if (pthread_mutex_trylock(session->mutex) != 0) {
454 LOGGER_WARNING("Maximum capacity reached!"); 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)
243 return -1;
492 244
493 uint32_t i; 245 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
494 int rc = -1;
495 246
496 for (i = 0; i < timers_container->max_capacity; ++i) { 247 MSISession *session = call->session;
497 if (timed_events[i] && timed_events[i]->id == id) {
498 rc = i;
499 break;
500 }
501 }
502 248
503 if (rc == -1) { 249 if (pthread_mutex_trylock(session->mutex) != 0) {
504 LOGGER_WARNING("No event with id: %d", id); 250 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
505 return -1; 251 return -1;
506 } 252 }
507 253
508 free(timed_events[rc]); 254 if (call->state != msi_CallRequested) {
509 255 /* Though sending in invalid state will not cause anything wierd
510 timed_events[rc] = NULL; 256 * Its better to not do it like a maniac */
511 257 LOGGER_ERROR("Call is in invalid state!");
512 i = rc + 1; 258 pthread_mutex_unlock(session->mutex);
513 259 return -1;
514 for (; i < timers_container->max_capacity && timed_events[i]; i ++) {
515 timed_events[i - 1] = timed_events[i];
516 timed_events[i] = NULL;
517 } 260 }
518 261
519 timers_container->size--; 262 call->self_capabilities = capabilities;
520 263
521 LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size); 264 MSIMessage msg;
522 return 0; 265 msg_init(&msg, requ_push);
523}
524 266
525/** 267 msg.capabilities.exists = true;
526 * Generate _random_ alphanumerical string. 268 msg.capabilities.value = capabilities;
527 */
528static void t_randomstr ( uint8_t *str, uint32_t size )
529{
530 if (str == NULL) {
531 LOGGER_DEBUG("Empty destination!");
532 return;
533 }
534 269
535 static const uint8_t _bytes[] = 270 send_message (session->messenger, call->friend_number, &msg);
536 "0123456789"
537 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
538 "abcdefghijklmnopqrstuvwxyz";
539 271
540 uint32_t _it = 0; 272 call->state = msi_CallActive;
273 pthread_mutex_unlock(session->mutex);
541 274
542 for ( ; _it < size; _it++ ) { 275 return 0;
543 str[_it] = _bytes[ random_int() % 61 ];
544 }
545} 276}
546 277int msi_change_capabilities(MSICall *call, uint8_t capabilities)
547/* TODO: it would be nice to actually have some sane error codes */
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
553 error_no_callid, /* not having call id */
554 error_no_call, /* no call in session */
555 error_no_crypto_key, /* no crypto key */
556
557 error_busy
558
559} MSICallError; /* Error codes */
560
561
562/**
563 * Stringify error code.
564 */
565static const uint8_t *stringify_error ( MSICallError error_code )
566{ 278{
567 static const uint8_t *strings[] = { 279 if (!call || !call->session)
568 ( uint8_t *) "", 280 return -1;
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}
579 281
580static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to ) 282 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
581{
582 msi_msg_set_callid ( msg, call->id );
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
677 if (!peers) {
678 LOGGER_WARNING("Allocation failed! Program might misbehave!");
679 return;
680 }
681
682 call->peer_count ++;
683 call->peers = peers;
684 call->peers[call->peer_count - 1] = peer_id;
685
686 LOGGER_DEBUG("Added peer: %d", peer_id);
687}
688 324
325#define CHECK_SIZE(bytes, constraint, size) \
326 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
327 if (bytes[1] != size) { LOGGER_ERROR("Invalid data size!"); return -1; }
689 328
690static MSICall *find_call ( MSISession *session, uint8_t *call_id ) 329#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
691{ 330 if (bytes[2] > enum_high) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
692 if ( call_id == NULL ) return NULL;
693 331
694 int32_t i = 0; 332#define SET_UINT8(bytes, header) do { \
333 header.value = bytes[2]; \
334 header.exists = true; \
335 bytes += 3; \
336 } while(0)
695 337
696 for (; i < session->max_calls; i ++ ) 338#define SET_UINT16(bytes, header) do { \
697 if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) { 339 memcpy(&header.value, bytes + 2, 2);\
698 return session->calls[i]; 340 header.exists = true; \
699 } 341 bytes += 4; \
342 } while(0)
700 343
701 return NULL;
702}
703 344
704static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout ) 345 assert(dest);
705{
706 346
707 if (peers == 0) { 347 if (length == 0 || data[length - 1]) { /* End byte must have value 0 */
708 LOGGER_ERROR("No peers!"); 348 LOGGER_ERROR("Invalid end byte");
709 return NULL; 349 return -1;
710 } 350 }
711 351
712 int32_t call_idx = 0; 352 memset(dest, 0, sizeof(*dest));
713 353
714 for (; call_idx < session->max_calls; call_idx ++) { 354 const uint8_t *it = data;
715 if ( !session->calls[call_idx] ) { 355 int size_constraint = length;
716
717 if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) {
718 LOGGER_WARNING("Allocation failed! Program might misbehave!");
719 return NULL;
720 }
721
722 break;
723 }
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 389
776static void handle_remote_connection_change(Messenger *messenger, uint32_t friend_num, uint8_t status, void *session_p) 390#undef CHECK_SIZE
391#undef CHECK_ENUM_HIGH
392#undef SET_UINT8
393#undef SET_UINT16
394}
395uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length)
777{ 396{
778 (void)messenger; 397 /* Parse a single header for sending */
779 MSISession *session = session_p; 398 assert(dest);
399 assert(value);
400 assert(value_len);
780 401
781 switch ( status ) { 402 *dest = id;
782 case 0: { /* Went offline */ 403 dest ++;
783 int32_t j = 0; 404 *dest = value_len;
784 405 dest ++;
785 for ( ; j < session->max_calls; j ++ ) {
786
787 if ( !session->calls[j] ) continue;
788 406
789 uint16_t i = 0; 407 memcpy(dest, value, value_len);
790 408
791 for ( ; i < session->calls[j]->peer_count; i ++ ) 409 *length += (2 + value_len);
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 410
802 default: 411 return dest + value_len; /* Set to next position ready to be written */
803 break;
804 }
805} 412}
806 413int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg)
807/**
808 * Function called at request timeout
809 */
810static void handle_timeout ( Timer *timer )
811{ 414{
812 /* TODO: Cancel might not arrive there; set up 415 /* Parse and send message */
813 * timers on these cancels and terminate call on 416 assert(m);
814 * their timeout
815 */
816 MSICall *call = timer->session->calls[timer->call_idx];
817 417
418 uint8_t parsed [MSI_MAXMSG_SIZE];
818 419
819 if (call) { 420 uint8_t *it = parsed;
820 LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); 421 uint16_t size = 0;
821 422
822 invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout); 423 if (msg->request.exists) {
823 msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out"); 424 uint8_t cast = msg->request.value;
425 it = msg_parse_header_out(IDRequest, it, &cast,
426 sizeof(cast), &size);
427 } else {
428 LOGGER_DEBUG("Must have request field");
429 return -1;
824 } 430 }
825}
826
827
828/********** Request handlers **********/
829static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg )
830{
831 LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1);
832 431
833 432 if (msg->error.exists) {
834 if (!msg->csettings.exists) {/**/ 433 uint8_t cast = msg->error.value;
835 LOGGER_WARNING("Peer sent invalid codec settings!"); 434 it = msg_parse_header_out(IDError, it, &cast,
836 send_error ( session, call, error_no_callid, msg->friend_id ); 435 sizeof(cast), &size);
837 return 0;
838 } 436 }
839 437
840 if ( call ) { 438 if (msg->capabilities.exists) {
841 if ( call->peers[0] == (uint32_t)msg->friend_id ) { 439 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
842 if (call->state == msi_CallInviting) { 440 sizeof(msg->capabilities.value), &size);
843 /* The glare case. A calls B when at the same time
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 {
885 call = init_call ( session, 1, 0 );
886
887 if ( !call ) {
888 LOGGER_ERROR("Starting call");
889 return 0;
890 }
891 } 441 }
892 442
893 if ( !msg->callid.exists ) { 443 if (it == parsed) {
894 send_error ( session, call, error_no_callid, msg->friend_id ); 444 LOGGER_WARNING("Parsing message failed; empty message");
895 terminate_call(session, call); 445 return -1;
896 return 0;
897 } 446 }
898 447
899 memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); 448 *it = 0;
900 call->state = msi_CallStarting; 449 size ++;
901
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 450
910static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg ) 451 if (m_msi_packet(m, friend_number, parsed, size)) {
911{ 452 LOGGER_DEBUG("Sent message");
912 if ( !call ) {
913 LOGGER_WARNING("Session: %p Handling 'start' on no call");
914 return 0; 453 return 0;
915 } 454 }
916 455
917 (void)msg; 456 return -1;
918
919 LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id );
920
921 call->state = msi_CallActive;
922 invoke_callback(session, call->call_idx, msi_OnStart);
923 return 1;
924} 457}
925 458int send_error (Messenger *m, uint32_t friend_number, MSIError error)
926static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg )
927{ 459{
928 if ( !call ) { 460 /* Send error message */
929 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 461 assert(m);
930 return 0;
931 }
932 462
933 LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); 463 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
934 464
935 invoke_callback(session, call->call_idx, msi_OnReject); 465 MSIMessage msg;
466 msg_init(&msg, requ_pop);
936 467
937 send_reponse(session, call, ending, msg->friend_id); 468 msg.error.exists = true;
938 terminate_call(session, call); 469 msg.error.value = error;
939 470
940 return 1; 471 send_message (m, friend_number, &msg);
472 return 0;
941} 473}
942 474int invoke_callback(MSICall *call, MSICallbackID cb)
943static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg )
944{ 475{
945 if ( !call ) { 476 assert(call);
946 LOGGER_WARNING("Session: %p Handling 'start' on no call");
947 return 0;
948 }
949
950 (void)msg;
951 477
952 LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); 478 if (call->session->callbacks[cb]) {
479 LOGGER_DEBUG("Invoking callback function: %d", cb);
953 480
954 invoke_callback(session, call->call_idx, msi_OnCancel); 481 if (call->session->callbacks[cb] (call->session->av, call) != 0) {
955 terminate_call ( session, call ); 482 LOGGER_WARNING("Callback state handling failed, sending error");
956 483 goto FAILURE;
957 return 1; 484 }
958}
959 485
960static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
961{
962 if ( !call ) {
963 LOGGER_WARNING("Session: %p Handling 'start' on no call");
964 return 0; 486 return 0;
965 } 487 }
966 488
967 LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); 489FAILURE:
490 /* If no callback present or error happened while handling,
491 * an error message will be sent to friend
492 */
968 493
969 invoke_callback(session, call->call_idx, msi_OnEnd); 494 if (call->error == msi_ENone)
970 send_reponse(session, call, ending, msg->friend_id); 495 call->error = msi_EHandle;
971 terminate_call ( session, call );
972 496
973 return 1; 497 return -1;
974} 498}
975 499static MSICall *get_call (MSISession *session, uint32_t friend_number)
976/********** Response handlers **********/
977static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
978{ 500{
979 if ( !call ) { 501 assert(session);
980 LOGGER_WARNING("Session: %p Handling 'start' on no call");
981 return 0;
982 }
983
984 (void)msg;
985 502
986 if ( call->ringing_timer_id ) { 503 if (session->calls == NULL || session->calls_tail < friend_number)
987 LOGGER_WARNING("Call already ringing"); 504 return NULL;
988 return 0;
989 }
990
991 LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
992 505
993 call->ringing_timer_id = timer_alloc 506 return session->calls[friend_number];
994 ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
995 invoke_callback(session, call->call_idx, msi_OnRinging);
996 return 1;
997} 507}
998static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) 508MSICall *new_call (MSISession *session, uint32_t friend_number)
999{ 509{
1000 if ( !call ) { 510 assert(session);
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 511
1007 LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); 512 MSICall *rc = calloc(sizeof(MSICall), 1);
1008 513
1009 invoke_callback(session, call->call_idx, msi_OnSelfCSChange); 514 if (rc == NULL)
1010 515 return NULL;
1011 } else if ( call->state == msi_CallInviting ) {
1012 LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx );
1013 516
1014 call->state = msi_CallActive; 517 rc->session = session;
518 rc->friend_number = friend_number;
1015 519
1016 MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); 520 if (session->calls == NULL) { /* Creating */
1017 send_message ( session, call, msg_start, msg->friend_id ); 521 session->calls = calloc (sizeof(MSICall *), friend_number + 1);
1018 free ( msg_start );
1019 522
523 if (session->calls == NULL) {
524 free(rc);
525 return NULL;
526 }
1020 527
1021 flush_peer_csettings ( call, msg, 0 ); 528 session->calls_tail = session->calls_head = friend_number;
1022 529
1023 /* This is here in case of glare */ 530 } else if (session->calls_tail < friend_number) { /* Appending */
1024 timer_release(session->timer_handler, call->ringing_timer_id); 531 void *tmp = realloc(session->calls, sizeof(MSICall *) * (friend_number + 1));
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 532
1032 return 1; 533 if (tmp == NULL) {
1033} 534 free(rc);
1034static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg ) 535 return NULL;
1035{ 536 }
1036 if ( !call ) {
1037 LOGGER_WARNING("Session: %p Handling 'start' on no call");
1038 return 0;
1039 }
1040
1041 (void)msg;
1042 537
1043 LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); 538 session->calls = tmp;
1044 539
1045 invoke_callback(session, call->call_idx, msi_OnEnd); 540 /* Set fields in between to null */
1046 terminate_call ( session, call ); 541 uint32_t i = session->calls_tail + 1;
1047 542
1048 return 1; 543 for (; i < friend_number; i ++)
1049} 544 session->calls[i] = NULL;
1050static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg )
1051{
1052 if ( !call ) {
1053 LOGGER_WARNING("Handling 'error' on non-existing call!");
1054 return -1;
1055 }
1056 545
1057 LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); 546 rc->prev = session->calls[session->calls_tail];
547 session->calls[session->calls_tail]->next = rc;
1058 548
1059 invoke_callback(session, call->call_idx, msi_OnEnd); 549 session->calls_tail = friend_number;
1060 550
1061 /* Handle error accordingly */ 551 } else if (session->calls_head > friend_number) { /* Inserting at front */
1062 if ( msg->reason.exists ) { 552 rc->next = session->calls[session->calls_head];
1063 /* TODO */ 553 session->calls[session->calls_head]->prev = rc;
554 session->calls_head = friend_number;
1064 } 555 }
1065 556
1066 terminate_call ( session, call ); 557 session->calls[friend_number] = rc;
1067 558 return rc;
1068 return 1;
1069} 559}
1070 560void kill_call (MSICall *call)
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{ 561{
1106 LOGGER_DEBUG("Got msi message"); 562 /* Assume that session mutex is locked */
1107 /* Unused */ 563 if (call == NULL)
1108 (void)messenger;
1109
1110 MSISession *session = object;
1111 MSIMessage *msg;
1112
1113 if ( !length ) {
1114 LOGGER_WARNING("Length param negative");
1115 return; 564 return;
1116 }
1117 565
1118 msg = parse_recv ( data, length ); 566 LOGGER_DEBUG("Killing call: %p", call);
1119 567
1120 if ( !msg ) { 568 MSISession *session = call->session;
1121 LOGGER_WARNING("Error parsing message");
1122 return;
1123 } else {
1124 LOGGER_DEBUG("Successfully parsed message");
1125 }
1126 569
1127 msg->friend_id = source; 570 MSICall *prev = call->prev;
571 MSICall *next = call->next;
1128 572
1129 pthread_mutex_lock(session->mutex); 573 if (prev)
574 prev->next = next;
575 else if (next)
576 session->calls_head = next->friend_number;
577 else goto CLEAR_CONTAINER;
1130 578
1131 /* Find what call */ 579 if (next)
1132 MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; 580 next->prev = prev;
581 else if (prev)
582 session->calls_tail = prev->friend_number;
583 else goto CLEAR_CONTAINER;
1133 584
1134 /* Now handle message */ 585 session->calls[call->friend_number] = NULL;
586 free(call);
587 return;
1135 588
1136 if ( msg->request.exists ) { /* Handle request */ 589CLEAR_CONTAINER:
1137 590 session->calls_head = session->calls_tail = 0;
1138 switch (msg->request.value) { 591 free(session->calls);
1139 case invite: 592 free(call);
1140 handle_recv_invite ( session, call, msg ); 593 session->calls = NULL;
1141 break;
1142
1143 case start:
1144 handle_recv_start ( session, call, msg );
1145 break;
1146
1147 case cancel:
1148 handle_recv_cancel ( session, call, msg );
1149 break;
1150
1151 case reject:
1152 handle_recv_reject ( session, call, msg );
1153 break;
1154
1155 case end:
1156 handle_recv_end ( session, call, msg );
1157 break;
1158 }
1159
1160 } else if ( msg->response.exists ) { /* Handle response */
1161
1162 /* Got response so cancel timer */
1163 if ( call ) timer_release(session->timer_handler, call->request_timer_id);
1164
1165 switch (msg->response.value) {
1166 case ringing:
1167 handle_recv_ringing ( session, call, msg );
1168 break;
1169
1170 case starting:
1171 handle_recv_starting ( session, call, msg );
1172 break;
1173
1174 case ending:
1175 handle_recv_ending ( session, call, msg );
1176 break;
1177
1178 case error:
1179 handle_recv_error ( session, call, msg );
1180 break;
1181 }
1182
1183 } else {
1184 LOGGER_WARNING("Invalid message: no resp nor requ headers");
1185 }
1186
1187 free ( msg );
1188
1189 pthread_mutex_unlock(session->mutex);
1190} 594}
1191 595void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data)
1192
1193
1194/********** User functions **********/
1195void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1196{ 596{
1197 session->callbacks[id].first = callback; 597 (void)m;
1198 session->callbacks[id].second = userdata; 598 MSISession *session = data;
1199}
1200 599
600 switch (status) {
601 case 0: { /* Friend is now offline */
602 LOGGER_DEBUG("Friend %d is now offline", friend_number);
1201 603
1202MSISession *msi_new ( Messenger *messenger, int32_t max_calls ) 604 pthread_mutex_lock(session->mutex);
1203{ 605 MSICall *call = get_call(session, friend_number);
1204 if (messenger == NULL) {
1205 LOGGER_ERROR("Could not init session on empty messenger!");
1206 return NULL;
1207 }
1208 606
1209 if ( !max_calls ) { 607 if (call == NULL) {
1210 LOGGER_WARNING("Invalid max call treshold!"); 608 pthread_mutex_unlock(session->mutex);
1211 return NULL; 609 return;
1212 } 610 }
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
1241 if (create_recursive_mutex(retu->mutex) != 0) {
1242 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
1243 goto error;
1244 }
1245
1246 retu->messenger_handle = messenger;
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
1252 m_callback_msi_packet(messenger, msi_handle_packet, retu );
1253
1254 /* This is called when remote terminates session */
1255 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu);
1256
1257 LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls);
1258 return retu;
1259 611
1260error: 612 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
613 kill_call(call);
614 pthread_mutex_unlock(session->mutex);
615 }
616 break;
1261 617
1262 if (retu->timer_handler) { 618 default:
1263 free(((TimerHandler *)retu->timer_handler)->timers); 619 break;
1264 free(retu->timer_handler);
1265 } 620 }
1266
1267 free(retu->calls);
1268 free(retu);
1269 return NULL;
1270} 621}
1271 622void handle_init (MSICall *call, const MSIMessage *msg)
1272
1273int msi_kill ( MSISession *session )
1274{ 623{
1275 if (session == NULL) { 624 assert(call);
1276 LOGGER_ERROR("Tried to terminate non-existing session"); 625 LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number);
1277 return -1; 626
627 if (!msg->capabilities.exists) {
628 LOGGER_WARNING("Session: %p Invalid capabilities on 'init'");
629 call->error = msi_EInvalidMessage;
630 goto FAILURE;
1278 } 631 }
1279 632
1280 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL); 633 switch (call->state) {
1281 pthread_mutex_lock(session->mutex); 634 case msi_CallInactive: {
635 /* Call requested */
636 call->peer_capabilities = msg->capabilities.value;
637 call->state = msi_CallRequested;
1282 638
1283 /* Cancel active calls */ 639 if (invoke_callback(call, msi_OnInvite) == -1)
1284 int32_t idx = 0; 640 goto FAILURE;
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 } 641 }
642 break;
1300 643
1301 free(((TimerHandler *)session->timer_handler)->timers); 644 case msi_CallActive: {
1302 free(session->timer_handler); 645 /* If peer sent init while the call is already
646 * active it's probable that he is trying to
647 * re-call us while the call is not terminated
648 * on our side. We can assume that in this case
649 * we can automatically answer the re-call.
650 */
1303 651
1304 free ( session->calls ); 652 LOGGER_INFO("Friend is recalling us");
1305 pthread_mutex_unlock(session->mutex);
1306 pthread_mutex_destroy(session->mutex);
1307 653
1308 LOGGER_DEBUG("Terminated session: %p", session); 654 MSIMessage msg;
1309 free ( session ); 655 msg_init(&msg, requ_push);
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 656
657 msg.capabilities.exists = true;
658 msg.capabilities.value = call->self_capabilities;
1323 659
1324 int i = 0; 660 send_message (call->session->messenger, call->friend_number, &msg);
1325 661
1326 for (; i < session->max_calls; i ++) 662 /* If peer changed capabilities during re-call they will
1327 if (session->calls[i] && session->calls[i]->peers[0] == friend_id) { 663 * be handled accordingly during the next step
1328 LOGGER_ERROR("Already in a call with friend %d", friend_id); 664 */
1329 pthread_mutex_unlock(session->mutex);
1330 return msi_ErrorAlreadyInCallWithPeer;
1331 } 665 }
666 break;
1332 667
1333 668 default: {
1334 MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ 669 LOGGER_WARNING("Session: %p Invalid state on 'init'");
1335 670 call->error = msi_EInvalidState;
1336 if ( !call ) { 671 goto FAILURE;
1337 pthread_mutex_unlock(session->mutex); 672 }
1338 LOGGER_ERROR("Cannot handle more calls"); 673 break;
1339 return msi_ErrorReachedCallLimit;
1340 } 674 }
1341 675
1342 *call_index = call->call_idx; 676 return;
1343 677FAILURE:
1344 t_randomstr ( call->id, sizeof(call->id) ); 678 send_error(call->session->messenger, call->friend_number, call->error);
1345 679 kill_call(call);
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} 680}
1366 681void handle_push (MSICall *call, const MSIMessage *msg)
1367int msi_hangup ( MSISession *session, int32_t call_index )
1368{ 682{
1369 pthread_mutex_lock(session->mutex); 683 assert(call);
1370 LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index);
1371 684
1372 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 685 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
1373 LOGGER_ERROR("Invalid call index!");
1374 pthread_mutex_unlock(session->mutex);
1375 return msi_ErrorNoCall;
1376 }
1377 686
1378 if ( session->calls[call_index]->state != msi_CallActive ) { 687 if (!msg->capabilities.exists) {
1379 LOGGER_ERROR("Call is not active!"); 688 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
1380 pthread_mutex_unlock(session->mutex); 689 call->error = msi_EInvalidMessage;
1381 return msi_ErrorInvalidState; 690 goto FAILURE;
1382 } 691 }
1383 692
1384 MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); 693 switch (call->state) {
1385 694 case msi_CallActive: {
1386 /* hangup for each peer */ 695 /* Only act if capabilities changed */
1387 int it = 0; 696 if (call->peer_capabilities != msg->capabilities.value) {
697 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
1388 698
1389 for ( ; it < session->calls[call_index]->peer_count; it ++ ) 699 call->peer_capabilities = msg->capabilities.value;
1390 send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1391 700
1392 session->calls[call_index]->state = msi_CallOver; 701 if (invoke_callback(call, msi_OnCapabilities) == -1)
702 goto FAILURE;
703 }
704 }
705 break;
1393 706
1394 free ( msg_end ); 707 case msi_CallRequesting: {
708 LOGGER_INFO("Friend answered our call");
1395 709
1396 session->calls[call_index]->request_timer_id = 710 /* Call started */
1397 timer_alloc ( session, handle_timeout, call_index, m_deftout ); 711 call->peer_capabilities = msg->capabilities.value;
712 call->state = msi_CallActive;
1398 713
1399 pthread_mutex_unlock(session->mutex); 714 if (invoke_callback(call, msi_OnStart) == -1)
1400 return 0; 715 goto FAILURE;
1401}
1402 716
1403int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings ) 717 }
1404{ 718 break;
1405 pthread_mutex_lock(session->mutex);
1406 LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1407
1408 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1409 LOGGER_ERROR("Invalid call index!");
1410 pthread_mutex_unlock(session->mutex);
1411 return msi_ErrorNoCall;
1412 }
1413 719
1414 if ( session->calls[call_index]->state != msi_CallStarting ) { 720 /* Pushes during initialization state are ignored */
1415 LOGGER_ERROR("Call is in invalid state!"); 721 case msi_CallInactive:
1416 pthread_mutex_unlock(session->mutex); 722 case msi_CallRequested: {
1417 return msi_ErrorInvalidState; 723 LOGGER_WARNING("Ignoring invalid push");
724 }
725 break;
1418 } 726 }
1419 727
1420 MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); 728 return;
1421
1422 session->calls[call_index]->csettings_local = *csettings;
1423
1424 msi_msg_set_csettings(msg_starting, csettings);
1425
1426 send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] );
1427 free ( msg_starting );
1428
1429 session->calls[call_index]->state = msi_CallActive;
1430 729
1431 pthread_mutex_unlock(session->mutex); 730FAILURE:
1432 return 0; 731 send_error(call->session->messenger, call->friend_number, call->error);
732 kill_call(call);
1433} 733}
1434 734void handle_pop (MSICall *call, const MSIMessage *msg)
1435int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason )
1436{ 735{
1437 pthread_mutex_lock(session->mutex); 736 assert(call);
1438 LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1439 737
1440 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 738 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
1441 LOGGER_ERROR("Invalid call index!");
1442 pthread_mutex_unlock(session->mutex);
1443 return msi_ErrorNoCall;
1444 }
1445
1446 if ( session->calls[call_index]->state != msi_CallInviting ) {
1447 LOGGER_ERROR("Call is in invalid state: %u", session->calls[call_index]->state);
1448 pthread_mutex_unlock(session->mutex);
1449 return msi_ErrorInvalidState;
1450 }
1451 739
1452 MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); 740 /* callback errors are ignored */
1453 741
1454 /* FIXME */ 742 if (msg->error.exists) {
1455#if 0 743 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
744 call->error = msg->error.value;
745 invoke_callback(call, msi_OnError);
1456 746
1457 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { 747 } else switch (call->state) {
1458 MSIReasonStrType reason_cast; 748 case msi_CallInactive: {
1459 memset(reason_cast, '\0', sizeof(MSIReasonStrType)); 749 LOGGER_ERROR("Handling what should be impossible case");
1460 memcpy(reason_cast, reason, strlen(reason)); 750 abort();
1461 msi_msg_set_reason(msg_cancel, reason_cast); 751 }
1462 } 752 break;
1463
1464#else
1465 (void)reason;
1466 753
1467#endif 754 case msi_CallActive: {
755 /* Hangup */
756 LOGGER_INFO("Friend hung up on us");
757 invoke_callback(call, msi_OnEnd);
758 }
759 break;
1468 760
1469 send_message ( session, session->calls[call_index], msg_cancel, peer ); 761 case msi_CallRequesting: {
1470 free ( msg_cancel ); 762 /* Reject */
763 LOGGER_INFO("Friend rejected our call");
764 invoke_callback(call, msi_OnEnd);
765 }
766 break;
1471 767
1472 terminate_call ( session, session->calls[call_index] ); 768 case msi_CallRequested: {
1473 pthread_mutex_unlock(session->mutex); 769 /* Cancel */
770 LOGGER_INFO("Friend canceled call invite");
771 invoke_callback(call, msi_OnEnd);
772 }
773 break;
774 }
1474 775
1475 return 0; 776 kill_call (call);
1476} 777}
1477 778void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
1478int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1479{ 779{
1480 pthread_mutex_lock(session->mutex); 780 LOGGER_DEBUG("Got msi message");
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
1497 /* FIXME */
1498#if 0
1499
1500 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
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
1512 send_message ( session, session->calls[call_index], msg_reject,
1513 session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] );
1514 free ( msg_reject );
1515
1516 session->calls[call_index]->state = msi_CallOver;
1517 session->calls[call_index]->request_timer_id =
1518 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1519
1520 pthread_mutex_unlock(session->mutex);
1521 return 0;
1522}
1523 781
1524int msi_stopcall ( MSISession *session, int32_t call_index ) 782 MSISession *session = object;
1525{ 783 MSIMessage msg;
1526 pthread_mutex_lock(session->mutex);
1527 LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index);
1528 784
1529 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 785 if (msg_parse_in (&msg, data, length) == -1) {
1530 pthread_mutex_unlock(session->mutex); 786 LOGGER_WARNING("Error parsing message");
1531 return msi_ErrorNoCall; 787 send_error(m, friend_number, msi_EInvalidMessage);
788 return;
789 } else {
790 LOGGER_DEBUG("Successfully parsed message");
1532 } 791 }
1533 792
1534 /* just terminate it */
1535
1536 terminate_call ( session, session->calls[call_index] );
1537
1538 pthread_mutex_unlock(session->mutex);
1539 return 0;
1540}
1541
1542int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
1543{
1544 pthread_mutex_lock(session->mutex); 793 pthread_mutex_lock(session->mutex);
794 MSICall *call = get_call(session, friend_number);
1545 795
1546 LOGGER_DEBUG("Changing media on call: %d", call_index); 796 if (call == NULL) {
1547 797 if (msg.request.value != requ_init) {
1548 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 798 send_error(m, friend_number, msi_EStrayMessage);
1549 LOGGER_ERROR("Invalid call index!"); 799 pthread_mutex_unlock(session->mutex);
1550 pthread_mutex_unlock(session->mutex); 800 return;
1551 return msi_ErrorNoCall; 801 }
1552 }
1553 802
1554 MSICall *call = session->calls[call_index]; 803 call = new_call(session, friend_number);
1555 804
1556 if ( call->state != msi_CallActive ) { 805 if (call == NULL) {
1557 LOGGER_ERROR("Call is not active!"); 806 send_error(m, friend_number, msi_ESystem);
1558 pthread_mutex_unlock(session->mutex); 807 pthread_mutex_unlock(session->mutex);
1559 return msi_ErrorInvalidState; 808 return;
1560 } 809 }
1561
1562 MSICSettings *local = &call->csettings_local;
1563
1564 if (
1565 local->call_type == csettings->call_type &&
1566 local->video_bitrate == csettings->video_bitrate &&
1567 local->max_video_width == csettings->max_video_width &&
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 } 810 }
1577 811
1578 *local = *csettings; 812 switch (msg.request.value) {
1579 813 case requ_init:
1580 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); 814 handle_init(call, &msg);
1581 815 break;
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);
1596
1597 TimerHandler *timer = session->timer_handler;
1598
1599 uint64_t time = current_time_monotonic();
1600
1601 while ( timer->timers[0] && timer->timers[0]->timeout < time ) {
1602 LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout);
1603 816
1604 int id = timer->timers[0]->id; 817 case requ_push:
1605 timer->timers[0]->func(timer->timers[0]); 818 handle_push(call, &msg);
819 break;
1606 820
1607 /* In case function has released timer */ 821 case requ_pop:
1608 if (timer->timers[0] && timer->timers[0]->id == id) 822 handle_pop(call, &msg); /* always kills the call */
1609 timer_release(timer, id); 823 break;
1610 } 824 }
1611 825
1612 pthread_mutex_unlock(session->mutex); 826 pthread_mutex_unlock(session->mutex);