-man page for gnunet-conversation
[oweals/gnunet.git] / src / conversation / gnunet-service-conversation-new.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  * @file conversation/gnunet-service-conversation.c
22  * @brief conversation service implementation
23  * @author Simon Dieterle
24  * @author Andreas Fuchs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_applications.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_signatures.h"
33 #include "gnunet_mesh_service.h"
34 #include "gnunet_conversation_service.h"
35 #include "conversation.h"
36
37
38 /**
39  * How long is our signature on a call valid?  Needs to be long enough for time zone
40  * differences and network latency to not matter.  No strong need for it to be short,
41  * but we simply like all signatures to eventually expire.
42  */
43 #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
44
45
46 /**
47  * The possible connection status
48  */
49 enum LineStatus
50 {
51   /**
52    * We are waiting for incoming calls.
53    */
54   LS_CALLEE_LISTEN,
55
56   /**
57    * Our phone is ringing, waiting for the client to pick up.
58    */
59   LS_CALLEE_RINGING,
60
61   /**
62    * We are talking!
63    */
64   LS_CALLEE_CONNECTED,
65
66   /**
67    * We're in shutdown, sending hangup messages before cleaning up.
68    */
69   LS_CALLEE_SHUTDOWN,
70
71   /**
72    * We are waiting for the phone to be picked up.
73    */
74   LS_CALLER_CALLING,
75
76   /**
77    * We are talking!
78    */
79   LS_CALLER_CONNECTED,
80
81   /**
82    * We're in shutdown, sending hangup messages before cleaning up.
83    */
84   LS_CALLER_SHUTDOWN
85 };
86
87
88 /**
89  * A line connects a local client with a mesh tunnel (or, if it is an
90  * open line, is waiting for a mesh tunnel).
91  */
92 struct Line
93 {
94   /**
95    * Kept in a DLL.
96    */
97   struct Line *next;
98
99   /**
100    * Kept in a DLL.
101    */
102   struct Line *prev;
103
104   /**
105    * Handle for the reliable tunnel (contol data)
106    */
107   struct GNUNET_MESH_Tunnel *tunnel_reliable;
108   
109   /**
110    * Handle for unreliable tunnel (audio data)
111    */
112   struct GNUNET_MESH_Tunnel *tunnel_unreliable;
113
114   /**
115    * Transmit handle for pending audio messages
116    */
117   struct GNUNET_MESH_TransmitHandle *unreliable_mth;
118
119   /**
120    * Message queue for control messages
121    */
122   struct GNUNET_MQ_Handle *reliable_mq;
123
124   /**
125    * Handle to the line client.
126    */
127   struct GNUNET_SERVER_Client *client;
128
129   /**
130    * Target of the line, if we are the caller.
131    */
132   struct GNUNET_PeerIdentity target;
133
134   /**
135    * Temporary buffer for audio data.
136    */
137   void *audio_data;
138
139   /**
140    * Number of bytes in @e audio_data.
141    */
142   size_t audio_size;
143
144   /**
145    * Our line number.
146    */
147   uint32_t local_line;
148
149   /**
150    * Remote line number.
151    */
152   uint32_t remote_line;
153
154   /**
155    * Current status of this line.
156    */ 
157   enum LineStatus status;
158
159 };
160
161
162 /**
163  * Our configuration.
164  */
165 static const struct GNUNET_CONFIGURATION_Handle *cfg;
166
167 /**
168  * Notification context containing all connected clients.
169  */
170 static struct GNUNET_SERVER_NotificationContext *nc;
171
172 /**
173  * Handle for mesh
174  */
175 static struct GNUNET_MESH_Handle *mesh;
176
177 /**
178  * Identity of this peer.
179  */
180 static struct GNUNET_PeerIdentity my_identity;
181
182 /**
183  * Head of DLL of active lines.
184  */
185 static struct Line *lines_head;
186
187 /**
188  * Tail of DLL of active lines.
189  */
190 static struct Line *lines_tail;
191
192 /**
193  * Counter for generating local line numbers.
194  * FIXME: randomize generation in the future
195  * to eliminate information leakage.
196  */
197 static uint32_t local_line_cnt;
198
199
200 /**
201  * Function to register a phone.
202  *
203  * @param cls closure, NULL
204  * @param client the client from which the message is
205  * @param message the message from the client
206  */
207 static void
208 handle_client_register_message (void *cls,
209                                 struct GNUNET_SERVER_Client *client,
210                                 const struct GNUNET_MessageHeader *message)
211 {
212   const struct ClientPhoneRegisterMessage *msg;
213   struct Line *line;
214
215   msg = (struct ClientPhoneRegisterMessage *) message;
216   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
217   if (NULL != line)
218   {
219     GNUNET_break (0);
220     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
221     return;
222   }
223   line = GNUNET_new (struct Line);
224   line->client = client;
225   GNUNET_SERVER_notification_context_add (nc, client);
226   GNUNET_CONTAINER_DLL_insert (lines_head,
227                                lines_tail,
228                                line);
229   line->local_line = ntohl (msg->line);
230   GNUNET_SERVER_client_set_user_context (client, line);
231   GNUNET_SERVER_receive_done (client, GNUNET_OK);
232 }
233
234
235 /**
236  * Function to handle a pickup request message from the client
237  *
238  * @param cls closure, NULL
239  * @param client the client from which the message is
240  * @param message the message from the client
241  */
242 static void
243 handle_client_pickup_message (void *cls,
244                               struct GNUNET_SERVER_Client *client,
245                               const struct GNUNET_MessageHeader *message)
246 {
247   const struct ClientPhonePickupMessage *msg;
248   struct GNUNET_MQ_Envelope *e;
249   struct MeshPhonePickupMessage *mppm;
250   const char *meta;
251   struct Line *line;
252   size_t len;
253
254   msg = (struct ClientPhonePickupMessage *) message;
255   meta = (const char *) &msg[1];
256   len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage);
257   if ( (0 == len) ||
258        ('\0' != meta[len - 1]) )
259   {
260     meta = NULL;
261     len = 0;
262   }
263   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
264   if (NULL == line)
265   {
266     GNUNET_break (0);
267     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
268     return;
269   }
270   switch (line->status)
271   {
272   case LS_CALLEE_LISTEN:
273     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274                 "Ignoring client's PICKUP message, caller has HUNG UP already\n");
275     GNUNET_SERVER_receive_done (client, GNUNET_OK);
276     break;
277   case LS_CALLEE_RINGING:
278     line->status = LS_CALLEE_CONNECTED;
279     break;
280   case LS_CALLEE_CONNECTED:
281     GNUNET_break (0);
282     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
283     return;
284   case LS_CALLEE_SHUTDOWN:
285     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286                 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
287     GNUNET_SERVER_receive_done (client, GNUNET_OK);
288     break;
289   case LS_CALLER_CALLING:
290   case LS_CALLER_CONNECTED:
291   case LS_CALLER_SHUTDOWN:
292     GNUNET_break (0);
293     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
294     return;
295   }
296   line->status = LS_CALLEE_CONNECTED;
297   e = GNUNET_MQ_msg_extra (mppm,
298                            len,
299                            GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
300   memcpy (&mppm[1], meta, len);
301   GNUNET_MQ_send (line->reliable_mq, e);
302   GNUNET_SERVER_receive_done (client, GNUNET_OK);
303 }
304
305
306 /**
307  * Destroy the mesh tunnels of a line.
308  *
309  * @param line line to shutdown tunnels of
310  */
311 static void
312 destroy_line_mesh_tunnels (struct Line *line)
313 {
314   if (NULL != line->reliable_mq)
315   {
316     GNUNET_MQ_destroy (line->reliable_mq);
317     line->reliable_mq = NULL;
318   }
319   if (NULL != line->unreliable_mth)
320   {
321     GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
322     line->unreliable_mth = NULL;
323   }
324   if (NULL != line->tunnel_unreliable)
325   {
326     GNUNET_MESH_tunnel_destroy (line->tunnel_unreliable);
327     line->tunnel_unreliable = NULL;
328   }
329   if (NULL != line->tunnel_reliable)
330   {
331     GNUNET_MESH_tunnel_destroy (line->tunnel_reliable);
332     line->tunnel_reliable = NULL;
333   }
334 }
335
336
337 /**
338  * We are done signalling shutdown to the other peer.  Close down
339  * (or reset) the line.
340  *
341  * @param cls the `struct Line` to reset/terminate
342  */
343 static void
344 mq_done_finish_caller_shutdown (void *cls)
345 {
346   struct Line *line = cls;
347
348   switch (line->status)
349   {
350   case LS_CALLEE_LISTEN:
351     GNUNET_break (0);
352     break;
353   case LS_CALLEE_RINGING:
354     GNUNET_break (0);
355     break;
356   case LS_CALLEE_CONNECTED:
357     GNUNET_break (0);
358     break;
359   case LS_CALLEE_SHUTDOWN:
360     line->status = LS_CALLEE_LISTEN;
361     destroy_line_mesh_tunnels (line);
362     return;
363   case LS_CALLER_CALLING:
364     line->status = LS_CALLER_SHUTDOWN;
365     break;
366   case LS_CALLER_CONNECTED:
367     line->status = LS_CALLER_SHUTDOWN;
368     break;
369   case LS_CALLER_SHUTDOWN:
370     destroy_line_mesh_tunnels (line);
371     GNUNET_CONTAINER_DLL_remove (lines_head,
372                                  lines_tail,
373                                  line);
374     GNUNET_free_non_null (line->audio_data);
375     GNUNET_free (line);
376     break;
377   }  
378 }
379
380
381 /**
382  * Function to handle a hangup request message from the client
383  *
384  * @param cls closure, NULL
385  * @param client the client from which the message is
386  * @param message the message from the client
387  */
388 static void
389 handle_client_hangup_message (void *cls,
390                               struct GNUNET_SERVER_Client *client,
391                               const struct GNUNET_MessageHeader *message)
392 {
393   const struct ClientPhoneHangupMessage *msg;
394   struct GNUNET_MQ_Envelope *e;
395   struct MeshPhoneHangupMessage *mhum;
396   const char *meta;
397   struct Line *line;
398   size_t len;
399
400   msg = (struct ClientPhoneHangupMessage *) message;
401   meta = (const char *) &msg[1];
402   len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage);
403   if ( (0 == len) ||
404        ('\0' != meta[len - 1]) )
405   {
406     meta = NULL;
407     len = 0;
408   }
409   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
410   if (NULL == line)
411   {
412     GNUNET_break (0);
413     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
414     return;
415   }
416   switch (line->status)
417   {
418   case LS_CALLEE_LISTEN:
419     GNUNET_break (0);
420     GNUNET_SERVER_receive_done (client, GNUNET_OK);
421     return;
422   case LS_CALLEE_RINGING:
423     line->status = LS_CALLEE_SHUTDOWN;
424     break;
425   case LS_CALLEE_CONNECTED:
426     line->status = LS_CALLEE_SHUTDOWN;
427     break;
428   case LS_CALLEE_SHUTDOWN:
429     GNUNET_break (0);
430     GNUNET_SERVER_receive_done (client, GNUNET_OK);
431     return;
432   case LS_CALLER_CALLING:
433     line->status = LS_CALLER_SHUTDOWN;
434     break;
435   case LS_CALLER_CONNECTED:
436     line->status = LS_CALLER_SHUTDOWN;
437     break;
438   case LS_CALLER_SHUTDOWN:
439     GNUNET_break (0);
440     GNUNET_SERVER_receive_done (client, GNUNET_OK);
441     return;
442   }
443   e = GNUNET_MQ_msg_extra (mhum,
444                            len,
445                            GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
446   memcpy (&mhum[1], meta, len);
447   GNUNET_MQ_notify_sent (e,
448                          &mq_done_finish_caller_shutdown,
449                          line);
450   GNUNET_MQ_send (line->reliable_mq, e);
451   GNUNET_SERVER_receive_done (client, GNUNET_OK);
452 }
453
454
455 /**
456  * Function to handle call request the client
457  *
458  * @param cls closure, NULL
459  * @param client the client from which the message is
460  * @param message the message from the client
461  */
462 static void
463 handle_client_call_message (void *cls,
464                             struct GNUNET_SERVER_Client *client,
465                             const struct GNUNET_MessageHeader *message)
466 {
467   const struct ClientCallMessage *msg;
468   struct Line *line;
469   struct GNUNET_MQ_Envelope *e;
470   struct MeshPhoneRingMessage *ring;
471
472   msg = (struct ClientCallMessage *) message;
473   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
474   if (NULL != line)
475   {
476     GNUNET_break (0);
477     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
478     return;
479   }
480   line = GNUNET_new (struct Line);
481   line->target = msg->target;
482   GNUNET_CONTAINER_DLL_insert (lines_head,
483                                lines_tail,
484                                line);
485   line->remote_line = ntohl (msg->line);
486   line->status = LS_CALLER_CALLING;
487   line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh,
488                                                      line,
489                                                      &msg->target,
490                                                      GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
491                                                      GNUNET_NO,
492                                                      GNUNET_YES);
493   line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
494   line->local_line = local_line_cnt++;
495   e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
496   ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
497   ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
498                               sizeof (struct GNUNET_TIME_AbsoluteNBO) +
499                               sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
500                               sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
501   GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id,
502                                                   &ring->caller_id);
503   ring->remote_line = msg->line;
504   ring->source_line = line->local_line;
505   ring->target = msg->target;
506   ring->source = my_identity;
507   ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
508   GNUNET_CRYPTO_ecc_sign (&msg->caller_id,
509                           &ring->purpose,
510                           &ring->signature);
511   GNUNET_MQ_send (line->reliable_mq, e);
512   GNUNET_SERVER_client_set_user_context (client, line);
513   GNUNET_SERVER_receive_done (client, GNUNET_OK);
514 }
515
516
517 /**
518  * Transmit audio data via unreliable mesh channel.
519  *
520  * @param cls the `struct Line` we are transmitting for
521  * @param size number of bytes available in @a buf
522  * @param buf where to copy the data
523  * @return number of bytes copied to @buf
524  */
525 static size_t
526 transmit_line_audio (void *cls,
527                      size_t size,
528                      void *buf)
529 {
530   struct Line *line = cls;
531   struct MeshAudioMessage *mam = buf;
532   
533   line->unreliable_mth = NULL;
534   if ( (NULL == buf) ||
535        (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
536     {
537     /* eh, other error handling? */
538     return 0;
539   }
540   mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size);
541   mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
542   mam->remote_line = htonl (line->remote_line);
543   memcpy (&mam[1], line->audio_data, line->audio_size);
544   GNUNET_free (line->audio_data);
545   line->audio_data = NULL;
546   return sizeof (struct MeshAudioMessage) + line->audio_size;  
547 }
548
549
550 /**
551  * Function to handle audio data from the client
552  *
553  * @param cls closure, NULL
554  * @param client the client from which the message is
555  * @param message the message from the client
556  */
557 static void
558 handle_client_audio_message (void *cls,
559                              struct GNUNET_SERVER_Client *client,
560                              const struct GNUNET_MessageHeader *message)
561 {
562   const struct ClientAudioMessage *msg;
563   struct Line *line;
564   size_t size;
565
566   size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
567   msg = (struct ClientAudioMessage *) message;
568   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
569   if (NULL == line)
570   {
571     GNUNET_break (0);
572     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
573     return;
574   }
575   switch (line->status)
576   {
577   case LS_CALLEE_LISTEN:
578   case LS_CALLEE_RINGING:
579   case LS_CALLER_CALLING:
580     GNUNET_break (0);
581     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
582     return;
583   case LS_CALLEE_CONNECTED:
584   case LS_CALLER_CONNECTED:
585     /* common case, handled below */
586     break;
587   case LS_CALLEE_SHUTDOWN:
588   case LS_CALLER_SHUTDOWN:
589     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590                 "Mesh audio channel in shutdown; audio data dropped\n");
591     GNUNET_SERVER_receive_done (client, GNUNET_OK);
592     return;
593   }
594   if (NULL == line->tunnel_unreliable)
595   {
596     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
597                 _("Mesh audio channel not ready; audio data dropped\n"));
598     GNUNET_SERVER_receive_done (client, GNUNET_OK);    
599     return;
600   }
601   if (NULL != line->unreliable_mth)
602   {
603     /* NOTE: we may want to not do this and instead combine the data */
604     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605                 "Dropping previous audio data segment with %u bytes\n",
606                 line->audio_size);
607     GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
608     GNUNET_free (line->audio_data);
609   }
610   line->audio_size = size;
611   line->audio_data = GNUNET_malloc (line->audio_size);
612   memcpy (line->audio_data,
613           &msg[1],
614           size);
615   line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable,
616                                                             GNUNET_NO,
617                                                             GNUNET_TIME_UNIT_FOREVER_REL,
618                                                             sizeof (struct MeshAudioMessage) 
619                                                             + line->audio_size,
620                                                             &transmit_line_audio,
621                                                             line);
622   GNUNET_SERVER_receive_done (client, GNUNET_OK);
623 }
624
625
626 /**
627  * We are done signalling shutdown to the other peer.  
628  * Destroy the tunnel.
629  *
630  * @param cls the `struct GNUNET_MESH_tunnel` to destroy
631  */
632 static void
633 mq_done_destroy_tunnel (void *cls)
634 {
635   struct GNUNET_MESH_Tunnel *tunnel = cls;
636   
637   GNUNET_MESH_tunnel_destroy (tunnel);
638 }
639
640
641 /**
642  * Function to handle a ring message incoming over mesh
643  *
644  * @param cls closure, NULL
645  * @param tunnel the tunnel over which the message arrived
646  * @param tunnel_ctx the tunnel context, can be NULL
647  * @param message the incoming message
648  * @return #GNUNET_OK
649  */
650 static int
651 handle_mesh_ring_message (void *cls,
652                           struct GNUNET_MESH_Tunnel *tunnel,
653                           void **tunnel_ctx,
654                           const struct GNUNET_MessageHeader *message)
655 {
656   const struct MeshPhoneRingMessage *msg;
657   struct Line *line;
658   struct GNUNET_MQ_Envelope *e;
659   struct MeshPhoneBusyMessage *busy;
660   struct ClientPhoneRingMessage cring;
661   
662   msg = (const struct MeshPhoneRingMessage *) message;
663   if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
664                                     sizeof (struct GNUNET_TIME_AbsoluteNBO) +
665                                     sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
666                                     sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) ||
667        (GNUNET_OK !=
668         GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
669                                   &msg->purpose,
670                                   &msg->signature,
671                                   &msg->caller_id)) )
672   {
673     GNUNET_break_op (0);
674     return GNUNET_SYSERR;
675   }
676   for (line = lines_head; NULL != line; line = line->next)  
677     if ( (line->local_line == ntohl (msg->remote_line)) &&
678          (LS_CALLEE_LISTEN == line->status) )
679       break;
680   if (NULL == line) 
681   {
682     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
683                 _("No available phone for incoming call on line %u, sending BUSY signal\n"),
684                 ntohl (msg->remote_line));
685     e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
686     GNUNET_MQ_notify_sent (e,
687                            &mq_done_destroy_tunnel,
688                            tunnel);
689     GNUNET_MQ_send (line->reliable_mq, e);
690     GNUNET_MESH_receive_done (tunnel); /* needed? */
691     return GNUNET_OK;
692   }
693   line->status = LS_CALLEE_RINGING;
694   line->remote_line = ntohl (msg->source_line);
695   line->tunnel_reliable = tunnel;
696   line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
697   *tunnel_ctx = line;
698   cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
699   cring.header.size = htons (sizeof (cring));
700   cring.reserved = htonl (0);
701   cring.caller_id = msg->caller_id;
702   GNUNET_SERVER_notification_context_unicast (nc,
703                                               line->client,
704                                               &cring.header,
705                                               GNUNET_NO);
706   GNUNET_MESH_receive_done (tunnel);
707   return GNUNET_OK;
708 }
709
710
711 /**
712  * Function to handle a hangup message incoming over mesh
713  *
714  * @param cls closure, NULL
715  * @param tunnel the tunnel over which the message arrived
716  * @param tunnel_ctx the tunnel context, can be NULL
717  * @param message the incoming message
718  * @return #GNUNET_OK
719  */
720 static int
721 handle_mesh_hangup_message (void *cls,
722                             struct GNUNET_MESH_Tunnel *tunnel,
723                             void **tunnel_ctx,
724                             const struct GNUNET_MessageHeader *message)
725 {
726   struct Line *line = *tunnel_ctx;
727   const struct MeshPhoneHangupMessage *msg;
728   const char *reason;
729   size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage);
730   char buf[len + sizeof (struct ClientPhoneHangupMessage)];
731   struct ClientPhoneHangupMessage *hup;
732   
733   msg = (const struct MeshPhoneHangupMessage *) message;
734   len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage);
735   reason = (const char *) &msg[1];
736   if ( (0 == len) ||
737        ('\0' != reason[len - 1]) )
738   {
739     reason = NULL;
740     len = 0;
741   }
742   if (NULL == line)
743   {
744     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745                 "HANGUP message received for non-existing line, dropping tunnel.\n");
746     return GNUNET_SYSERR;
747   }
748   *tunnel_ctx = NULL;
749   switch (line->status)
750   {
751   case LS_CALLEE_LISTEN:
752     GNUNET_break (0);
753     return GNUNET_SYSERR;
754   case LS_CALLEE_RINGING:
755     line->status = LS_CALLEE_LISTEN;
756     destroy_line_mesh_tunnels (line);
757     break;
758   case LS_CALLEE_CONNECTED:
759     line->status = LS_CALLEE_LISTEN;
760     destroy_line_mesh_tunnels (line);
761     break;
762   case LS_CALLEE_SHUTDOWN:
763     line->status = LS_CALLEE_LISTEN;
764     destroy_line_mesh_tunnels (line);
765     return GNUNET_OK;
766   case LS_CALLER_CALLING:
767     line->status = LS_CALLER_SHUTDOWN;
768     mq_done_finish_caller_shutdown (line);
769     break;
770   case LS_CALLER_CONNECTED:
771     line->status = LS_CALLER_SHUTDOWN;
772     mq_done_finish_caller_shutdown (line);
773     break;
774   case LS_CALLER_SHUTDOWN:
775     mq_done_finish_caller_shutdown (line);
776     return GNUNET_OK;
777   }
778   hup = (struct ClientPhoneHangupMessage *) buf;
779   hup->header.size = sizeof (buf);
780   hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
781   memcpy (&hup[1], reason, len);
782   GNUNET_SERVER_notification_context_unicast (nc,
783                                               line->client,
784                                               &hup->header,
785                                               GNUNET_NO);
786   GNUNET_MESH_receive_done (tunnel);
787   return GNUNET_OK;
788 }
789
790
791 /**
792  * Function to handle a pickup message incoming over mesh
793  *
794  * @param cls closure, NULL
795  * @param tunnel the tunnel over which the message arrived
796  * @param tunnel_ctx the tunnel context, can be NULL
797  * @param message the incoming message
798  * @return #GNUNET_OK
799  */
800 static int
801 handle_mesh_pickup_message (void *cls,
802                             struct GNUNET_MESH_Tunnel *tunnel,
803                             void **tunnel_ctx,
804                             const struct GNUNET_MessageHeader *message)
805 {
806   const struct MeshPhonePickupMessage *msg;
807   struct Line *line = *tunnel_ctx;
808   const char *metadata;
809   size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage);
810   char buf[len + sizeof (struct ClientPhonePickupMessage)];
811   struct ClientPhonePickupMessage *pick;
812   
813   msg = (const struct MeshPhonePickupMessage *) message;
814   len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage);
815   metadata = (const char *) &msg[1];
816   if ( (0 == len) ||
817        ('\0' != metadata[len - 1]) )
818   {
819     metadata = NULL;
820     len = 0;
821   }
822   if (NULL == line)
823   {
824     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825                 "PICKUP message received for non-existing line, dropping tunnel.\n");
826     return GNUNET_SYSERR;
827   }
828   GNUNET_MESH_receive_done (tunnel);
829   switch (line->status)
830   {
831   case LS_CALLEE_LISTEN:
832     GNUNET_break (0);
833     return GNUNET_SYSERR;
834   case LS_CALLEE_RINGING:
835   case LS_CALLEE_CONNECTED:
836     GNUNET_break_op (0);
837     destroy_line_mesh_tunnels (line);
838     line->status = LS_CALLEE_LISTEN;
839     return GNUNET_SYSERR;
840   case LS_CALLEE_SHUTDOWN:
841     GNUNET_break_op (0);
842     line->status = LS_CALLEE_LISTEN;
843     destroy_line_mesh_tunnels (line);
844     break;
845   case LS_CALLER_CALLING:
846     line->status = LS_CALLER_CONNECTED;
847     break;
848   case LS_CALLER_CONNECTED:
849     GNUNET_break_op (0);
850     return GNUNET_OK;
851   case LS_CALLER_SHUTDOWN:
852     GNUNET_break_op (0);
853     mq_done_finish_caller_shutdown (line);
854     return GNUNET_SYSERR;
855   }
856   pick = (struct ClientPhonePickupMessage *) buf;
857   pick->header.size = sizeof (buf);
858   pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
859   memcpy (&pick[1], metadata, len);
860   GNUNET_SERVER_notification_context_unicast (nc,
861                                               line->client,
862                                               &pick->header,
863                                               GNUNET_NO);
864   line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
865                                                        line,
866                                                        &line->target,
867                                                        GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
868                                                        GNUNET_YES,
869                                                        GNUNET_NO);
870   return GNUNET_OK;
871 }
872
873
874 /**
875  * Function to handle a busy message incoming over mesh
876  *
877  * @param cls closure, NULL
878  * @param tunnel the tunnel over which the message arrived
879  * @param tunnel_ctx the tunnel context, can be NULL
880  * @param message the incoming message
881  * @return #GNUNET_OK
882  */
883 static int
884 handle_mesh_busy_message (void *cls,
885                           struct GNUNET_MESH_Tunnel *tunnel,
886                           void **tunnel_ctx,
887                           const struct GNUNET_MessageHeader *message)
888 {
889   struct Line *line = *tunnel_ctx;
890   struct ClientPhoneBusyMessage busy;
891
892   if (NULL == line)
893   {
894     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
895                 "HANGUP message received for non-existing line, dropping tunnel.\n");
896     return GNUNET_SYSERR;
897   }
898   busy.header.size = sizeof (busy);
899   busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY);
900   GNUNET_SERVER_notification_context_unicast (nc,
901                                               line->client,
902                                               &busy.header,
903                                               GNUNET_NO);
904   GNUNET_MESH_receive_done (tunnel);
905   *tunnel_ctx = NULL;
906   switch (line->status)
907   {
908   case LS_CALLEE_LISTEN:
909     GNUNET_break (0);
910     return GNUNET_SYSERR;
911   case LS_CALLEE_RINGING:
912     GNUNET_break_op (0);
913     break;
914   case LS_CALLEE_CONNECTED:
915     GNUNET_break_op (0);
916     break;
917   case LS_CALLEE_SHUTDOWN:
918     GNUNET_break_op (0);
919     break;
920   case LS_CALLER_CALLING:
921     line->status = LS_CALLER_SHUTDOWN;
922     mq_done_finish_caller_shutdown (line);
923     break;
924   case LS_CALLER_CONNECTED:
925     line->status = LS_CALLER_SHUTDOWN;
926     mq_done_finish_caller_shutdown (line);
927     break;
928   case LS_CALLER_SHUTDOWN:
929     mq_done_finish_caller_shutdown (line);
930     break;
931   }
932   return GNUNET_OK;
933 }
934
935
936 /**
937  * Function to handle an audio message incoming over mesh
938  *
939  * @param cls closure, NULL
940  * @param tunnel the tunnel over which the message arrived
941  * @param tunnel_ctx the tunnel context, can be NULL
942  * @param message the incoming message
943  * @return #GNUNET_OK
944  */
945 static int
946 handle_mesh_audio_message (void *cls,
947                            struct GNUNET_MESH_Tunnel *tunnel,
948                            void **tunnel_ctx,
949                            const struct GNUNET_MessageHeader *message)
950 {
951   const struct MeshAudioMessage *msg;
952   struct Line *line = *tunnel_ctx;
953   struct GNUNET_PeerIdentity sender;
954   size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
955   char buf[msize + sizeof (struct ClientAudioMessage)];
956   struct ClientAudioMessage *cam;
957   
958   msg = (const struct MeshAudioMessage *) message;
959   if (NULL == line)
960   {
961     sender = *GNUNET_MESH_tunnel_get_info (tunnel,
962                                            GNUNET_MESH_OPTION_PEER)->peer;
963     for (line = lines_head; NULL != line; line = line->next)
964       if ( (line->local_line == ntohl (msg->remote_line)) &&
965            (LS_CALLEE_CONNECTED == line->status) &&
966            (0 == memcmp (&line->target,
967                          &sender,
968                          sizeof (struct GNUNET_PeerIdentity))) &&
969            (NULL == line->tunnel_unreliable) )
970         break;
971     if (NULL == line)
972     {
973       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974                   "Received AUDIO data for non-existing line %u, dropping.\n",
975                   ntohl (msg->remote_line));
976       return GNUNET_SYSERR;
977     }
978     line->tunnel_unreliable = tunnel;
979     *tunnel_ctx = line;
980   }
981   cam = (struct ClientAudioMessage *) buf;
982   cam->header.size = htons (sizeof (buf));
983   cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
984   memcpy (&cam[1], &msg[1], msize);
985   GNUNET_SERVER_notification_context_unicast (nc,
986                                               line->client,
987                                               &cam->header,
988                                               GNUNET_YES);
989   GNUNET_MESH_receive_done (tunnel);
990   return GNUNET_OK;
991 }
992
993
994 /**
995  * Method called whenever another peer has added us to a tunnel
996  * the other peer initiated.
997  *
998  * @param cls closure
999  * @param tunnel new handle to the tunnel
1000  * @param initiator peer that started the tunnel
1001  * @param port port
1002  * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
1003  */
1004 static void *
1005 inbound_tunnel (void *cls,
1006                 struct GNUNET_MESH_Tunnel *tunnel,
1007                 const struct GNUNET_PeerIdentity *initiator, 
1008                 uint32_t port)
1009 {
1010   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1011               _("Received incoming tunnel on port %d\n"), 
1012               port);
1013   return NULL;
1014 }
1015
1016
1017 /**
1018  * Function called whenever an inbound tunnel is destroyed.  Should clean up
1019  * any associated state.
1020  *
1021  * @param cls closure (set from #GNUNET_MESH_connect)
1022  * @param tunnel connection to the other end (henceforth invalid)
1023  * @param tunnel_ctx place where local state associated
1024  *                   with the tunnel is stored
1025  */
1026 static void
1027 inbound_end (void *cls,
1028              const struct GNUNET_MESH_Tunnel *tunnel,
1029              void *tunnel_ctx)
1030 {
1031   struct Line *line = tunnel_ctx;
1032   struct ClientPhoneHangupMessage hup;
1033
1034   if (NULL == line)
1035     return;
1036   if (line->tunnel_unreliable == tunnel)
1037   {
1038     line->tunnel_unreliable = NULL;
1039     return;
1040   }
1041   hup.header.size = sizeof (hup);
1042   hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1043   switch (line->status)
1044   {
1045   case LS_CALLEE_LISTEN:
1046     GNUNET_break (0);
1047     return;
1048   case LS_CALLEE_RINGING:
1049   case LS_CALLEE_CONNECTED:
1050     GNUNET_SERVER_notification_context_unicast (nc,
1051                                                 line->client,
1052                                                 &hup.header,
1053                                                 GNUNET_NO);
1054     line->status = LS_CALLEE_LISTEN;
1055     break;
1056   case LS_CALLEE_SHUTDOWN:
1057     line->status = LS_CALLEE_LISTEN;
1058     destroy_line_mesh_tunnels (line);
1059     break;
1060   case LS_CALLER_CALLING:
1061   case LS_CALLER_CONNECTED:
1062     GNUNET_SERVER_notification_context_unicast (nc,
1063                                                 line->client,
1064                                                 &hup.header,
1065                                                 GNUNET_NO);
1066     destroy_line_mesh_tunnels (line);
1067     GNUNET_CONTAINER_DLL_remove (lines_head,
1068                                  lines_tail,
1069                                  line);
1070     GNUNET_free_non_null (line->audio_data);
1071     GNUNET_free (line);
1072     break;
1073   case LS_CALLER_SHUTDOWN:
1074     destroy_line_mesh_tunnels (line);
1075     GNUNET_CONTAINER_DLL_remove (lines_head,
1076                                  lines_tail,
1077                                  line);
1078     GNUNET_free (line);
1079     break;
1080   }
1081 }
1082
1083
1084 /**
1085  * A client disconnected.  Remove all of its data structure entries.
1086  *
1087  * @param cls closure, NULL
1088  * @param client identification of the client
1089  */
1090 static void
1091 handle_client_disconnect (void *cls, 
1092                           struct GNUNET_SERVER_Client *client)
1093 {
1094   struct Line *line;
1095
1096   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1097   if (NULL == line)
1098     return;
1099   GNUNET_CONTAINER_DLL_remove (lines_head,
1100                                lines_tail,
1101                                line);
1102   GNUNET_free (line);
1103   GNUNET_SERVER_client_set_user_context (client, NULL);
1104 }
1105
1106
1107 /**
1108  * Shutdown nicely
1109  * 
1110  * @param cls closure, NULL
1111  * @param tc the task context
1112  */
1113 static void
1114 do_shutdown (void *cls,
1115              const struct GNUNET_SCHEDULER_TaskContext *tc)
1116 {
1117   if (NULL != mesh)
1118   {
1119     GNUNET_MESH_disconnect (mesh);
1120     mesh = NULL;
1121   }
1122   if (NULL != nc)
1123   {
1124     GNUNET_SERVER_notification_context_destroy (nc);
1125     nc = NULL;
1126   }
1127 }
1128
1129
1130 /**
1131  * Main function that will be run by the scheduler.
1132  *
1133  * @param cls closure
1134  * @param server server handle
1135  * @param c configuration
1136  */
1137 static void
1138 run (void *cls, 
1139      struct GNUNET_SERVER_Handle *server,
1140      const struct GNUNET_CONFIGURATION_Handle *c)
1141 {
1142   static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1143     {&handle_client_register_message, NULL,
1144      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1145      sizeof (struct ClientPhoneRegisterMessage)},
1146     {&handle_client_pickup_message, NULL,
1147      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1148      0},
1149     {&handle_client_hangup_message, NULL,
1150      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1151      0},
1152     {&handle_client_call_message, NULL,
1153      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1154      0},
1155     {&handle_client_audio_message, NULL,
1156      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1157      0},
1158     {NULL, NULL, 0, 0}
1159   };
1160   static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1161     {&handle_mesh_ring_message,
1162      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1163      sizeof (struct MeshPhoneRingMessage)},
1164     {&handle_mesh_hangup_message, 
1165      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1166      0},
1167     {&handle_mesh_pickup_message, 
1168      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1169      0},
1170     {&handle_mesh_busy_message, 
1171      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY,
1172      sizeof (struct MeshPhoneBusyMessage)},
1173     {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1174      0},
1175     {NULL, 0, 0}
1176   };
1177   static uint32_t ports[] = { 
1178     GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1179     GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1180     0 
1181   };
1182
1183   cfg = c;
1184   GNUNET_assert (GNUNET_OK ==
1185                  GNUNET_CRYPTO_get_host_identity (cfg,
1186                                                   &my_identity));
1187   mesh = GNUNET_MESH_connect (cfg,
1188                               NULL,
1189                               &inbound_tunnel,
1190                               &inbound_end, 
1191                               mesh_handlers, 
1192                               ports);
1193
1194   if (NULL == mesh)
1195   {
1196     GNUNET_break (0);
1197     GNUNET_SCHEDULER_shutdown ();
1198     return;
1199   }
1200   nc = GNUNET_SERVER_notification_context_create (server, 16);
1201   GNUNET_SERVER_add_handlers (server, server_handlers);
1202   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1203   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 
1204                                 &do_shutdown,
1205                                 NULL);
1206 }
1207
1208
1209 /**
1210  * The main function for the conversation service.
1211  *
1212  * @param argc number of arguments from the command line
1213  * @param argv command line arguments
1214  * @return 0 ok, 1 on error
1215  */
1216 int
1217 main (int argc, 
1218       char *const *argv)
1219 {
1220   return (GNUNET_OK ==
1221           GNUNET_SERVICE_run (argc, argv,
1222                               "conversation", 
1223                               GNUNET_SERVICE_OPTION_NONE,
1224                               &run, NULL)) ? 0 : 1;
1225 }
1226
1227 /* end of gnunet-service-conversation.c */