-fix FTBFS
[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 channel (or, if it is an
90  * open line, is waiting for a mesh channel).
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 channel (contol data)
106    */
107   struct GNUNET_MESH_Channel *channel_reliable;
108
109   /**
110    * Handle for unreliable channel (audio data)
111    */
112   struct GNUNET_MESH_Channel *channel_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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298               "Sending PICK_UP message to mesh with meta data `%s'\n",
299               meta);
300   e = GNUNET_MQ_msg_extra (mppm,
301                            len,
302                            GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
303   memcpy (&mppm[1], meta, len);
304   GNUNET_MQ_send (line->reliable_mq, e);
305   GNUNET_SERVER_receive_done (client, GNUNET_OK);
306 }
307
308
309 /**
310  * Destroy the mesh channels of a line.
311  *
312  * @param line line to shutdown channels of
313  */
314 static void
315 destroy_line_mesh_channels (struct Line *line)
316 {
317   struct GNUNET_MESH_Channel *t;
318
319   if (NULL != line->reliable_mq)
320   {
321     GNUNET_MQ_destroy (line->reliable_mq);
322     line->reliable_mq = NULL;
323   }
324   if (NULL != line->unreliable_mth)
325   {
326     GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
327     line->unreliable_mth = NULL;
328   }
329   if (NULL != (t = line->channel_unreliable))
330   {
331     line->channel_unreliable = NULL;
332     GNUNET_MESH_channel_destroy (t);
333   }
334   if (NULL != (t = line->channel_reliable))
335   {
336     line->channel_reliable = NULL;
337     GNUNET_MESH_channel_destroy (t);
338   }
339 }
340
341
342 /**
343  * We are done signalling shutdown to the other peer.  Close down
344  * (or reset) the line.
345  *
346  * @param cls the `struct Line` to reset/terminate
347  */
348 static void
349 mq_done_finish_caller_shutdown (void *cls)
350 {
351   struct Line *line = cls;
352
353   switch (line->status)
354   {
355   case LS_CALLEE_LISTEN:
356     GNUNET_break (0);
357     break;
358   case LS_CALLEE_RINGING:
359     GNUNET_break (0);
360     break;
361   case LS_CALLEE_CONNECTED:
362     GNUNET_break (0);
363     break;
364   case LS_CALLEE_SHUTDOWN:
365     line->status = LS_CALLEE_LISTEN;
366     destroy_line_mesh_channels (line);
367     return;
368   case LS_CALLER_CALLING:
369     line->status = LS_CALLER_SHUTDOWN;
370     break;
371   case LS_CALLER_CONNECTED:
372     line->status = LS_CALLER_SHUTDOWN;
373     break;
374   case LS_CALLER_SHUTDOWN:
375     destroy_line_mesh_channels (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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444               "Sending HANG_UP message via mesh with meta data `%s'\n",
445               meta);
446   e = GNUNET_MQ_msg_extra (mhum,
447                            len,
448                            GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
449   memcpy (&mhum[1], meta, len);
450   GNUNET_MQ_notify_sent (e,
451                          &mq_done_finish_caller_shutdown,
452                          line);
453   GNUNET_MQ_send (line->reliable_mq, e);
454   GNUNET_SERVER_receive_done (client, GNUNET_OK);
455 }
456
457
458 /**
459  * Function to handle call request the client
460  *
461  * @param cls closure, NULL
462  * @param client the client from which the message is
463  * @param message the message from the client
464  */
465 static void
466 handle_client_call_message (void *cls,
467                             struct GNUNET_SERVER_Client *client,
468                             const struct GNUNET_MessageHeader *message)
469 {
470   const struct ClientCallMessage *msg;
471   struct Line *line;
472   struct GNUNET_MQ_Envelope *e;
473   struct MeshPhoneRingMessage *ring;
474
475   msg = (struct ClientCallMessage *) message;
476   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
477   if (NULL != line)
478   {
479     GNUNET_break (0);
480     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
481     return;
482   }
483   line = GNUNET_new (struct Line);
484   line->client = client;
485   GNUNET_SERVER_client_set_user_context (client, line);
486   GNUNET_SERVER_notification_context_add (nc, client);
487   line->target = msg->target;
488   GNUNET_CONTAINER_DLL_insert (lines_head,
489                                lines_tail,
490                                line);
491   line->remote_line = ntohl (msg->line);
492   line->status = LS_CALLER_CALLING;
493   line->channel_reliable = GNUNET_MESH_channel_create (mesh,
494                                                      line,
495                                                      &msg->target,
496                                                      GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
497                                                      GNUNET_NO,
498                                                      GNUNET_YES);
499   line->reliable_mq = GNUNET_MESH_mq_create (line->channel_reliable);
500   line->local_line = local_line_cnt++;
501   e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
502   ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
503   ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
504                               sizeof (struct GNUNET_TIME_AbsoluteNBO) +
505                               sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
506                               sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
507   GNUNET_CRYPTO_ecdsa_key_get_public (&msg->caller_id,
508                                                   &ring->caller_id);
509   ring->remote_line = msg->line;
510   ring->source_line = line->local_line;
511   ring->target = msg->target;
512   ring->source = my_identity;
513   ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
514   GNUNET_CRYPTO_ecdsa_sign (&msg->caller_id,
515                           &ring->purpose,
516                           &ring->signature);
517   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518               "Sending RING message via mesh\n");
519   GNUNET_MQ_send (line->reliable_mq, e);
520   GNUNET_SERVER_receive_done (client, GNUNET_OK);
521 }
522
523
524 /**
525  * Transmit audio data via unreliable mesh channel.
526  *
527  * @param cls the `struct Line` we are transmitting for
528  * @param size number of bytes available in @a buf
529  * @param buf where to copy the data
530  * @return number of bytes copied to @a buf
531  */
532 static size_t
533 transmit_line_audio (void *cls,
534                      size_t size,
535                      void *buf)
536 {
537   struct Line *line = cls;
538   struct MeshAudioMessage *mam = buf;
539
540   line->unreliable_mth = NULL;
541   if ( (NULL == buf) ||
542        (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
543     {
544     /* eh, other error handling? */
545     return 0;
546   }
547   mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size);
548   mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
549   mam->remote_line = htonl (line->remote_line);
550   memcpy (&mam[1], line->audio_data, line->audio_size);
551   GNUNET_free (line->audio_data);
552   line->audio_data = NULL;
553   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554               "Sending %u bytes of audio data via mesh\n",
555               line->audio_size);
556   return sizeof (struct MeshAudioMessage) + line->audio_size;
557 }
558
559
560 /**
561  * Function to handle audio data from the client
562  *
563  * @param cls closure, NULL
564  * @param client the client from which the message is
565  * @param message the message from the client
566  */
567 static void
568 handle_client_audio_message (void *cls,
569                              struct GNUNET_SERVER_Client *client,
570                              const struct GNUNET_MessageHeader *message)
571 {
572   const struct ClientAudioMessage *msg;
573   struct Line *line;
574   size_t size;
575
576   size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
577   msg = (struct ClientAudioMessage *) message;
578   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
579   if (NULL == line)
580   {
581     GNUNET_break (0);
582     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
583     return;
584   }
585   switch (line->status)
586   {
587   case LS_CALLEE_LISTEN:
588     /* could be OK if the line just was closed by the other side */
589     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590                 "Audio data dropped, channel is down\n");
591     GNUNET_SERVER_receive_done (client, GNUNET_OK);
592     break;
593   case LS_CALLEE_RINGING:
594   case LS_CALLER_CALLING:
595     GNUNET_break (0);
596     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
597     return;
598   case LS_CALLEE_CONNECTED:
599   case LS_CALLER_CONNECTED:
600     /* common case, handled below */
601     break;
602   case LS_CALLEE_SHUTDOWN:
603   case LS_CALLER_SHUTDOWN:
604     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
605                 "Mesh audio channel in shutdown; audio data dropped\n");
606     GNUNET_SERVER_receive_done (client, GNUNET_OK);
607     return;
608   }
609   if (NULL == line->channel_unreliable)
610   {
611     GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
612                 _("Mesh audio channel not ready; audio data dropped\n"));
613     GNUNET_SERVER_receive_done (client, GNUNET_OK);
614     return;
615   }
616   if (NULL != line->unreliable_mth)
617   {
618     /* NOTE: we may want to not do this and instead combine the data */
619     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620                 "Bandwidth insufficient; dropping previous audio data segment with %u bytes\n",
621                 (unsigned int) line->audio_size);
622     GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
623     line->unreliable_mth = NULL;
624     GNUNET_free (line->audio_data);
625     line->audio_data = NULL;
626   }
627   line->audio_size = size;
628   line->audio_data = GNUNET_malloc (line->audio_size);
629   memcpy (line->audio_data,
630           &msg[1],
631           size);
632   line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->channel_unreliable,
633                                                             GNUNET_NO,
634                                                             GNUNET_TIME_UNIT_FOREVER_REL,
635                                                             sizeof (struct MeshAudioMessage)
636                                                             + line->audio_size,
637                                                             &transmit_line_audio,
638                                                             line);
639   GNUNET_SERVER_receive_done (client, GNUNET_OK);
640 }
641
642
643 /**
644  * We are done signalling shutdown to the other peer.
645  * Destroy the channel.
646  *
647  * @param cls the `struct GNUNET_MESH_channel` to destroy
648  */
649 static void
650 mq_done_destroy_channel (void *cls)
651 {
652   struct GNUNET_MESH_Channel *channel = cls;
653
654   GNUNET_MESH_channel_destroy (channel);
655 }
656
657
658 /**
659  * Function to handle a ring message incoming over mesh
660  *
661  * @param cls closure, NULL
662  * @param channel the channel over which the message arrived
663  * @param channel_ctx the channel context, can be NULL
664  * @param message the incoming message
665  * @return #GNUNET_OK
666  */
667 static int
668 handle_mesh_ring_message (void *cls,
669                           struct GNUNET_MESH_Channel *channel,
670                           void **channel_ctx,
671                           const struct GNUNET_MessageHeader *message)
672 {
673   const struct MeshPhoneRingMessage *msg;
674   struct Line *line;
675   struct GNUNET_MQ_Envelope *e;
676   struct MeshPhoneHangupMessage *hang_up;
677   struct ClientPhoneRingMessage cring;
678
679   msg = (const struct MeshPhoneRingMessage *) message;
680   if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
681                                     sizeof (struct GNUNET_TIME_AbsoluteNBO) +
682                                     sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
683                                     sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) ||
684        (GNUNET_OK !=
685         GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
686                                   &msg->purpose,
687                                   &msg->signature,
688                                   &msg->caller_id)) )
689   {
690     GNUNET_break_op (0);
691     return GNUNET_SYSERR;
692   }
693   for (line = lines_head; NULL != line; line = line->next)
694     if ( (line->local_line == ntohl (msg->remote_line)) &&
695          (LS_CALLEE_LISTEN == line->status) )
696       break;
697   if (NULL == line)
698   {
699     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
700                 _("No available phone for incoming call on line %u, sending HANG_UP signal\n"),
701                 ntohl (msg->remote_line));
702     e = GNUNET_MQ_msg (hang_up, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
703     GNUNET_MQ_notify_sent (e,
704                            &mq_done_destroy_channel,
705                            channel);
706     GNUNET_MQ_send (line->reliable_mq, e);
707     GNUNET_MESH_receive_done (channel); /* needed? */
708     return GNUNET_OK;
709   }
710   line->status = LS_CALLEE_RINGING;
711   line->remote_line = ntohl (msg->source_line);
712   line->channel_reliable = channel;
713   line->reliable_mq = GNUNET_MESH_mq_create (line->channel_reliable);
714   *channel_ctx = line;
715   cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
716   cring.header.size = htons (sizeof (cring));
717   cring.cid = htonl (0 /* FIXME */);
718   cring.caller_id = msg->caller_id;
719   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
720               "Sending RING message to client\n");
721   GNUNET_SERVER_notification_context_unicast (nc,
722                                               line->client,
723                                               &cring.header,
724                                               GNUNET_NO);
725   GNUNET_MESH_receive_done (channel);
726   return GNUNET_OK;
727 }
728
729
730 /**
731  * Function to handle a hangup message incoming over mesh
732  *
733  * @param cls closure, NULL
734  * @param channel the channel over which the message arrived
735  * @param channel_ctx the channel context, can be NULL
736  * @param message the incoming message
737  * @return #GNUNET_OK
738  */
739 static int
740 handle_mesh_hangup_message (void *cls,
741                             struct GNUNET_MESH_Channel *channel,
742                             void **channel_ctx,
743                             const struct GNUNET_MessageHeader *message)
744 {
745   struct Line *line = *channel_ctx;
746   const struct MeshPhoneHangupMessage *msg;
747   const char *reason;
748   size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage);
749   char buf[len + sizeof (struct ClientPhoneHangupMessage)];
750   struct ClientPhoneHangupMessage *hup;
751
752   msg = (const struct MeshPhoneHangupMessage *) message;
753   len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage);
754   reason = (const char *) &msg[1];
755   if ( (0 == len) ||
756        ('\0' != reason[len - 1]) )
757   {
758     reason = NULL;
759     len = 0;
760   }
761   if (NULL == line)
762   {
763     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764                 "HANGUP message received for non-existing line, dropping channel.\n");
765     return GNUNET_SYSERR;
766   }
767   *channel_ctx = NULL;
768   switch (line->status)
769   {
770   case LS_CALLEE_LISTEN:
771     GNUNET_break (0);
772     return GNUNET_SYSERR;
773   case LS_CALLEE_RINGING:
774     line->status = LS_CALLEE_LISTEN;
775     destroy_line_mesh_channels (line);
776     break;
777   case LS_CALLEE_CONNECTED:
778     line->status = LS_CALLEE_LISTEN;
779     destroy_line_mesh_channels (line);
780     break;
781   case LS_CALLEE_SHUTDOWN:
782     line->status = LS_CALLEE_LISTEN;
783     destroy_line_mesh_channels (line);
784     return GNUNET_OK;
785   case LS_CALLER_CALLING:
786     line->status = LS_CALLER_SHUTDOWN;
787     mq_done_finish_caller_shutdown (line);
788     break;
789   case LS_CALLER_CONNECTED:
790     line->status = LS_CALLER_SHUTDOWN;
791     mq_done_finish_caller_shutdown (line);
792     break;
793   case LS_CALLER_SHUTDOWN:
794     mq_done_finish_caller_shutdown (line);
795     return GNUNET_OK;
796   }
797   hup = (struct ClientPhoneHangupMessage *) buf;
798   hup->header.size = sizeof (buf);
799   hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
800   memcpy (&hup[1], reason, len);
801   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802               "Sending HANG UP message to client with reason `%s'\n",
803               reason);
804   GNUNET_SERVER_notification_context_unicast (nc,
805                                               line->client,
806                                               &hup->header,
807                                               GNUNET_NO);
808   GNUNET_MESH_receive_done (channel);
809   return GNUNET_OK;
810 }
811
812
813 /**
814  * Function to handle a pickup message incoming over mesh
815  *
816  * @param cls closure, NULL
817  * @param channel the channel over which the message arrived
818  * @param channel_ctx the channel context, can be NULL
819  * @param message the incoming message
820  * @return #GNUNET_OK
821  */
822 static int
823 handle_mesh_pickup_message (void *cls,
824                             struct GNUNET_MESH_Channel *channel,
825                             void **channel_ctx,
826                             const struct GNUNET_MessageHeader *message)
827 {
828   const struct MeshPhonePickupMessage *msg;
829   struct Line *line = *channel_ctx;
830   const char *metadata;
831   size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage);
832   char buf[len + sizeof (struct ClientPhonePickupMessage)];
833   struct ClientPhonePickupMessage *pick;
834
835   msg = (const struct MeshPhonePickupMessage *) message;
836   len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage);
837   metadata = (const char *) &msg[1];
838   if ( (0 == len) ||
839        ('\0' != metadata[len - 1]) )
840   {
841     metadata = NULL;
842     len = 0;
843   }
844   if (NULL == line)
845   {
846     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
847                 "PICKUP message received for non-existing line, dropping channel.\n");
848     return GNUNET_SYSERR;
849   }
850   GNUNET_MESH_receive_done (channel);
851   switch (line->status)
852   {
853   case LS_CALLEE_LISTEN:
854     GNUNET_break (0);
855     return GNUNET_SYSERR;
856   case LS_CALLEE_RINGING:
857   case LS_CALLEE_CONNECTED:
858     GNUNET_break_op (0);
859     destroy_line_mesh_channels (line);
860     line->status = LS_CALLEE_LISTEN;
861     return GNUNET_SYSERR;
862   case LS_CALLEE_SHUTDOWN:
863     GNUNET_break_op (0);
864     line->status = LS_CALLEE_LISTEN;
865     destroy_line_mesh_channels (line);
866     break;
867   case LS_CALLER_CALLING:
868     line->status = LS_CALLER_CONNECTED;
869     break;
870   case LS_CALLER_CONNECTED:
871     GNUNET_break_op (0);
872     return GNUNET_OK;
873   case LS_CALLER_SHUTDOWN:
874     GNUNET_break_op (0);
875     mq_done_finish_caller_shutdown (line);
876     return GNUNET_SYSERR;
877   }
878   pick = (struct ClientPhonePickupMessage *) buf;
879   pick->header.size = sizeof (buf);
880   pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
881   memcpy (&pick[1], metadata, len);
882   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
883               "Sending PICKED UP message to client with metadata `%s'\n",
884               metadata);
885   GNUNET_SERVER_notification_context_unicast (nc,
886                                               line->client,
887                                               &pick->header,
888                                               GNUNET_NO);
889   line->channel_unreliable = GNUNET_MESH_channel_create (mesh,
890                                                        line,
891                                                        &line->target,
892                                                        GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
893                                                        GNUNET_YES,
894                                                        GNUNET_NO);
895   if (NULL == line->channel_unreliable)
896   {
897     GNUNET_break (0);
898   }
899   return GNUNET_OK;
900 }
901
902
903 /**
904  * Function to handle a suspend message incoming over mesh
905  *
906  * @param cls closure, NULL
907  * @param channel the channel over which the message arrived
908  * @param channel_ctx the channel context, can be NULL
909  * @param message the incoming message
910  * @return #GNUNET_OK
911  */
912 static int
913 handle_mesh_suspend_message (void *cls,
914                              struct GNUNET_MESH_Channel *channel,
915                              void **channel_ctx,
916                              const struct GNUNET_MessageHeader *message)
917 {
918   struct Line *line = *channel_ctx;
919   struct ClientPhoneSuspendMessage suspend;
920
921   if (NULL == line)
922   {
923     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924                 "SUSPEND message received for non-existing line, dropping channel.\n");
925     return GNUNET_SYSERR;
926   }
927   suspend.header.size = sizeof (suspend);
928   suspend.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
929   suspend.cid = htonl (0 /* FIXME */);
930   GNUNET_MESH_receive_done (channel);
931   *channel_ctx = NULL;
932   switch (line->status)
933   {
934   case LS_CALLEE_LISTEN:
935     GNUNET_break (0);
936     return GNUNET_SYSERR;
937   case LS_CALLEE_RINGING:
938     GNUNET_break_op (0);
939     break;
940   case LS_CALLEE_CONNECTED:
941     GNUNET_break_op (0);
942     break;
943   case LS_CALLEE_SHUTDOWN:
944     GNUNET_break_op (0);
945     break;
946   case LS_CALLER_CALLING:
947     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948                 "Sending SUSPEND message to client\n");
949     GNUNET_SERVER_notification_context_unicast (nc,
950                                                 line->client,
951                                                 &suspend.header,
952                                                 GNUNET_NO);
953     line->status = LS_CALLER_SHUTDOWN;
954     mq_done_finish_caller_shutdown (line);
955     break;
956   case LS_CALLER_CONNECTED:
957     GNUNET_break_op (0);
958     line->status = LS_CALLER_SHUTDOWN;
959     mq_done_finish_caller_shutdown (line);
960     break;
961   case LS_CALLER_SHUTDOWN:
962     GNUNET_break_op (0);
963     mq_done_finish_caller_shutdown (line);
964     break;
965   }
966   return GNUNET_OK;
967 }
968
969
970 /**
971  * Function to handle a resume message incoming over mesh
972  *
973  * @param cls closure, NULL
974  * @param channel the channel over which the message arrived
975  * @param channel_ctx the channel context, can be NULL
976  * @param message the incoming message
977  * @return #GNUNET_OK
978  */
979 static int
980 handle_mesh_resume_message (void *cls,
981                              struct GNUNET_MESH_Channel *channel,
982                              void **channel_ctx,
983                              const struct GNUNET_MessageHeader *message)
984 {
985   struct Line *line = *channel_ctx;
986   struct ClientPhoneResumeMessage resume;
987
988   if (NULL == line)
989   {
990     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
991                 "RESUME message received for non-existing line, dropping channel.\n");
992     return GNUNET_SYSERR;
993   }
994   resume.header.size = sizeof (resume);
995   resume.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
996   resume.cid = htonl (0 /* FIXME */);
997   GNUNET_MESH_receive_done (channel);
998   *channel_ctx = NULL;
999   switch (line->status)
1000   {
1001   case LS_CALLEE_LISTEN:
1002     GNUNET_break (0);
1003     return GNUNET_SYSERR;
1004   case LS_CALLEE_RINGING:
1005     GNUNET_break_op (0);
1006     break;
1007   case LS_CALLEE_CONNECTED:
1008     GNUNET_break_op (0);
1009     break;
1010   case LS_CALLEE_SHUTDOWN:
1011     GNUNET_break_op (0);
1012     break;
1013   case LS_CALLER_CALLING:
1014     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1015                 "Sending SUSPEND message to client\n");
1016     GNUNET_SERVER_notification_context_unicast (nc,
1017                                                 line->client,
1018                                                 &resume.header,
1019                                                 GNUNET_NO);
1020     line->status = LS_CALLER_SHUTDOWN;
1021     mq_done_finish_caller_shutdown (line);
1022     break;
1023   case LS_CALLER_CONNECTED:
1024     GNUNET_break_op (0);
1025     line->status = LS_CALLER_SHUTDOWN;
1026     mq_done_finish_caller_shutdown (line);
1027     break;
1028   case LS_CALLER_SHUTDOWN:
1029     GNUNET_break_op (0);
1030     mq_done_finish_caller_shutdown (line);
1031     break;
1032   }
1033   return GNUNET_OK;
1034 }
1035
1036
1037 /**
1038  * Function to handle an audio message incoming over mesh
1039  *
1040  * @param cls closure, NULL
1041  * @param channel the channel over which the message arrived
1042  * @param channel_ctx the channel context, can be NULL
1043  * @param message the incoming message
1044  * @return #GNUNET_OK
1045  */
1046 static int
1047 handle_mesh_audio_message (void *cls,
1048                            struct GNUNET_MESH_Channel *channel,
1049                            void **channel_ctx,
1050                            const struct GNUNET_MessageHeader *message)
1051 {
1052   const struct MeshAudioMessage *msg;
1053   struct Line *line = *channel_ctx;
1054   struct GNUNET_PeerIdentity sender;
1055   size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
1056   char buf[msize + sizeof (struct ClientAudioMessage)];
1057   struct ClientAudioMessage *cam;
1058   const union GNUNET_MESH_ChannelInfo *info;
1059
1060   msg = (const struct MeshAudioMessage *) message;
1061   if (NULL == line)
1062   {
1063     info = GNUNET_MESH_channel_get_info (channel,
1064                                         GNUNET_MESH_OPTION_PEER);
1065     if (NULL == info)
1066     {
1067       GNUNET_break (0);
1068       return GNUNET_OK;
1069     }
1070     sender = *(info->peer);
1071     for (line = lines_head; NULL != line; line = line->next)
1072       if ( (line->local_line == ntohl (msg->remote_line)) &&
1073            (LS_CALLEE_CONNECTED == line->status) &&
1074            (0 == memcmp (&line->target,
1075                          &sender,
1076                          sizeof (struct GNUNET_PeerIdentity))) &&
1077            (NULL == line->channel_unreliable) )
1078         break;
1079     if (NULL == line)
1080     {
1081       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1082                   "Received AUDIO data for non-existing line %u, dropping.\n",
1083                   ntohl (msg->remote_line));
1084       return GNUNET_SYSERR;
1085     }
1086     line->channel_unreliable = channel;
1087     *channel_ctx = line;
1088   }
1089   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090               "Forwarding %u bytes of AUDIO data to client\n",
1091               msize);
1092   cam = (struct ClientAudioMessage *) buf;
1093   cam->header.size = htons (sizeof (buf));
1094   cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
1095   memcpy (&cam[1], &msg[1], msize);
1096   GNUNET_SERVER_notification_context_unicast (nc,
1097                                               line->client,
1098                                               &cam->header,
1099                                               GNUNET_YES);
1100   GNUNET_MESH_receive_done (channel);
1101   return GNUNET_OK;
1102 }
1103
1104
1105 /**
1106  * Method called whenever another peer has added us to a channel
1107  * the other peer initiated.
1108  *
1109  * @param cls closure
1110  * @param channel new handle to the channel
1111  * @param initiator peer that started the channel
1112  * @param port port
1113  * @return initial channel context for the channel (can be NULL -- that's not an error)
1114  */
1115 static void *
1116 inbound_channel (void *cls,
1117                 struct GNUNET_MESH_Channel *channel,
1118                 const struct GNUNET_PeerIdentity *initiator,
1119                 uint32_t port)
1120 {
1121   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1122               _("Received incoming channel on port %u\n"),
1123               (unsigned int) port);
1124   return NULL;
1125 }
1126
1127
1128 /**
1129  * Function called whenever an inbound channel is destroyed.  Should clean up
1130  * any associated state.
1131  *
1132  * @param cls closure (set from #GNUNET_MESH_connect)
1133  * @param channel connection to the other end (henceforth invalid)
1134  * @param channel_ctx place where local state associated
1135  *                   with the channel is stored
1136  */
1137 static void
1138 inbound_end (void *cls,
1139              const struct GNUNET_MESH_Channel *channel,
1140              void *channel_ctx)
1141 {
1142   struct Line *line = channel_ctx;
1143   struct ClientPhoneHangupMessage hup;
1144
1145   if (NULL == line)
1146     return;
1147   if (line->channel_unreliable == channel)
1148   {
1149     if (NULL != line->unreliable_mth)
1150     {
1151       GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
1152       line->unreliable_mth = NULL;
1153     }
1154     line->channel_unreliable = NULL;
1155     return;
1156   }
1157   if (line->channel_reliable != channel)
1158     return;
1159   line->channel_reliable = NULL;
1160
1161   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1162               "Mesh channel destroyed by mesh\n");
1163   hup.header.size = sizeof (hup);
1164   hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1165   switch (line->status)
1166   {
1167   case LS_CALLEE_LISTEN:
1168     GNUNET_break (0);
1169     break;
1170   case LS_CALLEE_RINGING:
1171   case LS_CALLEE_CONNECTED:
1172     GNUNET_SERVER_notification_context_unicast (nc,
1173                                                 line->client,
1174                                                 &hup.header,
1175                                                 GNUNET_NO);
1176     line->status = LS_CALLEE_LISTEN;
1177     break;
1178   case LS_CALLEE_SHUTDOWN:
1179     line->status = LS_CALLEE_LISTEN;
1180     break;
1181   case LS_CALLER_CALLING:
1182   case LS_CALLER_CONNECTED:
1183     GNUNET_SERVER_notification_context_unicast (nc,
1184                                                 line->client,
1185                                                 &hup.header,
1186                                                 GNUNET_NO);
1187     break;
1188   case LS_CALLER_SHUTDOWN:
1189     break;
1190   }
1191   destroy_line_mesh_channels (line);
1192 }
1193
1194
1195 /**
1196  * A client disconnected.  Remove all of its data structure entries.
1197  *
1198  * @param cls closure, NULL
1199  * @param client identification of the client
1200  */
1201 static void
1202 handle_client_disconnect (void *cls,
1203                           struct GNUNET_SERVER_Client *client)
1204 {
1205   struct Line *line;
1206
1207   if (NULL == client)
1208     return;
1209   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1210   if (NULL == line)
1211     return;
1212   GNUNET_SERVER_client_set_user_context (client, (void *)NULL);
1213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1214               "Client disconnected, closing line\n");
1215   GNUNET_CONTAINER_DLL_remove (lines_head,
1216                                lines_tail,
1217                                line);
1218   destroy_line_mesh_channels (line);
1219   GNUNET_free_non_null (line->audio_data);
1220   GNUNET_free (line);
1221 }
1222
1223
1224 /**
1225  * Shutdown nicely
1226  *
1227  * @param cls closure, NULL
1228  * @param tc the task context
1229  */
1230 static void
1231 do_shutdown (void *cls,
1232              const struct GNUNET_SCHEDULER_TaskContext *tc)
1233 {
1234   if (NULL != mesh)
1235   {
1236     GNUNET_MESH_disconnect (mesh);
1237     mesh = NULL;
1238   }
1239   if (NULL != nc)
1240   {
1241     GNUNET_SERVER_notification_context_destroy (nc);
1242     nc = NULL;
1243   }
1244 }
1245
1246
1247 /**
1248  * Main function that will be run by the scheduler.
1249  *
1250  * @param cls closure
1251  * @param server server handle
1252  * @param c configuration
1253  */
1254 static void
1255 run (void *cls,
1256      struct GNUNET_SERVER_Handle *server,
1257      const struct GNUNET_CONFIGURATION_Handle *c)
1258 {
1259   static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1260     {&handle_client_register_message, NULL,
1261      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1262      sizeof (struct ClientPhoneRegisterMessage)},
1263     {&handle_client_pickup_message, NULL,
1264      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1265      0},
1266     {&handle_client_hangup_message, NULL,
1267      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1268      0},
1269     {&handle_client_call_message, NULL,
1270      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1271      0},
1272     {&handle_client_audio_message, NULL,
1273      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1274      0},
1275     {NULL, NULL, 0, 0}
1276   };
1277   static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1278     {&handle_mesh_ring_message,
1279      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1280      sizeof (struct MeshPhoneRingMessage)},
1281     {&handle_mesh_hangup_message,
1282      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1283      0},
1284     {&handle_mesh_pickup_message,
1285      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1286      0},
1287     {&handle_mesh_suspend_message,
1288      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND,
1289      sizeof (struct MeshPhoneSuspendMessage)},
1290     {&handle_mesh_resume_message,
1291      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME,
1292      sizeof (struct MeshPhoneResumeMessage)},
1293     {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1294      0},
1295     {NULL, 0, 0}
1296   };
1297   static uint32_t ports[] = {
1298     GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1299     GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1300     0
1301   };
1302
1303   cfg = c;
1304   GNUNET_assert (GNUNET_OK ==
1305                  GNUNET_CRYPTO_get_peer_identity (cfg,
1306                                                   &my_identity));
1307   mesh = GNUNET_MESH_connect (cfg,
1308                               NULL,
1309                               &inbound_channel,
1310                               &inbound_end,
1311                               mesh_handlers,
1312                               ports);
1313
1314   if (NULL == mesh)
1315   {
1316     GNUNET_break (0);
1317     GNUNET_SCHEDULER_shutdown ();
1318     return;
1319   }
1320   nc = GNUNET_SERVER_notification_context_create (server, 16);
1321   GNUNET_SERVER_add_handlers (server, server_handlers);
1322   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1323   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1324                                 &do_shutdown,
1325                                 NULL);
1326 }
1327
1328
1329 /**
1330  * The main function for the conversation service.
1331  *
1332  * @param argc number of arguments from the command line
1333  * @param argv command line arguments
1334  * @return 0 ok, 1 on error
1335  */
1336 int
1337 main (int argc,
1338       char *const *argv)
1339 {
1340   return (GNUNET_OK ==
1341           GNUNET_SERVICE_run (argc, argv,
1342                               "conversation",
1343                               GNUNET_SERVICE_OPTION_NONE,
1344                               &run, NULL)) ? 0 : 1;
1345 }
1346
1347 /* end of gnunet-service-conversation.c */