2 * This file is part of GNUnet
3 * Copyright (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.
22 * @file social/gnunet-service-social.c
23 * @brief Social service
24 * @author Gabor X Toth
30 #include "gnunet_util_lib.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_psyc_service.h"
35 #include "gnunet_psyc_util_lib.h"
36 #include "gnunet_social_service.h"
41 * Handle to our current configuration.
43 static const struct GNUNET_CONFIGURATION_Handle *cfg;
46 * Handle to the statistics service.
48 static struct GNUNET_STATISTICS_Handle *stats;
51 * Notification context, simplifies client broadcasts.
53 static struct GNUNET_SERVER_NotificationContext *nc;
56 * All connected hosts.
57 * Place's pub_key_hash -> struct Host
59 static struct GNUNET_CONTAINER_MultiHashMap *hosts;
62 * All connected guests.
63 * Place's pub_key_hash -> struct Guest
65 static struct GNUNET_CONTAINER_MultiHashMap *guests;
68 * Connected guests per place.
69 * Place's pub_key_hash -> Guest's pub_key -> struct Guest
71 static struct GNUNET_CONTAINER_MultiHashMap *place_guests;
75 * Message fragment transmission queue.
77 struct FragmentTransmitQueue
79 struct FragmentTransmitQueue *prev;
80 struct FragmentTransmitQueue *next;
82 struct GNUNET_SERVER_Client *client;
85 * Pointer to the next message part inside the data after this struct.
87 struct GNUNET_MessageHeader *next_part;
95 * @see enum GNUNET_PSYC_MessageState
99 /* Followed by one or more message parts. */
104 * Message transmission queue.
106 struct MessageTransmitQueue
108 struct MessageTransmitQueue *prev;
109 struct MessageTransmitQueue *next;
111 struct FragmentTransmitQueue *frags_head;
112 struct FragmentTransmitQueue *frags_tail;
114 struct GNUNET_SERVER_Client *client;
118 * List of connected clients.
120 struct ClientListItem
122 struct ClientListItem *prev;
123 struct ClientListItem *next;
125 struct GNUNET_SERVER_Client *client;
130 * Common part of the client context for both a host and guest.
134 struct ClientListItem *clients_head;
135 struct ClientListItem *clients_tail;
137 struct MessageTransmitQueue *tmit_msgs_head;
138 struct MessageTransmitQueue *tmit_msgs_tail;
141 * Public key of the channel.
143 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
146 * Hash of @a pub_key.
148 struct GNUNET_HashCode pub_key_hash;
151 * Last message ID received for the place.
152 * 0 if there is no such message.
154 uint64_t max_message_id;
157 * Is this a host (#GNUNET_YES), or guest (#GNUNET_NO)?
162 * Is this place ready to receive messages from client?
163 * #GNUNET_YES or #GNUNET_NO
168 * Is the client disconnected?
169 * #GNUNET_YES or #GNUNET_NO
171 uint8_t is_disconnected;
176 * Client context for a host.
181 * Place struct common for Host and Guest
186 * Private key of the channel.
188 struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
191 * Handle for the multicast origin.
193 struct GNUNET_PSYC_Master *master;
196 * Transmit handle for multicast.
198 struct GNUNET_PSYC_MasterTransmitHandle *tmit_handle;
201 * Incoming join requests.
202 * guest_key -> struct GNUNET_PSYC_JoinHandle *
204 struct GNUNET_CONTAINER_MultiHashMap *join_reqs;
207 * @see enum GNUNET_PSYC_Policy
209 enum GNUNET_PSYC_Policy policy;
214 * Client context for a guest.
219 * Place struct common for Host and Guest.
224 * Private key of the slave.
226 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
229 * Public key of the slave.
231 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
234 * Hash of @a pub_key.
236 struct GNUNET_HashCode pub_key_hash;
239 * Handle for the PSYC slave.
241 struct GNUNET_PSYC_Slave *slave;
244 * Transmit handle for multicast.
246 struct GNUNET_PSYC_SlaveTransmitHandle *tmit_handle;
249 * Peer identity of the origin.
251 struct GNUNET_PeerIdentity origin;
254 * Number of items in @a relays.
256 uint32_t relay_count;
259 * Relays that multicast can use to connect.
261 struct GNUNET_PeerIdentity *relays;
264 * Join request to be transmitted to the master on join.
266 struct GNUNET_MessageHeader *join_req;
269 * Join decision received from PSYC.
271 struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
279 * Place where the client entered.
284 * Message queue for the message currently being transmitted
287 struct MessageTransmitQueue *tmit_msg;
292 psyc_transmit_message (struct Place *plc);
296 * Task run during shutdown.
302 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
306 GNUNET_SERVER_notification_context_destroy (nc);
311 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
318 * Clean up host data structures after a client disconnected.
321 cleanup_host (struct Host *hst)
323 struct Place *plc = &hst->plc;
325 if (NULL != hst->master)
326 GNUNET_PSYC_master_stop (hst->master, GNUNET_NO, NULL, NULL); // FIXME
327 GNUNET_CONTAINER_multihashmap_destroy (hst->join_reqs);
328 GNUNET_CONTAINER_multihashmap_remove (hosts, &plc->pub_key_hash, plc);
333 * Clean up guest data structures after a client disconnected.
336 cleanup_guest (struct Guest *gst)
338 struct Place *plc = &gst->plc;
339 struct GNUNET_CONTAINER_MultiHashMap *
340 plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
342 GNUNET_assert (NULL != plc_gst);
343 GNUNET_CONTAINER_multihashmap_remove (plc_gst, &gst->pub_key_hash, gst);
345 if (0 == GNUNET_CONTAINER_multihashmap_size (plc_gst))
347 GNUNET_CONTAINER_multihashmap_remove (place_guests, &plc->pub_key_hash,
349 GNUNET_CONTAINER_multihashmap_destroy (plc_gst);
351 GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, gst);
353 if (NULL != gst->join_req)
354 GNUNET_free (gst->join_req);
355 if (NULL != gst->relays)
356 GNUNET_free (gst->relays);
357 if (NULL != gst->slave)
358 GNUNET_PSYC_slave_part (gst->slave, GNUNET_NO, NULL, NULL); // FIXME
359 GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, plc);
364 * Clean up place data structures after a client disconnected.
367 cleanup_place (struct Place *plc)
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "%p Cleaning up place %s\n",
371 plc, GNUNET_h2s (&plc->pub_key_hash));
373 (GNUNET_YES == plc->is_host)
374 ? cleanup_host ((struct Host *) plc)
375 : cleanup_guest ((struct Guest *) plc);
381 schedule_cleanup_place (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
388 * Called whenever a client is disconnected.
389 * Frees our resources associated with that client.
391 * @param cls Closure.
392 * @param client Identification of the client.
395 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
401 ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
405 "%p User context is NULL in client_disconnect()\n", ctx);
410 struct Place *plc = ctx->plc;
411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412 "%p Client (%s) disconnected from place %s\n",
413 plc, (GNUNET_YES == plc->is_host) ? "host" : "guest",
414 GNUNET_h2s (&plc->pub_key_hash));
416 struct ClientListItem *cli = plc->clients_head;
419 if (cli->client == client)
421 GNUNET_CONTAINER_DLL_remove (plc->clients_head, plc->clients_tail, cli);
428 if (NULL == plc->clients_head)
429 { /* Last client disconnected. */
430 if (GNUNET_YES != plc->is_disconnected)
432 plc->is_disconnected = GNUNET_YES;
433 if (NULL != plc->tmit_msgs_head)
434 { /* Send pending messages to PSYC before cleanup. */
435 psyc_transmit_message (plc);
447 * Send message to all clients connected to the channel.
450 client_send_msg (const struct Place *plc,
451 const struct GNUNET_MessageHeader *msg)
453 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
454 "%p Sending message to clients.\n", plc);
456 struct ClientListItem *cli = plc->clients_head;
459 GNUNET_SERVER_notification_context_add (nc, cli->client);
460 GNUNET_SERVER_notification_context_unicast (nc, cli->client, msg, GNUNET_NO);
467 * Called after a PSYC master is started.
470 psyc_master_started (void *cls, int result, uint64_t max_message_id)
472 struct Host *hst = cls;
473 struct Place *plc = &hst->plc;
474 plc->max_message_id = max_message_id;
475 plc->is_ready = GNUNET_YES;
477 struct GNUNET_PSYC_CountersResultMessage res;
478 res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
479 res.header.size = htons (sizeof (res));
480 res.result_code = htonl (result - INT32_MIN);
481 res.max_message_id = GNUNET_htonll (plc->max_message_id);
483 client_send_msg (plc, &res.header);
488 * Called when a PSYC master receives a join request.
491 psyc_recv_join_request (void *cls,
492 const struct GNUNET_PSYC_JoinRequestMessage *req,
493 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
494 const struct GNUNET_PSYC_Message *join_msg,
495 struct GNUNET_PSYC_JoinHandle *jh)
497 struct Host *hst = cls;
498 struct GNUNET_HashCode slave_key_hash;
499 GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
500 GNUNET_CONTAINER_multihashmap_put (hst->join_reqs, &slave_key_hash, jh,
501 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
502 client_send_msg (&hst->plc, &req->header);
507 * Called after a PSYC slave is connected.
510 psyc_slave_connected (void *cls, int result, uint64_t max_message_id)
512 struct Guest *gst = cls;
513 struct Place *plc = &gst->plc;
514 plc->max_message_id = max_message_id;
515 plc->is_ready = GNUNET_YES;
517 struct GNUNET_PSYC_CountersResultMessage res;
518 res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
519 res.header.size = htons (sizeof (res));
520 res.result_code = htonl (result - INT32_MIN);
521 res.max_message_id = GNUNET_htonll (plc->max_message_id);
523 client_send_msg (plc, &res.header);
528 * Called when a PSYC slave receives a join decision.
531 psyc_recv_join_dcsn (void *cls,
532 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
534 const struct GNUNET_PSYC_Message *join_msg)
536 struct Guest *gst = cls;
537 client_send_msg (&gst->plc, &dcsn->header);
542 * Called when a PSYC master or slave receives a message.
545 psyc_recv_message (void *cls,
548 const struct GNUNET_PSYC_MessageHeader *msg)
550 struct Place *plc = cls;
551 client_send_msg (plc, &msg->header);
553 /* FIXME: further processing */
558 * Initialize place data structure.
561 place_init (struct Place *plc)
568 * Handle a connecting client entering a place as host.
571 client_recv_host_enter (void *cls, struct GNUNET_SERVER_Client *client,
572 const struct GNUNET_MessageHeader *msg)
574 const struct HostEnterRequest *req
575 = (const struct HostEnterRequest *) msg;
577 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
578 struct GNUNET_HashCode pub_key_hash;
580 GNUNET_CRYPTO_eddsa_key_get_public (&req->place_key, &pub_key);
581 GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
584 hst = GNUNET_CONTAINER_multihashmap_get (hosts, &pub_key_hash);
589 hst = GNUNET_new (struct Host);
590 hst->policy = ntohl (req->policy);
591 hst->priv_key = req->place_key;
592 hst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
595 plc->is_host = GNUNET_YES;
596 plc->pub_key = pub_key;
597 plc->pub_key_hash = pub_key_hash;
600 GNUNET_CONTAINER_multihashmap_put (hosts, &plc->pub_key_hash, plc,
601 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
602 hst->master = GNUNET_PSYC_master_start (cfg, &hst->priv_key, hst->policy,
603 &psyc_master_started,
604 &psyc_recv_join_request,
605 &psyc_recv_message, NULL, hst);
611 struct GNUNET_PSYC_CountersResultMessage res;
612 res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
613 res.header.size = htons (sizeof (res));
614 res.result_code = htonl (GNUNET_OK);
615 res.max_message_id = GNUNET_htonll (plc->max_message_id);
617 GNUNET_SERVER_notification_context_add (nc, client);
618 GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
622 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
623 "%p Client connected as host to place %s.\n",
624 hst, GNUNET_h2s (&plc->pub_key_hash));
626 struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
627 cli->client = client;
628 GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
630 struct Client *ctx = GNUNET_new (struct Client);
632 GNUNET_SERVER_client_set_user_context (client, ctx);
633 GNUNET_SERVER_receive_done (client, GNUNET_OK);
638 * Handle a connecting client entering a place as guest.
641 client_recv_guest_enter (void *cls, struct GNUNET_SERVER_Client *client,
642 const struct GNUNET_MessageHeader *msg)
644 const struct GuestEnterRequest *req
645 = (const struct GuestEnterRequest *) msg;
646 uint16_t req_size = ntohs (req->header.size);
648 struct GNUNET_CRYPTO_EcdsaPublicKey gst_pub_key;
649 struct GNUNET_HashCode pub_key_hash, gst_pub_key_hash;
651 GNUNET_CRYPTO_ecdsa_key_get_public (&req->guest_key, &gst_pub_key);
652 GNUNET_CRYPTO_hash (&gst_pub_key, sizeof (gst_pub_key), &gst_pub_key_hash);
653 GNUNET_CRYPTO_hash (&req->place_key, sizeof (req->place_key), &pub_key_hash);
655 struct GNUNET_CONTAINER_MultiHashMap *
656 plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests, &pub_key_hash);
657 struct Guest *gst = NULL;
662 gst = GNUNET_CONTAINER_multihashmap_get (plc_gst, &gst_pub_key_hash);
664 if (NULL == gst || NULL == gst->slave)
666 gst = GNUNET_new (struct Guest);
667 gst->priv_key = req->guest_key;
668 gst->pub_key = gst_pub_key;
669 gst->pub_key_hash = gst_pub_key_hash;
670 gst->origin = req->origin;
671 gst->relay_count = ntohl (req->relay_count);
673 const struct GNUNET_PeerIdentity *
674 relays = (const struct GNUNET_PeerIdentity *) &req[1];
675 uint16_t relay_size = gst->relay_count * sizeof (*relays);
676 struct GNUNET_PSYC_Message *join_msg = NULL;
677 uint16_t join_msg_size = 0;
679 if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
682 join_msg = (struct GNUNET_PSYC_Message *)
683 (((char *) &req[1]) + relay_size);
684 join_msg_size = ntohs (join_msg->header.size);
686 if (sizeof (*req) + relay_size + join_msg_size != req_size)
688 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
689 "%u + %u + %u != %u\n",
690 sizeof (*req), relay_size, join_msg_size, req_size);
692 GNUNET_SERVER_client_disconnect (client);
696 if (0 < gst->relay_count)
698 gst->relays = GNUNET_malloc (relay_size);
699 memcpy (gst->relays, &req[1], relay_size);
703 plc->is_host = GNUNET_NO;
704 plc->pub_key = req->place_key;
705 plc->pub_key_hash = pub_key_hash;
710 plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
711 (void) GNUNET_CONTAINER_multihashmap_put (place_guests, &plc->pub_key_hash, plc_gst,
712 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
714 (void) GNUNET_CONTAINER_multihashmap_put (plc_gst, &gst->pub_key_hash, gst,
715 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
716 (void) GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, gst,
717 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
719 = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &gst->priv_key,
720 &gst->origin, gst->relay_count, gst->relays,
721 &psyc_recv_message, NULL, &psyc_slave_connected,
722 &psyc_recv_join_dcsn, gst, join_msg);
728 struct GNUNET_PSYC_CountersResultMessage res;
729 res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
730 res.header.size = htons (sizeof (res));
731 res.result_code = htonl (GNUNET_OK);
732 res.max_message_id = GNUNET_htonll (plc->max_message_id);
734 GNUNET_SERVER_notification_context_add (nc, client);
735 GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
737 if (NULL != gst->join_dcsn)
739 GNUNET_SERVER_notification_context_add (nc, client);
740 GNUNET_SERVER_notification_context_unicast (nc, client,
741 &gst->join_dcsn->header,
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747 "%p Client connected as guest to place %s.\n",
748 gst, GNUNET_h2s (&plc->pub_key_hash));
750 struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
751 cli->client = client;
752 GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
754 struct Client *ctx = GNUNET_new (struct Client);
756 GNUNET_SERVER_client_set_user_context (client, ctx);
757 GNUNET_SERVER_receive_done (client, GNUNET_OK);
761 struct JoinDecisionClosure
764 struct GNUNET_PSYC_Message *msg;
769 * Iterator callback for responding to join requests.
772 psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
775 struct JoinDecisionClosure *jcls = cls;
776 struct GNUNET_PSYC_JoinHandle *jh = value;
778 GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
784 * Handle an entry decision from a host client.
787 client_recv_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
788 const struct GNUNET_MessageHeader *msg)
791 ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
792 GNUNET_assert (NULL != ctx);
793 struct Place *plc = ctx->plc;
794 GNUNET_assert (GNUNET_YES == plc->is_host);
795 struct Host *hst = (struct Host *) plc;
797 struct GNUNET_PSYC_JoinDecisionMessage *
798 dcsn = (struct GNUNET_PSYC_JoinDecisionMessage *) msg;
799 struct JoinDecisionClosure jcls;
800 jcls.is_admitted = ntohl (dcsn->is_admitted);
802 = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
803 ? (struct GNUNET_PSYC_Message *) &dcsn[1]
806 struct GNUNET_HashCode slave_key_hash;
807 GNUNET_CRYPTO_hash (&dcsn->slave_key, sizeof (dcsn->slave_key),
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
811 "%p Got join decision (%d) from client for place %s..\n",
812 hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
814 "%p ..and slave %s.\n",
815 hst, GNUNET_h2s (&slave_key_hash));
817 GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_key_hash,
818 &psyc_send_join_decision, &jcls);
819 GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_key_hash);
820 GNUNET_SERVER_receive_done (client, GNUNET_OK);
825 * Send acknowledgement to a client.
827 * Sent after a message fragment has been passed on to multicast.
829 * @param plc The place struct for the client.
832 send_message_ack (struct Place *plc, struct GNUNET_SERVER_Client *client)
834 struct GNUNET_MessageHeader res;
835 res.size = htons (sizeof (res));
836 res.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
838 GNUNET_SERVER_notification_context_add (nc, client);
839 GNUNET_SERVER_notification_context_unicast (nc, client, &res, GNUNET_NO);
844 * Proceed to the next message part in the transmission queue.
847 * Place where the transmission is going on.
849 * Currently transmitted message.
851 * Currently transmitted message fragment.
853 * @return @a tmit_frag, or NULL if reached the end of fragment.
855 static struct FragmentTransmitQueue *
856 psyc_transmit_queue_next_part (struct Place *plc,
857 struct MessageTransmitQueue *tmit_msg,
858 struct FragmentTransmitQueue *tmit_frag)
860 uint16_t psize = ntohs (tmit_frag->next_part->size);
861 if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
865 = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + psize);
867 else /* Reached end of current fragment. */
869 if (NULL != tmit_frag->client)
870 send_message_ack (plc, tmit_frag->client);
871 GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
872 GNUNET_free (tmit_frag);
880 * Proceed to next message in transmission queue.
883 * Place where the transmission is going on.
885 * Currently transmitted message.
887 * @return The next message in queue, or NULL if queue is empty.
889 static struct MessageTransmitQueue *
890 psyc_transmit_queue_next_msg (struct Place *plc,
891 struct MessageTransmitQueue *tmit_msg)
893 GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
894 GNUNET_free (tmit_msg);
895 return plc->tmit_msgs_head;
900 * Callback for data transmission to PSYC.
903 psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
905 struct Place *plc = cls;
906 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
907 GNUNET_assert (NULL != tmit_msg);
908 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
909 if (NULL == tmit_frag)
910 { /* Rest of the message have not arrived yet, pause transmission */
914 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
917 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
918 "%p psyc_transmit_notify_data: nothing to send.\n", plc);
923 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924 "%p psyc_transmit_notify_data()\n", plc);
925 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
927 uint16_t ptype = ntohs (pmsg->type);
928 uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
933 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
934 if (*data_size < pdata_size)
936 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
937 "%p psyc_transmit_notify_data: buffer size too small for data.\n", plc);
941 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
942 "%p psyc_transmit_notify_data: sending %u bytes.\n",
945 *data_size = pdata_size;
946 memcpy (data, &pmsg[1], *data_size);
950 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
955 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
961 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
962 "%p psyc_transmit_notify_data: unexpected message part of type %u.\n",
967 if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
970 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
971 plc->is_disconnected = GNUNET_YES;
972 GNUNET_SERVER_client_disconnect (tmit_frag->client);
973 GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
978 tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
979 if (NULL != tmit_frag)
981 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
982 ptype = ntohs (pmsg->type);
985 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
988 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
994 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
995 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
996 tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1000 if (NULL == tmit_msg->frags_head
1001 && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1002 { /* Reached end of current message. */
1003 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1007 if (ret != GNUNET_NO)
1009 if (NULL != tmit_msg)
1011 psyc_transmit_message (plc);
1013 else if (GNUNET_YES == plc->is_disconnected)
1015 /* FIXME: handle partial message (when still in_transmit) */
1016 cleanup_place (plc);
1024 * Callback for modifier transmission to PSYC.
1027 psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1028 uint8_t *oper, uint32_t *full_value_size)
1030 struct Place *plc = cls;
1031 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1032 GNUNET_assert (NULL != tmit_msg);
1033 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1034 if (NULL == tmit_frag)
1035 { /* Rest of the message have not arrived yet, pause transmission */
1039 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1042 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043 "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
1048 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1049 "%p psyc_transmit_notify_mod()\n", plc);
1050 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
1052 uint16_t ptype = ntohs (pmsg->type);
1057 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1061 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1062 "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
1063 ret = GNUNET_SYSERR;
1066 struct GNUNET_PSYC_MessageModifier *
1067 pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
1068 uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
1070 if (*data_size < mod_size)
1072 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1073 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1078 *full_value_size = ntohl (pmod->value_size);
1080 *data_size = mod_size;
1081 memcpy (data, &pmod[1], mod_size);
1086 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1090 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1091 "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
1092 ret = GNUNET_SYSERR;
1095 uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
1096 if (*data_size < mod_size)
1098 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1099 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1104 "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, mod_size);
1106 *data_size = mod_size;
1107 memcpy (data, &pmsg[1], *data_size);
1112 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1113 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1114 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1120 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1121 "%p psyc_transmit_notify_mod: unexpected message part of type %u.\n",
1123 ret = GNUNET_SYSERR;
1126 if (GNUNET_SYSERR == ret)
1129 ret = GNUNET_SYSERR;
1130 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1131 plc->is_disconnected = GNUNET_YES;
1132 GNUNET_SERVER_client_disconnect (tmit_frag->client);
1133 GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
1137 if (GNUNET_YES != ret)
1138 psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1140 if (NULL == tmit_msg->frags_head
1141 && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1142 { /* Reached end of current message. */
1143 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1150 * Callback for data transmission from a host to PSYC.
1153 host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1155 int ret = psyc_transmit_notify_data (cls, data_size, data);
1157 if (GNUNET_NO != ret)
1159 struct Host *hst = cls;
1160 hst->tmit_handle = NULL;
1167 * Callback for the transmit functions of multicast.
1170 guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1172 int ret = psyc_transmit_notify_data (cls, data_size, data);
1174 if (GNUNET_NO != ret)
1176 struct Guest *gst = cls;
1177 gst->tmit_handle = NULL;
1184 * Callback for modifier transmission from a host to PSYC.
1187 host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1188 uint8_t *oper, uint32_t *full_value_size)
1190 int ret = psyc_transmit_notify_mod (cls, data_size, data,
1191 oper, full_value_size);
1192 if (GNUNET_SYSERR == ret)
1194 struct Host *hst = cls;
1195 hst->tmit_handle = NULL;
1202 * Callback for modifier transmission from a guest to PSYC.
1205 guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1206 uint8_t *oper, uint32_t *full_value_size)
1208 int ret = psyc_transmit_notify_mod (cls, data_size, data,
1209 oper, full_value_size);
1210 if (GNUNET_SYSERR == ret)
1212 struct Guest *gst = cls;
1213 gst->tmit_handle = NULL;
1220 * Get method part of next message from transmission queue.
1223 * Next item in message transmission queue.
1225 * The message method is returned here.
1227 * @return #GNUNET_OK on success
1228 * #GNUNET_NO if there are no more messages in queue.
1229 * #GNUNET_SYSERR if the next message is malformed.
1232 psyc_transmit_queue_next_method (struct Place *plc,
1233 struct GNUNET_PSYC_MessageMethod **pmeth)
1235 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1236 if (NULL == tmit_msg)
1239 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1240 if (NULL == tmit_frag)
1246 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1248 || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
1250 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1251 "%p psyc_transmit_queue_next_method: unexpected message part of type %u.\n",
1252 plc, ntohs (pmsg->type));
1254 return GNUNET_SYSERR;
1257 uint16_t psize = ntohs (pmsg->size);
1258 *pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
1259 if (psize < sizeof (**pmeth) + 1 || '\0' != *((char *) *pmeth + psize - 1))
1261 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1262 "%p psyc_transmit_queue_next_method: invalid method name.\n",
1263 plc, ntohs (pmsg->type));
1264 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1265 "%u <= %u || NUL != %u\n",
1266 sizeof (**pmeth), psize, *((char *) *pmeth + psize - 1));
1268 return GNUNET_SYSERR;
1271 psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1277 * Transmit the next message in queue from the host to the PSYC channel.
1280 psyc_master_transmit_message (struct Host *hst)
1283 if (NULL == hst->tmit_handle)
1285 struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1286 int ret = psyc_transmit_queue_next_method (&hst->plc, &pmeth);
1287 if (GNUNET_OK != ret)
1291 = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
1292 &host_transmit_notify_mod,
1293 &host_transmit_notify_data, hst,
1298 GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
1305 * Transmit the next message in queue from a guest to the PSYC channel.
1308 psyc_slave_transmit_message (struct Guest *gst)
1310 if (NULL == gst->tmit_handle)
1312 struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1313 int ret = psyc_transmit_queue_next_method (&gst->plc, &pmeth);
1314 if (GNUNET_OK != ret)
1318 = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
1319 &guest_transmit_notify_mod,
1320 &guest_transmit_notify_data, gst,
1325 GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
1332 * Transmit a message to PSYC.
1335 psyc_transmit_message (struct Place *plc)
1339 ? psyc_master_transmit_message ((struct Host *) plc)
1340 : psyc_slave_transmit_message ((struct Guest *) plc);
1345 * Queue message parts for sending to PSYC.
1347 * @param plc Place to send to.
1348 * @param client Client the message originates from.
1349 * @param data_size Size of @a data.
1350 * @param data Concatenated message parts.
1351 * @param first_ptype First message part type in @a data.
1352 * @param last_ptype Last message part type in @a data.
1354 static struct MessageTransmitQueue *
1355 psyc_transmit_queue_message (struct Place *plc,
1356 struct GNUNET_SERVER_Client *client,
1359 uint16_t first_ptype, uint16_t last_ptype,
1360 struct MessageTransmitQueue *tmit_msg)
1362 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
1364 tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
1365 GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
1367 else if (NULL == tmit_msg)
1372 struct FragmentTransmitQueue *
1373 tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
1374 memcpy (&tmit_frag[1], data, data_size);
1375 tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
1376 tmit_frag->client = client;
1377 tmit_frag->size = data_size;
1379 GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
1380 tmit_msg->client = client;
1386 * Cancel transmission of current message to PSYC.
1388 * @param plc Place to send to.
1389 * @param client Client the message originates from.
1392 psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVER_Client *client)
1394 uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
1396 struct GNUNET_MessageHeader msg;
1397 msg.size = htons (sizeof (msg));
1398 msg.type = htons (type);
1400 psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, NULL);
1401 psyc_transmit_message (plc);
1403 /* FIXME: cleanup */
1408 * Handle an incoming message from a client, to be transmitted to the place.
1411 client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
1412 const struct GNUNET_MessageHeader *msg)
1415 ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
1416 GNUNET_assert (NULL != ctx);
1417 struct Place *plc = ctx->plc;
1418 int ret = GNUNET_SYSERR;
1420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1421 "%p Received message from client.\n", plc);
1422 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
1424 if (GNUNET_YES != plc->is_ready)
1426 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1427 "%p Place is not ready yet, disconnecting client.\n", plc);
1429 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1433 uint16_t size = ntohs (msg->size);
1434 uint16_t psize = size - sizeof (*msg);
1435 if (psize < sizeof (struct GNUNET_MessageHeader)
1436 || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
1438 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1439 "%p Received message with invalid payload size (%u) from client.\n",
1442 psyc_transmit_cancel (plc, client);
1443 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1447 uint16_t first_ptype = 0, last_ptype = 0;
1449 == GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
1450 &first_ptype, &last_ptype))
1452 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1453 "%p Received invalid message part from client.\n", plc);
1455 psyc_transmit_cancel (plc, client);
1456 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1460 "%p Received message with first part type %u and last part type %u.\n",
1461 plc, first_ptype, last_ptype);
1464 = psyc_transmit_queue_message (plc, client, psize, &msg[1],
1465 first_ptype, last_ptype, ctx->tmit_msg);
1466 if (NULL != ctx->tmit_msg)
1468 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
1469 ctx->tmit_msg = NULL;
1470 ret = psyc_transmit_message (plc);
1473 if (GNUNET_OK != ret)
1475 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1476 "%p Received invalid message part from client.\n", plc);
1478 psyc_transmit_cancel (plc, client);
1479 ret = GNUNET_SYSERR;
1481 GNUNET_SERVER_receive_done (client, ret);
1486 * Initialize the PSYC service.
1488 * @param cls Closure.
1489 * @param server The initialized server.
1490 * @param c Configuration to use.
1493 run (void *cls, struct GNUNET_SERVER_Handle *server,
1494 const struct GNUNET_CONFIGURATION_Handle *c)
1496 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1497 { &client_recv_host_enter, NULL,
1498 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER, 0 },
1500 { &client_recv_guest_enter, NULL,
1501 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER, 0 },
1503 { &client_recv_join_decision, NULL,
1504 GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, 0 },
1506 { &client_recv_psyc_message, NULL,
1507 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 }
1511 stats = GNUNET_STATISTICS_create ("social", cfg);
1512 hosts = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1513 guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1514 place_guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1515 nc = GNUNET_SERVER_notification_context_create (server, 1);
1516 GNUNET_SERVER_add_handlers (server, handlers);
1517 GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
1518 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1519 &shutdown_task, NULL);
1524 * The main function for the service.
1526 * @param argc number of arguments from the command line
1527 * @param argv command line arguments
1528 * @return 0 ok, 1 on error
1531 main (int argc, char *const *argv)
1533 return (GNUNET_OK ==
1534 GNUNET_SERVICE_run (argc, argv, "social",
1535 GNUNET_SERVICE_OPTION_NONE,
1536 &run, NULL)) ? 0 : 1;
1539 /* end of gnunet-service-social.c */