-more work on call API
[oweals/gnunet.git] / src / conversation / conversation_api2.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_api2.c
23  * @brief 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_gns_service.h"
31 #include "conversation.h"
32
33
34 /**
35  * A phone record specifies which peer is hosting a given user and
36  * may also specify the phone line that is used (typically zero).
37  * The version is also right now always zero.
38  */
39 struct PhoneRecord
40 {
41
42   /**
43    * Version of the phone record, for now always zero.  We may
44    * use other versions for anonymously hosted phone lines in
45    * the future.
46    */
47   uint32_t version GNUNET_PACKED;
48
49   /**
50    * Phone line to use at the peer.
51    */
52   uint32_t line GNUNET_PACKED;
53
54   /**
55    * Identity of the peer hosting the phone service.
56    */
57   struct GNUNET_PeerIdentity peer;
58
59 };
60
61
62 /**
63  * Possible states of the phone.
64  */
65 enum PhoneState
66 {
67   /**
68    * We still need to register the phone.
69    */
70   PS_REGISTER = 0,
71
72   /**
73    * We are waiting for a call.
74    */
75   PS_WAITING,
76
77   /**
78    * The phone is ringing.
79    */
80   PS_RINGING,
81
82   /**
83    * The phone is in an active conversation.
84    */
85   PS_ACTIVE
86 };
87
88
89 /**
90  * A phone is a device that can ring to signal an incoming call and
91  * that you can pick up to answer the call and hang up to terminate
92  * the call.  You can also hang up a ringing phone immediately
93  * (without picking it up) to stop it from ringing.  Phones have
94  * caller ID.  You can ask the phone for its record and make that
95  * record available (via GNS) to enable others to call you.
96  * Multiple phones maybe connected to the same line (the line is
97  * something rather internal to a phone and not obvious from it).
98  * You can only have one conversation per phone at any time.
99  */
100 struct GNUNET_CONVERSATION_Phone
101 {
102   /**
103    * Our configuration.
104    */
105   const struct GNUNET_CONFIGURATION_Handle *cfg;
106   
107   /**
108    * Handle to talk with CONVERSATION service.
109    */
110   struct GNUNET_CLIENT_Connection *client;
111
112   /**
113    * Function to call for phone events.
114    */
115   GNUNET_CONVERSATION_EventHandler event_handler;
116
117   /**
118    * Closure for @e event_handler
119    */
120   void *event_handler_cls;
121
122   /**
123    * Speaker, or NULL if none is attached.
124    */
125   struct GNUNET_SPEAKER_Handle *speaker;
126
127   /**
128    * Microphone, or NULL if none is attached.
129    */
130   struct GNUNET_MICROPHONE_Handle *mic;
131
132   /**
133    * Connection to NAMESTORE (for reverse lookup).
134    */ 
135   struct GNUNET_NAMESTORE_Handle *ns;
136
137   /**
138    * Active NAMESTORE lookup (or NULL).
139    */
140   struct GNUNET_NAMESTORE_QueueEntry *qe;
141
142   /**
143    * Handle for transmitting to the CONVERSATION service.
144    */
145   struct GNUNET_MQ_Handle *mq;
146
147   /**
148    * This phone's record.
149    */
150   struct PhoneRecord my_record;  
151
152   /**
153    * My GNS zone.
154    */
155   struct GNUNET_CRYPTO_EccPrivateKey my_zone;
156
157   /**
158    * Identity of the person calling us (valid while in state #PS_RINGING).
159    */
160   struct GNUNET_CRYPTO_EccPublicSignKey caller_id;
161
162   /**
163    * State machine for the phone.
164    */
165   enum PhoneState state;
166
167 };
168
169
170 /**
171  * The phone got disconnected, reconnect to the service.
172  *
173  * @param phone phone to reconnect
174  */
175 static void
176 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
177
178
179 /**
180  * We have resolved the caller ID using our name service.
181  *
182  * @param cls the `struct GNUNET_CONVERSATION_Phone`
183  * @param zone our zone used for resolution
184  * @param label name of the caller
185  * @param rd_count number of records we have in @a rd
186  * @param rd records we have for the caller's label
187  */
188 static void
189 handle_caller_name (void *cls,
190                     const struct GNUNET_CRYPTO_EccPrivateKey *zone,
191                     const char *label,
192                     unsigned int rd_count,
193                     const struct GNUNET_NAMESTORE_RecordData *rd)
194 {
195   struct GNUNET_CONVERSATION_Phone *phone = cls;
196   char *name;
197
198   phone->qe = NULL;
199   if (NULL == label)
200     name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id));
201   else
202     GNUNET_asprintf (&name, "%.gnu", label);
203   phone->event_handler (phone->event_handler_cls,
204                         GNUNET_CONVERSATION_EC_RING,
205                         name);
206   GNUNET_free (name);
207 }
208
209
210 /**
211  * We received a `struct ClientPhoneRingMessage`
212  *
213  * @param cls the `struct GNUNET_CONVERSATION_Phone`
214  * @param msg the message
215  */
216 static void
217 handle_phone_ring (void *cls,
218                    const struct GNUNET_MessageHeader *msg)
219 {
220   struct GNUNET_CONVERSATION_Phone *phone = cls;
221   const struct ClientPhoneRingMessage *ring;
222
223   ring = (const struct ClientPhoneRingMessage *) msg;
224   switch (phone->state)
225   {
226   case PS_REGISTER:
227     GNUNET_assert (0);
228     break;
229   case PS_WAITING:
230     phone->state = PS_RINGING;
231     phone->caller_id = ring->caller_id;
232     phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
233                                                &phone->my_zone,
234                                                &ring->caller_id,
235                                                &handle_caller_name,
236                                                phone);
237     break;
238   case PS_RINGING:
239     GNUNET_break (0);
240     reconnect_phone (phone);
241     break;
242   case PS_ACTIVE:
243     GNUNET_break (0);
244     reconnect_phone (phone);
245     break;
246   }
247 }
248
249
250 /**
251  * We received a `struct ClientPhoneHangupMessage`.
252  *
253  * @param cls the `struct GNUNET_CONVERSATION_Phone`
254  * @param msg the message
255  */
256 static void
257 handle_phone_hangup (void *cls,
258                      const struct GNUNET_MessageHeader *msg)
259 {
260   struct GNUNET_CONVERSATION_Phone *phone = cls;
261   const struct ClientPhoneHangupMessage *hang;
262   size_t len;
263   const char *reason;
264
265   hang = (const struct ClientPhoneHangupMessage *) msg;
266   reason = (const char *) &hang[1];
267   len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage);
268   if ( (0 == len) ||
269        ('\0' != reason[len-1]) )
270   {
271     GNUNET_break (0);
272     reconnect_phone (phone);
273     return;
274   }
275   switch (phone->state)
276   {
277   case PS_REGISTER:
278     GNUNET_assert (0);
279     break;
280   case PS_WAITING:
281     GNUNET_break (0);
282     reconnect_phone (phone);
283     break;
284   case PS_RINGING:
285     if (NULL != phone->qe)
286     {
287       GNUNET_NAMESTORE_cancel (phone->qe);
288       phone->qe = NULL;
289       phone->state = PS_WAITING;
290       break;
291     }
292     phone->state = PS_WAITING;
293     phone->event_handler (phone->event_handler_cls,
294                           GNUNET_CONVERSATION_EC_TERMINATED,
295                           reason);
296     break;
297   case PS_ACTIVE:
298     GNUNET_break (NULL == phone->qe);
299     phone->state = PS_WAITING;
300     phone->event_handler (phone->event_handler_cls,
301                           GNUNET_CONVERSATION_EC_TERMINATED,
302                           reason);
303     break;
304   }
305 }
306
307
308 /**
309  * We received a `struct ClientAudioMessage`
310  *
311  * @param cls the `struct GNUNET_CONVERSATION_Phone`
312  * @param msg the message
313  */
314 static void
315 handle_phone_audio_message (void *cls,
316                       const struct GNUNET_MessageHeader *msg)
317 {
318   struct GNUNET_CONVERSATION_Phone *phone = cls;
319   const struct ClientAudioMessage *am;
320
321   am = (const struct ClientAudioMessage *) msg;
322   switch (phone->state)
323   {
324   case PS_REGISTER:
325     GNUNET_assert (0);
326     break;
327   case PS_WAITING:
328     GNUNET_break (0);
329     reconnect_phone (phone);
330     break;
331   case PS_RINGING:
332     GNUNET_break (0);
333     reconnect_phone (phone);
334     break;
335   case PS_ACTIVE:
336     phone->speaker->play (phone->speaker->cls,
337                           ntohs (msg->size) - sizeof (struct ClientAudioMessage),
338                           &am[1]);
339     break;
340   }
341 }
342
343
344 /**
345  * We encountered an error talking with the conversation service. 
346  *
347  * @param cls the `struct GNUNET_CONVERSATION_Phone`
348  * @param error details about the error
349  */
350 static void
351 phone_error_handler (void *cls,
352                      enum GNUNET_MQ_Error error)
353 {
354   struct GNUNET_CONVERSATION_Phone *phone = cls;
355
356   GNUNET_break (0);
357   reconnect_phone (phone);
358 }
359
360
361 /**
362  * The phone got disconnected, reconnect to the service.
363  *
364  * @param phone phone to reconnect
365  */
366 static void
367 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
368 {
369   static struct GNUNET_MQ_MessageHandler handlers[] =
370   {
371     { &handle_phone_ring,
372       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING, 
373       sizeof (struct ClientPhoneRingMessage) },
374     { &handle_phone_hangup,
375       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
376       0 },
377     { &handle_phone_audio_message,
378       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
379       0 },    
380     { NULL, 0, 0 }    
381   };
382   struct GNUNET_MQ_Envelope *e;
383   struct ClientPhoneRegisterMessage *reg;
384
385   if (NULL != phone->mq)
386   {
387     GNUNET_MQ_destroy (phone->mq);
388     phone->mq = NULL;
389   }
390   if (NULL != phone->client)
391   {
392     GNUNET_CLIENT_disconnect (phone->client);
393     phone->client = NULL;
394   }
395   phone->state = PS_REGISTER;
396   phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg);
397   if (NULL == phone->client)
398     return;
399   phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client,
400                                                      handlers,
401                                                      &phone_error_handler,
402                                                      phone);
403   e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
404   reg->line = phone->my_record.line;
405   GNUNET_MQ_send (phone->mq, e);
406   phone->state = PS_WAITING;
407 }
408
409
410 /**
411  * Create a new phone.
412  *
413  * @param cfg configuration for the phone; specifies the phone service and
414  *        which line the phone is to be connected to
415  * @param ego ego to use for name resolution (when determining caller ID)
416  * @param event_handler how to notify the owner of the phone about events
417  * @param event_handler_cls closure for @a event_handler
418  */
419 struct GNUNET_CONVERSATION_Phone *
420 GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
421                                   const struct GNUNET_IDENTITY_Ego *ego,
422                                   GNUNET_CONVERSATION_EventHandler event_handler,
423                                   void *event_handler_cls)
424 {
425   struct GNUNET_CONVERSATION_Phone *phone;
426   unsigned long long line;
427
428   if (GNUNET_OK !=
429       GNUNET_CONFIGURATION_get_value_number (cfg, 
430                                              "CONVERSATION",
431                                              "LINE",
432                                              &line))
433     return NULL;
434   phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
435   if (GNUNET_OK !=
436       GNUNET_CRYPTO_get_host_identity (cfg,
437                                        &phone->my_record.peer))
438   {
439     GNUNET_break (0);
440     GNUNET_free (phone);
441     return NULL;
442   }
443   phone->cfg = cfg;
444   phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
445   phone->event_handler = event_handler;
446   phone->event_handler_cls = event_handler_cls;
447   phone->ns = GNUNET_NAMESTORE_connect (cfg);
448   phone->my_record.line = htonl ((uint32_t) line);
449   phone->my_record.version = htonl (0);
450   reconnect_phone (phone);
451   if ( (NULL == phone->client) ||
452        (NULL == phone->ns) )
453   {
454     GNUNET_break (0);
455     GNUNET_CONVERSATION_phone_destroy (phone);
456     return NULL;
457   }
458   return phone;
459 }
460
461
462 /**
463  * Fill in a namestore record with the contact information
464  * for this phone.  Note that the filled in "data" value
465  * is only valid until the phone is destroyed.
466  *
467  * @param phone phone to create a record for
468  * @param rd namestore record to fill in
469  */
470 void
471 GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
472                                       struct GNUNET_NAMESTORE_RecordData *rd)
473 {
474   rd->data = &phone->my_record;
475   rd->expiration_time = 0;
476   rd->data_size = sizeof (struct PhoneRecord);
477   rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE;
478   rd->flags = GNUNET_NAMESTORE_RF_NONE;
479 }
480
481
482 /**
483  * Process recorded audio data.
484  *
485  * @param cls closure with the `struct GNUNET_CONVERSATION_Phone`
486  * @param data_size number of bytes in @a data
487  * @param data audio data to play
488  */
489 static void
490 transmit_audio (void *cls,
491                 size_t data_size,
492                 const void *data)
493 {
494   struct GNUNET_CONVERSATION_Phone *phone = cls;
495   struct GNUNET_MQ_Envelope *e;
496   struct ClientAudioMessage *am;
497
498   GNUNET_assert (PS_ACTIVE == phone->state);
499   e = GNUNET_MQ_msg_extra (am, data_size, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
500   GNUNET_MQ_send (phone->mq, e);
501 }
502
503
504 /**
505  * Picks up a (ringing) phone.  This will connect the speaker 
506  * to the microphone of the other party, and vice versa.
507  *
508  * @param phone phone to pick up
509  * @param metadata meta data to give to the other user about the pick up event
510  * @param speaker speaker to use
511  * @param mic microphone to use
512  */
513 void
514 GNUNET_CONVERSTATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
515                                     const char *metadata,
516                                     struct GNUNET_SPEAKER_Handle *speaker,
517                                     struct GNUNET_MICROPHONE_Handle *mic)
518 {
519   struct GNUNET_MQ_Envelope *e;
520   struct ClientPhonePickupMessage *pick;
521   size_t slen;
522
523   GNUNET_assert (PS_RINGING == phone->state);
524   phone->speaker = speaker;
525   phone->mic = mic;
526   slen = strlen (metadata) + 1;
527   e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
528   memcpy (&pick[1], metadata, slen);
529   GNUNET_MQ_send (phone->mq, e);
530   phone->state = PS_ACTIVE;
531   phone->speaker->enable_speaker (phone->speaker->cls);
532   phone->mic->enable_microphone (phone->mic->cls,
533                                  &transmit_audio,
534                                  phone);
535 }
536
537
538 /**
539  * Hang up up a (possibly ringing) phone.  This will notify the other
540  * party that we are no longer interested in talking with them.
541  *
542  * @param phone phone to pick up
543  * @param reason text we give to the other party about why we terminated the conversation
544  */
545 void
546 GNUNET_CONVERSTATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
547                                     const char *reason)
548 {
549   struct GNUNET_MQ_Envelope *e;
550   struct ClientPhoneHangupMessage *hang;
551   size_t slen;
552
553   GNUNET_assert ( (PS_RINGING == phone->state) ||
554                   (PS_ACTIVE == phone->state) );
555   phone->speaker->disable_speaker (phone->speaker->cls);
556   phone->mic->disable_microphone (phone->mic->cls);
557   phone->speaker = NULL;
558   phone->mic = NULL;
559   slen = strlen (reason) + 1;
560   e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
561   memcpy (&hang[1], reason, slen);
562   GNUNET_MQ_send (phone->mq, e);
563   phone->state = PS_WAITING;
564 }
565
566
567 /**
568  * Destroys a phone.
569  *
570  * @param phone phone to destroy
571  */
572 void
573 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
574 {
575   if (NULL != phone->speaker)
576   {
577     phone->speaker->disable_speaker (phone->speaker->cls);
578     phone->speaker = NULL;
579   }
580   if (NULL != phone->mic)
581   {
582     phone->mic->disable_microphone (phone->mic->cls);
583     phone->mic = NULL;
584   }
585   if (NULL != phone->qe)
586   {
587     GNUNET_NAMESTORE_cancel (phone->qe);
588     phone->qe = NULL;
589   }
590   if (NULL != phone->ns)
591   {
592     GNUNET_NAMESTORE_disconnect (phone->ns);
593     phone->ns = NULL;
594   }
595   if (NULL != phone->mq)
596   {
597     GNUNET_MQ_destroy (phone->mq);
598     phone->mq = NULL;
599   }
600   if (NULL != phone->client)
601   {
602     GNUNET_CLIENT_disconnect (phone->client);
603     phone->client = NULL;
604   }
605   GNUNET_free (phone);
606 }
607
608
609 /* ******************************* Call API *************************** */
610
611 /**
612  * Possible states of the phone.
613  */
614 enum CallState
615 {
616   /**
617    * We still need to lookup the callee.
618    */
619   CS_LOOKUP = 0,
620
621   /**
622    * The call is ringing.
623    */
624   CS_RINGING,
625
626   /**
627    * The call is in an active conversation.
628    */
629   CS_ACTIVE,
630
631   /**
632    * The call is in termination.
633    */
634   CS_SHUTDOWN
635 };
636
637
638 /**
639  * Handle for an outgoing call.
640  */
641 struct GNUNET_CONVERSATION_Call
642 {
643
644   /**
645    * Our configuration.
646    */
647   const struct GNUNET_CONFIGURATION_Handle *cfg;
648   
649   /**
650    * Handle to talk with CONVERSATION service.
651    */
652   struct GNUNET_CLIENT_Connection *client;
653
654   /**
655    * Our caller identity.
656    */
657   struct GNUNET_IDENTITY_Ego *caller_id;
658
659   /**
660    * Target callee as a GNS address/name.
661    */
662   char *callee;
663
664   /**
665    * Our speaker.
666    */
667   struct GNUNET_SPEAKER_Handle *speaker;
668
669   /**
670    * Our microphone.
671    */
672   struct GNUNET_MICROPHONE_Handle *mic;
673   
674   /**
675    * Function to call with events.
676    */
677   GNUNET_CONVERSATION_EventHandler event_handler;
678
679   /**
680    * Closure for @e event_handler
681    */
682   void *event_handler_cls;
683
684   /**
685    * Handle for transmitting to the CONVERSATION service.
686    */
687   struct GNUNET_MQ_Handle *mq;
688
689   /**
690    * Connection to GNS (can be NULL).
691    */ 
692   struct GNUNET_GNS_Handle *gns;
693
694   /**
695    * Active GNS lookup (or NULL).
696    */
697   struct GNUNET_GNS_LookupRequest *gns_lookup;
698
699   /**
700    * Target phone record, only valid after the lookup is done.
701    */
702   struct PhoneRecord phone_record;
703
704   /**
705    * State machine for the call.
706    */
707   enum CallState state;
708
709 };
710
711
712 /**
713  * The call got disconnected, reconnect to the service.
714  *
715  * @param call call to reconnect
716  */
717 static void
718 reconnect_call (struct GNUNET_CONVERSATION_Call *call);
719
720
721 /**
722  * We received a `struct ClientPhoneBusyMessage`
723  *
724  * @param cls the `struct GNUNET_CONVERSATION_Call`
725  * @param msg the message
726  */
727 static void
728 handle_call_busy (void *cls,
729                   const struct GNUNET_MessageHeader *msg)
730 {
731   struct GNUNET_CONVERSATION_Call *call = cls;
732
733   switch (call->state)
734   {
735   case CS_LOOKUP:
736     GNUNET_break (0);
737     reconnect_call (call);
738     break;
739   case CS_RINGING:
740     GNUNET_break (0); // FIXME
741     break;
742   case CS_ACTIVE:
743     GNUNET_break (0);
744     reconnect_call (call);
745     break;
746   case CS_SHUTDOWN:
747     GNUNET_CONVERSATION_call_stop (call, NULL);
748     break;
749   }
750 }
751
752
753 /**
754  * We received a `struct ClientPhonePickedupMessage`
755  *
756  * @param cls the `struct GNUNET_CONVERSATION_Call`
757  * @param msg the message
758  */
759 static void
760 handle_call_picked_up (void *cls,
761                        const struct GNUNET_MessageHeader *msg)
762 {
763   struct GNUNET_CONVERSATION_Call *call = cls;
764   const struct ClientPhonePickedupMessage *am;
765
766   am = (const struct ClientPhonePickedupMessage *) msg;
767   switch (call->state)
768   {
769   case CS_LOOKUP:
770     GNUNET_break (0);
771     reconnect_call (call);
772     break;
773   case CS_RINGING:
774     GNUNET_break (0); // FIXME
775     break;
776   case CS_ACTIVE:
777     GNUNET_break (0);
778     reconnect_call (call);
779     break;
780   case CS_SHUTDOWN:
781     GNUNET_CONVERSATION_call_stop (call, NULL);
782     break;
783   }
784 }
785
786
787 /**
788  * We received a `struct ClientPhoneHangupMessage`
789  *
790  * @param cls the `struct GNUNET_CONVERSATION_Call`
791  * @param msg the message
792  */
793 static void
794 handle_call_hangup (void *cls,
795                     const struct GNUNET_MessageHeader *msg)
796 {
797   struct GNUNET_CONVERSATION_Call *call = cls;
798   const struct ClientPhoneHangupMessage *am;
799
800   am = (const struct ClientPhoneHangupMessage *) msg;
801   switch (call->state)
802   {
803   case CS_LOOKUP:
804     GNUNET_break (0);
805     reconnect_call (call);
806     break;
807   case CS_RINGING:
808     GNUNET_break (0); // FIXME
809     break;
810   case CS_ACTIVE:
811     GNUNET_break (0); // FIXME
812     break;
813   case CS_SHUTDOWN:
814     GNUNET_CONVERSATION_call_stop (call, NULL);
815     break;
816   }
817 }
818
819
820 /**
821  * We received a `struct ClientAudioMessage`
822  *
823  * @param cls the `struct GNUNET_CONVERSATION_Call`
824  * @param msg the message
825  */
826 static void
827 handle_call_audio_message (void *cls,
828                            const struct GNUNET_MessageHeader *msg)
829 {
830   struct GNUNET_CONVERSATION_Call *call = cls;
831   const struct ClientAudioMessage *am;
832
833   am = (const struct ClientAudioMessage *) msg;
834   switch (call->state)
835   {
836   case CS_LOOKUP:
837     GNUNET_break (0);
838     reconnect_call (call);
839     break;
840   case CS_RINGING:
841     GNUNET_break (0);
842     reconnect_call (call);
843     break;
844   case CS_ACTIVE:
845     call->speaker->play (call->speaker->cls,
846                          ntohs (msg->size) - sizeof (struct ClientAudioMessage),
847                          &am[1]);
848     break;
849   case CS_SHUTDOWN:
850     GNUNET_CONVERSATION_call_stop (call, NULL);
851     break;
852
853   }
854 }
855
856
857 /**
858  * Iterator called on obtained result for a GNS lookup.
859  *
860  * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
861  * @param rd_count number of records in @a rd
862  * @param rd the records in reply
863  */
864 static void 
865 handle_gns_response (void *cls,
866                      uint32_t rd_count,
867                      const struct GNUNET_NAMESTORE_RecordData *rd)
868 {
869   struct GNUNET_CONVERSATION_Call *call = cls;
870   uint32_t i;
871
872   for (i=0;i<rd_count;i++)
873   {
874     if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type)
875     {
876       if (rd[i].data_size != sizeof (struct PhoneRecord))
877       {
878         GNUNET_break_op (0);
879         continue;
880       }      
881       memcpy (&call->phone_record,
882               rd[i].data,
883               rd[i].data_size);
884       GNUNET_break (0);
885       // FIXME: send call request!
886       return;
887     }
888   }
889   /* not found */
890   call->event_handler (call->event_handler_cls,
891                        GNUNET_CONVERSATION_EC_GNS_FAIL);
892   GNUNET_CONVERSATION_call_stop (call, NULL);
893 }
894
895
896 /**
897  * We encountered an error talking with the conversation service. 
898  *
899  * @param cls the `struct GNUNET_CONVERSATION_Call`
900  * @param error details about the error
901  */
902 static void
903 call_error_handler (void *cls,
904                     enum GNUNET_MQ_Error error)
905 {
906   struct GNUNET_CONVERSATION_Call *call = cls;
907
908   GNUNET_break (0);
909   reconnect_call (call);
910 }
911
912
913 /**
914  * The call got disconnected, reconnect to the service.
915  *
916  * @param call call to reconnect
917  */
918 static void
919 reconnect_call (struct GNUNET_CONVERSATION_Call *call)
920 {
921   static struct GNUNET_MQ_MessageHandler handlers[] =
922   {
923     { &handle_call_busy,
924       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY, 
925       sizeof (struct ClientPhoneBusyMessage) },
926     { &handle_call_picked_up,
927       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP, 
928       0 },
929     { &handle_call_hangup,
930       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
931       0 },
932     { &handle_call_audio_message,
933       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
934       0 },    
935     { NULL, 0, 0 }    
936   };
937  if (NULL != call->mq)
938   {
939     GNUNET_MQ_destroy (call->mq);
940     call->mq = NULL;
941   }
942   if (NULL != call->client)
943   {
944     GNUNET_CLIENT_disconnect (call->client);
945     call->client = NULL;
946   }
947   call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
948   if (NULL == call->client)
949     return;
950   call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
951                                                     handlers,
952                                                     &call_error_handler,
953                                                     call);
954 }
955
956
957 /**
958  * Call the phone of another user.
959  *
960  * @param cfg configuration to use, specifies our phone service
961  * @param caller_id identity of the caller
962  * @param callee GNS name of the callee (used to locate the callee's record)
963  * @param speaker speaker to use (will be used automatically immediately once the
964  *        #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
965  *        a ring tone on the speaker
966  * @param mic microphone to use (will be used automatically immediately once the
967  *        #GNUNET_CONVERSATION_EC_READY event is generated)
968  * @param event_handler how to notify the owner of the phone about events
969  * @param event_handler_cls closure for @a event_handler
970  */
971 struct GNUNET_CONVERSATION_Call *
972 GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
973                                 struct GNUNET_IDENTITY_Ego *caller_id,
974                                 const char *callee,
975                                 struct GNUNET_SPEAKER_Handle *speaker,
976                                 struct GNUNET_MICROPHONE_Handle *mic,
977                                 GNUNET_CONVERSATION_EventHandler event_handler,
978                                 void *event_handler_cls)
979 {
980   struct GNUNET_CONVERSATION_Call *call;
981   struct GNUNET_CRYPTO_EccPublicSignKey my_zone;
982
983   GNUNET_IDENTITY_ego_get_public_key (caller_id,
984                                       &my_zone);
985   call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
986   call->cfg = cfg;
987   call->caller_id = caller_id;
988   call->callee = GNUNET_strdup (callee);
989   call->speaker = speaker;
990   call->mic = mic;
991   call->event_handler = event_handler;
992   call->event_handler_cls = event_handler_cls;
993   call->gns = GNUNET_GNS_connect (cfg);
994   reconnect_call (call);
995
996   if ( (NULL == call->client) ||
997        (NULL == call->gns) )
998   {
999     GNUNET_CONVERSATION_call_stop (call, NULL);
1000     return NULL;
1001   }
1002   call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee,
1003                                         &my_zone,
1004                                         GNUNET_NAMESTORE_TYPE_PHONE,
1005                                         GNUNET_NO,
1006                                         NULL /* FIXME: add shortening support */,
1007                                         &handle_gns_response, call);
1008   GNUNET_assert (NULL != call->gns_lookup);
1009   return call;
1010 }
1011
1012
1013 /**
1014  * Terminate a call.  The call may be ringing or ready at this time.
1015  *
1016  * @param call call to terminate
1017  * @param reason if the call was active (ringing or ready) this will be the
1018  *        reason given to the other user for why we hung up
1019  */
1020 void
1021 GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call,
1022                                const char *reason)
1023 {
1024   if (NULL != reason)
1025   {
1026     // FIXME: transmit reason to service...
1027     return;
1028   }
1029   if (NULL != call->speaker)
1030   {
1031     call->speaker->disable_speaker (call->speaker->cls);
1032     call->speaker = NULL;
1033   }
1034   if (NULL != call->mic)
1035   {
1036     call->mic->disable_microphone (call->mic->cls);
1037     call->mic =NULL;
1038   }
1039   if (NULL != call->mq)
1040   {
1041     GNUNET_MQ_destroy (call->mq);
1042     call->mq = NULL;
1043   }
1044   if (NULL != call->client)
1045   {
1046     GNUNET_CLIENT_disconnect (call->client);
1047     call->client = NULL;
1048   }
1049   if (NULL != call->gns_lookup)
1050   {
1051     GNUNET_GNS_lookup_cancel (call->gns_lookup);
1052     call->gns_lookup = NULL;
1053   }
1054   if (NULL != call->gns)
1055   {
1056     GNUNET_GNS_disconnect (call->gns);
1057     call->gns = NULL;
1058   }
1059
1060   GNUNET_free (call);
1061 }
1062
1063
1064 /* end of conversation_api.c */