missing fi
[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_log (GNUNET_ERROR_TYPE_WARNING,
557               _("Connection to conversation service lost, trying to reconnect\n"));
558   reconnect_phone (phone);
559 }
560
561
562 /**
563  * The phone got disconnected, reconnect to the service.
564  *
565  * @param phone phone to reconnect
566  */
567 static void
568 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
569 {
570   static struct GNUNET_MQ_MessageHandler handlers[] =
571   {
572     { &handle_phone_ring,
573       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
574       sizeof (struct ClientPhoneRingMessage) },
575     { &handle_phone_hangup,
576       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
577       sizeof (struct ClientPhoneHangupMessage) },
578     { &handle_phone_suspend,
579       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
580       sizeof (struct ClientPhoneSuspendMessage) },
581     { &handle_phone_resume,
582       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
583       sizeof (struct ClientPhoneResumeMessage) },
584     { &handle_phone_audio_message,
585       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
586       0 },
587     { NULL, 0, 0 }
588   };
589   struct GNUNET_MQ_Envelope *e;
590   struct ClientPhoneRegisterMessage *reg;
591   struct GNUNET_CONVERSATION_Caller *caller;
592
593   while (NULL != (caller = phone->caller_head))
594   {
595     phone->event_handler (phone->event_handler_cls,
596                           GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
597                           caller,
598                           caller->caller_id_str);
599     GNUNET_CONVERSATION_caller_hang_up (caller);
600   }
601   if (NULL != phone->mq)
602   {
603     GNUNET_MQ_destroy (phone->mq);
604     phone->mq = NULL;
605   }
606   if (NULL != phone->client)
607   {
608     GNUNET_CLIENT_disconnect (phone->client);
609     phone->client = NULL;
610   }
611   phone->state = PS_REGISTER;
612   phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg);
613   if (NULL == phone->client)
614     return;
615   phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client,
616                                                      handlers,
617                                                      &phone_error_handler,
618                                                      phone);
619   e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
620   reg->line = phone->my_record.line;
621   GNUNET_MQ_send (phone->mq, e);
622   phone->state = PS_READY;
623 }
624
625
626 /**
627  * Create a new phone.
628  *
629  * @param cfg configuration for the phone; specifies the phone service and
630  *        which line the phone is to be connected to
631  * @param ego ego to use for name resolution (when determining caller ID)
632  * @param event_handler how to notify the owner of the phone about events
633  * @param event_handler_cls closure for @a event_handler
634  * @return NULL on error (no valid line configured)
635  */
636 struct GNUNET_CONVERSATION_Phone *
637 GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
638                                   const struct GNUNET_IDENTITY_Ego *ego,
639                                   GNUNET_CONVERSATION_PhoneEventHandler event_handler,
640                                   void *event_handler_cls)
641 {
642   struct GNUNET_CONVERSATION_Phone *phone;
643   unsigned long long line;
644
645   if (GNUNET_OK !=
646       GNUNET_CONFIGURATION_get_value_number (cfg,
647                                              "CONVERSATION",
648                                              "LINE",
649                                              &line))
650     return NULL;
651   if (line >= (1 << 31))
652     return NULL;
653   phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
654   if (GNUNET_OK !=
655       GNUNET_CRYPTO_get_peer_identity (cfg,
656                                        &phone->my_record.peer))
657   {
658     GNUNET_break (0);
659     GNUNET_free (phone);
660     return NULL;
661   }
662   phone->cfg = cfg;
663   phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
664   phone->event_handler = event_handler;
665   phone->event_handler_cls = event_handler_cls;
666   phone->ns = GNUNET_NAMESTORE_connect (cfg);
667   phone->my_record.line = htonl ((uint32_t) line);
668   phone->my_record.version = htonl (0);
669   reconnect_phone (phone);
670   if ( (NULL == phone->client) ||
671        (NULL == phone->ns) )
672   {
673     GNUNET_break (0);
674     GNUNET_CONVERSATION_phone_destroy (phone);
675     return NULL;
676   }
677   return phone;
678 }
679
680
681 /**
682  * Fill in a namestore record with the contact information
683  * for this phone.  Note that the filled in "data" value
684  * is only valid until the phone is destroyed.
685  *
686  * @param phone phone to create a record for
687  * @param rd namestore record to fill in
688  */
689 void
690 GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
691                                       struct GNUNET_GNSRECORD_Data *rd)
692 {
693   rd->data = &phone->my_record;
694   rd->expiration_time = 0;
695   rd->data_size = sizeof (struct GNUNET_CONVERSATION_PhoneRecord);
696   rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
697   rd->flags = GNUNET_GNSRECORD_RF_NONE;
698 }
699
700
701 /**
702  * Picks up a (ringing) phone.  This will connect the speaker
703  * to the microphone of the other party, and vice versa.
704  *
705  * @param caller handle that identifies which caller should be answered
706  * @param event_handler how to notify about events by the caller
707  * @param event_handler_cls closure for @a event_handler
708  * @param speaker speaker to use
709  * @param mic microphone to use
710  */
711 void
712 GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
713                                     GNUNET_CONVERSATION_CallerEventHandler event_handler,
714                                     void *event_handler_cls,
715                                     struct GNUNET_SPEAKER_Handle *speaker,
716                                     struct GNUNET_MICROPHONE_Handle *mic)
717 {
718   struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
719   struct GNUNET_MQ_Envelope *e;
720   struct ClientPhonePickupMessage *pick;
721
722   GNUNET_assert (CS_RINGING == caller->state);
723   caller->speaker = speaker;
724   caller->mic = mic;
725   e = GNUNET_MQ_msg (pick, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
726   pick->cid = caller->cid;
727   GNUNET_MQ_send (phone->mq, e);
728   caller->state = CS_ACTIVE;
729   caller->event_handler = event_handler;
730   caller->event_handler_cls = event_handler_cls;
731   caller->speaker->enable_speaker (caller->speaker->cls);
732   caller->mic->enable_microphone (caller->mic->cls,
733                                   &transmit_phone_audio,
734                                   caller);
735 }
736
737
738 /**
739  * Hang up up a (possibly ringing) phone.  This will notify the other
740  * party that we are no longer interested in talking with them.
741  *
742  * @param caller conversation to hang up on
743  */
744 void
745 GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
746 {
747   struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
748   struct GNUNET_MQ_Envelope *e;
749   struct ClientPhoneHangupMessage *hang;
750
751   switch (caller->state)
752   {
753   case CS_RESOLVE:
754     GNUNET_NAMESTORE_cancel (caller->qe);
755     caller->qe = NULL;
756     break;
757   case CS_ACTIVE:
758     caller->speaker->disable_speaker (caller->speaker->cls);
759     caller->mic->disable_microphone (caller->mic->cls);
760     break;
761   default:
762     break;
763   }
764   GNUNET_CONTAINER_DLL_remove (phone->caller_head,
765                                phone->caller_tail,
766                                caller);
767   GNUNET_free_non_null (caller->caller_id_str);
768   GNUNET_free (caller);
769   e = GNUNET_MQ_msg (hang, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
770   GNUNET_MQ_send (phone->mq, e);
771 }
772
773
774 /**
775  * Destroys a phone.
776  *
777  * @param phone phone to destroy
778  */
779 void
780 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
781 {
782   struct GNUNET_CONVERSATION_Caller *caller;
783
784   while (NULL != (caller = phone->caller_head))
785   {
786     phone->event_handler (phone->event_handler_cls,
787                           GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
788                           caller,
789                           caller->caller_id_str);
790     GNUNET_CONVERSATION_caller_hang_up (caller);
791   }
792   if (NULL != phone->ns)
793   {
794     GNUNET_NAMESTORE_disconnect (phone->ns);
795     phone->ns = NULL;
796   }
797   if (NULL != phone->mq)
798   {
799     GNUNET_MQ_destroy (phone->mq);
800     phone->mq = NULL;
801   }
802   if (NULL != phone->client)
803   {
804     GNUNET_CLIENT_disconnect (phone->client);
805     phone->client = NULL;
806   }
807   GNUNET_free (phone);
808 }
809
810
811 /**
812  * Pause conversation of an active call.  This will disconnect the speaker
813  * and the microphone.  The call can later be resumed with
814  * #GNUNET_CONVERSATION_caller_resume.
815  *
816  * @param caller call to suspend
817  */
818 void
819 GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
820 {
821   struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
822   struct GNUNET_MQ_Envelope *e;
823   struct ClientPhoneSuspendMessage *suspend;
824
825   GNUNET_assert ( (CS_ACTIVE == caller->state) ||
826                   (CS_CALLER_SUSPENDED == caller->state) );
827   if (CS_ACTIVE == caller->state)
828   {
829     caller->speaker->disable_speaker (caller->speaker->cls);
830     caller->mic->disable_microphone (caller->mic->cls);
831   }
832   caller->speaker = NULL;
833   caller->mic = NULL;
834   e = GNUNET_MQ_msg (suspend, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
835   suspend->cid = caller->cid;
836   GNUNET_MQ_send (phone->mq, e);
837   if (CS_ACTIVE == caller->state)
838     caller->state = CS_CALLEE_SUSPENDED;
839   else
840     caller->state = CS_BOTH_SUSPENDED;
841 }
842
843
844 /**
845  * Resume suspended conversation of a phone.
846  *
847  * @param caller call to resume
848  * @param speaker speaker to use
849  * @param mic microphone to use
850  */
851 void
852 GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
853                                    struct GNUNET_SPEAKER_Handle *speaker,
854                                    struct GNUNET_MICROPHONE_Handle *mic)
855 {
856   struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
857   struct GNUNET_MQ_Envelope *e;
858   struct ClientPhoneResumeMessage *resume;
859
860   GNUNET_assert ( (CS_CALLEE_SUSPENDED == caller->state) ||
861                   (CS_BOTH_SUSPENDED == caller->state) );
862   caller->speaker = speaker;
863   caller->mic = mic;
864   e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
865   resume->cid = caller->cid;
866   GNUNET_MQ_send (phone->mq, e);
867   if (CS_CALLEE_SUSPENDED == caller->state)
868   {
869     caller->state = CS_ACTIVE;
870     caller->speaker->enable_speaker (caller->speaker->cls);
871     caller->mic->enable_microphone (caller->mic->cls,
872                                     &transmit_phone_audio,
873                                     caller);
874   }
875   else
876   {
877     caller->state = CS_CALLER_SUSPENDED;
878   }
879 }
880
881 /* end of conversation_api.c */