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