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