diff options
Diffstat (limited to 'toxav/msi.c')
-rw-r--r-- | toxav/msi.c | 1808 |
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 | ||
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); |
116 | 83 | int send_error (Messenger *m, uint32_t friend_number, MSIError error); | |
117 | s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second ); | 84 | static int invoke_callback(MSICall *call, MSICallbackID cb); |
118 | } | 85 | static MSICall *get_call (MSISession *session, uint32_t friend_number); |
119 | } | 86 | MSICall *new_call (MSISession *session, uint32_t friend_number); |
120 | 87 | void kill_call (MSICall *call); | |
121 | /** | 88 | void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); |
122 | * Parse raw 'data' received from socket into MSIMessage struct. | 89 | void handle_init (MSICall *call, const MSIMessage *msg); |
123 | * Every message has to have end value of 'end_byte' or _undefined_ behavior | 90 | void handle_push (MSICall *call, const MSIMessage *msg); |
124 | * occures. The best practice is to check the end of the message at the handle_packet. | 91 | void handle_pop (MSICall *call, const MSIMessage *msg); |
125 | */ | 92 | void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object); |
126 | static 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 | */ |
205 | MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) | 98 | void 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 | 107 | MSISession *msi_new (Messenger *m) | |
226 | |||
227 | /** | ||
228 | * Parse data from handle_packet. | ||
229 | */ | ||
230 | MSIMessage *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 | */ | ||
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 | { | ||
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 | 137 | int msi_kill (MSISession *session) | |
285 | |||
286 | /** | ||
287 | * Parse MSIMessage to send. | ||
288 | */ | ||
289 | uint16_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 | |||
332 | void 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 | 170 | int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities) | |
340 | void 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 | ||
348 | void 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 | ||
392 | void 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 | 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 | 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 | 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) |
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 | */ | ||
528 | static 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 | 277 | int msi_change_capabilities(MSICall *call, uint8_t capabilities) | |
547 | /* TODO: it would be nice to actually have some sane error codes */ | ||
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 | |||
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 | */ | ||
565 | static 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 | ||
580 | static 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 | |||
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 | |||
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 | ||
690 | static 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 | ||
704 | static 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 | |||
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 | 389 | ||
776 | static 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 | } | ||
395 | uint8_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 | 413 | int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg) | |
807 | /** | ||
808 | * Function called at request timeout | ||
809 | */ | ||
810 | static 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 **********/ | ||
829 | static 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 | ||
910 | static 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 | 458 | int send_error (Messenger *m, uint32_t friend_number, MSIError error) | |
926 | static 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 | 474 | int invoke_callback(MSICall *call, MSICallbackID cb) | |
943 | static 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 | ||
960 | static 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); | 489 | FAILURE: |
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 | 499 | static MSICall *get_call (MSISession *session, uint32_t friend_number) | |
976 | /********** Response handlers **********/ | ||
977 | static 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 | } |
998 | static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) | 508 | MSICall *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); |
1034 | static 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; |
1050 | static 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 | 560 | void 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 | */ | ||
1103 | static 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 */ | 589 | CLEAR_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 | 595 | void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data) | |
1192 | |||
1193 | |||
1194 | /********** User functions **********/ | ||
1195 | void 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 | ||
1202 | MSISession *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 | ||
1260 | error: | 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 | 622 | void handle_init (MSICall *call, const MSIMessage *msg) | |
1272 | |||
1273 | int 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 | |||
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 | 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 | 677 | FAILURE: | |
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 | 681 | void handle_push (MSICall *call, const MSIMessage *msg) | |
1367 | int 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 | ||
1403 | int 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); | 730 | FAILURE: |
1432 | return 0; | 731 | send_error(call->session->messenger, call->friend_number, call->error); |
732 | kill_call(call); | ||
1433 | } | 733 | } |
1434 | 734 | void handle_pop (MSICall *call, const MSIMessage *msg) | |
1435 | int 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 | 778 | void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object) | |
1478 | int 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 | ||
1524 | int 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 | |||
1542 | int 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 | |||
1593 | void 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); |