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",
328 GNUNET_SERVER_receive_done (client, GNUNET_YES);
333 case CS_CALLEE_RINGING:
334 ch->status = CS_CALLEE_CONNECTED;
336 case CS_CALLEE_CONNECTED:
338 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
340 case CS_CALLEE_SHUTDOWN:
341 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
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 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373 "Destroying mesh channels\n");
374 if (NULL != ch->reliable_mq)
376 GNUNET_MQ_destroy (ch->reliable_mq);
377 ch->reliable_mq = NULL;
379 if (NULL != ch->unreliable_mth)
381 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
382 ch->unreliable_mth = NULL;
384 if (NULL != (t = ch->channel_unreliable))
386 ch->channel_unreliable = NULL;
387 GNUNET_MESH_channel_destroy (t);
389 if (NULL != (t = ch->channel_reliable))
391 ch->channel_reliable = NULL;
392 GNUNET_MESH_channel_destroy (t);
394 GNUNET_CONTAINER_DLL_remove (line->channel_head,
397 GNUNET_free_non_null (ch->audio_data);
403 * We are done signalling shutdown to the other peer. Close down
406 * @param cls the `struct Channel` to reset/terminate
409 mq_done_finish_caller_shutdown (void *cls)
411 struct Channel *ch = cls;
415 case CS_CALLEE_RINGING:
418 case CS_CALLEE_CONNECTED:
421 case CS_CALLEE_SHUTDOWN:
422 destroy_line_mesh_channels (ch);
424 case CS_CALLER_CALLING:
427 case CS_CALLER_CONNECTED:
430 case CS_CALLER_SHUTDOWN:
431 destroy_line_mesh_channels (ch);
438 * Function to handle a hangup request message from the client
440 * @param cls closure, NULL
441 * @param client the client from which the message is
442 * @param message the message from the client
445 handle_client_hangup_message (void *cls,
446 struct GNUNET_SERVER_Client *client,
447 const struct GNUNET_MessageHeader *message)
449 const struct ClientPhoneHangupMessage *msg;
450 struct GNUNET_MQ_Envelope *e;
451 struct MeshPhoneHangupMessage *mhum;
455 msg = (const struct ClientPhoneHangupMessage *) message;
456 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
460 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
463 for (ch = line->channel_head; NULL != ch; ch = ch->next)
464 if (msg->cid == ch->cid)
468 /* could have been destroyed asynchronously, ignore message */
469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
470 "Channel %u not found\n",
472 GNUNET_SERVER_receive_done (client, GNUNET_OK);
478 case CS_CALLEE_RINGING:
479 ch->status = CS_CALLEE_SHUTDOWN;
481 case CS_CALLEE_CONNECTED:
482 ch->status = CS_CALLEE_SHUTDOWN;
484 case CS_CALLEE_SHUTDOWN:
485 /* maybe the other peer closed asynchronously... */
486 GNUNET_SERVER_receive_done (client, GNUNET_OK);
488 case CS_CALLER_CALLING:
489 ch->status = CS_CALLER_SHUTDOWN;
491 case CS_CALLER_CONNECTED:
492 ch->status = CS_CALLER_SHUTDOWN;
494 case CS_CALLER_SHUTDOWN:
495 /* maybe the other peer closed asynchronously... */
496 GNUNET_SERVER_receive_done (client, GNUNET_OK);
499 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
500 "Sending HANG_UP message via mesh\n");
501 e = GNUNET_MQ_msg (mhum,
502 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
503 GNUNET_MQ_notify_sent (e,
504 &mq_done_finish_caller_shutdown,
506 GNUNET_MQ_send (ch->reliable_mq, e);
507 GNUNET_SERVER_receive_done (client, GNUNET_OK);
512 * Function to handle a suspend request message from the client
514 * @param cls closure, NULL
515 * @param client the client from which the message is
516 * @param message the message from the client
519 handle_client_suspend_message (void *cls,
520 struct GNUNET_SERVER_Client *client,
521 const struct GNUNET_MessageHeader *message)
523 const struct ClientPhoneSuspendMessage *msg;
524 struct GNUNET_MQ_Envelope *e;
525 struct MeshPhoneSuspendMessage *mhum;
529 msg = (const struct ClientPhoneSuspendMessage *) message;
530 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
534 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
537 for (ch = line->channel_head; NULL != ch; ch = ch->next)
538 if (msg->cid == ch->cid)
542 /* could have been destroyed asynchronously, ignore message */
543 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
544 "Channel %u not found\n",
546 GNUNET_SERVER_receive_done (client, GNUNET_OK);
549 if (GNUNET_YES == ch->suspended_local)
552 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
557 case CS_CALLEE_RINGING:
559 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
561 case CS_CALLEE_CONNECTED:
562 ch->suspended_local = GNUNET_YES;
564 case CS_CALLEE_SHUTDOWN:
565 /* maybe the other peer closed asynchronously... */
566 GNUNET_SERVER_receive_done (client, GNUNET_OK);
568 case CS_CALLER_CALLING:
570 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
572 case CS_CALLER_CONNECTED:
573 ch->suspended_local = GNUNET_YES;
575 case CS_CALLER_SHUTDOWN:
576 /* maybe the other peer closed asynchronously... */
577 GNUNET_SERVER_receive_done (client, GNUNET_OK);
580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581 "Sending SUSPEND message via mesh\n");
582 e = GNUNET_MQ_msg (mhum,
583 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND);
584 GNUNET_MQ_send (ch->reliable_mq, e);
585 GNUNET_SERVER_receive_done (client, GNUNET_OK);
590 * Function to handle a resume request message from the client
592 * @param cls closure, NULL
593 * @param client the client from which the message is
594 * @param message the message from the client
597 handle_client_resume_message (void *cls,
598 struct GNUNET_SERVER_Client *client,
599 const struct GNUNET_MessageHeader *message)
601 const struct ClientPhoneResumeMessage *msg;
602 struct GNUNET_MQ_Envelope *e;
603 struct MeshPhoneResumeMessage *mhum;
607 msg = (const struct ClientPhoneResumeMessage *) message;
608 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
612 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
615 for (ch = line->channel_head; NULL != ch; ch = ch->next)
616 if (msg->cid == ch->cid)
620 /* could have been destroyed asynchronously, ignore message */
621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622 "Channel %u not found\n",
624 GNUNET_SERVER_receive_done (client, GNUNET_OK);
627 if (GNUNET_YES != ch->suspended_local)
630 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
635 case CS_CALLEE_RINGING:
637 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
639 case CS_CALLEE_CONNECTED:
640 ch->suspended_local = GNUNET_NO;
642 case CS_CALLEE_SHUTDOWN:
643 /* maybe the other peer closed asynchronously... */
644 GNUNET_SERVER_receive_done (client, GNUNET_OK);
646 case CS_CALLER_CALLING:
648 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
650 case CS_CALLER_CONNECTED:
651 ch->suspended_local = GNUNET_NO;
653 case CS_CALLER_SHUTDOWN:
654 /* maybe the other peer closed asynchronously... */
655 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Sending RESUME message via mesh\n");
660 e = GNUNET_MQ_msg (mhum,
661 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME);
662 GNUNET_MQ_send (ch->reliable_mq, e);
663 GNUNET_SERVER_receive_done (client, GNUNET_OK);
668 * Function to handle call request from the client
670 * @param cls closure, NULL
671 * @param client the client from which the message is
672 * @param message the message from the client
675 handle_client_call_message (void *cls,
676 struct GNUNET_SERVER_Client *client,
677 const struct GNUNET_MessageHeader *message)
679 const struct ClientCallMessage *msg;
682 struct GNUNET_MQ_Envelope *e;
683 struct MeshPhoneRingMessage *ring;
685 msg = (const struct ClientCallMessage *) message;
686 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
690 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
693 line = GNUNET_new (struct Line);
694 line->client = client;
695 line->local_line = (local_line_cnt++) | (1 << 31);
696 GNUNET_SERVER_client_set_user_context (client, line);
697 GNUNET_SERVER_notification_context_add (nc, client);
698 GNUNET_CONTAINER_DLL_insert (lines_head,
701 ch = GNUNET_new (struct Channel);
703 GNUNET_CONTAINER_DLL_insert (line->channel_head,
706 ch->target = msg->target;
707 ch->remote_line = ntohl (msg->line);
708 ch->status = CS_CALLER_CALLING;
709 ch->channel_reliable = GNUNET_MESH_channel_create (mesh,
712 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
713 GNUNET_MESH_OPTION_RELIABLE);
714 ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
715 e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
716 ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
717 ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
718 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
719 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
720 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
721 GNUNET_CRYPTO_ecdsa_key_get_public (&msg->caller_id,
723 ring->remote_line = msg->line;
724 ring->source_line = htonl (line->local_line);
725 ring->target = msg->target;
726 ring->source = my_identity;
727 ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
728 GNUNET_CRYPTO_ecdsa_sign (&msg->caller_id,
731 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732 "Sending RING message via mesh\n");
733 GNUNET_MQ_send (ch->reliable_mq, e);
734 GNUNET_SERVER_receive_done (client, GNUNET_OK);
739 * Transmit audio data via unreliable mesh channel.
741 * @param cls the `struct Channel` we are transmitting for
742 * @param size number of bytes available in @a buf
743 * @param buf where to copy the data
744 * @return number of bytes copied to @a buf
747 transmit_line_audio (void *cls,
751 struct Channel *ch = cls;
752 struct MeshAudioMessage *mam = buf;
754 ch->unreliable_mth = NULL;
755 if ( (NULL == buf) ||
756 (size < sizeof (struct MeshAudioMessage) + ch->audio_size) )
758 /* eh, other error handling? */
761 mam->header.size = htons (sizeof (struct MeshAudioMessage) + ch->audio_size);
762 mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
763 mam->remote_line = htonl (ch->remote_line);
764 mam->source_line = htonl (ch->line->local_line);
765 memcpy (&mam[1], ch->audio_data, ch->audio_size);
766 GNUNET_free (ch->audio_data);
767 ch->audio_data = NULL;
768 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
769 "Sending %u bytes of audio data from line %u to remote line %u via mesh\n",
770 ch->audio_size, ch->line->local_line, ch->remote_line);
771 return sizeof (struct MeshAudioMessage) + ch->audio_size;
776 * Function to handle audio data from the client
778 * @param cls closure, NULL
779 * @param client the client from which the message is
780 * @param message the message from the client
783 handle_client_audio_message (void *cls,
784 struct GNUNET_SERVER_Client *client,
785 const struct GNUNET_MessageHeader *message)
787 const struct ClientAudioMessage *msg;
792 size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
793 msg = (const struct ClientAudioMessage *) message;
794 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
798 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
801 for (ch = line->channel_head; NULL != ch; ch = ch->next)
802 if (msg->cid == ch->cid)
806 /* could have been destroyed asynchronously, ignore message */
807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
808 "Channel %u not found\n",
810 GNUNET_SERVER_receive_done (client, GNUNET_OK);
816 case CS_CALLEE_RINGING:
817 case CS_CALLER_CALLING:
819 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
821 case CS_CALLEE_CONNECTED:
822 case CS_CALLER_CONNECTED:
823 /* common case, handled below */
825 case CS_CALLEE_SHUTDOWN:
826 case CS_CALLER_SHUTDOWN:
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
828 "Mesh audio channel in shutdown; audio data dropped\n");
829 GNUNET_SERVER_receive_done (client, GNUNET_OK);
832 if (GNUNET_YES == ch->suspended_local)
834 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "This channel is suspended locally\n");
835 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
838 if (NULL == ch->channel_unreliable)
840 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
841 _("Mesh audio channel not ready; audio data dropped\n"));
842 GNUNET_SERVER_receive_done (client, GNUNET_OK);
845 if (NULL != ch->unreliable_mth)
847 /* NOTE: we may want to not do this and instead combine the data */
848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
849 "Bandwidth insufficient; dropping previous audio data segment with %u bytes\n",
850 (unsigned int) ch->audio_size);
851 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
852 ch->unreliable_mth = NULL;
853 GNUNET_free (ch->audio_data);
854 ch->audio_data = NULL;
856 ch->audio_size = size;
857 ch->audio_data = GNUNET_malloc (ch->audio_size);
858 memcpy (ch->audio_data,
861 ch->unreliable_mth = GNUNET_MESH_notify_transmit_ready (ch->channel_unreliable,
863 GNUNET_TIME_UNIT_FOREVER_REL,
864 sizeof (struct MeshAudioMessage)
866 &transmit_line_audio,
868 GNUNET_SERVER_receive_done (client, GNUNET_OK);
873 * We are done signalling shutdown to the other peer.
874 * Destroy the channel.
876 * @param cls the `struct GNUNET_MESH_channel` to destroy
879 mq_done_destroy_channel (void *cls)
881 struct GNUNET_MESH_Channel *channel = cls;
883 GNUNET_MESH_channel_destroy (channel);
888 * Function to handle a ring message incoming over mesh
890 * @param cls closure, NULL
891 * @param channel the channel over which the message arrived
892 * @param channel_ctx the channel context, can be NULL
893 * or point to the `struct Channel`
894 * @param message the incoming message
898 handle_mesh_ring_message (void *cls,
899 struct GNUNET_MESH_Channel *channel,
901 const struct GNUNET_MessageHeader *message)
903 const struct MeshPhoneRingMessage *msg;
906 struct GNUNET_MQ_Envelope *e;
907 struct MeshPhoneHangupMessage *hang_up;
908 struct ClientPhoneRingMessage cring;
909 struct GNUNET_MQ_Handle *reliable_mq;
911 msg = (const struct MeshPhoneRingMessage *) message;
912 if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
913 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
914 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
915 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) ||
917 GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
923 return GNUNET_SYSERR;
925 GNUNET_MESH_receive_done (channel); /* needed? */
926 for (line = lines_head; NULL != line; line = line->next)
927 if (line->local_line == ntohl (msg->remote_line))
931 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
932 _("No available phone for incoming call on line %u, sending HANG_UP signal\n"),
933 ntohl (msg->remote_line));
934 e = GNUNET_MQ_msg (hang_up,
935 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
936 GNUNET_MQ_notify_sent (e,
937 &mq_done_destroy_channel,
939 reliable_mq = GNUNET_MESH_mq_create (channel);
940 GNUNET_MQ_send (reliable_mq, e);
941 /* FIXME: do we need to clean up reliable_mq somehow/somewhere? */
944 ch = GNUNET_new (struct Channel);
946 GNUNET_CONTAINER_DLL_insert (line->channel_head,
949 ch->status = CS_CALLEE_RINGING;
950 ch->remote_line = ntohl (msg->source_line);
951 ch->channel_reliable = channel;
952 ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
953 ch->cid = line->cid_gen++;
954 ch->target = msg->source;
956 cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
957 cring.header.size = htons (sizeof (cring));
959 cring.caller_id = msg->caller_id;
960 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961 "Sending RING message to client. CID %u:(%u, %u)\n",
962 ch->cid, ch->remote_line, line->local_line);
963 GNUNET_SERVER_notification_context_unicast (nc,
972 * Function to handle a hangup message incoming over mesh
974 * @param cls closure, NULL
975 * @param channel the channel over which the message arrived
976 * @param channel_ctx the channel context, can be NULL
977 * or point to the `struct Channel`
978 * @param message the incoming message
982 handle_mesh_hangup_message (void *cls,
983 struct GNUNET_MESH_Channel *channel,
985 const struct GNUNET_MessageHeader *message)
987 struct Channel *ch = *channel_ctx;
989 struct ClientPhoneHangupMessage hup;
990 enum ChannelStatus status;
994 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
995 "HANGUP message received for non-existing line, dropping channel.\n");
996 return GNUNET_SYSERR;
1000 hup.header.size = htons (sizeof (hup));
1001 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1003 status = ch->status;
1004 GNUNET_MESH_receive_done (channel);
1005 destroy_line_mesh_channels (ch);
1008 case CS_CALLEE_RINGING:
1009 case CS_CALLEE_CONNECTED:
1011 case CS_CALLEE_SHUTDOWN:
1013 case CS_CALLER_CALLING:
1014 case CS_CALLER_CONNECTED:
1016 case CS_CALLER_SHUTDOWN:
1019 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1020 "Sending HANG UP message to client\n");
1021 GNUNET_SERVER_notification_context_unicast (nc,
1030 * Function to handle a pickup message incoming over mesh
1032 * @param cls closure, NULL
1033 * @param channel the channel over which the message arrived
1034 * @param channel_ctx the channel context, can be NULL
1035 * or point to the `struct Channel`
1036 * @param message the incoming message
1037 * @return #GNUNET_OK
1040 handle_mesh_pickup_message (void *cls,
1041 struct GNUNET_MESH_Channel *channel,
1043 const struct GNUNET_MessageHeader *message)
1045 struct Channel *ch = *channel_ctx;
1047 struct ClientPhonePickupMessage pick;
1051 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1052 "PICKUP message received for non-existing channel, dropping channel.\n");
1053 return GNUNET_SYSERR;
1056 GNUNET_MESH_receive_done (channel);
1059 case CS_CALLEE_RINGING:
1060 case CS_CALLEE_CONNECTED:
1061 GNUNET_break_op (0);
1062 destroy_line_mesh_channels (ch);
1063 return GNUNET_SYSERR;
1064 case CS_CALLEE_SHUTDOWN:
1065 GNUNET_break_op (0);
1066 destroy_line_mesh_channels (ch);
1068 case CS_CALLER_CALLING:
1069 ch->status = CS_CALLER_CONNECTED;
1071 case CS_CALLER_CONNECTED:
1072 GNUNET_break_op (0);
1074 case CS_CALLER_SHUTDOWN:
1075 GNUNET_break_op (0);
1076 mq_done_finish_caller_shutdown (ch);
1077 return GNUNET_SYSERR;
1079 pick.header.size = htons (sizeof (pick));
1080 pick.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
1082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1083 "Sending PICKED UP message to client\n");
1084 GNUNET_SERVER_notification_context_unicast (nc,
1088 ch->channel_unreliable = GNUNET_MESH_channel_create (mesh,
1091 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1092 GNUNET_MESH_OPTION_DEFAULT);
1093 if (NULL == ch->channel_unreliable)
1102 * Function to handle a suspend message incoming over mesh
1104 * @param cls closure, NULL
1105 * @param channel the channel over which the message arrived
1106 * @param channel_ctx the channel context, can be NULL
1107 * or point to the `struct Channel`
1108 * @param message the incoming message
1109 * @return #GNUNET_OK
1112 handle_mesh_suspend_message (void *cls,
1113 struct GNUNET_MESH_Channel *channel,
1115 const struct GNUNET_MessageHeader *message)
1117 struct Channel *ch = *channel_ctx;
1119 struct ClientPhoneSuspendMessage suspend;
1123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1124 "SUSPEND message received for non-existing line, dropping channel.\n");
1125 return GNUNET_SYSERR;
1128 suspend.header.size = htons (sizeof (suspend));
1129 suspend.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
1130 suspend.cid = ch->cid;
1131 GNUNET_MESH_receive_done (channel);
1132 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1133 "Suspending channel CID: %u(%u:%u)\n",
1134 ch->cid, ch->remote_line, line->local_line);
1137 case CS_CALLEE_RINGING:
1138 GNUNET_break_op (0);
1140 case CS_CALLEE_CONNECTED:
1141 ch->suspended_remote = GNUNET_YES;
1143 case CS_CALLEE_SHUTDOWN:
1145 case CS_CALLER_CALLING:
1146 GNUNET_break_op (0);
1148 case CS_CALLER_CONNECTED:
1149 ch->suspended_remote = GNUNET_YES;
1151 case CS_CALLER_SHUTDOWN:
1154 GNUNET_SERVER_notification_context_unicast (nc,
1163 * Function to handle a resume message incoming over mesh
1165 * @param cls closure, NULL
1166 * @param channel the channel over which the message arrived
1167 * @param channel_ctx the channel context, can be NULL
1168 * or point to the `struct Channel`
1169 * @param message the incoming message
1170 * @return #GNUNET_OK
1173 handle_mesh_resume_message (void *cls,
1174 struct GNUNET_MESH_Channel *channel,
1176 const struct GNUNET_MessageHeader *message)
1178 struct Channel *ch = *channel_ctx;
1180 struct ClientPhoneResumeMessage resume;
1184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1185 "RESUME message received for non-existing line, dropping channel.\n");
1186 return GNUNET_SYSERR;
1189 resume.header.size = htons (sizeof (resume));
1190 resume.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
1191 resume.cid = ch->cid;
1192 GNUNET_MESH_receive_done (channel);
1193 if (GNUNET_YES != ch->suspended_remote)
1195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1196 "RESUME message received for non-suspended channel, dropping channel.\n");
1197 return GNUNET_SYSERR;
1201 case CS_CALLEE_RINGING:
1204 case CS_CALLEE_CONNECTED:
1205 ch->suspended_remote = GNUNET_NO;
1207 case CS_CALLEE_SHUTDOWN:
1209 case CS_CALLER_CALLING:
1212 case CS_CALLER_CONNECTED:
1213 ch->suspended_remote = GNUNET_NO;
1215 case CS_CALLER_SHUTDOWN:
1218 GNUNET_SERVER_notification_context_unicast (nc,
1227 * Function to handle an audio message incoming over mesh
1229 * @param cls closure, NULL
1230 * @param channel the channel over which the message arrived
1231 * @param channel_ctx the channel context, can be NULL
1232 * or point to the `struct Channel`
1233 * @param message the incoming message
1234 * @return #GNUNET_OK
1237 handle_mesh_audio_message (void *cls,
1238 struct GNUNET_MESH_Channel *channel,
1240 const struct GNUNET_MessageHeader *message)
1242 const struct MeshAudioMessage *msg;
1243 struct Channel *ch = *channel_ctx;
1245 struct GNUNET_PeerIdentity sender;
1246 size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
1247 char buf[msize + sizeof (struct ClientAudioMessage)];
1248 struct ClientAudioMessage *cam;
1249 const union GNUNET_MESH_ChannelInfo *info;
1251 msg = (const struct MeshAudioMessage *) message;
1254 info = GNUNET_MESH_channel_get_info (channel,
1255 GNUNET_MESH_OPTION_PEER);
1259 return GNUNET_SYSERR;
1261 sender = info->peer;
1262 for (line = lines_head; NULL != line; line = line->next)
1263 if (line->local_line == ntohl (msg->remote_line))
1265 for (ch = line->channel_head; NULL != ch; ch = ch->next)
1267 if ( (CS_CALLEE_CONNECTED == ch->status) &&
1268 (0 == memcmp (&ch->target,
1270 sizeof (struct GNUNET_PeerIdentity))) &&
1271 (NULL == ch->channel_unreliable) &&
1272 (ch->remote_line == ntohl (msg->source_line)) )
1279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1280 "Received %u bytes of AUDIO data for non-existing line %u, dropping.\n",
1281 msize, ntohl (msg->remote_line));
1282 return GNUNET_SYSERR;
1286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1287 "Received %u bytes of AUDIO data for unknown sender.\n",
1289 return GNUNET_SYSERR;
1291 if ((GNUNET_YES == ch->suspended_local) || (GNUNET_YES == ch->suspended_remote))
1293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1294 "Received %u bytes of AUDIO data on suspended channel CID %u:(%u:%u); dropping\n",
1295 msize, ch->cid, ch->remote_line, line->local_line);
1296 GNUNET_MESH_receive_done (channel);
1299 ch->channel_unreliable = channel;
1302 GNUNET_MESH_receive_done (channel);
1303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1304 "Forwarding %u bytes of AUDIO data to client CID %u:(%u:%u)\n",
1305 msize, ch->cid, ch->remote_line, ch->line->local_line);
1306 cam = (struct ClientAudioMessage *) buf;
1307 cam->header.size = htons (sizeof (buf));
1308 cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
1310 memcpy (&cam[1], &msg[1], msize);
1311 GNUNET_SERVER_notification_context_unicast (nc,
1320 * Method called whenever another peer has added us to a channel
1321 * the other peer initiated.
1323 * @param cls closure
1324 * @param channel new handle to the channel
1325 * @param initiator peer that started the channel
1327 * @param options channel option flags
1328 * @return initial channel context for the channel;
1329 * (can be NULL -- that's not an error)
1332 inbound_channel (void *cls,
1333 struct GNUNET_MESH_Channel *channel,
1334 const struct GNUNET_PeerIdentity *initiator,
1335 uint32_t port, enum GNUNET_MESH_ChannelOption options)
1337 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1338 _("Received incoming channel on port %u\n"),
1339 (unsigned int) port);
1345 * Function called whenever an inbound channel is destroyed. Should clean up
1346 * any associated state.
1348 * @param cls closure (set from #GNUNET_MESH_connect)
1349 * @param channel connection to the other end (henceforth invalid)
1350 * @param channel_ctx place where local state associated
1351 * with the channel is stored;
1352 * may point to the `struct Channel`
1355 inbound_end (void *cls,
1356 const struct GNUNET_MESH_Channel *channel,
1359 struct Channel *ch = channel_ctx;
1361 struct ClientPhoneHangupMessage hup;
1365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1366 "Mesh channel destroyed, but channel is unknown to us\n");
1370 if (ch->channel_unreliable == channel)
1372 if (NULL != ch->unreliable_mth)
1374 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
1375 ch->unreliable_mth = NULL;
1377 ch->channel_unreliable = NULL;
1378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1379 "Unreliable channel destroyed\n");
1382 if (ch->channel_reliable != channel)
1384 /* recursive call, I'm the one destroying 'ch' right now */
1387 ch->channel_reliable = NULL;
1389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1390 "Mesh channel destroyed by mesh in state %d\n",
1392 hup.header.size = htons (sizeof (hup));
1393 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1397 case CS_CALLEE_RINGING:
1398 case CS_CALLEE_CONNECTED:
1399 GNUNET_SERVER_notification_context_unicast (nc,
1404 case CS_CALLEE_SHUTDOWN:
1406 case CS_CALLER_CALLING:
1407 case CS_CALLER_CONNECTED:
1408 GNUNET_SERVER_notification_context_unicast (nc,
1413 case CS_CALLER_SHUTDOWN:
1416 destroy_line_mesh_channels (ch);
1421 * A client disconnected. Remove all of its data structure entries.
1423 * @param cls closure, NULL
1424 * @param client identification of the client
1427 handle_client_disconnect (void *cls,
1428 struct GNUNET_SERVER_Client *client)
1434 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1437 GNUNET_SERVER_client_set_user_context (client, (void *)NULL);
1438 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1439 "Client disconnected, closing line\n");
1440 GNUNET_CONTAINER_DLL_remove (lines_head,
1443 while (NULL != line->channel_head)
1444 destroy_line_mesh_channels (line->channel_head);
1452 * @param cls closure, NULL
1453 * @param tc the task context
1456 do_shutdown (void *cls,
1457 const struct GNUNET_SCHEDULER_TaskContext *tc)
1462 while (NULL != (line = lines_head))
1464 while (NULL != (ch = line->channel_head))
1465 destroy_line_mesh_channels (ch);
1466 GNUNET_CONTAINER_DLL_remove (lines_head,
1469 GNUNET_SERVER_client_set_user_context (line->client, (void *) NULL);
1474 GNUNET_MESH_disconnect (mesh);
1479 GNUNET_SERVER_notification_context_destroy (nc);
1486 * Main function that will be run by the scheduler.
1488 * @param cls closure
1489 * @param server server handle
1490 * @param c configuration
1494 struct GNUNET_SERVER_Handle *server,
1495 const struct GNUNET_CONFIGURATION_Handle *c)
1497 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1498 {&handle_client_register_message, NULL,
1499 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1500 sizeof (struct ClientPhoneRegisterMessage)},
1501 {&handle_client_pickup_message, NULL,
1502 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1503 sizeof (struct ClientPhonePickupMessage) },
1504 {&handle_client_suspend_message, NULL,
1505 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
1506 sizeof (struct ClientPhoneSuspendMessage) },
1507 {&handle_client_resume_message, NULL,
1508 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
1509 sizeof (struct ClientPhoneResumeMessage) },
1510 {&handle_client_hangup_message, NULL,
1511 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1512 sizeof (struct ClientPhoneHangupMessage) },
1513 {&handle_client_call_message, NULL,
1514 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1515 sizeof (struct ClientCallMessage) },
1516 {&handle_client_audio_message, NULL,
1517 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1521 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1522 {&handle_mesh_ring_message,
1523 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1524 sizeof (struct MeshPhoneRingMessage)},
1525 {&handle_mesh_hangup_message,
1526 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1527 sizeof (struct MeshPhoneHangupMessage)},
1528 {&handle_mesh_pickup_message,
1529 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1530 sizeof (struct MeshPhonePickupMessage)},
1531 {&handle_mesh_suspend_message,
1532 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND,
1533 sizeof (struct MeshPhoneSuspendMessage)},
1534 {&handle_mesh_resume_message,
1535 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME,
1536 sizeof (struct MeshPhoneResumeMessage)},
1537 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1541 static uint32_t ports[] = {
1542 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1543 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1548 GNUNET_assert (GNUNET_OK ==
1549 GNUNET_CRYPTO_get_peer_identity (cfg,
1551 mesh = GNUNET_MESH_connect (cfg,
1561 GNUNET_SCHEDULER_shutdown ();
1564 nc = GNUNET_SERVER_notification_context_create (server, 16);
1565 GNUNET_SERVER_add_handlers (server, server_handlers);
1566 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1567 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1574 * The main function for the conversation service.
1576 * @param argc number of arguments from the command line
1577 * @param argv command line arguments
1578 * @return 0 ok, 1 on error
1584 return (GNUNET_OK ==
1585 GNUNET_SERVICE_run (argc, argv,
1587 GNUNET_SERVICE_OPTION_NONE,
1588 &run, NULL)) ? 0 : 1;
1591 /* end of gnunet-service-conversation.c */