-fix message type
[oweals/gnunet.git] / src / conversation / gnunet-service-conversation.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_SERVER_client_set_user_context (client, line);
227   GNUNET_CONTAINER_DLL_insert (lines_head,
228                                lines_tail,
229                                line);
230   line->local_line = ntohl (msg->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->client = client;
482   GNUNET_SERVER_client_set_user_context (client, line);
483   GNUNET_SERVER_notification_context_add (nc, client);
484   line->target = msg->target;
485   GNUNET_CONTAINER_DLL_insert (lines_head,
486                                lines_tail,
487                                line);
488   line->remote_line = ntohl (msg->line);
489   line->status = LS_CALLER_CALLING;
490   line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh,
491                                                      line,
492                                                      &msg->target,
493                                                      GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
494                                                      GNUNET_NO,
495                                                      GNUNET_YES);
496   line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
497   line->local_line = local_line_cnt++;
498   e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
499   ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
500   ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
501                               sizeof (struct GNUNET_TIME_AbsoluteNBO) +
502                               sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
503                               sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
504   GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id,
505                                                   &ring->caller_id);
506   ring->remote_line = msg->line;
507   ring->source_line = line->local_line;
508   ring->target = msg->target;
509   ring->source = my_identity;
510   ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
511   GNUNET_CRYPTO_ecc_sign (&msg->caller_id,
512                           &ring->purpose,
513                           &ring->signature);
514   GNUNET_MQ_send (line->reliable_mq, e);
515   GNUNET_SERVER_receive_done (client, GNUNET_OK);
516 }
517
518
519 /**
520  * Transmit audio data via unreliable mesh channel.
521  *
522  * @param cls the `struct Line` we are transmitting for
523  * @param size number of bytes available in @a buf
524  * @param buf where to copy the data
525  * @return number of bytes copied to @buf
526  */
527 static size_t
528 transmit_line_audio (void *cls,
529                      size_t size,
530                      void *buf)
531 {
532   struct Line *line = cls;
533   struct MeshAudioMessage *mam = buf;
534   
535   line->unreliable_mth = NULL;
536   if ( (NULL == buf) ||
537        (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
538     {
539     /* eh, other error handling? */
540     return 0;
541   }
542   mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size);
543   mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
544   mam->remote_line = htonl (line->remote_line);
545   memcpy (&mam[1], line->audio_data, line->audio_size);
546   GNUNET_free (line->audio_data);
547   line->audio_data = NULL;
548   return sizeof (struct MeshAudioMessage) + line->audio_size;  
549 }
550
551
552 /**
553  * Function to handle audio data from the client
554  *
555  * @param cls closure, NULL
556  * @param client the client from which the message is
557  * @param message the message from the client
558  */
559 static void
560 handle_client_audio_message (void *cls,
561                              struct GNUNET_SERVER_Client *client,
562                              const struct GNUNET_MessageHeader *message)
563 {
564   const struct ClientAudioMessage *msg;
565   struct Line *line;
566   size_t size;
567
568   size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
569   msg = (struct ClientAudioMessage *) message;
570   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
571   if (NULL == line)
572   {
573     GNUNET_break (0);
574     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
575     return;
576   }
577   switch (line->status)
578   {
579   case LS_CALLEE_LISTEN:
580   case LS_CALLEE_RINGING:
581   case LS_CALLER_CALLING:
582     GNUNET_break (0);
583     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
584     return;
585   case LS_CALLEE_CONNECTED:
586   case LS_CALLER_CONNECTED:
587     /* common case, handled below */
588     break;
589   case LS_CALLEE_SHUTDOWN:
590   case LS_CALLER_SHUTDOWN:
591     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592                 "Mesh audio channel in shutdown; audio data dropped\n");
593     GNUNET_SERVER_receive_done (client, GNUNET_OK);
594     return;
595   }
596   if (NULL == line->tunnel_unreliable)
597   {
598     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
599                 _("Mesh audio channel not ready; audio data dropped\n"));
600     GNUNET_SERVER_receive_done (client, GNUNET_OK);    
601     return;
602   }
603   if (NULL != line->unreliable_mth)
604   {
605     /* NOTE: we may want to not do this and instead combine the data */
606     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607                 "Dropping previous audio data segment with %u bytes\n",
608                 line->audio_size);
609     GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
610     GNUNET_free (line->audio_data);
611   }
612   line->audio_size = size;
613   line->audio_data = GNUNET_malloc (line->audio_size);
614   memcpy (line->audio_data,
615           &msg[1],
616           size);
617   line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable,
618                                                             GNUNET_NO,
619                                                             GNUNET_TIME_UNIT_FOREVER_REL,
620                                                             sizeof (struct MeshAudioMessage) 
621                                                             + line->audio_size,
622                                                             &transmit_line_audio,
623                                                             line);
624   GNUNET_SERVER_receive_done (client, GNUNET_OK);
625 }
626
627
628 /**
629  * We are done signalling shutdown to the other peer.  
630  * Destroy the tunnel.
631  *
632  * @param cls the `struct GNUNET_MESH_tunnel` to destroy
633  */
634 static void
635 mq_done_destroy_tunnel (void *cls)
636 {
637   struct GNUNET_MESH_Tunnel *tunnel = cls;
638   
639   GNUNET_MESH_tunnel_destroy (tunnel);
640 }
641
642
643 /**
644  * Function to handle a ring message incoming over mesh
645  *
646  * @param cls closure, NULL
647  * @param tunnel the tunnel over which the message arrived
648  * @param tunnel_ctx the tunnel context, can be NULL
649  * @param message the incoming message
650  * @return #GNUNET_OK
651  */
652 static int
653 handle_mesh_ring_message (void *cls,
654                           struct GNUNET_MESH_Tunnel *tunnel,
655                           void **tunnel_ctx,
656                           const struct GNUNET_MessageHeader *message)
657 {
658   const struct MeshPhoneRingMessage *msg;
659   struct Line *line;
660   struct GNUNET_MQ_Envelope *e;
661   struct MeshPhoneBusyMessage *busy;
662   struct ClientPhoneRingMessage cring;
663   
664   msg = (const struct MeshPhoneRingMessage *) message;
665   if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
666                                     sizeof (struct GNUNET_TIME_AbsoluteNBO) +
667                                     sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
668                                     sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) ||
669        (GNUNET_OK !=
670         GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
671                                   &msg->purpose,
672                                   &msg->signature,
673                                   &msg->caller_id)) )
674   {
675     GNUNET_break_op (0);
676     return GNUNET_SYSERR;
677   }
678   for (line = lines_head; NULL != line; line = line->next)  
679     if ( (line->local_line == ntohl (msg->remote_line)) &&
680          (LS_CALLEE_LISTEN == line->status) )
681       break;
682   if (NULL == line) 
683   {
684     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
685                 _("No available phone for incoming call on line %u, sending BUSY signal\n"),
686                 ntohl (msg->remote_line));
687     e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
688     GNUNET_MQ_notify_sent (e,
689                            &mq_done_destroy_tunnel,
690                            tunnel);
691     GNUNET_MQ_send (line->reliable_mq, e);
692     GNUNET_MESH_receive_done (tunnel); /* needed? */
693     return GNUNET_OK;
694   }
695   line->status = LS_CALLEE_RINGING;
696   line->remote_line = ntohl (msg->source_line);
697   line->tunnel_reliable = tunnel;
698   line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
699   *tunnel_ctx = line;
700   cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
701   cring.header.size = htons (sizeof (cring));
702   cring.reserved = htonl (0);
703   cring.caller_id = msg->caller_id;
704   GNUNET_SERVER_notification_context_unicast (nc,
705                                               line->client,
706                                               &cring.header,
707                                               GNUNET_NO);
708   GNUNET_MESH_receive_done (tunnel);
709   return GNUNET_OK;
710 }
711
712
713 /**
714  * Function to handle a hangup message incoming over mesh
715  *
716  * @param cls closure, NULL
717  * @param tunnel the tunnel over which the message arrived
718  * @param tunnel_ctx the tunnel context, can be NULL
719  * @param message the incoming message
720  * @return #GNUNET_OK
721  */
722 static int
723 handle_mesh_hangup_message (void *cls,
724                             struct GNUNET_MESH_Tunnel *tunnel,
725                             void **tunnel_ctx,
726                             const struct GNUNET_MessageHeader *message)
727 {
728   struct Line *line = *tunnel_ctx;
729   const struct MeshPhoneHangupMessage *msg;
730   const char *reason;
731   size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage);
732   char buf[len + sizeof (struct ClientPhoneHangupMessage)];
733   struct ClientPhoneHangupMessage *hup;
734   
735   msg = (const struct MeshPhoneHangupMessage *) message;
736   len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage);
737   reason = (const char *) &msg[1];
738   if ( (0 == len) ||
739        ('\0' != reason[len - 1]) )
740   {
741     reason = NULL;
742     len = 0;
743   }
744   if (NULL == line)
745   {
746     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747                 "HANGUP message received for non-existing line, dropping tunnel.\n");
748     return GNUNET_SYSERR;
749   }
750   *tunnel_ctx = NULL;
751   switch (line->status)
752   {
753   case LS_CALLEE_LISTEN:
754     GNUNET_break (0);
755     return GNUNET_SYSERR;
756   case LS_CALLEE_RINGING:
757     line->status = LS_CALLEE_LISTEN;
758     destroy_line_mesh_tunnels (line);
759     break;
760   case LS_CALLEE_CONNECTED:
761     line->status = LS_CALLEE_LISTEN;
762     destroy_line_mesh_tunnels (line);
763     break;
764   case LS_CALLEE_SHUTDOWN:
765     line->status = LS_CALLEE_LISTEN;
766     destroy_line_mesh_tunnels (line);
767     return GNUNET_OK;
768   case LS_CALLER_CALLING:
769     line->status = LS_CALLER_SHUTDOWN;
770     mq_done_finish_caller_shutdown (line);
771     break;
772   case LS_CALLER_CONNECTED:
773     line->status = LS_CALLER_SHUTDOWN;
774     mq_done_finish_caller_shutdown (line);
775     break;
776   case LS_CALLER_SHUTDOWN:
777     mq_done_finish_caller_shutdown (line);
778     return GNUNET_OK;
779   }
780   hup = (struct ClientPhoneHangupMessage *) buf;
781   hup->header.size = sizeof (buf);
782   hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
783   memcpy (&hup[1], reason, len);
784   GNUNET_SERVER_notification_context_unicast (nc,
785                                               line->client,
786                                               &hup->header,
787                                               GNUNET_NO);
788   GNUNET_MESH_receive_done (tunnel);
789   return GNUNET_OK;
790 }
791
792
793 /**
794  * Function to handle a pickup message incoming over mesh
795  *
796  * @param cls closure, NULL
797  * @param tunnel the tunnel over which the message arrived
798  * @param tunnel_ctx the tunnel context, can be NULL
799  * @param message the incoming message
800  * @return #GNUNET_OK
801  */
802 static int
803 handle_mesh_pickup_message (void *cls,
804                             struct GNUNET_MESH_Tunnel *tunnel,
805                             void **tunnel_ctx,
806                             const struct GNUNET_MessageHeader *message)
807 {
808   const struct MeshPhonePickupMessage *msg;
809   struct Line *line = *tunnel_ctx;
810   const char *metadata;
811   size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage);
812   char buf[len + sizeof (struct ClientPhonePickupMessage)];
813   struct ClientPhonePickupMessage *pick;
814   
815   msg = (const struct MeshPhonePickupMessage *) message;
816   len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage);
817   metadata = (const char *) &msg[1];
818   if ( (0 == len) ||
819        ('\0' != metadata[len - 1]) )
820   {
821     metadata = NULL;
822     len = 0;
823   }
824   if (NULL == line)
825   {
826     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827                 "PICKUP message received for non-existing line, dropping tunnel.\n");
828     return GNUNET_SYSERR;
829   }
830   GNUNET_MESH_receive_done (tunnel);
831   switch (line->status)
832   {
833   case LS_CALLEE_LISTEN:
834     GNUNET_break (0);
835     return GNUNET_SYSERR;
836   case LS_CALLEE_RINGING:
837   case LS_CALLEE_CONNECTED:
838     GNUNET_break_op (0);
839     destroy_line_mesh_tunnels (line);
840     line->status = LS_CALLEE_LISTEN;
841     return GNUNET_SYSERR;
842   case LS_CALLEE_SHUTDOWN:
843     GNUNET_break_op (0);
844     line->status = LS_CALLEE_LISTEN;
845     destroy_line_mesh_tunnels (line);
846     break;
847   case LS_CALLER_CALLING:
848     line->status = LS_CALLER_CONNECTED;
849     break;
850   case LS_CALLER_CONNECTED:
851     GNUNET_break_op (0);
852     return GNUNET_OK;
853   case LS_CALLER_SHUTDOWN:
854     GNUNET_break_op (0);
855     mq_done_finish_caller_shutdown (line);
856     return GNUNET_SYSERR;
857   }
858   pick = (struct ClientPhonePickupMessage *) buf;
859   pick->header.size = sizeof (buf);
860   pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
861   memcpy (&pick[1], metadata, len);
862   GNUNET_SERVER_notification_context_unicast (nc,
863                                               line->client,
864                                               &pick->header,
865                                               GNUNET_NO);
866   line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
867                                                        line,
868                                                        &line->target,
869                                                        GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
870                                                        GNUNET_YES,
871                                                        GNUNET_NO);
872   return GNUNET_OK;
873 }
874
875
876 /**
877  * Function to handle a busy message incoming over mesh
878  *
879  * @param cls closure, NULL
880  * @param tunnel the tunnel over which the message arrived
881  * @param tunnel_ctx the tunnel context, can be NULL
882  * @param message the incoming message
883  * @return #GNUNET_OK
884  */
885 static int
886 handle_mesh_busy_message (void *cls,
887                           struct GNUNET_MESH_Tunnel *tunnel,
888                           void **tunnel_ctx,
889                           const struct GNUNET_MessageHeader *message)
890 {
891   struct Line *line = *tunnel_ctx;
892   struct ClientPhoneBusyMessage busy;
893
894   if (NULL == line)
895   {
896     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897                 "HANGUP message received for non-existing line, dropping tunnel.\n");
898     return GNUNET_SYSERR;
899   }
900   busy.header.size = sizeof (busy);
901   busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY);
902   GNUNET_SERVER_notification_context_unicast (nc,
903                                               line->client,
904                                               &busy.header,
905                                               GNUNET_NO);
906   GNUNET_MESH_receive_done (tunnel);
907   *tunnel_ctx = NULL;
908   switch (line->status)
909   {
910   case LS_CALLEE_LISTEN:
911     GNUNET_break (0);
912     return GNUNET_SYSERR;
913   case LS_CALLEE_RINGING:
914     GNUNET_break_op (0);
915     break;
916   case LS_CALLEE_CONNECTED:
917     GNUNET_break_op (0);
918     break;
919   case LS_CALLEE_SHUTDOWN:
920     GNUNET_break_op (0);
921     break;
922   case LS_CALLER_CALLING:
923     line->status = LS_CALLER_SHUTDOWN;
924     mq_done_finish_caller_shutdown (line);
925     break;
926   case LS_CALLER_CONNECTED:
927     line->status = LS_CALLER_SHUTDOWN;
928     mq_done_finish_caller_shutdown (line);
929     break;
930   case LS_CALLER_SHUTDOWN:
931     mq_done_finish_caller_shutdown (line);
932     break;
933   }
934   return GNUNET_OK;
935 }
936
937
938 /**
939  * Function to handle an audio message incoming over mesh
940  *
941  * @param cls closure, NULL
942  * @param tunnel the tunnel over which the message arrived
943  * @param tunnel_ctx the tunnel context, can be NULL
944  * @param message the incoming message
945  * @return #GNUNET_OK
946  */
947 static int
948 handle_mesh_audio_message (void *cls,
949                            struct GNUNET_MESH_Tunnel *tunnel,
950                            void **tunnel_ctx,
951                            const struct GNUNET_MessageHeader *message)
952 {
953   const struct MeshAudioMessage *msg;
954   struct Line *line = *tunnel_ctx;
955   struct GNUNET_PeerIdentity sender;
956   size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
957   char buf[msize + sizeof (struct ClientAudioMessage)];
958   struct ClientAudioMessage *cam;
959   
960   msg = (const struct MeshAudioMessage *) message;
961   if (NULL == line)
962   {
963     sender = *GNUNET_MESH_tunnel_get_info (tunnel,
964                                            GNUNET_MESH_OPTION_PEER)->peer;
965     for (line = lines_head; NULL != line; line = line->next)
966       if ( (line->local_line == ntohl (msg->remote_line)) &&
967            (LS_CALLEE_CONNECTED == line->status) &&
968            (0 == memcmp (&line->target,
969                          &sender,
970                          sizeof (struct GNUNET_PeerIdentity))) &&
971            (NULL == line->tunnel_unreliable) )
972         break;
973     if (NULL == line)
974     {
975       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976                   "Received AUDIO data for non-existing line %u, dropping.\n",
977                   ntohl (msg->remote_line));
978       return GNUNET_SYSERR;
979     }
980     line->tunnel_unreliable = tunnel;
981     *tunnel_ctx = line;
982   }
983   cam = (struct ClientAudioMessage *) buf;
984   cam->header.size = htons (sizeof (buf));
985   cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
986   memcpy (&cam[1], &msg[1], msize);
987   GNUNET_SERVER_notification_context_unicast (nc,
988                                               line->client,
989                                               &cam->header,
990                                               GNUNET_YES);
991   GNUNET_MESH_receive_done (tunnel);
992   return GNUNET_OK;
993 }
994
995
996 /**
997  * Method called whenever another peer has added us to a tunnel
998  * the other peer initiated.
999  *
1000  * @param cls closure
1001  * @param tunnel new handle to the tunnel
1002  * @param initiator peer that started the tunnel
1003  * @param port port
1004  * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
1005  */
1006 static void *
1007 inbound_tunnel (void *cls,
1008                 struct GNUNET_MESH_Tunnel *tunnel,
1009                 const struct GNUNET_PeerIdentity *initiator, 
1010                 uint32_t port)
1011 {
1012   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1013               _("Received incoming tunnel on port %d\n"), 
1014               port);
1015   return NULL;
1016 }
1017
1018
1019 /**
1020  * Function called whenever an inbound tunnel is destroyed.  Should clean up
1021  * any associated state.
1022  *
1023  * @param cls closure (set from #GNUNET_MESH_connect)
1024  * @param tunnel connection to the other end (henceforth invalid)
1025  * @param tunnel_ctx place where local state associated
1026  *                   with the tunnel is stored
1027  */
1028 static void
1029 inbound_end (void *cls,
1030              const struct GNUNET_MESH_Tunnel *tunnel,
1031              void *tunnel_ctx)
1032 {
1033   struct Line *line = tunnel_ctx;
1034   struct ClientPhoneHangupMessage hup;
1035
1036   if (NULL == line)
1037     return;
1038   if (line->tunnel_unreliable == tunnel)
1039   {
1040     line->tunnel_unreliable = NULL;
1041     return;
1042   }
1043   hup.header.size = sizeof (hup);
1044   hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1045   switch (line->status)
1046   {
1047   case LS_CALLEE_LISTEN:
1048     GNUNET_break (0);
1049     return;
1050   case LS_CALLEE_RINGING:
1051   case LS_CALLEE_CONNECTED:
1052     GNUNET_SERVER_notification_context_unicast (nc,
1053                                                 line->client,
1054                                                 &hup.header,
1055                                                 GNUNET_NO);
1056     line->status = LS_CALLEE_LISTEN;
1057     break;
1058   case LS_CALLEE_SHUTDOWN:
1059     line->status = LS_CALLEE_LISTEN;
1060     destroy_line_mesh_tunnels (line);
1061     break;
1062   case LS_CALLER_CALLING:
1063   case LS_CALLER_CONNECTED:
1064     GNUNET_SERVER_notification_context_unicast (nc,
1065                                                 line->client,
1066                                                 &hup.header,
1067                                                 GNUNET_NO);
1068     destroy_line_mesh_tunnels (line);
1069     GNUNET_CONTAINER_DLL_remove (lines_head,
1070                                  lines_tail,
1071                                  line);
1072     GNUNET_free_non_null (line->audio_data);
1073     GNUNET_free (line);
1074     break;
1075   case LS_CALLER_SHUTDOWN:
1076     destroy_line_mesh_tunnels (line);
1077     GNUNET_CONTAINER_DLL_remove (lines_head,
1078                                  lines_tail,
1079                                  line);
1080     GNUNET_free (line);
1081     break;
1082   }
1083 }
1084
1085
1086 /**
1087  * A client disconnected.  Remove all of its data structure entries.
1088  *
1089  * @param cls closure, NULL
1090  * @param client identification of the client
1091  */
1092 static void
1093 handle_client_disconnect (void *cls, 
1094                           struct GNUNET_SERVER_Client *client)
1095 {
1096   struct Line *line;
1097
1098   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1099   if (NULL == line)
1100     return;
1101   GNUNET_CONTAINER_DLL_remove (lines_head,
1102                                lines_tail,
1103                                line);
1104   GNUNET_free (line);
1105   GNUNET_SERVER_client_set_user_context (client, NULL);
1106 }
1107
1108
1109 /**
1110  * Shutdown nicely
1111  * 
1112  * @param cls closure, NULL
1113  * @param tc the task context
1114  */
1115 static void
1116 do_shutdown (void *cls,
1117              const struct GNUNET_SCHEDULER_TaskContext *tc)
1118 {
1119   if (NULL != mesh)
1120   {
1121     GNUNET_MESH_disconnect (mesh);
1122     mesh = NULL;
1123   }
1124   if (NULL != nc)
1125   {
1126     GNUNET_SERVER_notification_context_destroy (nc);
1127     nc = NULL;
1128   }
1129 }
1130
1131
1132 /**
1133  * Main function that will be run by the scheduler.
1134  *
1135  * @param cls closure
1136  * @param server server handle
1137  * @param c configuration
1138  */
1139 static void
1140 run (void *cls, 
1141      struct GNUNET_SERVER_Handle *server,
1142      const struct GNUNET_CONFIGURATION_Handle *c)
1143 {
1144   static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1145     {&handle_client_register_message, NULL,
1146      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1147      sizeof (struct ClientPhoneRegisterMessage)},
1148     {&handle_client_pickup_message, NULL,
1149      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1150      0},
1151     {&handle_client_hangup_message, NULL,
1152      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1153      0},
1154     {&handle_client_call_message, NULL,
1155      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1156      0},
1157     {&handle_client_audio_message, NULL,
1158      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1159      0},
1160     {NULL, NULL, 0, 0}
1161   };
1162   static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1163     {&handle_mesh_ring_message,
1164      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1165      sizeof (struct MeshPhoneRingMessage)},
1166     {&handle_mesh_hangup_message, 
1167      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1168      0},
1169     {&handle_mesh_pickup_message, 
1170      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1171      0},
1172     {&handle_mesh_busy_message, 
1173      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY,
1174      sizeof (struct MeshPhoneBusyMessage)},
1175     {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1176      0},
1177     {NULL, 0, 0}
1178   };
1179   static uint32_t ports[] = { 
1180     GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1181     GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1182     0 
1183   };
1184
1185   cfg = c;
1186   GNUNET_assert (GNUNET_OK ==
1187                  GNUNET_CRYPTO_get_host_identity (cfg,
1188                                                   &my_identity));
1189   mesh = GNUNET_MESH_connect (cfg,
1190                               NULL,
1191                               &inbound_tunnel,
1192                               &inbound_end, 
1193                               mesh_handlers, 
1194                               ports);
1195
1196   if (NULL == mesh)
1197   {
1198     GNUNET_break (0);
1199     GNUNET_SCHEDULER_shutdown ();
1200     return;
1201   }
1202   nc = GNUNET_SERVER_notification_context_create (server, 16);
1203   GNUNET_SERVER_add_handlers (server, server_handlers);
1204   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1205   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 
1206                                 &do_shutdown,
1207                                 NULL);
1208 }
1209
1210
1211 /**
1212  * The main function for the conversation service.
1213  *
1214  * @param argc number of arguments from the command line
1215  * @param argv command line arguments
1216  * @return 0 ok, 1 on error
1217  */
1218 int
1219 main (int argc, 
1220       char *const *argv)
1221 {
1222   return (GNUNET_OK ==
1223           GNUNET_SERVICE_run (argc, argv,
1224                               "conversation", 
1225                               GNUNET_SERVICE_OPTION_NONE,
1226                               &run, NULL)) ? 0 : 1;
1227 }
1228
1229 /* end of gnunet-service-conversation.c */