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