2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
21 * @file conversation/gnunet-service-conversation.c
22 * @brief conversation service implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
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"
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.
43 #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
47 * A line connects a local client with a mesh channel (or, if it is an
48 * open line, is waiting for a mesh channel).
53 * The possible connection status
58 * Our phone is ringing, waiting for the client to pick up.
68 * We're in shutdown, sending hangup messages before cleaning up.
73 * We are waiting for the phone to be picked up.
83 * We're in shutdown, sending hangup messages before cleaning up.
91 * A `struct Channel` represents a mesh channel, which is a P2P
92 * connection to another conversation service. Multiple channels can
93 * be attached the the same `struct Line`, which represents a local
94 * client. We keep them in a linked list.
102 struct Channel *next;
107 struct Channel *prev;
110 * Line associated with the channel.
115 * Handle for the reliable channel (contol data)
117 struct GNUNET_MESH_Channel *channel_reliable;
120 * Handle for unreliable channel (audio data)
122 struct GNUNET_MESH_Channel *channel_unreliable;
125 * Transmit handle for pending audio messages
127 struct GNUNET_MESH_TransmitHandle *unreliable_mth;
130 * Message queue for control messages
132 struct GNUNET_MQ_Handle *reliable_mq;
135 * Target of the line, if we are the caller.
137 struct GNUNET_PeerIdentity target;
140 * Temporary buffer for audio data.
145 * Number of bytes in @e audio_data.
150 * Channel identifier.
155 * Remote line number.
157 uint32_t remote_line;
160 * Current status of this line.
162 enum ChannelStatus status;
165 * #GNUNET_YES if the channel was suspended by the other peer.
167 int8_t suspended_remote;
170 * #GNUNET_YES if the channel was suspended by the local client.
172 int8_t suspended_local;
178 * A `struct Line` connects a local client with mesh channels.
195 struct Channel *channel_head;
200 struct Channel *channel_tail;
203 * Handle to the line client.
205 struct GNUNET_SERVER_Client *client;
208 * Generator for channel IDs.
223 static const struct GNUNET_CONFIGURATION_Handle *cfg;
226 * Notification context containing all connected clients.
228 static struct GNUNET_SERVER_NotificationContext *nc;
233 static struct GNUNET_MESH_Handle *mesh;
236 * Identity of this peer.
238 static struct GNUNET_PeerIdentity my_identity;
241 * Head of DLL of active lines.
243 static struct Line *lines_head;
246 * Tail of DLL of active lines.
248 static struct Line *lines_tail;
251 * Counter for generating local line numbers.
252 * FIXME: randomize generation in the future
253 * to eliminate information leakage.
255 static uint32_t local_line_cnt;
259 * Function to register a phone.
261 * @param cls closure, NULL
262 * @param client the client from which the message is
263 * @param message the message from the client
266 handle_client_register_message (void *cls,
267 struct GNUNET_SERVER_Client *client,
268 const struct GNUNET_MessageHeader *message)
270 const struct ClientPhoneRegisterMessage *msg;
273 msg = (const struct ClientPhoneRegisterMessage *) message;
274 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
278 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
281 line = GNUNET_new (struct Line);
282 line->client = client;
283 GNUNET_SERVER_notification_context_add (nc, client);
284 GNUNET_SERVER_client_set_user_context (client, line);
285 GNUNET_CONTAINER_DLL_insert (lines_head,
288 line->local_line = ntohl (msg->line) & (~ (1 << 31));
289 GNUNET_SERVER_receive_done (client, GNUNET_OK);
294 * Function to handle a pickup request message from the client
296 * @param cls closure, NULL
297 * @param client the client from which the message is
298 * @param message the message from the client
301 handle_client_pickup_message (void *cls,
302 struct GNUNET_SERVER_Client *client,
303 const struct GNUNET_MessageHeader *message)
305 const struct ClientPhonePickupMessage *msg;
306 struct GNUNET_MQ_Envelope *e;
307 struct MeshPhonePickupMessage *mppm;
311 msg = (const struct ClientPhonePickupMessage *) message;
312 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
316 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
319 for (ch = line->channel_head; NULL != ch; ch = ch->next)
320 if (msg->cid == ch->cid)
324 /* could have been destroyed asynchronously, ignore message */
325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326 "Channel %u not found\n",
332 case CS_CALLEE_RINGING:
333 ch->status = CS_CALLEE_CONNECTED;
335 case CS_CALLEE_CONNECTED:
337 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
339 case CS_CALLEE_SHUTDOWN:
340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
342 GNUNET_SERVER_receive_done (client, GNUNET_OK);
344 case CS_CALLER_CALLING:
345 case CS_CALLER_CONNECTED:
346 case CS_CALLER_SHUTDOWN:
348 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
351 GNUNET_break (CS_CALLEE_CONNECTED == ch->status);
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "Sending PICK_UP message to mesh\n");
354 e = GNUNET_MQ_msg (mppm,
355 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
356 GNUNET_MQ_send (ch->reliable_mq, e);
357 GNUNET_SERVER_receive_done (client, GNUNET_OK);
364 * @param ch channel to destroy.
367 destroy_line_mesh_channels (struct Channel *ch)
369 struct Line *line = ch->line;
370 struct GNUNET_MESH_Channel *t;
372 if (NULL != ch->reliable_mq)
374 GNUNET_MQ_destroy (ch->reliable_mq);
375 ch->reliable_mq = NULL;
377 if (NULL != ch->unreliable_mth)
379 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
380 ch->unreliable_mth = NULL;
382 if (NULL != (t = ch->channel_unreliable))
384 ch->channel_unreliable = NULL;
385 GNUNET_MESH_channel_destroy (t);
387 if (NULL != (t = ch->channel_reliable))
389 ch->channel_reliable = NULL;
390 GNUNET_MESH_channel_destroy (t);
392 GNUNET_CONTAINER_DLL_remove (line->channel_head,
395 GNUNET_free_non_null (ch->audio_data);
401 * We are done signalling shutdown to the other peer. Close down
404 * @param cls the `struct Channel` to reset/terminate
407 mq_done_finish_caller_shutdown (void *cls)
409 struct Channel *ch = cls;
413 case CS_CALLEE_RINGING:
416 case CS_CALLEE_CONNECTED:
419 case CS_CALLEE_SHUTDOWN:
420 destroy_line_mesh_channels (ch);
422 case CS_CALLER_CALLING:
425 case CS_CALLER_CONNECTED:
428 case CS_CALLER_SHUTDOWN:
429 destroy_line_mesh_channels (ch);
436 * Function to handle a hangup request message from the client
438 * @param cls closure, NULL
439 * @param client the client from which the message is
440 * @param message the message from the client
443 handle_client_hangup_message (void *cls,
444 struct GNUNET_SERVER_Client *client,
445 const struct GNUNET_MessageHeader *message)
447 const struct ClientPhoneHangupMessage *msg;
448 struct GNUNET_MQ_Envelope *e;
449 struct MeshPhoneHangupMessage *mhum;
453 msg = (const struct ClientPhoneHangupMessage *) message;
454 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
458 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
461 for (ch = line->channel_head; NULL != ch; ch = ch->next)
462 if (msg->cid == ch->cid)
466 /* could have been destroyed asynchronously, ignore message */
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468 "Channel %u not found\n",
475 case CS_CALLEE_RINGING:
476 ch->status = CS_CALLEE_SHUTDOWN;
478 case CS_CALLEE_CONNECTED:
479 ch->status = CS_CALLEE_SHUTDOWN;
481 case CS_CALLEE_SHUTDOWN:
482 /* maybe the other peer closed asynchronously... */
484 case CS_CALLER_CALLING:
485 ch->status = CS_CALLER_SHUTDOWN;
487 case CS_CALLER_CONNECTED:
488 ch->status = CS_CALLER_SHUTDOWN;
490 case CS_CALLER_SHUTDOWN:
491 /* maybe the other peer closed asynchronously... */
494 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495 "Sending HANG_UP message via mesh\n");
496 e = GNUNET_MQ_msg (mhum,
497 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
498 GNUNET_MQ_notify_sent (e,
499 &mq_done_finish_caller_shutdown,
501 GNUNET_MQ_send (ch->reliable_mq, e);
502 GNUNET_SERVER_receive_done (client, GNUNET_OK);
507 * Function to handle a suspend request message from the client
509 * @param cls closure, NULL
510 * @param client the client from which the message is
511 * @param message the message from the client
514 handle_client_suspend_message (void *cls,
515 struct GNUNET_SERVER_Client *client,
516 const struct GNUNET_MessageHeader *message)
518 const struct ClientPhoneSuspendMessage *msg;
519 struct GNUNET_MQ_Envelope *e;
520 struct MeshPhoneSuspendMessage *mhum;
524 msg = (const struct ClientPhoneSuspendMessage *) message;
525 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
529 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
532 for (ch = line->channel_head; NULL != ch; ch = ch->next)
533 if (msg->cid == ch->cid)
537 /* could have been destroyed asynchronously, ignore message */
538 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
539 "Channel %u not found\n",
543 if (GNUNET_YES == ch->suspended_local)
546 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
551 case CS_CALLEE_RINGING:
553 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
555 case CS_CALLEE_CONNECTED:
556 ch->suspended_local = GNUNET_YES;
558 case CS_CALLEE_SHUTDOWN:
559 /* maybe the other peer closed asynchronously... */
561 case CS_CALLER_CALLING:
563 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
565 case CS_CALLER_CONNECTED:
566 ch->suspended_local = GNUNET_YES;
568 case CS_CALLER_SHUTDOWN:
569 /* maybe the other peer closed asynchronously... */
572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
573 "Sending SUSPEND message via mesh\n");
574 e = GNUNET_MQ_msg (mhum,
575 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND);
576 GNUNET_MQ_send (ch->reliable_mq, e);
577 GNUNET_SERVER_receive_done (client, GNUNET_OK);
582 * Function to handle a resume request message from the client
584 * @param cls closure, NULL
585 * @param client the client from which the message is
586 * @param message the message from the client
589 handle_client_resume_message (void *cls,
590 struct GNUNET_SERVER_Client *client,
591 const struct GNUNET_MessageHeader *message)
593 const struct ClientPhoneResumeMessage *msg;
594 struct GNUNET_MQ_Envelope *e;
595 struct MeshPhoneResumeMessage *mhum;
599 msg = (const struct ClientPhoneResumeMessage *) message;
600 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
604 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
607 for (ch = line->channel_head; NULL != ch; ch = ch->next)
608 if (msg->cid == ch->cid)
612 /* could have been destroyed asynchronously, ignore message */
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "Channel %u not found\n",
618 if (GNUNET_YES != ch->suspended_local)
621 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
626 case CS_CALLEE_RINGING:
628 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
630 case CS_CALLEE_CONNECTED:
631 ch->suspended_local = GNUNET_NO;
633 case CS_CALLEE_SHUTDOWN:
634 /* maybe the other peer closed asynchronously... */
636 case CS_CALLER_CALLING:
638 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
640 case CS_CALLER_CONNECTED:
641 ch->suspended_local = GNUNET_NO;
643 case CS_CALLER_SHUTDOWN:
644 /* maybe the other peer closed asynchronously... */
647 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
648 "Sending RESUME message via mesh\n");
649 e = GNUNET_MQ_msg (mhum,
650 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME);
651 GNUNET_MQ_send (ch->reliable_mq, e);
652 GNUNET_SERVER_receive_done (client, GNUNET_OK);
657 * Function to handle call request from the client
659 * @param cls closure, NULL
660 * @param client the client from which the message is
661 * @param message the message from the client
664 handle_client_call_message (void *cls,
665 struct GNUNET_SERVER_Client *client,
666 const struct GNUNET_MessageHeader *message)
668 const struct ClientCallMessage *msg;
671 struct GNUNET_MQ_Envelope *e;
672 struct MeshPhoneRingMessage *ring;
674 msg = (const struct ClientCallMessage *) message;
675 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
679 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
682 line = GNUNET_new (struct Line);
683 line->client = client;
684 line->local_line = (local_line_cnt++) | (1 << 31);
685 GNUNET_SERVER_client_set_user_context (client, line);
686 GNUNET_SERVER_notification_context_add (nc, client);
687 GNUNET_CONTAINER_DLL_insert (lines_head,
690 ch = GNUNET_new (struct Channel);
692 GNUNET_CONTAINER_DLL_insert (line->channel_head,
695 ch->target = msg->target;
696 ch->remote_line = ntohl (msg->line);
697 ch->status = CS_CALLER_CALLING;
698 ch->channel_reliable = GNUNET_MESH_channel_create (mesh,
701 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
704 ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
705 e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
706 ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
707 ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
708 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
709 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
710 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
711 GNUNET_CRYPTO_ecdsa_key_get_public (&msg->caller_id,
713 ring->remote_line = msg->line;
714 ring->source_line = line->local_line;
715 ring->target = msg->target;
716 ring->source = my_identity;
717 ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
718 GNUNET_CRYPTO_ecdsa_sign (&msg->caller_id,
721 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
722 "Sending RING message via mesh\n");
723 GNUNET_MQ_send (ch->reliable_mq, e);
724 GNUNET_SERVER_receive_done (client, GNUNET_OK);
729 * Transmit audio data via unreliable mesh channel.
731 * @param cls the `struct Channel` we are transmitting for
732 * @param size number of bytes available in @a buf
733 * @param buf where to copy the data
734 * @return number of bytes copied to @a buf
737 transmit_line_audio (void *cls,
741 struct Channel *ch = cls;
742 struct MeshAudioMessage *mam = buf;
744 ch->unreliable_mth = NULL;
745 if ( (NULL == buf) ||
746 (size < sizeof (struct MeshAudioMessage) + ch->audio_size) )
748 /* eh, other error handling? */
751 mam->header.size = htons (sizeof (struct MeshAudioMessage) + ch->audio_size);
752 mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
753 mam->remote_line = htonl (ch->remote_line);
754 memcpy (&mam[1], ch->audio_data, ch->audio_size);
755 GNUNET_free (ch->audio_data);
756 ch->audio_data = NULL;
757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
758 "Sending %u bytes of audio data via mesh\n",
760 return sizeof (struct MeshAudioMessage) + ch->audio_size;
765 * Function to handle audio data from the client
767 * @param cls closure, NULL
768 * @param client the client from which the message is
769 * @param message the message from the client
772 handle_client_audio_message (void *cls,
773 struct GNUNET_SERVER_Client *client,
774 const struct GNUNET_MessageHeader *message)
776 const struct ClientAudioMessage *msg;
781 size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
782 msg = (const struct ClientAudioMessage *) message;
783 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
787 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
790 for (ch = line->channel_head; NULL != ch; ch = ch->next)
791 if (msg->cid == ch->cid)
795 /* could have been destroyed asynchronously, ignore message */
796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
797 "Channel %u not found\n",
804 case CS_CALLEE_RINGING:
805 case CS_CALLER_CALLING:
807 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
809 case CS_CALLEE_CONNECTED:
810 case CS_CALLER_CONNECTED:
811 /* common case, handled below */
813 case CS_CALLEE_SHUTDOWN:
814 case CS_CALLER_SHUTDOWN:
815 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
816 "Mesh audio channel in shutdown; audio data dropped\n");
817 GNUNET_SERVER_receive_done (client, GNUNET_OK);
820 if (NULL == ch->channel_unreliable)
822 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
823 _("Mesh audio channel not ready; audio data dropped\n"));
824 GNUNET_SERVER_receive_done (client, GNUNET_OK);
827 if (NULL != ch->unreliable_mth)
829 /* NOTE: we may want to not do this and instead combine the data */
830 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831 "Bandwidth insufficient; dropping previous audio data segment with %u bytes\n",
832 (unsigned int) ch->audio_size);
833 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
834 ch->unreliable_mth = NULL;
835 GNUNET_free (ch->audio_data);
836 ch->audio_data = NULL;
838 ch->audio_size = size;
839 ch->audio_data = GNUNET_malloc (ch->audio_size);
840 memcpy (ch->audio_data,
843 ch->unreliable_mth = GNUNET_MESH_notify_transmit_ready (ch->channel_unreliable,
845 GNUNET_TIME_UNIT_FOREVER_REL,
846 sizeof (struct MeshAudioMessage)
848 &transmit_line_audio,
850 GNUNET_SERVER_receive_done (client, GNUNET_OK);
855 * We are done signalling shutdown to the other peer.
856 * Destroy the channel.
858 * @param cls the `struct GNUNET_MESH_channel` to destroy
861 mq_done_destroy_channel (void *cls)
863 struct GNUNET_MESH_Channel *channel = cls;
865 GNUNET_MESH_channel_destroy (channel);
870 * Function to handle a ring message incoming over mesh
872 * @param cls closure, NULL
873 * @param channel the channel over which the message arrived
874 * @param channel_ctx the channel context, can be NULL
875 * or point to the `struct Channel`
876 * @param message the incoming message
880 handle_mesh_ring_message (void *cls,
881 struct GNUNET_MESH_Channel *channel,
883 const struct GNUNET_MessageHeader *message)
885 const struct MeshPhoneRingMessage *msg;
888 struct GNUNET_MQ_Envelope *e;
889 struct MeshPhoneHangupMessage *hang_up;
890 struct ClientPhoneRingMessage cring;
891 struct GNUNET_MQ_Handle *reliable_mq;
893 msg = (const struct MeshPhoneRingMessage *) message;
894 if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
895 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
896 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
897 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) ||
899 GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
905 return GNUNET_SYSERR;
907 for (line = lines_head; NULL != line; line = line->next)
908 if (line->local_line == ntohl (msg->remote_line))
912 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
913 _("No available phone for incoming call on line %u, sending HANG_UP signal\n"),
914 ntohl (msg->remote_line));
915 e = GNUNET_MQ_msg (hang_up,
916 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
917 GNUNET_MQ_notify_sent (e,
918 &mq_done_destroy_channel,
920 reliable_mq = GNUNET_MESH_mq_create (channel);
921 GNUNET_MQ_send (reliable_mq, e);
922 /* FIXME: do we need to clean up reliable_mq somehow/somewhere? */
923 GNUNET_MESH_receive_done (channel); /* needed? */
926 ch = GNUNET_new (struct Channel);
928 GNUNET_CONTAINER_DLL_insert (line->channel_head,
931 ch->status = CS_CALLEE_RINGING;
932 ch->remote_line = ntohl (msg->source_line);
933 ch->channel_reliable = channel;
934 ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
935 ch->cid = line->cid_gen++;
937 cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
938 cring.header.size = htons (sizeof (cring));
940 cring.caller_id = msg->caller_id;
941 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
942 "Sending RING message to client\n");
943 GNUNET_SERVER_notification_context_unicast (nc,
947 GNUNET_MESH_receive_done (channel);
953 * Function to handle a hangup message incoming over mesh
955 * @param cls closure, NULL
956 * @param channel the channel over which the message arrived
957 * @param channel_ctx the channel context, can be NULL
958 * or point to the `struct Channel`
959 * @param message the incoming message
963 handle_mesh_hangup_message (void *cls,
964 struct GNUNET_MESH_Channel *channel,
966 const struct GNUNET_MessageHeader *message)
968 struct Channel *ch = *channel_ctx;
970 struct ClientPhoneHangupMessage hup;
974 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
975 "HANGUP message received for non-existing line, dropping channel.\n");
976 return GNUNET_SYSERR;
980 hup.header.size = sizeof (hup);
981 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
983 destroy_line_mesh_channels (ch);
986 case CS_CALLEE_RINGING:
987 case CS_CALLEE_CONNECTED:
989 case CS_CALLEE_SHUTDOWN:
991 case CS_CALLER_CALLING:
992 case CS_CALLER_CONNECTED:
994 case CS_CALLER_SHUTDOWN:
997 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
998 "Sending HANG UP message to client\n");
999 GNUNET_SERVER_notification_context_unicast (nc,
1003 GNUNET_MESH_receive_done (channel);
1009 * Function to handle a pickup message incoming over mesh
1011 * @param cls closure, NULL
1012 * @param channel the channel over which the message arrived
1013 * @param channel_ctx the channel context, can be NULL
1014 * or point to the `struct Channel`
1015 * @param message the incoming message
1016 * @return #GNUNET_OK
1019 handle_mesh_pickup_message (void *cls,
1020 struct GNUNET_MESH_Channel *channel,
1022 const struct GNUNET_MessageHeader *message)
1024 struct Channel *ch = *channel_ctx;
1026 struct ClientPhonePickupMessage pick;
1030 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1031 "PICKUP message received for non-existing channel, dropping channel.\n");
1032 return GNUNET_SYSERR;
1035 GNUNET_MESH_receive_done (channel);
1038 case CS_CALLEE_RINGING:
1039 case CS_CALLEE_CONNECTED:
1040 GNUNET_break_op (0);
1041 destroy_line_mesh_channels (ch);
1042 return GNUNET_SYSERR;
1043 case CS_CALLEE_SHUTDOWN:
1044 GNUNET_break_op (0);
1045 destroy_line_mesh_channels (ch);
1047 case CS_CALLER_CALLING:
1048 ch->status = CS_CALLER_CONNECTED;
1050 case CS_CALLER_CONNECTED:
1051 GNUNET_break_op (0);
1053 case CS_CALLER_SHUTDOWN:
1054 GNUNET_break_op (0);
1055 mq_done_finish_caller_shutdown (ch);
1056 return GNUNET_SYSERR;
1058 pick.header.size = sizeof (pick);
1059 pick.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
1061 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1062 "Sending PICKED UP message to client\n");
1063 GNUNET_SERVER_notification_context_unicast (nc,
1067 ch->channel_unreliable = GNUNET_MESH_channel_create (mesh,
1070 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1073 if (NULL == ch->channel_unreliable)
1082 * Function to handle a suspend message incoming over mesh
1084 * @param cls closure, NULL
1085 * @param channel the channel over which the message arrived
1086 * @param channel_ctx the channel context, can be NULL
1087 * or point to the `struct Channel`
1088 * @param message the incoming message
1089 * @return #GNUNET_OK
1092 handle_mesh_suspend_message (void *cls,
1093 struct GNUNET_MESH_Channel *channel,
1095 const struct GNUNET_MessageHeader *message)
1097 struct Channel *ch = *channel_ctx;
1099 struct ClientPhoneSuspendMessage suspend;
1103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1104 "SUSPEND message received for non-existing line, dropping channel.\n");
1105 return GNUNET_SYSERR;
1108 suspend.header.size = sizeof (suspend);
1109 suspend.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
1110 suspend.cid = ch->cid;
1111 GNUNET_MESH_receive_done (channel);
1114 case CS_CALLEE_RINGING:
1115 GNUNET_break_op (0);
1117 case CS_CALLEE_CONNECTED:
1118 ch->suspended_remote = GNUNET_YES;
1120 case CS_CALLEE_SHUTDOWN:
1122 case CS_CALLER_CALLING:
1123 GNUNET_break_op (0);
1125 case CS_CALLER_CONNECTED:
1126 ch->suspended_remote = GNUNET_YES;
1128 case CS_CALLER_SHUTDOWN:
1131 GNUNET_SERVER_notification_context_unicast (nc,
1140 * Function to handle a resume message incoming over mesh
1142 * @param cls closure, NULL
1143 * @param channel the channel over which the message arrived
1144 * @param channel_ctx the channel context, can be NULL
1145 * or point to the `struct Channel`
1146 * @param message the incoming message
1147 * @return #GNUNET_OK
1150 handle_mesh_resume_message (void *cls,
1151 struct GNUNET_MESH_Channel *channel,
1153 const struct GNUNET_MessageHeader *message)
1155 struct Channel *ch = *channel_ctx;
1157 struct ClientPhoneResumeMessage resume;
1161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1162 "RESUME message received for non-existing line, dropping channel.\n");
1163 return GNUNET_SYSERR;
1166 resume.header.size = sizeof (resume);
1167 resume.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
1168 resume.cid = ch->cid;
1169 GNUNET_MESH_receive_done (channel);
1170 if (GNUNET_YES != ch->suspended_remote)
1172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1173 "RESUME message received for non-suspended channel, dropping channel.\n");
1174 return GNUNET_SYSERR;
1178 case CS_CALLEE_RINGING:
1181 case CS_CALLEE_CONNECTED:
1182 ch->suspended_remote = GNUNET_NO;
1184 case CS_CALLEE_SHUTDOWN:
1186 case CS_CALLER_CALLING:
1189 case CS_CALLER_CONNECTED:
1190 ch->suspended_remote = GNUNET_NO;
1192 case CS_CALLER_SHUTDOWN:
1195 GNUNET_SERVER_notification_context_unicast (nc,
1204 * Function to handle an audio message incoming over mesh
1206 * @param cls closure, NULL
1207 * @param channel the channel over which the message arrived
1208 * @param channel_ctx the channel context, can be NULL
1209 * or point to the `struct Channel`
1210 * @param message the incoming message
1211 * @return #GNUNET_OK
1214 handle_mesh_audio_message (void *cls,
1215 struct GNUNET_MESH_Channel *channel,
1217 const struct GNUNET_MessageHeader *message)
1219 const struct MeshAudioMessage *msg;
1220 struct Channel *ch = *channel_ctx;
1222 struct GNUNET_PeerIdentity sender;
1223 size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
1224 char buf[msize + sizeof (struct ClientAudioMessage)];
1225 struct ClientAudioMessage *cam;
1226 const union GNUNET_MESH_ChannelInfo *info;
1228 msg = (const struct MeshAudioMessage *) message;
1231 info = GNUNET_MESH_channel_get_info (channel,
1232 GNUNET_MESH_OPTION_PEER);
1238 sender = *(info->peer);
1239 for (line = lines_head; NULL != line; line = line->next)
1240 if (line->local_line == ntohl (msg->remote_line))
1242 for (ch = line->channel_head; NULL != ch; ch = ch->next)
1244 if ( (CS_CALLEE_CONNECTED == ch->status) &&
1245 (0 == memcmp (&ch->target,
1247 sizeof (struct GNUNET_PeerIdentity))) &&
1248 (NULL == ch->channel_unreliable) )
1254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1255 "Received AUDIO data for non-existing line %u, dropping.\n",
1256 ntohl (msg->remote_line));
1257 return GNUNET_SYSERR;
1259 ch->channel_unreliable = channel;
1262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1263 "Forwarding %u bytes of AUDIO data to client\n",
1265 cam = (struct ClientAudioMessage *) buf;
1266 cam->header.size = htons (sizeof (buf));
1267 cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
1269 memcpy (&cam[1], &msg[1], msize);
1270 GNUNET_SERVER_notification_context_unicast (nc,
1274 GNUNET_MESH_receive_done (channel);
1280 * Method called whenever another peer has added us to a channel
1281 * the other peer initiated.
1283 * @param cls closure
1284 * @param channel new handle to the channel
1285 * @param initiator peer that started the channel
1287 * @return initial channel context for the channel;
1288 * (can be NULL -- that's not an error)
1291 inbound_channel (void *cls,
1292 struct GNUNET_MESH_Channel *channel,
1293 const struct GNUNET_PeerIdentity *initiator,
1296 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1297 _("Received incoming channel on port %u\n"),
1298 (unsigned int) port);
1304 * Function called whenever an inbound channel is destroyed. Should clean up
1305 * any associated state.
1307 * @param cls closure (set from #GNUNET_MESH_connect)
1308 * @param channel connection to the other end (henceforth invalid)
1309 * @param channel_ctx place where local state associated
1310 * with the channel is stored;
1311 * may point to the `struct Channel`
1314 inbound_end (void *cls,
1315 const struct GNUNET_MESH_Channel *channel,
1318 struct Channel *ch = channel_ctx;
1320 struct ClientPhoneHangupMessage hup;
1325 if (ch->channel_unreliable == channel)
1327 if (NULL != ch->unreliable_mth)
1329 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
1330 ch->unreliable_mth = NULL;
1332 ch->channel_unreliable = NULL;
1335 if (ch->channel_reliable != channel)
1337 ch->channel_reliable = NULL;
1339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1340 "Mesh channel destroyed by mesh in state %d\n",
1342 hup.header.size = sizeof (hup);
1343 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1347 case CS_CALLEE_RINGING:
1348 case CS_CALLEE_CONNECTED:
1349 GNUNET_SERVER_notification_context_unicast (nc,
1354 case CS_CALLEE_SHUTDOWN:
1356 case CS_CALLER_CALLING:
1357 case CS_CALLER_CONNECTED:
1358 GNUNET_SERVER_notification_context_unicast (nc,
1363 case CS_CALLER_SHUTDOWN:
1366 destroy_line_mesh_channels (ch);
1371 * A client disconnected. Remove all of its data structure entries.
1373 * @param cls closure, NULL
1374 * @param client identification of the client
1377 handle_client_disconnect (void *cls,
1378 struct GNUNET_SERVER_Client *client)
1384 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1387 GNUNET_SERVER_client_set_user_context (client, (void *)NULL);
1388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1389 "Client disconnected, closing line\n");
1390 GNUNET_CONTAINER_DLL_remove (lines_head,
1393 while (NULL != line->channel_head)
1394 destroy_line_mesh_channels (line->channel_head);
1402 * @param cls closure, NULL
1403 * @param tc the task context
1406 do_shutdown (void *cls,
1407 const struct GNUNET_SCHEDULER_TaskContext *tc)
1411 GNUNET_MESH_disconnect (mesh);
1416 GNUNET_SERVER_notification_context_destroy (nc);
1423 * Main function that will be run by the scheduler.
1425 * @param cls closure
1426 * @param server server handle
1427 * @param c configuration
1431 struct GNUNET_SERVER_Handle *server,
1432 const struct GNUNET_CONFIGURATION_Handle *c)
1434 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1435 {&handle_client_register_message, NULL,
1436 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1437 sizeof (struct ClientPhoneRegisterMessage)},
1438 {&handle_client_pickup_message, NULL,
1439 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1440 sizeof (struct ClientPhonePickupMessage) },
1441 {&handle_client_suspend_message, NULL,
1442 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
1443 sizeof (struct ClientPhoneSuspendMessage) },
1444 {&handle_client_resume_message, NULL,
1445 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
1446 sizeof (struct ClientPhoneResumeMessage) },
1447 {&handle_client_hangup_message, NULL,
1448 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1449 sizeof (struct ClientPhoneHangupMessage) },
1450 {&handle_client_call_message, NULL,
1451 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1452 sizeof (struct ClientCallMessage) },
1453 {&handle_client_audio_message, NULL,
1454 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1458 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1459 {&handle_mesh_ring_message,
1460 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1461 sizeof (struct MeshPhoneRingMessage)},
1462 {&handle_mesh_hangup_message,
1463 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1464 sizeof (struct MeshPhoneHangupMessage)},
1465 {&handle_mesh_pickup_message,
1466 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1467 sizeof (struct MeshPhonePickupMessage)},
1468 {&handle_mesh_suspend_message,
1469 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND,
1470 sizeof (struct MeshPhoneSuspendMessage)},
1471 {&handle_mesh_resume_message,
1472 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME,
1473 sizeof (struct MeshPhoneResumeMessage)},
1474 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1478 static uint32_t ports[] = {
1479 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1480 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1485 GNUNET_assert (GNUNET_OK ==
1486 GNUNET_CRYPTO_get_peer_identity (cfg,
1488 mesh = GNUNET_MESH_connect (cfg,
1498 GNUNET_SCHEDULER_shutdown ();
1501 nc = GNUNET_SERVER_notification_context_create (server, 16);
1502 GNUNET_SERVER_add_handlers (server, server_handlers);
1503 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1504 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1511 * The main function for the conversation service.
1513 * @param argc number of arguments from the command line
1514 * @param argv command line arguments
1515 * @return 0 ok, 1 on error
1521 return (GNUNET_OK ==
1522 GNUNET_SERVICE_run (argc, argv,
1524 GNUNET_SERVICE_OPTION_NONE,
1525 &run, NULL)) ? 0 : 1;
1528 /* end of gnunet-service-conversation.c */