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