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