-improve indentation, reduce duplication of PIDs in core's neighbour map
[oweals/gnunet.git] / src / social / gnunet-service-social.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2013 Christian Grothoff (and other contributing authors)
4  *
5  * GNUnet is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published
7  * by the Free Software Foundation; either version 3, or (at your
8  * option) any later version.
9  *
10  * GNUnet is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with GNUnet; see the file COPYING.  If not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file social/gnunet-service-social.c
23  * @brief Social service
24  * @author Gabor X Toth
25  */
26
27 #include <inttypes.h>
28
29 #include "platform.h"
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"
37 #include "social.h"
38
39
40 /**
41  * Handle to our current configuration.
42  */
43 static const struct GNUNET_CONFIGURATION_Handle *cfg;
44
45 /**
46  * Handle to the statistics service.
47  */
48 static struct GNUNET_STATISTICS_Handle *stats;
49
50 /**
51  * Notification context, simplifies client broadcasts.
52  */
53 static struct GNUNET_SERVER_NotificationContext *nc;
54
55 /**
56  * All connected hosts.
57  * Place's pub_key_hash -> struct Host
58  */
59 static struct GNUNET_CONTAINER_MultiHashMap *hosts;
60
61 /**
62  * All connected guests.
63  * Place's pub_key_hash -> struct Guest
64  */
65 static struct GNUNET_CONTAINER_MultiHashMap *guests;
66
67 /**
68  * Connected guests per place.
69  * Place's pub_key_hash -> Guest's pub_key -> struct Guest
70  */
71 static struct GNUNET_CONTAINER_MultiHashMap *place_guests;
72
73
74 /**
75  * Message fragment transmission queue.
76  */
77 struct FragmentTransmitQueue
78 {
79   struct FragmentTransmitQueue *prev;
80   struct FragmentTransmitQueue *next;
81
82   struct GNUNET_SERVER_Client *client;
83
84   /**
85    * Pointer to the next message part inside the data after this struct.
86    */
87   struct GNUNET_MessageHeader *next_part;
88
89   /**
90    * Size of message.
91    */
92   uint16_t size;
93
94   /**
95    * @see enum GNUNET_PSYC_MessageState
96    */
97   uint8_t state;
98
99   /* Followed by one or more message parts. */
100 };
101
102
103 /**
104  * Message transmission queue.
105  */
106 struct MessageTransmitQueue
107 {
108   struct MessageTransmitQueue *prev;
109   struct MessageTransmitQueue *next;
110
111   struct FragmentTransmitQueue *frags_head;
112   struct FragmentTransmitQueue *frags_tail;
113
114   struct GNUNET_SERVER_Client *client;
115 };
116
117 /**
118  * List of connected clients.
119  */
120 struct ClientListItem
121 {
122   struct ClientListItem *prev;
123   struct ClientListItem *next;
124
125   struct GNUNET_SERVER_Client *client;
126 };
127
128
129 /**
130  * Common part of the client context for both a host and guest.
131  */
132 struct Place
133 {
134   struct ClientListItem *clients_head;
135   struct ClientListItem *clients_tail;
136
137   struct MessageTransmitQueue *tmit_msgs_head;
138   struct MessageTransmitQueue *tmit_msgs_tail;
139
140   struct GNUNET_PSYC_Channel *channel;
141
142   /**
143    * Public key of the channel.
144    */
145   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
146
147   /**
148    * Hash of @a pub_key.
149    */
150   struct GNUNET_HashCode pub_key_hash;
151
152   /**
153    * Last message ID received for the place.
154    * 0 if there is no such message.
155    */
156   uint64_t max_message_id;
157
158   /**
159    * Is this a host (#GNUNET_YES), or guest (#GNUNET_NO)?
160    */
161   uint8_t is_host;
162
163   /**
164    * Is this place ready to receive messages from client?
165    * #GNUNET_YES or #GNUNET_NO
166    */
167   uint8_t is_ready;
168
169   /**
170    * Is the client disconnected?
171    * #GNUNET_YES or #GNUNET_NO
172    */
173   uint8_t is_disconnected;
174 };
175
176
177 /**
178  * Client context for a host.
179  */
180 struct Host
181 {
182   /**
183    * Place struct common for Host and Guest
184    */
185   struct Place plc;
186
187   /**
188    * Private key of the channel.
189    */
190   struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
191
192   /**
193    * Handle for the multicast origin.
194    */
195   struct GNUNET_PSYC_Master *master;
196
197   /**
198    * Transmit handle for multicast.
199    */
200   struct GNUNET_PSYC_MasterTransmitHandle *tmit_handle;
201
202   /**
203    * Incoming join requests.
204    * guest_key -> struct GNUNET_PSYC_JoinHandle *
205    */
206   struct GNUNET_CONTAINER_MultiHashMap *join_reqs;
207
208   /**
209    * @see enum GNUNET_PSYC_Policy
210    */
211   enum GNUNET_PSYC_Policy policy;
212 };
213
214
215 /**
216  * Client context for a guest.
217  */
218 struct Guest
219 {
220   /**
221    * Place struct common for Host and Guest.
222    */
223   struct Place plc;
224
225   /**
226    * Private key of the slave.
227    */
228   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
229
230   /**
231    * Public key of the slave.
232    */
233   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
234
235   /**
236    * Hash of @a pub_key.
237    */
238   struct GNUNET_HashCode pub_key_hash;
239
240   /**
241    * Handle for the PSYC slave.
242    */
243   struct GNUNET_PSYC_Slave *slave;
244
245   /**
246    * Transmit handle for multicast.
247    */
248   struct GNUNET_PSYC_SlaveTransmitHandle *tmit_handle;
249
250   /**
251    * Peer identity of the origin.
252    */
253   struct GNUNET_PeerIdentity origin;
254
255   /**
256    * Number of items in @a relays.
257    */
258   uint32_t relay_count;
259
260   /**
261    * Relays that multicast can use to connect.
262    */
263   struct GNUNET_PeerIdentity *relays;
264
265   /**
266    * Join request to be transmitted to the master on join.
267    */
268   struct GNUNET_MessageHeader *join_req;
269
270   /**
271    * Join decision received from PSYC.
272    */
273   struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
274
275 };
276
277
278 struct Client
279 {
280   /**
281    * Place where the client entered.
282    */
283   struct Place *plc;
284
285   /**
286    * Message queue for the message currently being transmitted
287    * by this client.
288    */
289   struct MessageTransmitQueue *tmit_msg;
290 };
291
292
293 struct OperationClosure
294 {
295   struct GNUNET_SERVER_Client *client;
296   struct Place *plc;
297   uint64_t op_id;
298   uint32_t flags;
299 };
300
301
302 static int
303 psyc_transmit_message (struct Place *plc);
304
305
306 /**
307  * Task run during shutdown.
308  *
309  * @param cls unused
310  * @param tc unused
311  */
312 static void
313 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
314 {
315   if (NULL != nc)
316   {
317     GNUNET_SERVER_notification_context_destroy (nc);
318     nc = NULL;
319   }
320   if (NULL != stats)
321   {
322     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
323     stats = NULL;
324   }
325 }
326
327
328 /**
329  * Clean up host data structures after a client disconnected.
330  */
331 static void
332 cleanup_host (struct Host *hst)
333 {
334   struct Place *plc = &hst->plc;
335
336   if (NULL != hst->master)
337     GNUNET_PSYC_master_stop (hst->master, GNUNET_NO, NULL, NULL); // FIXME
338   GNUNET_CONTAINER_multihashmap_destroy (hst->join_reqs);
339   GNUNET_CONTAINER_multihashmap_remove (hosts, &plc->pub_key_hash, plc);
340 }
341
342
343 /**
344  * Clean up guest data structures after a client disconnected.
345  */
346 static void
347 cleanup_guest (struct Guest *gst)
348 {
349   struct Place *plc = &gst->plc;
350   struct GNUNET_CONTAINER_MultiHashMap *
351     plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
352                                                 &plc->pub_key_hash);
353   GNUNET_assert (NULL != plc_gst);
354   GNUNET_CONTAINER_multihashmap_remove (plc_gst, &gst->pub_key_hash, gst);
355
356   if (0 == GNUNET_CONTAINER_multihashmap_size (plc_gst))
357   {
358     GNUNET_CONTAINER_multihashmap_remove (place_guests, &plc->pub_key_hash,
359                                           plc_gst);
360     GNUNET_CONTAINER_multihashmap_destroy (plc_gst);
361   }
362   GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, gst);
363
364   if (NULL != gst->join_req)
365     GNUNET_free (gst->join_req);
366   if (NULL != gst->relays)
367     GNUNET_free (gst->relays);
368   if (NULL != gst->slave)
369     GNUNET_PSYC_slave_part (gst->slave, GNUNET_NO, NULL, NULL); // FIXME
370   GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, plc);
371 }
372
373
374 /**
375  * Clean up place data structures after a client disconnected.
376  */
377 static void
378 cleanup_place (struct Place *plc)
379 {
380   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381               "%p Cleaning up place %s\n",
382               plc, GNUNET_h2s (&plc->pub_key_hash));
383
384   (GNUNET_YES == plc->is_host)
385     ? cleanup_host ((struct Host *) plc)
386     : cleanup_guest ((struct Guest *) plc);
387   GNUNET_free (plc);
388 }
389
390
391 static void
392 schedule_cleanup_place (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
393 {
394   cleanup_place (cls);
395 }
396
397
398 /**
399  * Called whenever a client is disconnected.
400  * Frees our resources associated with that client.
401  *
402  * @param cls Closure.
403  * @param client Identification of the client.
404  */
405 static void
406 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
407 {
408   if (NULL == client)
409     return;
410
411   struct Client *
412     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
413   if (NULL == ctx)
414   {
415     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
416                 "%p User context is NULL in client_disconnect()\n", ctx);
417     GNUNET_break (0);
418     return;
419   }
420
421   struct Place *plc = ctx->plc;
422   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
423               "%p Client (%s) disconnected from place %s\n",
424               plc, (GNUNET_YES == plc->is_host) ? "host" : "guest",
425               GNUNET_h2s (&plc->pub_key_hash));
426
427   struct ClientListItem *cli = plc->clients_head;
428   while (NULL != cli)
429   {
430     if (cli->client == client)
431     {
432       GNUNET_CONTAINER_DLL_remove (plc->clients_head, plc->clients_tail, cli);
433       GNUNET_free (cli);
434       break;
435     }
436     cli = cli->next;
437   }
438
439   if (NULL == plc->clients_head)
440   { /* Last client disconnected. */
441     if (GNUNET_YES != plc->is_disconnected)
442     {
443       plc->is_disconnected = GNUNET_YES;
444       if (NULL != plc->tmit_msgs_head)
445       { /* Send pending messages to PSYC before cleanup. */
446         psyc_transmit_message (plc);
447       }
448       else
449       {
450         cleanup_place (plc);
451       }
452     }
453   }
454 }
455
456
457 /**
458  * Send message to all clients connected to the channel.
459  */
460 static void
461 client_send_msg (const struct Place *plc,
462                  const struct GNUNET_MessageHeader *msg)
463 {
464   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465               "%p Sending message to clients.\n", plc);
466
467   struct ClientListItem *cli = plc->clients_head;
468   while (NULL != cli)
469   {
470     GNUNET_SERVER_notification_context_add (nc, cli->client);
471     GNUNET_SERVER_notification_context_unicast (nc, cli->client, msg, GNUNET_NO);
472     cli = cli->next;
473   }
474 }
475
476
477 /**
478  * Send a result code back to the client.
479  *
480  * @param client
481  *        Client that should receive the result code.
482  * @param result_code
483  *        Code to transmit.
484  * @param op_id
485  *        Operation ID in network byte order.
486  * @param data
487  *        Data payload or NULL.
488  * @param data_size
489  *        Size of @a data.
490  */
491 static void
492 client_send_result (struct GNUNET_SERVER_Client *client, uint64_t op_id,
493                     int64_t result_code, const void *data, uint16_t data_size)
494 {
495   struct GNUNET_OperationResultMessage *res;
496
497   res = GNUNET_malloc (sizeof (*res) + data_size);
498   res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE);
499   res->header.size = htons (sizeof (*res) + data_size);
500   res->result_code = GNUNET_htonll (result_code);
501   res->op_id = op_id;
502   if (0 < data_size)
503     memcpy (&res[1], data, data_size);
504
505   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506               "%p Sending result to client for operation #%" PRIu64 ": "
507               "%" PRId64 " (size: %u)\n",
508               client, GNUNET_ntohll (op_id), result_code, data_size);
509
510   GNUNET_SERVER_notification_context_add (nc, client);
511   GNUNET_SERVER_notification_context_unicast (nc, client, &res->header,
512                                               GNUNET_NO);
513   GNUNET_free (res);
514 }
515
516
517 /**
518  * Called after a PSYC master is started.
519  */
520 static void
521 psyc_master_started (void *cls, int result, uint64_t max_message_id)
522 {
523   struct Host *hst = cls;
524   struct Place *plc = &hst->plc;
525   plc->max_message_id = max_message_id;
526   plc->is_ready = GNUNET_YES;
527
528   struct GNUNET_PSYC_CountersResultMessage res;
529   res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
530   res.header.size = htons (sizeof (res));
531   res.result_code = htonl (result - INT32_MIN);
532   res.max_message_id = GNUNET_htonll (plc->max_message_id);
533
534   client_send_msg (plc, &res.header);
535 }
536
537
538 /**
539  * Called when a PSYC master receives a join request.
540  */
541 static void
542 psyc_recv_join_request (void *cls,
543                         const struct GNUNET_PSYC_JoinRequestMessage *req,
544                         const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
545                         const struct GNUNET_PSYC_Message *join_msg,
546                         struct GNUNET_PSYC_JoinHandle *jh)
547 {
548   struct Host *hst = cls;
549   struct GNUNET_HashCode slave_key_hash;
550   GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
551   GNUNET_CONTAINER_multihashmap_put (hst->join_reqs, &slave_key_hash, jh,
552                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
553   client_send_msg (&hst->plc, &req->header);
554 }
555
556
557 /**
558  * Called after a PSYC slave is connected.
559  */
560 static void
561 psyc_slave_connected (void *cls, int result, uint64_t max_message_id)
562 {
563   struct Guest *gst = cls;
564   struct Place *plc = &gst->plc;
565   plc->max_message_id = max_message_id;
566   plc->is_ready = GNUNET_YES;
567
568   struct GNUNET_PSYC_CountersResultMessage res;
569   res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
570   res.header.size = htons (sizeof (res));
571   res.result_code = htonl (result - INT32_MIN);
572   res.max_message_id = GNUNET_htonll (plc->max_message_id);
573
574   client_send_msg (plc, &res.header);
575 }
576
577
578 /**
579  * Called when a PSYC slave receives a join decision.
580  */
581 static void
582 psyc_recv_join_dcsn (void *cls,
583                      const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
584                      int is_admitted,
585                      const struct GNUNET_PSYC_Message *join_msg)
586 {
587   struct Guest *gst = cls;
588   client_send_msg (&gst->plc, &dcsn->header);
589 }
590
591
592 /**
593  * Called when a PSYC master or slave receives a message.
594  */
595 static void
596 psyc_recv_message (void *cls,
597                    uint64_t message_id,
598                    uint32_t flags,
599                    const struct GNUNET_PSYC_MessageHeader *msg)
600 {
601   struct Place *plc = cls;
602   client_send_msg (plc, &msg->header);
603
604   /* FIXME: further processing */
605 }
606
607
608 /**
609  * Initialize place data structure.
610  */
611 static void
612 place_init (struct Place *plc)
613 {
614
615 }
616
617
618 /**
619  * Handle a connecting client entering a place as host.
620  */
621 static void
622 client_recv_host_enter (void *cls, struct GNUNET_SERVER_Client *client,
623                         const struct GNUNET_MessageHeader *msg)
624 {
625   const struct HostEnterRequest *req
626     = (const struct HostEnterRequest *) msg;
627
628   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
629   struct GNUNET_HashCode pub_key_hash;
630
631   GNUNET_CRYPTO_eddsa_key_get_public (&req->place_key, &pub_key);
632   GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
633
634   struct Host *
635     hst = GNUNET_CONTAINER_multihashmap_get (hosts, &pub_key_hash);
636   struct Place *plc;
637
638   if (NULL == hst)
639   {
640     hst = GNUNET_new (struct Host);
641     hst->policy = ntohl (req->policy);
642     hst->priv_key = req->place_key;
643     hst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
644
645     plc = &hst->plc;
646     plc->is_host = GNUNET_YES;
647     plc->pub_key = pub_key;
648     plc->pub_key_hash = pub_key_hash;
649     place_init (plc);
650
651     GNUNET_CONTAINER_multihashmap_put (hosts, &plc->pub_key_hash, plc,
652                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
653     hst->master = GNUNET_PSYC_master_start (cfg, &hst->priv_key, hst->policy,
654                                             &psyc_master_started,
655                                             &psyc_recv_join_request,
656                                             &psyc_recv_message, NULL, hst);
657     hst->plc.channel = GNUNET_PSYC_master_get_channel (hst->master);
658   }
659   else
660   {
661     plc = &hst->plc;
662
663     struct GNUNET_PSYC_CountersResultMessage res;
664     res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
665     res.header.size = htons (sizeof (res));
666     res.result_code = htonl (GNUNET_OK);
667     res.max_message_id = GNUNET_htonll (plc->max_message_id);
668
669     GNUNET_SERVER_notification_context_add (nc, client);
670     GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
671                                                 GNUNET_NO);
672   }
673
674   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
675               "%p Client connected as host to place %s.\n",
676               hst, GNUNET_h2s (&plc->pub_key_hash));
677
678   struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
679   cli->client = client;
680   GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
681
682   struct Client *ctx = GNUNET_new (struct Client);
683   ctx->plc = plc;
684   GNUNET_SERVER_client_set_user_context (client, ctx);
685   GNUNET_SERVER_receive_done (client, GNUNET_OK);
686 }
687
688
689 /**
690  * Handle a connecting client entering a place as guest.
691  */
692 static void
693 client_recv_guest_enter (void *cls, struct GNUNET_SERVER_Client *client,
694                          const struct GNUNET_MessageHeader *msg)
695 {
696   const struct GuestEnterRequest *req
697     = (const struct GuestEnterRequest *) msg;
698   uint16_t req_size = ntohs (req->header.size);
699
700   struct GNUNET_CRYPTO_EcdsaPublicKey gst_pub_key;
701   struct GNUNET_HashCode pub_key_hash, gst_pub_key_hash;
702
703   GNUNET_CRYPTO_ecdsa_key_get_public (&req->guest_key, &gst_pub_key);
704   GNUNET_CRYPTO_hash (&gst_pub_key, sizeof (gst_pub_key), &gst_pub_key_hash);
705   GNUNET_CRYPTO_hash (&req->place_key, sizeof (req->place_key), &pub_key_hash);
706
707   struct GNUNET_CONTAINER_MultiHashMap *
708     plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests, &pub_key_hash);
709   struct Guest *gst = NULL;
710   struct Place *plc;
711
712   if (NULL != plc_gst)
713   {
714     gst = GNUNET_CONTAINER_multihashmap_get (plc_gst, &gst_pub_key_hash);
715   }
716   if (NULL == gst || NULL == gst->slave)
717   {
718     gst = GNUNET_new (struct Guest);
719     gst->priv_key = req->guest_key;
720     gst->pub_key = gst_pub_key;
721     gst->pub_key_hash = gst_pub_key_hash;
722     gst->origin = req->origin;
723     gst->relay_count = ntohl (req->relay_count);
724
725     const struct GNUNET_PeerIdentity *
726       relays = (const struct GNUNET_PeerIdentity *) &req[1];
727     uint16_t relay_size = gst->relay_count * sizeof (*relays);
728     struct GNUNET_PSYC_Message *join_msg = NULL;
729     uint16_t join_msg_size = 0;
730
731     if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
732         <= req_size)
733     {
734       join_msg = (struct GNUNET_PSYC_Message *)
735         (((char *) &req[1]) + relay_size);
736       join_msg_size = ntohs (join_msg->header.size);
737     }
738     if (sizeof (*req) + relay_size + join_msg_size != req_size)
739     {
740       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
741                   "%u + %u + %u != %u\n",
742                   sizeof (*req), relay_size, join_msg_size, req_size);
743       GNUNET_break (0);
744       GNUNET_SERVER_client_disconnect (client);
745       GNUNET_free (gst);
746       return;
747     }
748     if (0 < gst->relay_count)
749     {
750       gst->relays = GNUNET_malloc (relay_size);
751       memcpy (gst->relays, &req[1], relay_size);
752     }
753
754     plc = &gst->plc;
755     plc->is_host = GNUNET_NO;
756     plc->pub_key = req->place_key;
757     plc->pub_key_hash = pub_key_hash;
758     place_init (plc);
759
760     if (NULL == plc_gst)
761     {
762       plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
763       (void) GNUNET_CONTAINER_multihashmap_put (place_guests, &plc->pub_key_hash, plc_gst,
764                                                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
765     }
766     (void) GNUNET_CONTAINER_multihashmap_put (plc_gst, &gst->pub_key_hash, gst,
767                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
768     (void) GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, gst,
769                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
770     gst->slave
771       = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &gst->priv_key,
772                                 &gst->origin, gst->relay_count, gst->relays,
773                                 &psyc_recv_message, NULL, &psyc_slave_connected,
774                                 &psyc_recv_join_dcsn, gst, join_msg);
775     gst->plc.channel = GNUNET_PSYC_slave_get_channel (gst->slave);
776   }
777   else
778   {
779     plc = &gst->plc;
780
781     struct GNUNET_PSYC_CountersResultMessage res;
782     res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
783     res.header.size = htons (sizeof (res));
784     res.result_code = htonl (GNUNET_OK);
785     res.max_message_id = GNUNET_htonll (plc->max_message_id);
786
787     GNUNET_SERVER_notification_context_add (nc, client);
788     GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
789                                                 GNUNET_NO);
790     if (NULL != gst->join_dcsn)
791     {
792       GNUNET_SERVER_notification_context_add (nc, client);
793       GNUNET_SERVER_notification_context_unicast (nc, client,
794                                                   &gst->join_dcsn->header,
795                                                   GNUNET_NO);
796     }
797   }
798
799   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800               "%p Client connected as guest to place %s.\n",
801               gst, GNUNET_h2s (&plc->pub_key_hash));
802
803   struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
804   cli->client = client;
805   GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
806
807   struct Client *ctx = GNUNET_new (struct Client);
808   ctx->plc = plc;
809   GNUNET_SERVER_client_set_user_context (client, ctx);
810   GNUNET_SERVER_receive_done (client, GNUNET_OK);
811 }
812
813
814 struct JoinDecisionClosure
815 {
816   int32_t is_admitted;
817   struct GNUNET_PSYC_Message *msg;
818 };
819
820
821 /**
822  * Iterator callback for responding to join requests.
823  */
824 static int
825 psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
826                          void *value)
827 {
828   struct JoinDecisionClosure *jcls = cls;
829   struct GNUNET_PSYC_JoinHandle *jh = value;
830   // FIXME: add relays
831   GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
832   return GNUNET_YES;
833 }
834
835
836 /**
837  * Handle an entry decision from a host client.
838  */
839 static void
840 client_recv_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
841                            const struct GNUNET_MessageHeader *msg)
842 {
843   struct Client *
844     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
845   GNUNET_assert (NULL != ctx);
846   struct Place *plc = ctx->plc;
847   GNUNET_assert (GNUNET_YES == plc->is_host);
848   struct Host *hst = (struct Host *) plc;
849
850   struct GNUNET_PSYC_JoinDecisionMessage *
851     dcsn = (struct GNUNET_PSYC_JoinDecisionMessage *) msg;
852   struct JoinDecisionClosure jcls;
853   jcls.is_admitted = ntohl (dcsn->is_admitted);
854   jcls.msg
855     = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
856     ? (struct GNUNET_PSYC_Message *) &dcsn[1]
857     : NULL;
858
859   struct GNUNET_HashCode slave_key_hash;
860   GNUNET_CRYPTO_hash (&dcsn->slave_key, sizeof (dcsn->slave_key),
861                       &slave_key_hash);
862
863   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864               "%p Got join decision (%d) from client for place %s..\n",
865               hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
866   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867               "%p ..and slave %s.\n",
868               hst, GNUNET_h2s (&slave_key_hash));
869
870   GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_key_hash,
871                                               &psyc_send_join_decision, &jcls);
872   GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_key_hash);
873   GNUNET_SERVER_receive_done (client, GNUNET_OK);
874 }
875
876
877 /**
878  * Send acknowledgement to a client.
879  *
880  * Sent after a message fragment has been passed on to multicast.
881  *
882  * @param plc The place struct for the client.
883  */
884 static void
885 send_message_ack (struct Place *plc, struct GNUNET_SERVER_Client *client)
886 {
887   struct GNUNET_MessageHeader res;
888   res.size = htons (sizeof (res));
889   res.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
890
891   GNUNET_SERVER_notification_context_add (nc, client);
892   GNUNET_SERVER_notification_context_unicast (nc, client, &res, GNUNET_NO);
893 }
894
895
896 /**
897  * Proceed to the next message part in the transmission queue.
898  *
899  * @param plc
900  *        Place where the transmission is going on.
901  * @param tmit_msg
902  *        Currently transmitted message.
903  * @param tmit_frag
904  *        Currently transmitted message fragment.
905  *
906  * @return @a tmit_frag, or NULL if reached the end of fragment.
907  */
908 static struct FragmentTransmitQueue *
909 psyc_transmit_queue_next_part (struct Place *plc,
910                                struct MessageTransmitQueue *tmit_msg,
911                                struct FragmentTransmitQueue *tmit_frag)
912 {
913   uint16_t psize = ntohs (tmit_frag->next_part->size);
914   if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
915       < tmit_frag->size)
916   {
917     tmit_frag->next_part
918       = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + psize);
919   }
920   else /* Reached end of current fragment. */
921   {
922     if (NULL != tmit_frag->client)
923       send_message_ack (plc, tmit_frag->client);
924     GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
925     GNUNET_free (tmit_frag);
926     tmit_frag = NULL;
927   }
928   return tmit_frag;
929 }
930
931
932 /**
933  * Proceed to next message in transmission queue.
934  *
935  * @param plc
936  *        Place where the transmission is going on.
937  * @param tmit_msg
938  *        Currently transmitted message.
939  *
940  * @return The next message in queue, or NULL if queue is empty.
941  */
942 static struct MessageTransmitQueue *
943 psyc_transmit_queue_next_msg (struct Place *plc,
944                               struct MessageTransmitQueue *tmit_msg)
945 {
946   GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
947   GNUNET_free (tmit_msg);
948   return plc->tmit_msgs_head;
949 }
950
951
952 /**
953  * Callback for data transmission to PSYC.
954  */
955 static int
956 psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
957 {
958   struct Place *plc = cls;
959   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
960   GNUNET_assert (NULL != tmit_msg);
961   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
962   if (NULL == tmit_frag)
963   { /* Rest of the message have not arrived yet, pause transmission */
964     *data_size = 0;
965     return GNUNET_NO;
966   }
967   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
968   if (NULL == pmsg)
969   {
970     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
971                 "%p psyc_transmit_notify_data: nothing to send.\n", plc);
972     *data_size = 0;
973     return GNUNET_NO;
974   }
975
976   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
977               "%p psyc_transmit_notify_data()\n", plc);
978   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
979
980   uint16_t ptype = ntohs (pmsg->type);
981   uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
982   int ret;
983
984   switch (ptype)
985   {
986   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
987     if (*data_size < pdata_size)
988     {
989       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
990                 "%p psyc_transmit_notify_data: buffer size too small for data.\n", plc);
991       *data_size = 0;
992       return GNUNET_NO;
993     }
994     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
995                 "%p psyc_transmit_notify_data: sending %u bytes.\n",
996                 plc, pdata_size);
997
998     *data_size = pdata_size;
999     memcpy (data, &pmsg[1], *data_size);
1000     ret = GNUNET_NO;
1001     break;
1002
1003   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1004     *data_size = 0;
1005     ret = GNUNET_YES;
1006     break;
1007
1008   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1009     *data_size = 0;
1010     ret = GNUNET_SYSERR;
1011     break;
1012
1013   default:
1014     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1015                 "%p psyc_transmit_notify_data: unexpected message part of type %u.\n",
1016                 plc, ptype);
1017     ret = GNUNET_SYSERR;
1018   }
1019
1020   if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
1021   {
1022     *data_size = 0;
1023     tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1024     plc->is_disconnected = GNUNET_YES;
1025     GNUNET_SERVER_client_disconnect (tmit_frag->client);
1026     GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
1027     return ret;
1028   }
1029   else
1030   {
1031     tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1032     if (NULL != tmit_frag)
1033     {
1034       struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1035       ptype = ntohs (pmsg->type);
1036       switch (ptype)
1037       {
1038       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1039         ret = GNUNET_YES;
1040         break;
1041       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1042         ret = GNUNET_SYSERR;
1043         break;
1044       }
1045       switch (ptype)
1046       {
1047       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1048       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1049         tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1050       }
1051     }
1052
1053     if (NULL == tmit_msg->frags_head
1054         && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1055     { /* Reached end of current message. */
1056       tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1057     }
1058   }
1059
1060   if (ret != GNUNET_NO)
1061   {
1062     if (NULL != tmit_msg)
1063     {
1064       psyc_transmit_message (plc);
1065     }
1066     else if (GNUNET_YES == plc->is_disconnected)
1067     {
1068       /* FIXME: handle partial message (when still in_transmit) */
1069       cleanup_place (plc);
1070     }
1071   }
1072   return ret;
1073 }
1074
1075
1076 /**
1077  * Callback for modifier transmission to PSYC.
1078  */
1079 static int
1080 psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1081                           uint8_t *oper, uint32_t *full_value_size)
1082 {
1083   struct Place *plc = cls;
1084   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1085   GNUNET_assert (NULL != tmit_msg);
1086   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1087   if (NULL == tmit_frag)
1088   { /* Rest of the message have not arrived yet, pause transmission */
1089     *data_size = 0;
1090     return GNUNET_NO;
1091   }
1092   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1093   if (NULL == pmsg)
1094   {
1095     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096                 "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
1097     *data_size = 0;
1098     return GNUNET_NO;
1099   }
1100
1101   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102               "%p psyc_transmit_notify_mod()\n", plc);
1103   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
1104
1105   uint16_t ptype = ntohs (pmsg->type);
1106   int ret;
1107
1108   switch (ptype)
1109   {
1110   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1111   {
1112     if (NULL == oper)
1113     {
1114       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1115                   "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
1116       ret = GNUNET_SYSERR;
1117       break;
1118     }
1119     struct GNUNET_PSYC_MessageModifier *
1120       pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
1121     uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
1122
1123     if (*data_size < mod_size)
1124     {
1125       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126                 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1127       *data_size = 0;
1128       return GNUNET_NO;
1129     }
1130
1131     *full_value_size = ntohl (pmod->value_size);
1132     *oper = pmod->oper;
1133     *data_size = mod_size;
1134     memcpy (data, &pmod[1], mod_size);
1135     ret = GNUNET_NO;
1136     break;
1137   }
1138
1139   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1140   {
1141     if (NULL != oper)
1142     {
1143       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1144                   "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
1145       ret = GNUNET_SYSERR;
1146       break;
1147     }
1148     uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
1149     if (*data_size < mod_size)
1150     {
1151       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1152                 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1153       *data_size = 0;
1154       return GNUNET_NO;
1155     }
1156     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157                 "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, mod_size);
1158
1159     *data_size = mod_size;
1160     memcpy (data, &pmsg[1], *data_size);
1161     ret = GNUNET_NO;
1162     break;
1163   }
1164
1165   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1166   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1167   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1168     *data_size = 0;
1169     ret = GNUNET_YES;
1170     break;
1171
1172   default:
1173     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1174                 "%p psyc_transmit_notify_mod: unexpected message part of type %u.\n",
1175                 plc, ptype);
1176     ret = GNUNET_SYSERR;
1177   }
1178
1179   if (GNUNET_SYSERR == ret)
1180   {
1181     *data_size = 0;
1182     ret = GNUNET_SYSERR;
1183     tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1184     plc->is_disconnected = GNUNET_YES;
1185     GNUNET_SERVER_client_disconnect (tmit_frag->client);
1186     GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
1187   }
1188   else
1189   {
1190     if (GNUNET_YES != ret)
1191       psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1192
1193     if (NULL == tmit_msg->frags_head
1194         && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1195     { /* Reached end of current message. */
1196       tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1197     }
1198   }
1199   return ret;
1200 }
1201
1202 /**
1203  * Callback for data transmission from a host to PSYC.
1204  */
1205 static int
1206 host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1207 {
1208   int ret = psyc_transmit_notify_data (cls, data_size, data);
1209
1210   if (GNUNET_NO != ret)
1211   {
1212     struct Host *hst = cls;
1213     hst->tmit_handle = NULL;
1214   }
1215   return ret;
1216 }
1217
1218
1219 /**
1220  * Callback for the transmit functions of multicast.
1221  */
1222 static int
1223 guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1224 {
1225   int ret = psyc_transmit_notify_data (cls, data_size, data);
1226
1227   if (GNUNET_NO != ret)
1228   {
1229     struct Guest *gst = cls;
1230     gst->tmit_handle = NULL;
1231   }
1232   return ret;
1233 }
1234
1235
1236 /**
1237  * Callback for modifier transmission from a host to PSYC.
1238  */
1239 static int
1240 host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1241                           uint8_t *oper, uint32_t *full_value_size)
1242 {
1243   int ret = psyc_transmit_notify_mod (cls, data_size, data,
1244                                       oper, full_value_size);
1245   if (GNUNET_SYSERR == ret)
1246   {
1247     struct Host *hst = cls;
1248     hst->tmit_handle = NULL;
1249   }
1250   return ret;
1251 }
1252
1253
1254 /**
1255  * Callback for modifier transmission from a guest to PSYC.
1256  */
1257 static int
1258 guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1259                            uint8_t *oper, uint32_t *full_value_size)
1260 {
1261   int ret = psyc_transmit_notify_mod (cls, data_size, data,
1262                                       oper, full_value_size);
1263   if (GNUNET_SYSERR == ret)
1264   {
1265     struct Guest *gst = cls;
1266     gst->tmit_handle = NULL;
1267   }
1268   return ret;
1269 }
1270
1271
1272 /**
1273  * Get method part of next message from transmission queue.
1274  *
1275  * @param tmit_msg
1276  *        Next item in message transmission queue.
1277  * @param[out] pmeth
1278  *        The message method is returned here.
1279  *
1280  * @return #GNUNET_OK on success
1281  *         #GNUNET_NO if there are no more messages in queue.
1282  *         #GNUNET_SYSERR if the next message is malformed.
1283  */
1284 static int
1285 psyc_transmit_queue_next_method (struct Place *plc,
1286                                  struct GNUNET_PSYC_MessageMethod **pmeth)
1287 {
1288   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1289   if (NULL == tmit_msg)
1290     return GNUNET_NO;
1291
1292   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1293   if (NULL == tmit_frag)
1294   {
1295     GNUNET_break (0);
1296     return GNUNET_NO;
1297   }
1298
1299   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1300   if (NULL == pmsg
1301       || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
1302   {
1303     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1304                 "%p psyc_transmit_queue_next_method: unexpected message part of type %u.\n",
1305                 plc, ntohs (pmsg->type));
1306     GNUNET_break (0);
1307     return GNUNET_SYSERR;
1308   }
1309
1310   uint16_t psize = ntohs (pmsg->size);
1311   *pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
1312   if (psize < sizeof (**pmeth) + 1 || '\0' != *((char *) *pmeth + psize - 1))
1313   {
1314     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1315                 "%p psyc_transmit_queue_next_method: invalid method name.\n",
1316                 plc, ntohs (pmsg->type));
1317     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1318                 "%u <= %u || NUL != %u\n",
1319                 sizeof (**pmeth), psize, *((char *) *pmeth + psize - 1));
1320     GNUNET_break (0);
1321     return GNUNET_SYSERR;
1322   }
1323
1324   psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1325   return GNUNET_OK;
1326 }
1327
1328
1329 /**
1330  * Transmit the next message in queue from the host to the PSYC channel.
1331  */
1332 static int
1333 psyc_master_transmit_message (struct Host *hst)
1334 {
1335
1336   if (NULL == hst->tmit_handle)
1337   {
1338     struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1339     int ret = psyc_transmit_queue_next_method (&hst->plc, &pmeth);
1340     if (GNUNET_OK != ret)
1341       return ret;
1342
1343     hst->tmit_handle
1344       = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
1345                                      &host_transmit_notify_mod,
1346                                      &host_transmit_notify_data, hst,
1347                                      pmeth->flags);
1348   }
1349   else
1350   {
1351     GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
1352   }
1353   return GNUNET_OK;
1354 }
1355
1356
1357 /**
1358  * Transmit the next message in queue from a guest to the PSYC channel.
1359  */
1360 static int
1361 psyc_slave_transmit_message (struct Guest *gst)
1362 {
1363   if (NULL == gst->tmit_handle)
1364   {
1365     struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1366     int ret = psyc_transmit_queue_next_method (&gst->plc, &pmeth);
1367     if (GNUNET_OK != ret)
1368       return ret;
1369
1370     gst->tmit_handle
1371       = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
1372                                     &guest_transmit_notify_mod,
1373                                     &guest_transmit_notify_data, gst,
1374                                     pmeth->flags);
1375   }
1376   else
1377   {
1378     GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
1379   }
1380   return GNUNET_OK;
1381 }
1382
1383
1384 /**
1385  * Transmit a message to PSYC.
1386  */
1387 static int
1388 psyc_transmit_message (struct Place *plc)
1389 {
1390   return
1391     (plc->is_host)
1392     ? psyc_master_transmit_message ((struct Host *) plc)
1393     : psyc_slave_transmit_message ((struct Guest *) plc);
1394 }
1395
1396
1397 /**
1398  * Queue message parts for sending to PSYC.
1399  *
1400  * @param plc          Place to send to.
1401  * @param client       Client the message originates from.
1402  * @param data_size    Size of @a data.
1403  * @param data         Concatenated message parts.
1404  * @param first_ptype  First message part type in @a data.
1405  * @param last_ptype   Last message part type in @a data.
1406  */
1407 static struct MessageTransmitQueue *
1408 psyc_transmit_queue_message (struct Place *plc,
1409                              struct GNUNET_SERVER_Client *client,
1410                              size_t data_size,
1411                              const void *data,
1412                              uint16_t first_ptype, uint16_t last_ptype,
1413                              struct MessageTransmitQueue *tmit_msg)
1414 {
1415   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
1416   {
1417     tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
1418     GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
1419   }
1420   else if (NULL == tmit_msg)
1421   {
1422     return NULL;
1423   }
1424
1425   struct FragmentTransmitQueue *
1426     tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
1427   memcpy (&tmit_frag[1], data, data_size);
1428   tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
1429   tmit_frag->client = client;
1430   tmit_frag->size = data_size;
1431
1432   GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
1433   tmit_msg->client = client;
1434   return tmit_msg;
1435 }
1436
1437
1438 /**
1439  * Cancel transmission of current message to PSYC.
1440  *
1441  * @param plc     Place to send to.
1442  * @param client  Client the message originates from.
1443  */
1444 static void
1445 psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVER_Client *client)
1446 {
1447   uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
1448
1449   struct GNUNET_MessageHeader msg;
1450   msg.size = htons (sizeof (msg));
1451   msg.type = htons (type);
1452
1453   psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, NULL);
1454   psyc_transmit_message (plc);
1455
1456   /* FIXME: cleanup */
1457 }
1458
1459
1460 /**
1461  * Handle an incoming message from a client, to be transmitted to the place.
1462  */
1463 static void
1464 client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
1465                           const struct GNUNET_MessageHeader *msg)
1466 {
1467   struct Client *
1468     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
1469   GNUNET_assert (NULL != ctx);
1470   struct Place *plc = ctx->plc;
1471   int ret = GNUNET_SYSERR;
1472
1473   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1474               "%p Received message from client.\n", plc);
1475   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
1476
1477   if (GNUNET_YES != plc->is_ready)
1478   {
1479     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1480                 "%p Place is not ready yet, disconnecting client.\n", plc);
1481     GNUNET_break (0);
1482     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1483     return;
1484   }
1485
1486   uint16_t size = ntohs (msg->size);
1487   uint16_t psize = size - sizeof (*msg);
1488   if (psize < sizeof (struct GNUNET_MessageHeader)
1489       || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
1490   {
1491     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1492                 "%p Received message with invalid payload size (%u) from client.\n",
1493                 plc, psize);
1494     GNUNET_break (0);
1495     psyc_transmit_cancel (plc, client);
1496     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1497     return;
1498   }
1499
1500   uint16_t first_ptype = 0, last_ptype = 0;
1501   if (GNUNET_SYSERR
1502       == GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
1503                                           &first_ptype, &last_ptype))
1504   {
1505     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1506                 "%p Received invalid message part from client.\n", plc);
1507     GNUNET_break (0);
1508     psyc_transmit_cancel (plc, client);
1509     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1510     return;
1511   }
1512   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1513               "%p Received message with first part type %u and last part type %u.\n",
1514               plc, first_ptype, last_ptype);
1515
1516   ctx->tmit_msg
1517     = psyc_transmit_queue_message (plc, client, psize, &msg[1],
1518                                    first_ptype, last_ptype, ctx->tmit_msg);
1519   if (NULL != ctx->tmit_msg)
1520   {
1521     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
1522       ctx->tmit_msg = NULL;
1523     ret = psyc_transmit_message (plc);
1524   }
1525
1526   if (GNUNET_OK != ret)
1527   {
1528     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1529                 "%p Received invalid message part from client.\n", plc);
1530     GNUNET_break (0);
1531     psyc_transmit_cancel (plc, client);
1532     ret = GNUNET_SYSERR;
1533   }
1534   GNUNET_SERVER_receive_done (client, ret);
1535 }
1536
1537
1538 /**
1539  * A historic message result arrived from PSYC.
1540  */
1541 static void
1542 psyc_recv_history_message (void *cls,
1543                            uint64_t message_id,
1544                            uint32_t flags,
1545                            const struct GNUNET_PSYC_MessageHeader *msg)
1546 {
1547   struct OperationClosure *opcls = cls;
1548   struct Place *plc = opcls->plc;
1549
1550   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1551               "%p Received historic message #%" PRId64 " (flags: %x)\n",
1552               plc, message_id, flags);
1553
1554   uint16_t size = ntohs (msg->header.size);
1555
1556   struct GNUNET_OperationResultMessage *
1557     res = GNUNET_malloc (sizeof (*res) + size);
1558   res->header.size = htons (sizeof (*res) + size);
1559   res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT);
1560   res->op_id = opcls->op_id;
1561   res->result_code = GNUNET_htonll (GNUNET_OK);
1562
1563   memcpy (&res[1], msg, size);
1564
1565   /** @todo FIXME: send only to requesting client */
1566   client_send_msg (plc, &res->header);
1567 }
1568
1569
1570 static void
1571 psyc_recv_history_result (void *cls, int64_t result,
1572                           const void *err_msg, uint16_t err_msg_size)
1573 {
1574   struct OperationClosure *opcls = cls;
1575   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1576               "%p History replay #%" PRIu64 ": "
1577               "PSYCSTORE returned %" PRId64 " (%.*s)\n",
1578               opcls->plc, GNUNET_ntohll (opcls->op_id), result, err_msg_size, err_msg);
1579
1580   // FIXME: place might have been destroyed
1581   client_send_result (opcls->client, opcls->op_id, result, err_msg, err_msg_size);
1582 }
1583
1584
1585 /**
1586  * Client requests channel history.
1587  */
1588 static void
1589 client_recv_history_replay (void *cls, struct GNUNET_SERVER_Client *client,
1590                             const struct GNUNET_MessageHeader *msg)
1591 {
1592   struct Client *
1593     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
1594   GNUNET_assert (NULL != ctx);
1595   struct Place *plc = ctx->plc;
1596
1597   const struct GNUNET_PSYC_HistoryRequestMessage *
1598     req = (const struct GNUNET_PSYC_HistoryRequestMessage *) msg;
1599   uint16_t size = ntohs (msg->size);
1600   const char *method_prefix = (const char *) &req[1];
1601
1602   if (size < sizeof (*req) + 1
1603       || '\0' != method_prefix[size - sizeof (*req) - 1])
1604   {
1605     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1606                 "%p History replay #%" PRIu64 ": "
1607                 "invalid method prefix. size: %u < %u?\n",
1608                 plc, GNUNET_ntohll (req->op_id), size, sizeof (*req) + 1);
1609     GNUNET_break (0);
1610     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1611     return;
1612   }
1613
1614   struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
1615   opcls->client = client;
1616   opcls->plc = plc;
1617   opcls->op_id = req->op_id;
1618   opcls->flags = ntohl (req->flags);
1619
1620   if (0 == req->message_limit)
1621     GNUNET_PSYC_channel_history_replay (plc->channel,
1622                                         GNUNET_ntohll (req->start_message_id),
1623                                         GNUNET_ntohll (req->end_message_id),
1624                                         method_prefix, opcls->flags,
1625                                         &psyc_recv_history_message, NULL,
1626                                         &psyc_recv_history_result, opcls);
1627   else
1628     GNUNET_PSYC_channel_history_replay_latest (plc->channel,
1629                                                GNUNET_ntohll (req->message_limit),
1630                                                method_prefix, opcls->flags,
1631                                                &psyc_recv_history_message, NULL,
1632                                                &psyc_recv_history_result, opcls);
1633
1634   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1635 }
1636
1637
1638 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1639   { &client_recv_host_enter, NULL,
1640     GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER, 0 },
1641
1642   { &client_recv_guest_enter, NULL,
1643     GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER, 0 },
1644
1645   { &client_recv_join_decision, NULL,
1646     GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, 0 },
1647
1648   { &client_recv_psyc_message, NULL,
1649     GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 },
1650
1651   { &client_recv_history_replay, NULL,
1652     GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY, 0 },
1653 #if FIXME
1654   { &client_recv_state_get, NULL,
1655     GNUNET_MESSAGE_TYPE_PSYC_STATE_GET, 0 },
1656
1657   { &client_recv_state_get_prefix, NULL,
1658     GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX, 0 },
1659 #endif
1660   { NULL, NULL, 0, 0 }
1661 };
1662
1663
1664 /**
1665  * Initialize the PSYC service.
1666  *
1667  * @param cls Closure.
1668  * @param server The initialized server.
1669  * @param c Configuration to use.
1670  */
1671 static void
1672 run (void *cls, struct GNUNET_SERVER_Handle *server,
1673      const struct GNUNET_CONFIGURATION_Handle *c)
1674 {
1675   cfg = c;
1676   stats = GNUNET_STATISTICS_create ("social", cfg);
1677   hosts = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1678   guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1679   place_guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1680   nc = GNUNET_SERVER_notification_context_create (server, 1);
1681   GNUNET_SERVER_add_handlers (server, handlers);
1682   GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
1683   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1684                                 &shutdown_task, NULL);
1685 }
1686
1687
1688 /**
1689  * The main function for the service.
1690  *
1691  * @param argc number of arguments from the command line
1692  * @param argv command line arguments
1693  * @return 0 ok, 1 on error
1694  */
1695 int
1696 main (int argc, char *const *argv)
1697 {
1698   return (GNUNET_OK ==
1699           GNUNET_SERVICE_run (argc, argv, "social",
1700                               GNUNET_SERVICE_OPTION_NONE,
1701                               &run, NULL)) ? 0 : 1;
1702 }
1703
1704 /* end of gnunet-service-social.c */