diff options
Diffstat (limited to 'toxav/msi.c')
-rw-r--r-- | toxav/msi.c | 1837 |
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 | ||
50 | typedef uint8_t MSIRawCSettingsType[23]; | ||
51 | |||
52 | typedef enum { | 45 | typedef 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 | ||
61 | typedef enum { | ||
62 | TypeRequest, | ||
63 | TypeResponse, | ||
64 | |||
65 | } MSIMessageType; | ||
66 | 52 | ||
67 | typedef enum { | 53 | typedef 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 | ||
76 | typedef 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) \ |
86 | typedef struct _MSIHeader##header { \ | 61 | typedef struct { \ |
87 | val_type value; \ | 62 | val_type value; \ |
88 | _Bool exists; \ | 63 | bool exists; \ |
89 | } MSIHeader##header; | 64 | } MSIHeader##header |
90 | |||
91 | 65 | ||
92 | GENERIC_HEADER ( Request, MSIRequest ) | ||
93 | GENERIC_HEADER ( Response, MSIResponse ) | ||
94 | GENERIC_HEADER ( CallId, MSICallIDType ) | ||
95 | GENERIC_HEADER ( Reason, MSIReasonStrType ) | ||
96 | GENERIC_HEADER ( CSettings, MSIRawCSettingsType ) | ||
97 | 66 | ||
67 | GENERIC_HEADER (Request, MSIRequest); | ||
68 | GENERIC_HEADER (Error, MSIError); | ||
69 | GENERIC_HEADER (Capabilities, uint8_t); | ||
98 | 70 | ||
99 | typedef 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 | ||
72 | typedef struct { | ||
73 | MSIHeaderRequest request; | ||
74 | MSIHeaderError error; | ||
75 | MSIHeaderCapabilities capabilities; | ||
109 | } MSIMessage; | 76 | } MSIMessage; |
110 | 77 | ||
111 | 78 | ||
112 | static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) | 79 | void msg_init (MSIMessage *dest, MSIRequest request); |
113 | { | 80 | int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length); |
114 | if ( s->callbacks[i].first ) { | 81 | uint8_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); | 82 | static int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg); |
83 | int send_error (Messenger *m, uint32_t friend_number, MSIError error); | ||
84 | static int invoke_callback(MSICall *call, MSICallbackID cb); | ||
85 | static MSICall *get_call (MSISession *session, uint32_t friend_number); | ||
86 | MSICall *new_call (MSISession *session, uint32_t friend_number); | ||
87 | void kill_call (MSICall *call); | ||
88 | void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); | ||
89 | void handle_init (MSICall *call, const MSIMessage *msg); | ||
90 | void handle_push (MSICall *call, const MSIMessage *msg); | ||
91 | void handle_pop (MSICall *call, const MSIMessage *msg); | ||
92 | void 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 | */ |
126 | static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) | 98 | void 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 | 107 | MSISession *msi_new (Messenger *m) | |
202 | /** | ||
203 | * Create the message. | ||
204 | */ | ||
205 | MSIMessage *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 | */ | ||
230 | MSIMessage *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 | 137 | int msi_kill (MSISession *session) | |
253 | |||
254 | /** | ||
255 | * Speaks for itself. | ||
256 | */ | ||
257 | uint8_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 */ |
289 | uint16_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 | } |
170 | int 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 | |||
332 | void 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 | |||
340 | void 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 | |||
348 | void 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 | |||
392 | void 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 | 211 | int msi_hangup (MSICall *call) | |
427 | typedef 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 | |||
436 | typedef struct _TimerHandler { | ||
437 | Timer **timers; | ||
438 | |||
439 | uint32_t max_capacity; | ||
440 | uint32_t size; | ||
441 | } TimerHandler; | ||
442 | |||
443 | |||
444 | static 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 | 240 | int msi_answer (MSICall *call, uint8_t capabilities) | |
489 | static 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 | */ | ||
528 | static 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; |
548 | typedef 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 | */ | ||
565 | static 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 | 277 | int msi_change_capabilities(MSICall *call, uint8_t capabilities) | |
580 | static 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 | |||
600 | static 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 | |||
608 | static 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 | */ | ||
629 | static 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 | */ |
638 | static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) | 315 | void 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 | 321 | int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length) | |
668 | |||
669 | /** | ||
670 | * Add peer to peer list. | ||
671 | */ | ||
672 | static 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 | ||
690 | static 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; | |
704 | static 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 | |||
753 | static 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 | |||
776 | static 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 | 395 | uint8_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 | */ | ||
810 | static 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 */ |
829 | static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) | 412 | } |
413 | int 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 | |||
910 | static 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 | |||
926 | static 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 | ||
943 | static 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 | 458 | int send_error (Messenger *m, uint32_t friend_number, MSIError error) | |
960 | static 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 **********/ | ||
977 | static 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 | } |
998 | static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) | 474 | int 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 | } | ||
1034 | static 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; | 489 | FAILURE: |
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 | } |
1050 | static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) | 499 | static 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 | 508 | MSICall *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 | */ | ||
1103 | static 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 | 560 | void kill_call (MSICall *call) | |
1192 | |||
1193 | |||
1194 | /********** User functions **********/ | ||
1195 | void 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 | |||
1202 | MSISession *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 | ||
1260 | error: | 585 | session->calls[call->friend_number] = NULL; |
586 | free(call); | ||
587 | return; | ||
1261 | 588 | ||
1262 | if (retu->timer_handler) { | 589 | CLEAR_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 | 595 | void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data) | |
1272 | |||
1273 | int 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 | |||
1313 | int 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 | 622 | void handle_init (MSICall* call, const MSIMessage* msg) | |
1367 | int 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 | 678 | FAILURE: | |
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 | 682 | void handle_push (MSICall *call, const MSIMessage *msg) | |
1403 | int 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 | ||
1435 | int 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 ); | 731 | FAILURE: |
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 | 735 | void handle_pop (MSICall *call, const MSIMessage *msg) | |
1478 | int 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 | ||
1524 | int 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 | 779 | void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object) | |
1542 | int 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 | |||
1593 | void 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); |