diff options
Diffstat (limited to 'src/conversation/conversation_api.c')
-rw-r--r-- | src/conversation/conversation_api.c | 897 |
1 files changed, 0 insertions, 897 deletions
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c deleted file mode 100644 index 9c4c520be..000000000 --- a/src/conversation/conversation_api.c +++ /dev/null | |||
@@ -1,897 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2013, 2014 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file conversation/conversation_api.c | ||
23 | * @brief phone and caller API to the conversation service | ||
24 | * @author Simon Dieterle | ||
25 | * @author Andreas Fuchs | ||
26 | * @author Christian Grothoff | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | #include "gnunet_conversation_service.h" | ||
30 | #include "conversation.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Possible states of a caller. | ||
35 | */ | ||
36 | enum CallerState | ||
37 | { | ||
38 | /** | ||
39 | * The phone is ringing (user knows about incoming call). | ||
40 | */ | ||
41 | CS_RINGING, | ||
42 | |||
43 | /** | ||
44 | * The phone is in an active conversation. | ||
45 | */ | ||
46 | CS_ACTIVE, | ||
47 | |||
48 | /** | ||
49 | * We suspended the conversation. | ||
50 | */ | ||
51 | CS_CALLEE_SUSPENDED, | ||
52 | |||
53 | /** | ||
54 | * Caller suspended the conversation. | ||
55 | */ | ||
56 | CS_CALLER_SUSPENDED, | ||
57 | |||
58 | /** | ||
59 | * Both sides suspended the conversation. | ||
60 | */ | ||
61 | CS_BOTH_SUSPENDED | ||
62 | }; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * A caller is the handle we have for an incoming call. | ||
67 | */ | ||
68 | struct GNUNET_CONVERSATION_Caller | ||
69 | { | ||
70 | /** | ||
71 | * We keep all callers in a DLL. | ||
72 | */ | ||
73 | struct GNUNET_CONVERSATION_Caller *next; | ||
74 | |||
75 | /** | ||
76 | * We keep all callers in a DLL. | ||
77 | */ | ||
78 | struct GNUNET_CONVERSATION_Caller *prev; | ||
79 | |||
80 | /** | ||
81 | * Our phone. | ||
82 | */ | ||
83 | struct GNUNET_CONVERSATION_Phone *phone; | ||
84 | |||
85 | /** | ||
86 | * Function to call for phone events. | ||
87 | */ | ||
88 | GNUNET_CONVERSATION_CallerEventHandler event_handler; | ||
89 | |||
90 | /** | ||
91 | * Closure for @e event_handler | ||
92 | */ | ||
93 | void *event_handler_cls; | ||
94 | |||
95 | /** | ||
96 | * Speaker, or NULL if none is attached. | ||
97 | */ | ||
98 | struct GNUNET_SPEAKER_Handle *speaker; | ||
99 | |||
100 | /** | ||
101 | * Microphone, or NULL if none is attached. | ||
102 | */ | ||
103 | struct GNUNET_MICROPHONE_Handle *mic; | ||
104 | |||
105 | /** | ||
106 | * Identity of the person calling us. | ||
107 | */ | ||
108 | struct GNUNET_IDENTITY_PublicKey caller_id; | ||
109 | |||
110 | /** | ||
111 | * Internal handle to identify the caller with the service. | ||
112 | */ | ||
113 | uint32_t cid; | ||
114 | |||
115 | /** | ||
116 | * State machine for the phone. | ||
117 | */ | ||
118 | enum CallerState state; | ||
119 | }; | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Possible states of a phone. | ||
124 | */ | ||
125 | enum PhoneState | ||
126 | { | ||
127 | /** | ||
128 | * We still need to register the phone. | ||
129 | */ | ||
130 | PS_REGISTER = 0, | ||
131 | |||
132 | /** | ||
133 | * We are waiting for calls. | ||
134 | */ | ||
135 | PS_READY | ||
136 | }; | ||
137 | |||
138 | |||
139 | /** | ||
140 | * A phone is a device that can ring to signal an incoming call and | ||
141 | * that you can pick up to answer the call and hang up to terminate | ||
142 | * the call. You can also hang up a ringing phone immediately | ||
143 | * (without picking it up) to stop it from ringing. Phones have | ||
144 | * caller ID. You can ask the phone for its record and make that | ||
145 | * record available (via GNS) to enable others to call you. | ||
146 | * Multiple phones maybe connected to the same line (the line is | ||
147 | * something rather internal to a phone and not obvious from it). | ||
148 | * You can only have one conversation per phone at any time. | ||
149 | */ | ||
150 | struct GNUNET_CONVERSATION_Phone | ||
151 | { | ||
152 | /** | ||
153 | * Our configuration. | ||
154 | */ | ||
155 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
156 | |||
157 | /** | ||
158 | * We keep all callers in a DLL. | ||
159 | */ | ||
160 | struct GNUNET_CONVERSATION_Caller *caller_head; | ||
161 | |||
162 | /** | ||
163 | * We keep all callers in a DLL. | ||
164 | */ | ||
165 | struct GNUNET_CONVERSATION_Caller *caller_tail; | ||
166 | |||
167 | /** | ||
168 | * Function to call for phone events. | ||
169 | */ | ||
170 | GNUNET_CONVERSATION_PhoneEventHandler event_handler; | ||
171 | |||
172 | /** | ||
173 | * Closure for @e event_handler | ||
174 | */ | ||
175 | void *event_handler_cls; | ||
176 | |||
177 | /** | ||
178 | * Connection to NAMESTORE (for reverse lookup). | ||
179 | */ | ||
180 | struct GNUNET_NAMESTORE_Handle *ns; | ||
181 | |||
182 | /** | ||
183 | * Handle for transmitting to the CONVERSATION service. | ||
184 | */ | ||
185 | struct GNUNET_MQ_Handle *mq; | ||
186 | |||
187 | /** | ||
188 | * This phone's record. | ||
189 | */ | ||
190 | struct GNUNET_CONVERSATION_PhoneRecord my_record; | ||
191 | |||
192 | /** | ||
193 | * My GNS zone. | ||
194 | */ | ||
195 | struct GNUNET_IDENTITY_PrivateKey my_zone; | ||
196 | |||
197 | /** | ||
198 | * State machine for the phone. | ||
199 | */ | ||
200 | enum PhoneState state; | ||
201 | }; | ||
202 | |||
203 | |||
204 | /** | ||
205 | * The phone got disconnected, reconnect to the service. | ||
206 | * | ||
207 | * @param phone phone to reconnect | ||
208 | */ | ||
209 | static void | ||
210 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone); | ||
211 | |||
212 | |||
213 | /** | ||
214 | * Process recorded audio data. | ||
215 | * | ||
216 | * @param cls closure with the `struct GNUNET_CONVERSATION_Caller` | ||
217 | * @param data_size number of bytes in @a data | ||
218 | * @param data audio data to play | ||
219 | */ | ||
220 | static void | ||
221 | transmit_phone_audio (void *cls, | ||
222 | size_t data_size, | ||
223 | const void *data) | ||
224 | { | ||
225 | struct GNUNET_CONVERSATION_Caller *caller = cls; | ||
226 | struct GNUNET_CONVERSATION_Phone *phone = caller->phone; | ||
227 | struct GNUNET_MQ_Envelope *e; | ||
228 | struct ClientAudioMessage *am; | ||
229 | |||
230 | e = GNUNET_MQ_msg_extra (am, | ||
231 | data_size, | ||
232 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
233 | am->cid = caller->cid; | ||
234 | GNUNET_memcpy (&am[1], | ||
235 | data, | ||
236 | data_size); | ||
237 | GNUNET_MQ_send (phone->mq, | ||
238 | e); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * We received a `struct ClientPhoneRingMessage` | ||
243 | * | ||
244 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
245 | * @param ring the message | ||
246 | */ | ||
247 | static enum GNUNET_GenericReturnValue | ||
248 | check_phone_ring (void *cls, | ||
249 | const struct ClientPhoneRingMessage *ring) | ||
250 | { | ||
251 | //FIXME | ||
252 | return GNUNET_OK; | ||
253 | } | ||
254 | /** | ||
255 | * We received a `struct ClientPhoneRingMessage` | ||
256 | * | ||
257 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
258 | * @param ring the message | ||
259 | */ | ||
260 | static void | ||
261 | handle_phone_ring (void *cls, | ||
262 | const struct ClientPhoneRingMessage *ring) | ||
263 | { | ||
264 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
265 | struct GNUNET_CONVERSATION_Caller *caller; | ||
266 | struct GNUNET_IDENTITY_PublicKey caller_id; | ||
267 | size_t key_len; | ||
268 | size_t read; | ||
269 | |||
270 | key_len = ntohl (ring->key_len); | ||
271 | switch (phone->state) | ||
272 | { | ||
273 | case PS_REGISTER: | ||
274 | GNUNET_assert (0); | ||
275 | break; | ||
276 | |||
277 | case PS_READY: | ||
278 | if ((GNUNET_SYSERR == | ||
279 | GNUNET_IDENTITY_read_public_key_from_buffer (&ring[1], | ||
280 | key_len, | ||
281 | &caller_id, | ||
282 | &read)) || | ||
283 | (read != key_len)) | ||
284 | { | ||
285 | GNUNET_break (0); | ||
286 | break; | ||
287 | } | ||
288 | caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller); | ||
289 | caller->phone = phone; | ||
290 | GNUNET_CONTAINER_DLL_insert (phone->caller_head, | ||
291 | phone->caller_tail, | ||
292 | caller); | ||
293 | caller->caller_id = caller_id; | ||
294 | caller->cid = ring->cid; | ||
295 | caller->state = CS_RINGING; | ||
296 | phone->event_handler (phone->event_handler_cls, | ||
297 | GNUNET_CONVERSATION_EC_PHONE_RING, | ||
298 | caller, | ||
299 | &caller->caller_id); | ||
300 | break; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | |||
305 | /** | ||
306 | * Find the record of the caller matching the @a cid | ||
307 | * | ||
308 | * @param phone phone to search | ||
309 | * @param cid caller ID to search for (in NBO) | ||
310 | * @return NULL if @a cid was not found | ||
311 | */ | ||
312 | static struct GNUNET_CONVERSATION_Caller * | ||
313 | find_caller (struct GNUNET_CONVERSATION_Phone *phone, | ||
314 | uint32_t cid) | ||
315 | { | ||
316 | struct GNUNET_CONVERSATION_Caller *caller; | ||
317 | |||
318 | for (caller = phone->caller_head; NULL != caller; caller = caller->next) | ||
319 | if (cid == caller->cid) | ||
320 | return caller; | ||
321 | return NULL; | ||
322 | } | ||
323 | |||
324 | |||
325 | /** | ||
326 | * We received a `struct ClientPhoneHangupMessage`. | ||
327 | * | ||
328 | * @param cls the `struct GNUNET_CONVERSATION_Phone *` | ||
329 | * @param hang the message | ||
330 | */ | ||
331 | static void | ||
332 | handle_phone_hangup (void *cls, | ||
333 | const struct ClientPhoneHangupMessage *hang) | ||
334 | { | ||
335 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
336 | struct GNUNET_CONVERSATION_Caller *caller; | ||
337 | |||
338 | caller = find_caller (phone, | ||
339 | hang->cid); | ||
340 | if (NULL == caller) | ||
341 | { | ||
342 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
343 | "Received HANG_UP message for unknown caller ID %u\n", | ||
344 | (unsigned int) hang->cid); | ||
345 | return; | ||
346 | } | ||
347 | |||
348 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
349 | "Received HANG_UP message, terminating call with `%s'\n", | ||
350 | GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id)); | ||
351 | switch (caller->state) | ||
352 | { | ||
353 | case CS_RINGING: | ||
354 | phone->event_handler (phone->event_handler_cls, | ||
355 | GNUNET_CONVERSATION_EC_PHONE_HUNG_UP, | ||
356 | caller, | ||
357 | &caller->caller_id); | ||
358 | break; | ||
359 | |||
360 | case CS_ACTIVE: | ||
361 | caller->speaker->disable_speaker (caller->speaker->cls); | ||
362 | caller->mic->disable_microphone (caller->mic->cls); | ||
363 | phone->event_handler (phone->event_handler_cls, | ||
364 | GNUNET_CONVERSATION_EC_PHONE_HUNG_UP, | ||
365 | caller, | ||
366 | &caller->caller_id); | ||
367 | break; | ||
368 | |||
369 | case CS_CALLEE_SUSPENDED: | ||
370 | case CS_CALLER_SUSPENDED: | ||
371 | case CS_BOTH_SUSPENDED: | ||
372 | phone->event_handler (phone->event_handler_cls, | ||
373 | GNUNET_CONVERSATION_EC_PHONE_HUNG_UP, | ||
374 | caller, | ||
375 | &caller->caller_id); | ||
376 | break; | ||
377 | } | ||
378 | GNUNET_CONTAINER_DLL_remove (phone->caller_head, | ||
379 | phone->caller_tail, | ||
380 | caller); | ||
381 | GNUNET_free (caller); | ||
382 | } | ||
383 | |||
384 | |||
385 | /** | ||
386 | * We received a `struct ClientPhoneSuspendMessage`. | ||
387 | * | ||
388 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
389 | * @param suspend the message | ||
390 | */ | ||
391 | static void | ||
392 | handle_phone_suspend (void *cls, | ||
393 | const struct ClientPhoneSuspendMessage *suspend) | ||
394 | { | ||
395 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
396 | struct GNUNET_CONVERSATION_Caller *caller; | ||
397 | |||
398 | caller = find_caller (phone, | ||
399 | suspend->cid); | ||
400 | if (NULL == caller) | ||
401 | return; | ||
402 | switch (caller->state) | ||
403 | { | ||
404 | case CS_RINGING: | ||
405 | GNUNET_break_op (0); | ||
406 | break; | ||
407 | |||
408 | case CS_ACTIVE: | ||
409 | caller->state = CS_CALLER_SUSPENDED; | ||
410 | caller->speaker->disable_speaker (caller->speaker->cls); | ||
411 | caller->mic->disable_microphone (caller->mic->cls); | ||
412 | caller->event_handler (caller->event_handler_cls, | ||
413 | GNUNET_CONVERSATION_EC_CALLER_SUSPEND); | ||
414 | break; | ||
415 | |||
416 | case CS_CALLEE_SUSPENDED: | ||
417 | caller->state = CS_BOTH_SUSPENDED; | ||
418 | caller->event_handler (caller->event_handler_cls, | ||
419 | GNUNET_CONVERSATION_EC_CALLER_SUSPEND); | ||
420 | break; | ||
421 | |||
422 | case CS_CALLER_SUSPENDED: | ||
423 | case CS_BOTH_SUSPENDED: | ||
424 | GNUNET_break_op (0); | ||
425 | break; | ||
426 | } | ||
427 | } | ||
428 | |||
429 | |||
430 | /** | ||
431 | * We received a `struct ClientPhoneResumeMessage`. | ||
432 | * | ||
433 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
434 | * @param resume the message | ||
435 | */ | ||
436 | static void | ||
437 | handle_phone_resume (void *cls, | ||
438 | const struct ClientPhoneResumeMessage *resume) | ||
439 | { | ||
440 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
441 | struct GNUNET_CONVERSATION_Caller *caller; | ||
442 | |||
443 | caller = find_caller (phone, | ||
444 | resume->cid); | ||
445 | if (NULL == caller) | ||
446 | return; | ||
447 | switch (caller->state) | ||
448 | { | ||
449 | case CS_RINGING: | ||
450 | GNUNET_break_op (0); | ||
451 | break; | ||
452 | |||
453 | case CS_ACTIVE: | ||
454 | case CS_CALLEE_SUSPENDED: | ||
455 | GNUNET_break_op (0); | ||
456 | break; | ||
457 | |||
458 | case CS_CALLER_SUSPENDED: | ||
459 | caller->state = CS_ACTIVE; | ||
460 | caller->speaker->enable_speaker (caller->speaker->cls); | ||
461 | caller->mic->enable_microphone (caller->mic->cls, | ||
462 | &transmit_phone_audio, | ||
463 | caller); | ||
464 | caller->event_handler (caller->event_handler_cls, | ||
465 | GNUNET_CONVERSATION_EC_CALLER_RESUME); | ||
466 | break; | ||
467 | |||
468 | case CS_BOTH_SUSPENDED: | ||
469 | caller->state = CS_CALLEE_SUSPENDED; | ||
470 | caller->event_handler (caller->event_handler_cls, | ||
471 | GNUNET_CONVERSATION_EC_CALLER_RESUME); | ||
472 | break; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | |||
477 | /** | ||
478 | * We received a `struct ClientAudioMessage`, check it is well-formed. | ||
479 | * | ||
480 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
481 | * @param am the message | ||
482 | * @return #GNUNET_OK if @a am is well-formed | ||
483 | */ | ||
484 | static int | ||
485 | check_phone_audio (void *cls, | ||
486 | const struct ClientAudioMessage *am) | ||
487 | { | ||
488 | (void) cls; | ||
489 | (void) am; | ||
490 | |||
491 | /* any variable-size payload is OK */ | ||
492 | return GNUNET_OK; | ||
493 | } | ||
494 | |||
495 | |||
496 | /** | ||
497 | * We received a `struct ClientAudioMessage` | ||
498 | * | ||
499 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
500 | * @param am the message | ||
501 | */ | ||
502 | static void | ||
503 | handle_phone_audio (void *cls, | ||
504 | const struct ClientAudioMessage *am) | ||
505 | { | ||
506 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
507 | struct GNUNET_CONVERSATION_Caller *caller; | ||
508 | |||
509 | caller = find_caller (phone, | ||
510 | am->cid); | ||
511 | if (NULL == caller) | ||
512 | return; | ||
513 | switch (caller->state) | ||
514 | { | ||
515 | case CS_RINGING: | ||
516 | GNUNET_break_op (0); | ||
517 | break; | ||
518 | |||
519 | case CS_ACTIVE: | ||
520 | caller->speaker->play (caller->speaker->cls, | ||
521 | ntohs (am->header.size) - sizeof(struct | ||
522 | ClientAudioMessage), | ||
523 | &am[1]); | ||
524 | break; | ||
525 | |||
526 | case CS_CALLEE_SUSPENDED: | ||
527 | case CS_CALLER_SUSPENDED: | ||
528 | case CS_BOTH_SUSPENDED: | ||
529 | break; | ||
530 | } | ||
531 | } | ||
532 | |||
533 | |||
534 | /** | ||
535 | * We encountered an error talking with the conversation service. | ||
536 | * | ||
537 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
538 | * @param error details about the error | ||
539 | */ | ||
540 | static void | ||
541 | phone_error_handler (void *cls, | ||
542 | enum GNUNET_MQ_Error error) | ||
543 | { | ||
544 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
545 | |||
546 | (void) error; | ||
547 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
548 | _ ( | ||
549 | "Connection to conversation service lost, trying to reconnect\n")); | ||
550 | reconnect_phone (phone); | ||
551 | } | ||
552 | |||
553 | |||
554 | /** | ||
555 | * Clean up all callers of the given phone. | ||
556 | * | ||
557 | * @param phone phone to clean up callers for | ||
558 | */ | ||
559 | static void | ||
560 | clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone) | ||
561 | { | ||
562 | struct GNUNET_CONVERSATION_Caller *caller; | ||
563 | |||
564 | while (NULL != (caller = phone->caller_head)) | ||
565 | { | ||
566 | /* make sure mic/speaker are disabled *before* callback */ | ||
567 | if (CS_ACTIVE == caller->state) | ||
568 | { | ||
569 | caller->speaker->disable_speaker (caller->speaker->cls); | ||
570 | caller->mic->disable_microphone (caller->mic->cls); | ||
571 | caller->state = CS_CALLER_SUSPENDED; | ||
572 | } | ||
573 | phone->event_handler (phone->event_handler_cls, | ||
574 | GNUNET_CONVERSATION_EC_PHONE_HUNG_UP, | ||
575 | caller, | ||
576 | &caller->caller_id); | ||
577 | GNUNET_CONVERSATION_caller_hang_up (caller); | ||
578 | } | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * The phone got disconnected, reconnect to the service. | ||
584 | * | ||
585 | * @param phone phone to reconnect | ||
586 | */ | ||
587 | static void | ||
588 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone) | ||
589 | { | ||
590 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
591 | GNUNET_MQ_hd_var_size (phone_ring, | ||
592 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING, | ||
593 | struct ClientPhoneRingMessage, | ||
594 | phone), | ||
595 | GNUNET_MQ_hd_fixed_size (phone_hangup, | ||
596 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
597 | struct ClientPhoneHangupMessage, | ||
598 | phone), | ||
599 | GNUNET_MQ_hd_fixed_size (phone_suspend, | ||
600 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND, | ||
601 | struct ClientPhoneSuspendMessage, | ||
602 | phone), | ||
603 | GNUNET_MQ_hd_fixed_size (phone_resume, | ||
604 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME, | ||
605 | struct ClientPhoneResumeMessage, | ||
606 | phone), | ||
607 | GNUNET_MQ_hd_var_size (phone_audio, | ||
608 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
609 | struct ClientAudioMessage, | ||
610 | phone), | ||
611 | GNUNET_MQ_handler_end () | ||
612 | }; | ||
613 | struct GNUNET_MQ_Envelope *e; | ||
614 | struct ClientPhoneRegisterMessage *reg; | ||
615 | |||
616 | clean_up_callers (phone); | ||
617 | if (NULL != phone->mq) | ||
618 | { | ||
619 | GNUNET_MQ_destroy (phone->mq); | ||
620 | phone->mq = NULL; | ||
621 | } | ||
622 | phone->state = PS_REGISTER; | ||
623 | phone->mq = GNUNET_CLIENT_connect (phone->cfg, | ||
624 | "conversation", | ||
625 | handlers, | ||
626 | &phone_error_handler, | ||
627 | phone); | ||
628 | if (NULL == phone->mq) | ||
629 | return; | ||
630 | e = GNUNET_MQ_msg (reg, | ||
631 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER); | ||
632 | reg->line_port = phone->my_record.line_port; | ||
633 | GNUNET_MQ_send (phone->mq, | ||
634 | e); | ||
635 | phone->state = PS_READY; | ||
636 | } | ||
637 | |||
638 | |||
639 | /** | ||
640 | * Create a new phone. | ||
641 | * | ||
642 | * @param cfg configuration for the phone; specifies the phone service and | ||
643 | * which line the phone is to be connected to | ||
644 | * @param ego ego to use for name resolution (when determining caller ID) | ||
645 | * @param event_handler how to notify the owner of the phone about events | ||
646 | * @param event_handler_cls closure for @a event_handler | ||
647 | * @return NULL on error (no valid line configured) | ||
648 | */ | ||
649 | struct GNUNET_CONVERSATION_Phone * | ||
650 | GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
651 | const struct GNUNET_IDENTITY_Ego *ego, | ||
652 | GNUNET_CONVERSATION_PhoneEventHandler | ||
653 | event_handler, | ||
654 | void *event_handler_cls) | ||
655 | { | ||
656 | struct GNUNET_CONVERSATION_Phone *phone; | ||
657 | char *line; | ||
658 | struct GNUNET_HashCode line_port; | ||
659 | |||
660 | if (GNUNET_OK != | ||
661 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
662 | "CONVERSATION", | ||
663 | "LINE", | ||
664 | &line)) | ||
665 | { | ||
666 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
667 | "CONVERSATION", | ||
668 | "LINE"); | ||
669 | return NULL; | ||
670 | } | ||
671 | GNUNET_CRYPTO_hash (line, | ||
672 | strlen (line), | ||
673 | &line_port); | ||
674 | GNUNET_free (line); | ||
675 | phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone); | ||
676 | if (GNUNET_OK != | ||
677 | GNUNET_CRYPTO_get_peer_identity (cfg, | ||
678 | &phone->my_record.peer)) | ||
679 | { | ||
680 | GNUNET_break (0); | ||
681 | GNUNET_free (phone); | ||
682 | return NULL; | ||
683 | } | ||
684 | phone->cfg = cfg; | ||
685 | phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego); | ||
686 | phone->event_handler = event_handler; | ||
687 | phone->event_handler_cls = event_handler_cls; | ||
688 | phone->ns = GNUNET_NAMESTORE_connect (cfg); | ||
689 | phone->my_record.version = htonl (1); | ||
690 | phone->my_record.reserved = htonl (0); | ||
691 | phone->my_record.line_port = line_port; | ||
692 | reconnect_phone (phone); | ||
693 | if ((NULL == phone->mq) || | ||
694 | (NULL == phone->ns)) | ||
695 | { | ||
696 | GNUNET_break (0); | ||
697 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
698 | return NULL; | ||
699 | } | ||
700 | return phone; | ||
701 | } | ||
702 | |||
703 | |||
704 | /** | ||
705 | * Fill in a namestore record with the contact information | ||
706 | * for this phone. Note that the filled in "data" value | ||
707 | * is only valid until the phone is destroyed. | ||
708 | * | ||
709 | * @param phone phone to create a record for | ||
710 | * @param rd namestore record to fill in | ||
711 | */ | ||
712 | void | ||
713 | GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone, | ||
714 | struct GNUNET_GNSRECORD_Data *rd) | ||
715 | { | ||
716 | rd->data = &phone->my_record; | ||
717 | rd->expiration_time = 0; | ||
718 | rd->data_size = sizeof(struct GNUNET_CONVERSATION_PhoneRecord); | ||
719 | rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE; | ||
720 | rd->flags = GNUNET_GNSRECORD_RF_NONE; | ||
721 | } | ||
722 | |||
723 | |||
724 | /** | ||
725 | * Picks up a (ringing) phone. This will connect the speaker | ||
726 | * to the microphone of the other party, and vice versa. | ||
727 | * | ||
728 | * @param caller handle that identifies which caller should be answered | ||
729 | * @param event_handler how to notify about events by the caller | ||
730 | * @param event_handler_cls closure for @a event_handler | ||
731 | * @param speaker speaker to use | ||
732 | * @param mic microphone to use | ||
733 | */ | ||
734 | void | ||
735 | GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller, | ||
736 | GNUNET_CONVERSATION_CallerEventHandler | ||
737 | event_handler, | ||
738 | void *event_handler_cls, | ||
739 | struct GNUNET_SPEAKER_Handle *speaker, | ||
740 | struct GNUNET_MICROPHONE_Handle *mic) | ||
741 | { | ||
742 | struct GNUNET_CONVERSATION_Phone *phone = caller->phone; | ||
743 | struct GNUNET_MQ_Envelope *e; | ||
744 | struct ClientPhonePickupMessage *pick; | ||
745 | |||
746 | GNUNET_assert (CS_RINGING == caller->state); | ||
747 | caller->speaker = speaker; | ||
748 | caller->mic = mic; | ||
749 | e = GNUNET_MQ_msg (pick, | ||
750 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP); | ||
751 | pick->cid = caller->cid; | ||
752 | GNUNET_MQ_send (phone->mq, | ||
753 | e); | ||
754 | caller->state = CS_ACTIVE; | ||
755 | caller->event_handler = event_handler; | ||
756 | caller->event_handler_cls = event_handler_cls; | ||
757 | caller->speaker->enable_speaker (caller->speaker->cls); | ||
758 | caller->mic->enable_microphone (caller->mic->cls, | ||
759 | &transmit_phone_audio, | ||
760 | caller); | ||
761 | } | ||
762 | |||
763 | |||
764 | /** | ||
765 | * Hang up up a (possibly ringing) phone. This will notify the other | ||
766 | * party that we are no longer interested in talking with them. | ||
767 | * | ||
768 | * @param caller conversation to hang up on | ||
769 | */ | ||
770 | void | ||
771 | GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller) | ||
772 | { | ||
773 | struct GNUNET_CONVERSATION_Phone *phone = caller->phone; | ||
774 | struct GNUNET_MQ_Envelope *e; | ||
775 | struct ClientPhoneHangupMessage *hang; | ||
776 | |||
777 | switch (caller->state) | ||
778 | { | ||
779 | case CS_ACTIVE: | ||
780 | caller->speaker->disable_speaker (caller->speaker->cls); | ||
781 | caller->mic->disable_microphone (caller->mic->cls); | ||
782 | break; | ||
783 | |||
784 | default: | ||
785 | break; | ||
786 | } | ||
787 | GNUNET_CONTAINER_DLL_remove (phone->caller_head, | ||
788 | phone->caller_tail, | ||
789 | caller); | ||
790 | e = GNUNET_MQ_msg (hang, | ||
791 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
792 | hang->cid = caller->cid; | ||
793 | GNUNET_MQ_send (phone->mq, | ||
794 | e); | ||
795 | GNUNET_free (caller); | ||
796 | } | ||
797 | |||
798 | |||
799 | /** | ||
800 | * Destroys a phone. | ||
801 | * | ||
802 | * @param phone phone to destroy | ||
803 | */ | ||
804 | void | ||
805 | GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone) | ||
806 | { | ||
807 | clean_up_callers (phone); | ||
808 | if (NULL != phone->ns) | ||
809 | { | ||
810 | GNUNET_NAMESTORE_disconnect (phone->ns); | ||
811 | phone->ns = NULL; | ||
812 | } | ||
813 | if (NULL != phone->mq) | ||
814 | { | ||
815 | GNUNET_MQ_destroy (phone->mq); | ||
816 | phone->mq = NULL; | ||
817 | } | ||
818 | GNUNET_free (phone); | ||
819 | } | ||
820 | |||
821 | |||
822 | /** | ||
823 | * Pause conversation of an active call. This will disconnect the speaker | ||
824 | * and the microphone. The call can later be resumed with | ||
825 | * #GNUNET_CONVERSATION_caller_resume. | ||
826 | * | ||
827 | * @param caller call to suspend | ||
828 | */ | ||
829 | void | ||
830 | GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller) | ||
831 | { | ||
832 | struct GNUNET_CONVERSATION_Phone *phone = caller->phone; | ||
833 | struct GNUNET_MQ_Envelope *e; | ||
834 | struct ClientPhoneSuspendMessage *suspend; | ||
835 | |||
836 | GNUNET_assert ((CS_ACTIVE == caller->state) || | ||
837 | (CS_CALLER_SUSPENDED == caller->state)); | ||
838 | if (CS_ACTIVE == caller->state) | ||
839 | { | ||
840 | caller->speaker->disable_speaker (caller->speaker->cls); | ||
841 | caller->mic->disable_microphone (caller->mic->cls); | ||
842 | } | ||
843 | caller->speaker = NULL; | ||
844 | caller->mic = NULL; | ||
845 | e = GNUNET_MQ_msg (suspend, | ||
846 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND); | ||
847 | suspend->cid = caller->cid; | ||
848 | GNUNET_MQ_send (phone->mq, | ||
849 | e); | ||
850 | if (CS_ACTIVE == caller->state) | ||
851 | caller->state = CS_CALLEE_SUSPENDED; | ||
852 | else | ||
853 | caller->state = CS_BOTH_SUSPENDED; | ||
854 | } | ||
855 | |||
856 | |||
857 | /** | ||
858 | * Resume suspended conversation of a phone. | ||
859 | * | ||
860 | * @param caller call to resume | ||
861 | * @param speaker speaker to use | ||
862 | * @param mic microphone to use | ||
863 | */ | ||
864 | void | ||
865 | GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller, | ||
866 | struct GNUNET_SPEAKER_Handle *speaker, | ||
867 | struct GNUNET_MICROPHONE_Handle *mic) | ||
868 | { | ||
869 | struct GNUNET_CONVERSATION_Phone *phone = caller->phone; | ||
870 | struct GNUNET_MQ_Envelope *e; | ||
871 | struct ClientPhoneResumeMessage *resume; | ||
872 | |||
873 | GNUNET_assert ((CS_CALLEE_SUSPENDED == caller->state) || | ||
874 | (CS_BOTH_SUSPENDED == caller->state)); | ||
875 | caller->speaker = speaker; | ||
876 | caller->mic = mic; | ||
877 | e = GNUNET_MQ_msg (resume, | ||
878 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME); | ||
879 | resume->cid = caller->cid; | ||
880 | GNUNET_MQ_send (phone->mq, | ||
881 | e); | ||
882 | if (CS_CALLEE_SUSPENDED == caller->state) | ||
883 | { | ||
884 | caller->state = CS_ACTIVE; | ||
885 | caller->speaker->enable_speaker (caller->speaker->cls); | ||
886 | caller->mic->enable_microphone (caller->mic->cls, | ||
887 | &transmit_phone_audio, | ||
888 | caller); | ||
889 | } | ||
890 | else | ||
891 | { | ||
892 | caller->state = CS_CALLER_SUSPENDED; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | |||
897 | /* end of conversation_api.c */ | ||