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