-sync before server reboot, work on conversation service
[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_phone_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, 
500                            data_size,
501                            GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
502   memcpy (&am[1], data, data_size);
503   GNUNET_MQ_send (phone->mq, e);
504 }
505
506
507 /**
508  * Picks up a (ringing) phone.  This will connect the speaker 
509  * to the microphone of the other party, and vice versa.
510  *
511  * @param phone phone to pick up
512  * @param metadata meta data to give to the other user about the pick up event
513  * @param speaker speaker to use
514  * @param mic microphone to use
515  */
516 void
517 GNUNET_CONVERSTATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
518                                     const char *metadata,
519                                     struct GNUNET_SPEAKER_Handle *speaker,
520                                     struct GNUNET_MICROPHONE_Handle *mic)
521 {
522   struct GNUNET_MQ_Envelope *e;
523   struct ClientPhonePickupMessage *pick;
524   size_t slen;
525
526   GNUNET_assert (PS_RINGING == phone->state);
527   phone->speaker = speaker;
528   phone->mic = mic;
529   slen = strlen (metadata) + 1;
530   e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
531   memcpy (&pick[1], metadata, slen);
532   GNUNET_MQ_send (phone->mq, e);
533   phone->state = PS_ACTIVE;
534   phone->speaker->enable_speaker (phone->speaker->cls);
535   phone->mic->enable_microphone (phone->mic->cls,
536                                  &transmit_phone_audio,
537                                  phone);
538 }
539
540
541 /**
542  * Hang up up a (possibly ringing) phone.  This will notify the other
543  * party that we are no longer interested in talking with them.
544  *
545  * @param phone phone to pick up
546  * @param reason text we give to the other party about why we terminated the conversation
547  */
548 void
549 GNUNET_CONVERSTATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
550                                     const char *reason)
551 {
552   struct GNUNET_MQ_Envelope *e;
553   struct ClientPhoneHangupMessage *hang;
554   size_t slen;
555
556   GNUNET_assert ( (PS_RINGING == phone->state) ||
557                   (PS_ACTIVE == phone->state) );
558   phone->speaker->disable_speaker (phone->speaker->cls);
559   phone->mic->disable_microphone (phone->mic->cls);
560   phone->speaker = NULL;
561   phone->mic = NULL;
562   slen = strlen (reason) + 1;
563   e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
564   memcpy (&hang[1], reason, slen);
565   GNUNET_MQ_send (phone->mq, e);
566   phone->state = PS_WAITING;
567 }
568
569
570 /**
571  * Destroys a phone.
572  *
573  * @param phone phone to destroy
574  */
575 void
576 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
577 {
578   if (NULL != phone->speaker)
579   {
580     phone->speaker->disable_speaker (phone->speaker->cls);
581     phone->speaker = NULL;
582   }
583   if (NULL != phone->mic)
584   {
585     phone->mic->disable_microphone (phone->mic->cls);
586     phone->mic = NULL;
587   }
588   if (NULL != phone->qe)
589   {
590     GNUNET_NAMESTORE_cancel (phone->qe);
591     phone->qe = NULL;
592   }
593   if (NULL != phone->ns)
594   {
595     GNUNET_NAMESTORE_disconnect (phone->ns);
596     phone->ns = NULL;
597   }
598   if (NULL != phone->mq)
599   {
600     GNUNET_MQ_destroy (phone->mq);
601     phone->mq = NULL;
602   }
603   if (NULL != phone->client)
604   {
605     GNUNET_CLIENT_disconnect (phone->client);
606     phone->client = NULL;
607   }
608   GNUNET_free (phone);
609 }
610
611
612 /* ******************************* Call API *************************** */
613
614 /**
615  * Possible states of the phone.
616  */
617 enum CallState
618 {
619   /**
620    * We still need to lookup the callee.
621    */
622   CS_LOOKUP = 0,
623
624   /**
625    * The call is ringing.
626    */
627   CS_RINGING,
628
629   /**
630    * The call is in an active conversation.
631    */
632   CS_ACTIVE,
633
634   /**
635    * The call is in termination.
636    */
637   CS_SHUTDOWN
638 };
639
640
641 /**
642  * Handle for an outgoing call.
643  */
644 struct GNUNET_CONVERSATION_Call
645 {
646
647   /**
648    * Our configuration.
649    */
650   const struct GNUNET_CONFIGURATION_Handle *cfg;
651   
652   /**
653    * Handle to talk with CONVERSATION service.
654    */
655   struct GNUNET_CLIENT_Connection *client;
656
657   /**
658    * Our caller identity.
659    */
660   struct GNUNET_IDENTITY_Ego *caller_id;
661
662   /**
663    * Target callee as a GNS address/name.
664    */
665   char *callee;
666
667   /**
668    * Our speaker.
669    */
670   struct GNUNET_SPEAKER_Handle *speaker;
671
672   /**
673    * Our microphone.
674    */
675   struct GNUNET_MICROPHONE_Handle *mic;
676   
677   /**
678    * Function to call with events.
679    */
680   GNUNET_CONVERSATION_EventHandler event_handler;
681
682   /**
683    * Closure for @e event_handler
684    */
685   void *event_handler_cls;
686
687   /**
688    * Handle for transmitting to the CONVERSATION service.
689    */
690   struct GNUNET_MQ_Handle *mq;
691
692   /**
693    * Connection to GNS (can be NULL).
694    */ 
695   struct GNUNET_GNS_Handle *gns;
696
697   /**
698    * Active GNS lookup (or NULL).
699    */
700   struct GNUNET_GNS_LookupRequest *gns_lookup;
701
702   /**
703    * Target phone record, only valid after the lookup is done.
704    */
705   struct PhoneRecord phone_record;
706
707   /**
708    * State machine for the call.
709    */
710   enum CallState state;
711
712 };
713
714
715 /**
716  * The call got disconnected, reconnect to the service.
717  *
718  * @param call call to reconnect
719  */
720 static void
721 reconnect_call (struct GNUNET_CONVERSATION_Call *call);
722
723
724 /**
725  * We received a `struct ClientPhoneBusyMessage`
726  *
727  * @param cls the `struct GNUNET_CONVERSATION_Call`
728  * @param msg the message
729  */
730 static void
731 handle_call_busy (void *cls,
732                   const struct GNUNET_MessageHeader *msg)
733 {
734   struct GNUNET_CONVERSATION_Call *call = cls;
735
736   switch (call->state)
737   {
738   case CS_LOOKUP:
739     GNUNET_break (0);
740     reconnect_call (call);
741     break;
742   case CS_RINGING:
743     call->event_handler (call->event_handler_cls,
744                          GNUNET_CONVERSATION_EC_BUSY);
745     GNUNET_CONVERSATION_call_stop (call, NULL);
746     break;
747   case CS_ACTIVE:
748     GNUNET_break (0);
749     reconnect_call (call);
750     break;
751   case CS_SHUTDOWN:
752     GNUNET_CONVERSATION_call_stop (call, NULL);
753     break;
754   }
755 }
756
757
758 /**
759  * Process recorded audio data.
760  *
761  * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
762  * @param data_size number of bytes in @a data
763  * @param data audio data to play
764  */
765 static void
766 transmit_call_audio (void *cls,
767                      size_t data_size,
768                      const void *data)
769 {
770   struct GNUNET_CONVERSATION_Call *call = cls;
771   struct GNUNET_MQ_Envelope *e;
772   struct ClientAudioMessage *am;
773
774   GNUNET_assert (CS_ACTIVE == call->state);
775   e = GNUNET_MQ_msg_extra (am, 
776                            data_size,
777                            GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
778   memcpy (&am[1], data, data_size);
779   GNUNET_MQ_send (call->mq, e);
780 }
781
782
783 /**
784  * We received a `struct ClientPhonePickedupMessage`
785  *
786  * @param cls the `struct GNUNET_CONVERSATION_Call`
787  * @param msg the message
788  */
789 static void
790 handle_call_picked_up (void *cls,
791                        const struct GNUNET_MessageHeader *msg)
792 {
793   struct GNUNET_CONVERSATION_Call *call = cls;
794   const struct ClientPhonePickedupMessage *am;
795   const char *metadata;
796   size_t size;
797
798   am = (const struct ClientPhonePickedupMessage *) msg;
799   size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage);
800   metadata = (const char *) &am[1];
801   if ( (0 == size) ||
802        ('\0' != metadata[size - 1]) )
803     metadata = NULL;  
804   switch (call->state)
805   {
806   case CS_LOOKUP:
807     GNUNET_break (0);
808     reconnect_call (call);
809     break;
810   case CS_RINGING:
811     call->state = CS_ACTIVE;
812     call->event_handler (call->event_handler_cls,
813                          GNUNET_CONVERSATION_EC_READY,
814                          metadata);
815     call->speaker->enable_speaker (call->speaker->cls);
816     call->mic->enable_microphone (call->mic->cls,
817                                   &transmit_call_audio,
818                                   call);
819     break;
820   case CS_ACTIVE:
821     GNUNET_break (0);
822     reconnect_call (call);
823     break;
824   case CS_SHUTDOWN:
825     GNUNET_CONVERSATION_call_stop (call, NULL);
826     break;
827   }
828 }
829
830
831 /**
832  * We received a `struct ClientPhoneHangupMessage`
833  *
834  * @param cls the `struct GNUNET_CONVERSATION_Call`
835  * @param msg the message
836  */
837 static void
838 handle_call_hangup (void *cls,
839                     const struct GNUNET_MessageHeader *msg)
840 {
841   struct GNUNET_CONVERSATION_Call *call = cls;
842   const struct ClientPhoneHangupMessage *am;
843   const char *reason;
844   size_t size;
845
846   am = (const struct ClientPhoneHangupMessage *) msg;
847   size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage);
848   reason = (const char *) &am[1];
849   if ( (0 == size) ||
850        ('\0' != reason[size - 1]) )
851     reason = NULL;  
852   switch (call->state)
853   {
854   case CS_LOOKUP:
855     GNUNET_break (0);
856     reconnect_call (call);
857     break;
858   case CS_RINGING:
859     call->event_handler (call->event_handler_cls,
860                          GNUNET_CONVERSATION_EC_TERMINATED,
861                          reason);
862     GNUNET_CONVERSATION_call_stop (call, NULL);
863     return;
864   case CS_ACTIVE:
865     call->event_handler (call->event_handler_cls,
866                          GNUNET_CONVERSATION_EC_TERMINATED,
867                          reason);
868     GNUNET_CONVERSATION_call_stop (call, NULL);
869     return;
870   case CS_SHUTDOWN:
871     GNUNET_CONVERSATION_call_stop (call, NULL);
872     break;
873   }
874 }
875
876
877 /**
878  * We received a `struct ClientAudioMessage`
879  *
880  * @param cls the `struct GNUNET_CONVERSATION_Call`
881  * @param msg the message
882  */
883 static void
884 handle_call_audio_message (void *cls,
885                            const struct GNUNET_MessageHeader *msg)
886 {
887   struct GNUNET_CONVERSATION_Call *call = cls;
888   const struct ClientAudioMessage *am;
889
890   am = (const struct ClientAudioMessage *) msg;
891   switch (call->state)
892   {
893   case CS_LOOKUP:
894     GNUNET_break (0);
895     reconnect_call (call);
896     break;
897   case CS_RINGING:
898     GNUNET_break (0);
899     reconnect_call (call);
900     break;
901   case CS_ACTIVE:
902     call->speaker->play (call->speaker->cls,
903                          ntohs (msg->size) - sizeof (struct ClientAudioMessage),
904                          &am[1]);
905     break;
906   case CS_SHUTDOWN:
907     GNUNET_CONVERSATION_call_stop (call, NULL);
908     break;
909
910   }
911 }
912
913
914 /**
915  * Iterator called on obtained result for a GNS lookup.
916  *
917  * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
918  * @param rd_count number of records in @a rd
919  * @param rd the records in reply
920  */
921 static void 
922 handle_gns_response (void *cls,
923                      uint32_t rd_count,
924                      const struct GNUNET_NAMESTORE_RecordData *rd)
925 {
926   struct GNUNET_CONVERSATION_Call *call = cls;
927   uint32_t i;
928   struct GNUNET_MQ_Envelope *e;
929   struct ClientCallMessage *ccm;
930
931   for (i=0;i<rd_count;i++)
932   {
933     if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type)
934     {
935       if (rd[i].data_size != sizeof (struct PhoneRecord))
936       {
937         GNUNET_break_op (0);
938         continue;
939       }      
940       memcpy (&call->phone_record,
941               rd[i].data,
942               rd[i].data_size);
943       e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
944       ccm->line = call->phone_record.line;
945       ccm->target = call->phone_record.peer;
946       ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
947       GNUNET_MQ_send (call->mq, e);
948       call->state = CS_RINGING;
949       call->event_handler (call->event_handler_cls,
950                            GNUNET_CONVERSATION_EC_RINGING);
951       return;
952     }
953   }
954   /* not found */
955   call->event_handler (call->event_handler_cls,
956                        GNUNET_CONVERSATION_EC_GNS_FAIL);
957   GNUNET_CONVERSATION_call_stop (call, NULL);
958 }
959
960
961 /**
962  * We encountered an error talking with the conversation service. 
963  *
964  * @param cls the `struct GNUNET_CONVERSATION_Call`
965  * @param error details about the error
966  */
967 static void
968 call_error_handler (void *cls,
969                     enum GNUNET_MQ_Error error)
970 {
971   struct GNUNET_CONVERSATION_Call *call = cls;
972
973   GNUNET_break (0);
974   reconnect_call (call);
975 }
976
977
978 /**
979  * The call got disconnected, reconnect to the service.
980  *
981  * @param call call to reconnect
982  */
983 static void
984 reconnect_call (struct GNUNET_CONVERSATION_Call *call)
985 {
986   static struct GNUNET_MQ_MessageHandler handlers[] =
987   {
988     { &handle_call_busy,
989       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY, 
990       sizeof (struct ClientPhoneBusyMessage) },
991     { &handle_call_picked_up,
992       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP, 
993       0 },
994     { &handle_call_hangup,
995       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
996       0 },
997     { &handle_call_audio_message,
998       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
999       0 },    
1000     { NULL, 0, 0 }    
1001   };
1002  if (NULL != call->mq)
1003   {
1004     GNUNET_MQ_destroy (call->mq);
1005     call->mq = NULL;
1006   }
1007   if (NULL != call->client)
1008   {
1009     GNUNET_CLIENT_disconnect (call->client);
1010     call->client = NULL;
1011   }
1012   call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
1013   if (NULL == call->client)
1014     return;
1015   call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
1016                                                     handlers,
1017                                                     &call_error_handler,
1018                                                     call);
1019 }
1020
1021
1022 /**
1023  * Call the phone of another user.
1024  *
1025  * @param cfg configuration to use, specifies our phone service
1026  * @param caller_id identity of the caller
1027  * @param callee GNS name of the callee (used to locate the callee's record)
1028  * @param speaker speaker to use (will be used automatically immediately once the
1029  *        #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
1030  *        a ring tone on the speaker
1031  * @param mic microphone to use (will be used automatically immediately once the
1032  *        #GNUNET_CONVERSATION_EC_READY event is generated)
1033  * @param event_handler how to notify the owner of the phone about events
1034  * @param event_handler_cls closure for @a event_handler
1035  */
1036 struct GNUNET_CONVERSATION_Call *
1037 GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1038                                 struct GNUNET_IDENTITY_Ego *caller_id,
1039                                 const char *callee,
1040                                 struct GNUNET_SPEAKER_Handle *speaker,
1041                                 struct GNUNET_MICROPHONE_Handle *mic,
1042                                 GNUNET_CONVERSATION_EventHandler event_handler,
1043                                 void *event_handler_cls)
1044 {
1045   struct GNUNET_CONVERSATION_Call *call;
1046   struct GNUNET_CRYPTO_EccPublicSignKey my_zone;
1047
1048   GNUNET_IDENTITY_ego_get_public_key (caller_id,
1049                                       &my_zone);
1050   call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
1051   call->cfg = cfg;
1052   call->caller_id = caller_id;
1053   call->callee = GNUNET_strdup (callee);
1054   call->speaker = speaker;
1055   call->mic = mic;
1056   call->event_handler = event_handler;
1057   call->event_handler_cls = event_handler_cls;
1058   call->gns = GNUNET_GNS_connect (cfg);
1059   reconnect_call (call);
1060
1061   if ( (NULL == call->client) ||
1062        (NULL == call->gns) )
1063   {
1064     GNUNET_CONVERSATION_call_stop (call, NULL);
1065     return NULL;
1066   }
1067   call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee,
1068                                         &my_zone,
1069                                         GNUNET_NAMESTORE_TYPE_PHONE,
1070                                         GNUNET_NO,
1071                                         NULL /* FIXME: add shortening support */,
1072                                         &handle_gns_response, call);
1073   GNUNET_assert (NULL != call->gns_lookup);
1074   return call;
1075 }
1076
1077
1078 /**
1079  * Terminate a call.  The call may be ringing or ready at this time.
1080  *
1081  * @param call call to terminate
1082  * @param reason if the call was active (ringing or ready) this will be the
1083  *        reason given to the other user for why we hung up
1084  */
1085 void
1086 GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call,
1087                                const char *reason)
1088 {
1089   if (NULL != reason)
1090   {
1091     // FIXME: transmit reason to service...
1092     GNUNET_break (0);
1093     // return;
1094   }
1095   if (NULL != call->speaker)
1096   {
1097     call->speaker->disable_speaker (call->speaker->cls);
1098     call->speaker = NULL;
1099   }
1100   if (NULL != call->mic)
1101   {
1102     call->mic->disable_microphone (call->mic->cls);
1103     call->mic =NULL;
1104   }
1105   if (NULL != call->mq)
1106   {
1107     GNUNET_MQ_destroy (call->mq);
1108     call->mq = NULL;
1109   }
1110   if (NULL != call->client)
1111   {
1112     GNUNET_CLIENT_disconnect (call->client);
1113     call->client = NULL;
1114   }
1115   if (NULL != call->gns_lookup)
1116   {
1117     GNUNET_GNS_lookup_cancel (call->gns_lookup);
1118     call->gns_lookup = NULL;
1119   }
1120   if (NULL != call->gns)
1121   {
1122     GNUNET_GNS_disconnect (call->gns);
1123     call->gns = NULL;
1124   }
1125
1126   GNUNET_free (call);
1127 }
1128
1129
1130 /* end of conversation_api.c */