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.
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);
695 if (0 < gst->relay_count)
697 gst->relays = GNUNET_malloc (relay_size);
698 memcpy (gst->relays, &req[1], relay_size);
702 plc->is_host = GNUNET_NO;
703 plc->pub_key = req->place_key;
704 plc->pub_key_hash = pub_key_hash;
709 plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
710 GNUNET_CONTAINER_multihashmap_put (place_guests, &plc->pub_key_hash, plc_gst,
711 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
713 GNUNET_CONTAINER_multihashmap_put (plc_gst, &gst->pub_key_hash, plc,
714 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
715 GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, plc,
716 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
718 = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &gst->priv_key,
719 &gst->origin, gst->relay_count, gst->relays,
720 &psyc_recv_message, NULL, &psyc_slave_connected,
721 &psyc_recv_join_dcsn, gst, join_msg);
727 struct GNUNET_PSYC_CountersResultMessage res;
728 res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
729 res.header.size = htons (sizeof (res));
730 res.result_code = htonl (GNUNET_OK);
731 res.max_message_id = GNUNET_htonll (plc->max_message_id);
733 GNUNET_SERVER_notification_context_add (nc, client);
734 GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
736 if (NULL != gst->join_dcsn)
738 GNUNET_SERVER_notification_context_add (nc, client);
739 GNUNET_SERVER_notification_context_unicast (nc, client,
740 &gst->join_dcsn->header,
745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
746 "%p Client connected as guest to place %s.\n",
747 gst, GNUNET_h2s (&plc->pub_key_hash));
749 struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
750 cli->client = client;
751 GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
753 struct Client *ctx = GNUNET_new (struct Client);
755 GNUNET_SERVER_client_set_user_context (client, ctx);
756 GNUNET_SERVER_receive_done (client, GNUNET_OK);
760 struct JoinDecisionClosure
763 struct GNUNET_PSYC_Message *msg;
768 * Iterator callback for responding to join requests.
771 psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
774 struct JoinDecisionClosure *jcls = cls;
775 struct GNUNET_PSYC_JoinHandle *jh = value;
777 GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
783 * Handle an entry decision from a host client.
786 client_recv_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
787 const struct GNUNET_MessageHeader *msg)
790 ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
791 GNUNET_assert (NULL != ctx);
792 struct Place *plc = ctx->plc;
793 GNUNET_assert (GNUNET_YES == plc->is_host);
794 struct Host *hst = (struct Host *) plc;
796 struct GNUNET_PSYC_JoinDecisionMessage *
797 dcsn = (struct GNUNET_PSYC_JoinDecisionMessage *) msg;
798 struct JoinDecisionClosure jcls;
799 jcls.is_admitted = ntohl (dcsn->is_admitted);
801 = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
802 ? (struct GNUNET_PSYC_Message *) &dcsn[1]
805 struct GNUNET_HashCode slave_key_hash;
806 GNUNET_CRYPTO_hash (&dcsn->slave_key, sizeof (dcsn->slave_key),
809 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
810 "%p Got join decision (%d) from client for place %s..\n",
811 hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
812 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
813 "%p ..and slave %s.\n",
814 hst, GNUNET_h2s (&slave_key_hash));
816 GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_key_hash,
817 &psyc_send_join_decision, &jcls);
818 GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_key_hash);
819 GNUNET_SERVER_receive_done (client, GNUNET_OK);
824 * Send acknowledgement to a client.
826 * Sent after a message fragment has been passed on to multicast.
828 * @param plc The place struct for the client.
831 send_message_ack (struct Place *plc, struct GNUNET_SERVER_Client *client)
833 struct GNUNET_MessageHeader res;
834 res.size = htons (sizeof (res));
835 res.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
837 GNUNET_SERVER_notification_context_add (nc, client);
838 GNUNET_SERVER_notification_context_unicast (nc, client, &res, GNUNET_NO);
843 * Proceed to the next message part in the transmission queue.
846 * Place where the transmission is going on.
848 * Currently transmitted message.
850 * Currently transmitted message fragment.
852 * @return @a tmit_frag, or NULL if reached the end of fragment.
854 static struct FragmentTransmitQueue *
855 psyc_transmit_queue_next_part (struct Place *plc,
856 struct MessageTransmitQueue *tmit_msg,
857 struct FragmentTransmitQueue *tmit_frag)
859 uint16_t psize = ntohs (tmit_frag->next_part->size);
860 if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
864 = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + psize);
866 else /* Reached end of current fragment. */
868 if (NULL != tmit_frag->client)
869 send_message_ack (plc, tmit_frag->client);
870 GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
871 GNUNET_free (tmit_frag);
879 * Proceed to next message in transmission queue.
882 * Place where the transmission is going on.
884 * Currently transmitted message.
886 * @return The next message in queue, or NULL if queue is empty.
888 static struct MessageTransmitQueue *
889 psyc_transmit_queue_next_msg (struct Place *plc,
890 struct MessageTransmitQueue *tmit_msg)
892 GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
893 GNUNET_free (tmit_msg);
894 return plc->tmit_msgs_head;
899 * Callback for data transmission to PSYC.
902 psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
904 struct Place *plc = cls;
905 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
906 GNUNET_assert (NULL != tmit_msg);
907 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
908 if (NULL == tmit_frag)
909 { /* Rest of the message have not arrived yet, pause transmission */
913 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
916 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
917 "%p psyc_transmit_notify_data: nothing to send.\n", plc);
922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923 "%p psyc_transmit_notify_data()\n", plc);
924 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
926 uint16_t ptype = ntohs (pmsg->type);
927 uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
932 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
933 if (*data_size < pdata_size)
935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
936 "%p psyc_transmit_notify_data: buffer size too small for data.\n", plc);
940 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
941 "%p psyc_transmit_notify_data: sending %u bytes.\n",
944 *data_size = pdata_size;
945 memcpy (data, &pmsg[1], *data_size);
949 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
954 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
960 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
961 "%p psyc_transmit_notify_data: unexpected message part of type %u.\n",
966 if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
969 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
970 plc->is_disconnected = GNUNET_YES;
971 GNUNET_SERVER_client_disconnect (tmit_frag->client);
972 GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
977 tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
978 if (NULL != tmit_frag)
980 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
981 ptype = ntohs (pmsg->type);
984 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
987 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
993 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
994 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
995 tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
999 if (NULL == tmit_msg->frags_head
1000 && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1001 { /* Reached end of current message. */
1002 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1006 if (ret != GNUNET_NO)
1008 if (NULL != tmit_msg)
1010 psyc_transmit_message (plc);
1012 else if (GNUNET_YES == plc->is_disconnected)
1014 /* FIXME: handle partial message (when still in_transmit) */
1015 cleanup_place (plc);
1023 * Callback for modifier transmission to PSYC.
1026 psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1027 uint8_t *oper, uint32_t *full_value_size)
1029 struct Place *plc = cls;
1030 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1031 GNUNET_assert (NULL != tmit_msg);
1032 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1033 if (NULL == tmit_frag)
1034 { /* Rest of the message have not arrived yet, pause transmission */
1038 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1041 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1042 "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
1047 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1048 "%p psyc_transmit_notify_mod()\n", plc);
1049 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
1051 uint16_t ptype = ntohs (pmsg->type);
1056 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1060 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1061 "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
1062 ret = GNUNET_SYSERR;
1065 struct GNUNET_PSYC_MessageModifier *
1066 pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
1067 uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
1069 if (*data_size < mod_size)
1071 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1072 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1077 *full_value_size = ntohl (pmod->value_size);
1079 *data_size = mod_size;
1080 memcpy (data, &pmod[1], mod_size);
1085 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1089 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1090 "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
1091 ret = GNUNET_SYSERR;
1094 uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
1095 if (*data_size < mod_size)
1097 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1098 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1102 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1103 "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, mod_size);
1105 *data_size = mod_size;
1106 memcpy (data, &pmsg[1], *data_size);
1111 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1112 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1113 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1119 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1120 "%p psyc_transmit_notify_mod: unexpected message part of type %u.\n",
1122 ret = GNUNET_SYSERR;
1125 if (GNUNET_SYSERR == ret)
1128 ret = GNUNET_SYSERR;
1129 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1130 plc->is_disconnected = GNUNET_YES;
1131 GNUNET_SERVER_client_disconnect (tmit_frag->client);
1132 GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
1136 if (GNUNET_YES != ret)
1137 psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1139 if (NULL == tmit_msg->frags_head
1140 && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1141 { /* Reached end of current message. */
1142 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1149 * Callback for data transmission from a host to PSYC.
1152 host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1154 int ret = psyc_transmit_notify_data (cls, data_size, data);
1156 if (GNUNET_NO != ret)
1158 struct Host *hst = cls;
1159 hst->tmit_handle = NULL;
1166 * Callback for the transmit functions of multicast.
1169 guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1171 int ret = psyc_transmit_notify_data (cls, data_size, data);
1173 if (GNUNET_NO != ret)
1175 struct Guest *gst = cls;
1176 gst->tmit_handle = NULL;
1183 * Callback for modifier transmission from a host to PSYC.
1186 host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1187 uint8_t *oper, uint32_t *full_value_size)
1189 int ret = psyc_transmit_notify_mod (cls, data_size, data,
1190 oper, full_value_size);
1191 if (GNUNET_SYSERR == ret)
1193 struct Host *hst = cls;
1194 hst->tmit_handle = NULL;
1201 * Callback for modifier transmission from a guest to PSYC.
1204 guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1205 uint8_t *oper, uint32_t *full_value_size)
1207 int ret = psyc_transmit_notify_mod (cls, data_size, data,
1208 oper, full_value_size);
1209 if (GNUNET_SYSERR == ret)
1211 struct Guest *gst = cls;
1212 gst->tmit_handle = NULL;
1219 * Get method part of next message from transmission queue.
1222 * Next item in message transmission queue.
1224 * The message method is returned here.
1226 * @return #GNUNET_OK on success
1227 * #GNUNET_NO if there are no more messages in queue.
1228 * #GNUNET_SYSERR if the next message is malformed.
1231 psyc_transmit_queue_next_method (struct Place *plc,
1232 struct GNUNET_PSYC_MessageMethod **pmeth)
1234 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1235 if (NULL == tmit_msg)
1238 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1239 if (NULL == tmit_frag)
1245 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1247 || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
1249 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1250 "%p psyc_transmit_queue_next_method: unexpected message part of type %u.\n",
1251 plc, ntohs (pmsg->type));
1253 return GNUNET_SYSERR;
1256 uint16_t psize = ntohs (pmsg->size);
1257 *pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
1258 if (psize < sizeof (**pmeth) + 1 || '\0' != *((char *) *pmeth + psize - 1))
1260 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1261 "%p psyc_transmit_queue_next_method: invalid method name.\n",
1262 plc, ntohs (pmsg->type));
1263 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1264 "%u <= %u || NUL != %u\n",
1265 sizeof (**pmeth), psize, *((char *) *pmeth + psize - 1));
1267 return GNUNET_SYSERR;
1270 psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1276 * Transmit the next message in queue from the host to the PSYC channel.
1279 psyc_master_transmit_message (struct Host *hst)
1282 if (NULL == hst->tmit_handle)
1284 struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1285 int ret = psyc_transmit_queue_next_method (&hst->plc, &pmeth);
1286 if (GNUNET_OK != ret)
1290 = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
1291 &host_transmit_notify_mod,
1292 &host_transmit_notify_data, hst,
1297 GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
1304 * Transmit the next message in queue from a guest to the PSYC channel.
1307 psyc_slave_transmit_message (struct Guest *gst)
1309 if (NULL == gst->tmit_handle)
1311 struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1312 int ret = psyc_transmit_queue_next_method (&gst->plc, &pmeth);
1313 if (GNUNET_OK != ret)
1317 = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
1318 &guest_transmit_notify_mod,
1319 &guest_transmit_notify_data, gst,
1324 GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
1331 * Transmit a message to PSYC.
1334 psyc_transmit_message (struct Place *plc)
1338 ? psyc_master_transmit_message ((struct Host *) plc)
1339 : psyc_slave_transmit_message ((struct Guest *) plc);
1344 * Queue message parts for sending to PSYC.
1346 * @param plc Place to send to.
1347 * @param client Client the message originates from.
1348 * @param data_size Size of @a data.
1349 * @param data Concatenated message parts.
1350 * @param first_ptype First message part type in @a data.
1351 * @param last_ptype Last message part type in @a data.
1353 static struct MessageTransmitQueue *
1354 psyc_transmit_queue_message (struct Place *plc,
1355 struct GNUNET_SERVER_Client *client,
1358 uint16_t first_ptype, uint16_t last_ptype,
1359 struct MessageTransmitQueue *tmit_msg)
1361 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
1363 tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
1364 GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
1366 else if (NULL == tmit_msg)
1371 struct FragmentTransmitQueue *
1372 tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
1373 memcpy (&tmit_frag[1], data, data_size);
1374 tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
1375 tmit_frag->client = client;
1376 tmit_frag->size = data_size;
1378 GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
1379 tmit_msg->client = client;
1385 * Cancel transmission of current message to PSYC.
1387 * @param plc Place to send to.
1388 * @param client Client the message originates from.
1391 psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVER_Client *client)
1393 uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
1395 struct GNUNET_MessageHeader msg;
1396 msg.size = htons (sizeof (msg));
1397 msg.type = htons (type);
1399 psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, NULL);
1400 psyc_transmit_message (plc);
1402 /* FIXME: cleanup */
1407 * Handle an incoming message from a client, to be transmitted to the place.
1410 client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
1411 const struct GNUNET_MessageHeader *msg)
1414 ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
1415 GNUNET_assert (NULL != ctx);
1416 struct Place *plc = ctx->plc;
1417 int ret = GNUNET_SYSERR;
1419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1420 "%p Received message from client.\n", plc);
1421 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
1423 if (GNUNET_YES != plc->is_ready)
1425 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1426 "%p Place is not ready yet, disconnecting client.\n", plc);
1428 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1432 uint16_t size = ntohs (msg->size);
1433 uint16_t psize = size - sizeof (*msg);
1434 if (psize < sizeof (struct GNUNET_MessageHeader)
1435 || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
1437 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1438 "%p Received message with invalid payload size (%u) from client.\n",
1441 psyc_transmit_cancel (plc, client);
1442 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1446 uint16_t first_ptype = 0, last_ptype = 0;
1448 == GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
1449 &first_ptype, &last_ptype))
1451 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1452 "%p Received invalid message part from client.\n", plc);
1454 psyc_transmit_cancel (plc, client);
1455 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459 "%p Received message with first part type %u and last part type %u.\n",
1460 plc, first_ptype, last_ptype);
1463 = psyc_transmit_queue_message (plc, client, psize, &msg[1],
1464 first_ptype, last_ptype, ctx->tmit_msg);
1465 if (NULL != ctx->tmit_msg)
1467 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
1468 ctx->tmit_msg = NULL;
1469 ret = psyc_transmit_message (plc);
1472 if (GNUNET_OK != ret)
1474 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1475 "%p Received invalid message part from client.\n", plc);
1477 psyc_transmit_cancel (plc, client);
1478 ret = GNUNET_SYSERR;
1480 GNUNET_SERVER_receive_done (client, ret);
1485 * Initialize the PSYC service.
1487 * @param cls Closure.
1488 * @param server The initialized server.
1489 * @param c Configuration to use.
1492 run (void *cls, struct GNUNET_SERVER_Handle *server,
1493 const struct GNUNET_CONFIGURATION_Handle *c)
1495 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1496 { &client_recv_host_enter, NULL,
1497 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER, 0 },
1499 { &client_recv_guest_enter, NULL,
1500 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER, 0 },
1502 { &client_recv_join_decision, NULL,
1503 GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, 0 },
1505 { &client_recv_psyc_message, NULL,
1506 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 }
1510 stats = GNUNET_STATISTICS_create ("social", cfg);
1511 hosts = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1512 guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1513 place_guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1514 nc = GNUNET_SERVER_notification_context_create (server, 1);
1515 GNUNET_SERVER_add_handlers (server, handlers);
1516 GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
1517 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1518 &shutdown_task, NULL);
1523 * The main function for the service.
1525 * @param argc number of arguments from the command line
1526 * @param argv command line arguments
1527 * @return 0 ok, 1 on error
1530 main (int argc, char *const *argv)
1532 return (GNUNET_OK ==
1533 GNUNET_SERVICE_run (argc, argv, "social",
1534 GNUNET_SERVICE_OPTION_NONE,
1535 &run, NULL)) ? 0 : 1;
1538 /* end of gnunet-service-social.c */