reading alpha, beta from .conf
[oweals/gnunet.git] / src / social / gnunet-service-social.c
1 /*
2  * This file is part of GNUnet
3  * (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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, 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   /**
141    * Public key of the channel.
142    */
143   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
144
145   /**
146    * Hash of @a pub_key.
147    */
148   struct GNUNET_HashCode pub_key_hash;
149
150   /**
151    * Last message ID received for the place.
152    * 0 if there is no such message.
153    */
154   uint64_t max_message_id;
155
156   /**
157    * Is this a host (#GNUNET_YES), or guest (#GNUNET_NO)?
158    */
159   uint8_t is_host;
160
161   /**
162    * Is this place ready to receive messages from client?
163    * #GNUNET_YES or #GNUNET_NO
164    */
165   uint8_t is_ready;
166
167   /**
168    * Is the client disconnected?
169    * #GNUNET_YES or #GNUNET_NO
170    */
171   uint8_t is_disconnected;
172 };
173
174
175 /**
176  * Client context for a host.
177  */
178 struct Host
179 {
180   /**
181    * Place struct common for Host and Guest
182    */
183   struct Place plc;
184
185   /**
186    * Private key of the channel.
187    */
188   struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
189
190   /**
191    * Handle for the multicast origin.
192    */
193   struct GNUNET_PSYC_Master *master;
194
195   /**
196    * Transmit handle for multicast.
197    */
198   struct GNUNET_PSYC_MasterTransmitHandle *tmit_handle;
199
200   /**
201    * Incoming join requests.
202    * guest_key -> struct GNUNET_PSYC_JoinHandle *
203    */
204   struct GNUNET_CONTAINER_MultiHashMap *join_reqs;
205
206   /**
207    * @see enum GNUNET_PSYC_Policy
208    */
209   enum GNUNET_PSYC_Policy policy;
210 };
211
212
213 /**
214  * Client context for a guest.
215  */
216 struct Guest
217 {
218   /**
219    * Place struct common for Host and Guest.
220    */
221   struct Place plc;
222
223   /**
224    * Private key of the slave.
225    */
226   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
227
228   /**
229    * Public key of the slave.
230    */
231   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
232
233   /**
234    * Hash of @a pub_key.
235    */
236   struct GNUNET_HashCode pub_key_hash;
237
238   /**
239    * Handle for the PSYC slave.
240    */
241   struct GNUNET_PSYC_Slave *slave;
242
243   /**
244    * Transmit handle for multicast.
245    */
246   struct GNUNET_PSYC_SlaveTransmitHandle *tmit_handle;
247
248   /**
249    * Peer identity of the origin.
250    */
251   struct GNUNET_PeerIdentity origin;
252
253   /**
254    * Number of items in @a relays.
255    */
256   uint32_t relay_count;
257
258   /**
259    * Relays that multicast can use to connect.
260    */
261   struct GNUNET_PeerIdentity *relays;
262
263   /**
264    * Join request to be transmitted to the master on join.
265    */
266   struct GNUNET_MessageHeader *join_req;
267
268   /**
269    * Join decision received from PSYC.
270    */
271   struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
272
273 };
274
275
276 struct Client
277 {
278   /**
279    * Place where the client entered.
280    */
281   struct Place *plc;
282
283   /**
284    * Message queue for the message currently being transmitted
285    * by this client.
286    */
287   struct MessageTransmitQueue *tmit_msg;
288 };
289
290
291 static int
292 psyc_transmit_message (struct Place *plc);
293
294
295 /**
296  * Task run during shutdown.
297  *
298  * @param cls unused
299  * @param tc unused
300  */
301 static void
302 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
303 {
304   if (NULL != nc)
305   {
306     GNUNET_SERVER_notification_context_destroy (nc);
307     nc = NULL;
308   }
309   if (NULL != stats)
310   {
311     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
312     stats = NULL;
313   }
314 }
315
316
317 /**
318  * Clean up host data structures after a client disconnected.
319  */
320 static void
321 cleanup_host (struct Host *hst)
322 {
323   struct Place *plc = &hst->plc;
324
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);
329 }
330
331
332 /**
333  * Clean up guest data structures after a client disconnected.
334  */
335 static void
336 cleanup_guest (struct Guest *gst)
337 {
338   struct Place *plc = &gst->plc;
339   struct GNUNET_CONTAINER_MultiHashMap *
340     plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
341                                                 &plc->pub_key_hash);
342   GNUNET_assert (NULL != plc_gst);
343   GNUNET_CONTAINER_multihashmap_remove (plc_gst, &gst->pub_key_hash, gst);
344
345   if (0 == GNUNET_CONTAINER_multihashmap_size (plc_gst))
346   {
347     GNUNET_CONTAINER_multihashmap_remove (place_guests, &plc->pub_key_hash,
348                                           plc_gst);
349     GNUNET_CONTAINER_multihashmap_destroy (plc_gst);
350   }
351   GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, gst);
352
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);
360 }
361
362
363 /**
364  * Clean up place data structures after a client disconnected.
365  */
366 static void
367 cleanup_place (struct Place *plc)
368 {
369   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370               "%p Cleaning up place %s\n",
371               plc, GNUNET_h2s (&plc->pub_key_hash));
372
373   (GNUNET_YES == plc->is_host)
374     ? cleanup_host ((struct Host *) plc)
375     : cleanup_guest ((struct Guest *) plc);
376   GNUNET_free (plc);
377 }
378
379
380 static void
381 schedule_cleanup_place (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
382 {
383   cleanup_place (cls);
384 }
385
386
387 /**
388  * Called whenever a client is disconnected.
389  * Frees our resources associated with that client.
390  *
391  * @param cls Closure.
392  * @param client Identification of the client.
393  */
394 static void
395 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
396 {
397   if (NULL == client)
398     return;
399
400   struct Client *
401     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
402   if (NULL == ctx)
403   {
404     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
405                 "%p User context is NULL in client_disconnect()\n", ctx);
406     GNUNET_break (0);
407     return;
408   }
409
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));
415
416   struct ClientListItem *cli = plc->clients_head;
417   while (NULL != cli)
418   {
419     if (cli->client == client)
420     {
421       GNUNET_CONTAINER_DLL_remove (plc->clients_head, plc->clients_tail, cli);
422       GNUNET_free (cli);
423       break;
424     }
425     cli = cli->next;
426   }
427
428   if (NULL == plc->clients_head)
429   { /* Last client disconnected. */
430     if (GNUNET_YES != plc->is_disconnected)
431     {
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);
436       }
437       else
438       {
439         cleanup_place (plc);
440       }
441     }
442   }
443 }
444
445
446 /**
447  * Send message to all clients connected to the channel.
448  */
449 static void
450 client_send_msg (const struct Place *plc,
451                  const struct GNUNET_MessageHeader *msg)
452 {
453   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
454               "%p Sending message to clients.\n", plc);
455
456   struct ClientListItem *cli = plc->clients_head;
457   while (NULL != cli)
458   {
459     GNUNET_SERVER_notification_context_add (nc, cli->client);
460     GNUNET_SERVER_notification_context_unicast (nc, cli->client, msg, GNUNET_NO);
461     cli = cli->next;
462   }
463 }
464
465
466 /**
467  * Called after a PSYC master is started.
468  */
469 static void
470 psyc_master_started (void *cls, int result, uint64_t max_message_id)
471 {
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;
476
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);
482
483   client_send_msg (plc, &res.header);
484 }
485
486
487 /**
488  * Called when a PSYC master receives a join request.
489  */
490 static void
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)
496 {
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);
503 }
504
505
506 /**
507  * Called after a PSYC slave is connected.
508  */
509 static void
510 psyc_slave_connected (void *cls, int result, uint64_t max_message_id)
511 {
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;
516
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);
522
523   client_send_msg (plc, &res.header);
524 }
525
526
527 /**
528  * Called when a PSYC slave receives a join decision.
529  */
530 static void
531 psyc_recv_join_dcsn (void *cls,
532                      const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
533                      int is_admitted,
534                      const struct GNUNET_PSYC_Message *join_msg)
535 {
536   struct Guest *gst = cls;
537   client_send_msg (&gst->plc, &dcsn->header);
538 }
539
540
541 /**
542  * Called when a PSYC master or slave receives a message.
543  */
544 static void
545 psyc_recv_message (void *cls,
546                    uint64_t message_id,
547                    uint32_t flags,
548                    const struct GNUNET_PSYC_MessageHeader *msg)
549 {
550   struct Place *plc = cls;
551   client_send_msg (plc, &msg->header);
552
553   /* FIXME: further processing */
554 }
555
556
557 /**
558  * Initialize place data structure.
559  */
560 static void
561 place_init (struct Place *plc)
562 {
563
564 }
565
566
567 /**
568  * Handle a connecting client entering a place as host.
569  */
570 static void
571 client_recv_host_enter (void *cls, struct GNUNET_SERVER_Client *client,
572                         const struct GNUNET_MessageHeader *msg)
573 {
574   const struct HostEnterRequest *req
575     = (const struct HostEnterRequest *) msg;
576
577   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
578   struct GNUNET_HashCode pub_key_hash;
579
580   GNUNET_CRYPTO_eddsa_key_get_public (&req->place_key, &pub_key);
581   GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
582
583   struct Host *
584     hst = GNUNET_CONTAINER_multihashmap_get (hosts, &pub_key_hash);
585   struct Place *plc;
586
587   if (NULL == hst)
588   {
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);
593
594     plc = &hst->plc;
595     plc->is_host = GNUNET_YES;
596     plc->pub_key = pub_key;
597     plc->pub_key_hash = pub_key_hash;
598     place_init (plc);
599
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);
606   }
607   else
608   {
609     plc = &hst->plc;
610
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);
616
617     GNUNET_SERVER_notification_context_add (nc, client);
618     GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
619                                                 GNUNET_NO);
620   }
621
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));
625
626   struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
627   cli->client = client;
628   GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
629
630   struct Client *ctx = GNUNET_new (struct Client);
631   ctx->plc = plc;
632   GNUNET_SERVER_client_set_user_context (client, ctx);
633   GNUNET_SERVER_receive_done (client, GNUNET_OK);
634 }
635
636
637 /**
638  * Handle a connecting client entering a place as guest.
639  */
640 static void
641 client_recv_guest_enter (void *cls, struct GNUNET_SERVER_Client *client,
642                          const struct GNUNET_MessageHeader *msg)
643 {
644   const struct GuestEnterRequest *req
645     = (const struct GuestEnterRequest *) msg;
646   uint16_t req_size = ntohs (req->header.size);
647
648   struct GNUNET_CRYPTO_EcdsaPublicKey gst_pub_key;
649   struct GNUNET_HashCode pub_key_hash, gst_pub_key_hash;
650
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);
654
655   struct GNUNET_CONTAINER_MultiHashMap *
656     plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests, &pub_key_hash);
657   struct Guest *gst = NULL;
658   struct Place *plc;
659
660   if (NULL != plc_gst)
661   {
662     gst = GNUNET_CONTAINER_multihashmap_get (plc_gst, &gst_pub_key_hash);
663   }
664   if (NULL == gst || NULL == gst->slave)
665   {
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);
672
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;
678
679     if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
680         <= req_size)
681     {
682       join_msg = (struct GNUNET_PSYC_Message *)
683         (((char *) &req[1]) + relay_size);
684       join_msg_size = ntohs (join_msg->header.size);
685     }
686     if (sizeof (*req) + relay_size + join_msg_size != req_size)
687     {
688       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
689                   "%u + %u + %u != %u\n",
690                   sizeof (*req), relay_size, join_msg_size, req_size);
691       GNUNET_break (0);
692       GNUNET_SERVER_client_disconnect (client);
693       GNUNET_free (gst);
694       return;
695     }
696     if (0 < gst->relay_count)
697     {
698       gst->relays = GNUNET_malloc (relay_size);
699       memcpy (gst->relays, &req[1], relay_size);
700     }
701
702     plc = &gst->plc;
703     plc->is_host = GNUNET_NO;
704     plc->pub_key = req->place_key;
705     plc->pub_key_hash = pub_key_hash;
706     place_init (plc);
707
708     if (NULL == plc_gst)
709     {
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);
713     }
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);
718     gst->slave
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);
723   }
724   else
725   {
726     plc = &gst->plc;
727
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);
733
734     GNUNET_SERVER_notification_context_add (nc, client);
735     GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
736                                                 GNUNET_NO);
737     if (NULL != gst->join_dcsn)
738     {
739       GNUNET_SERVER_notification_context_add (nc, client);
740       GNUNET_SERVER_notification_context_unicast (nc, client,
741                                                   &gst->join_dcsn->header,
742                                                   GNUNET_NO);
743     }
744   }
745
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));
749
750   struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
751   cli->client = client;
752   GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
753
754   struct Client *ctx = GNUNET_new (struct Client);
755   ctx->plc = plc;
756   GNUNET_SERVER_client_set_user_context (client, ctx);
757   GNUNET_SERVER_receive_done (client, GNUNET_OK);
758 }
759
760
761 struct JoinDecisionClosure
762 {
763   int32_t is_admitted;
764   struct GNUNET_PSYC_Message *msg;
765 };
766
767
768 /**
769  * Iterator callback for responding to join requests.
770  */
771 static int
772 psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
773                          void *value)
774 {
775   struct JoinDecisionClosure *jcls = cls;
776   struct GNUNET_PSYC_JoinHandle *jh = value;
777   // FIXME: add relays
778   GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
779   return GNUNET_YES;
780 }
781
782
783 /**
784  * Handle an entry decision from a host client.
785  */
786 static void
787 client_recv_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
788                            const struct GNUNET_MessageHeader *msg)
789 {
790   struct Client *
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;
796
797   struct GNUNET_PSYC_JoinDecisionMessage *
798     dcsn = (struct GNUNET_PSYC_JoinDecisionMessage *) msg;
799   struct JoinDecisionClosure jcls;
800   jcls.is_admitted = ntohl (dcsn->is_admitted);
801   jcls.msg
802     = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
803     ? (struct GNUNET_PSYC_Message *) &dcsn[1]
804     : NULL;
805
806   struct GNUNET_HashCode slave_key_hash;
807   GNUNET_CRYPTO_hash (&dcsn->slave_key, sizeof (dcsn->slave_key),
808                       &slave_key_hash);
809
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));
816
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);
821 }
822
823
824 /**
825  * Send acknowledgement to a client.
826  *
827  * Sent after a message fragment has been passed on to multicast.
828  *
829  * @param plc The place struct for the client.
830  */
831 static void
832 send_message_ack (struct Place *plc, struct GNUNET_SERVER_Client *client)
833 {
834   struct GNUNET_MessageHeader res;
835   res.size = htons (sizeof (res));
836   res.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
837
838   GNUNET_SERVER_notification_context_add (nc, client);
839   GNUNET_SERVER_notification_context_unicast (nc, client, &res, GNUNET_NO);
840 }
841
842
843 /**
844  * Proceed to the next message part in the transmission queue.
845  *
846  * @param plc
847  *        Place where the transmission is going on.
848  * @param tmit_msg
849  *        Currently transmitted message.
850  * @param tmit_frag
851  *        Currently transmitted message fragment.
852  *
853  * @return @a tmit_frag, or NULL if reached the end of fragment.
854  */
855 static struct FragmentTransmitQueue *
856 psyc_transmit_queue_next_part (struct Place *plc,
857                                struct MessageTransmitQueue *tmit_msg,
858                                struct FragmentTransmitQueue *tmit_frag)
859 {
860   uint16_t psize = ntohs (tmit_frag->next_part->size);
861   if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
862       < tmit_frag->size)
863   {
864     tmit_frag->next_part
865       = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + psize);
866   }
867   else /* Reached end of current fragment. */
868   {
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);
873     tmit_frag = NULL;
874   }
875   return tmit_frag;
876 }
877
878
879 /**
880  * Proceed to next message in transmission queue.
881  *
882  * @param plc
883  *        Place where the transmission is going on.
884  * @param tmit_msg
885  *        Currently transmitted message.
886  *
887  * @return The next message in queue, or NULL if queue is empty.
888  */
889 static struct MessageTransmitQueue *
890 psyc_transmit_queue_next_msg (struct Place *plc,
891                               struct MessageTransmitQueue *tmit_msg)
892 {
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;
896 }
897
898
899 /**
900  * Callback for data transmission to PSYC.
901  */
902 static int
903 psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
904 {
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 */
911     *data_size = 0;
912     return GNUNET_NO;
913   }
914   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
915   if (NULL == pmsg)
916   {
917     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
918                 "%p psyc_transmit_notify_data: nothing to send.\n", plc);
919     *data_size = 0;
920     return GNUNET_NO;
921   }
922
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);
926
927   uint16_t ptype = ntohs (pmsg->type);
928   uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
929   int ret;
930
931   switch (ptype)
932   {
933   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
934     if (*data_size < pdata_size)
935     {
936       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
937                 "%p psyc_transmit_notify_data: buffer size too small for data.\n", plc);
938       *data_size = 0;
939       return GNUNET_NO;
940     }
941     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
942                 "%p psyc_transmit_notify_data: sending %u bytes.\n",
943                 plc, pdata_size);
944
945     *data_size = pdata_size;
946     memcpy (data, &pmsg[1], *data_size);
947     ret = GNUNET_NO;
948     break;
949
950   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
951     *data_size = 0;
952     ret = GNUNET_YES;
953     break;
954
955   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
956     *data_size = 0;
957     ret = GNUNET_SYSERR;
958     break;
959
960   default:
961     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
962                 "%p psyc_transmit_notify_data: unexpected message part of type %u.\n",
963                 plc, ptype);
964     ret = GNUNET_SYSERR;
965   }
966
967   if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
968   {
969     *data_size = 0;
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);
974     return ret;
975   }
976   else
977   {
978     tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
979     if (NULL != tmit_frag)
980     {
981       struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
982       ptype = ntohs (pmsg->type);
983       switch (ptype)
984       {
985       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
986         ret = GNUNET_YES;
987         break;
988       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
989         ret = GNUNET_SYSERR;
990         break;
991       }
992       switch (ptype)
993       {
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);
997       }
998     }
999
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);
1004     }
1005   }
1006
1007   if (ret != GNUNET_NO)
1008   {
1009     if (NULL != tmit_msg)
1010     {
1011       psyc_transmit_message (plc);
1012     }
1013     else if (GNUNET_YES == plc->is_disconnected)
1014     {
1015       /* FIXME: handle partial message (when still in_transmit) */
1016       cleanup_place (plc);
1017     }
1018   }
1019   return ret;
1020 }
1021
1022
1023 /**
1024  * Callback for modifier transmission to PSYC.
1025  */
1026 static int
1027 psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1028                           uint8_t *oper, uint32_t *full_value_size)
1029 {
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 */
1036     *data_size = 0;
1037     return GNUNET_NO;
1038   }
1039   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1040   if (NULL == pmsg)
1041   {
1042     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043                 "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
1044     *data_size = 0;
1045     return GNUNET_NO;
1046   }
1047
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);
1051
1052   uint16_t ptype = ntohs (pmsg->type);
1053   int ret;
1054
1055   switch (ptype)
1056   {
1057   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1058   {
1059     if (NULL == oper)
1060     {
1061       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1062                   "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
1063       ret = GNUNET_SYSERR;
1064       break;
1065     }
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);
1069
1070     if (*data_size < mod_size)
1071     {
1072       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1073                 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1074       *data_size = 0;
1075       return GNUNET_NO;
1076     }
1077
1078     *full_value_size = ntohl (pmod->value_size);
1079     *oper = pmod->oper;
1080     *data_size = mod_size;
1081     memcpy (data, &pmod[1], mod_size);
1082     ret = GNUNET_NO;
1083     break;
1084   }
1085
1086   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1087   {
1088     if (NULL != oper)
1089     {
1090       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1091                   "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
1092       ret = GNUNET_SYSERR;
1093       break;
1094     }
1095     uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
1096     if (*data_size < mod_size)
1097     {
1098       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1099                 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1100       *data_size = 0;
1101       return GNUNET_NO;
1102     }
1103     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1104                 "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, mod_size);
1105
1106     *data_size = mod_size;
1107     memcpy (data, &pmsg[1], *data_size);
1108     ret = GNUNET_NO;
1109     break;
1110   }
1111
1112   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1113   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1114   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1115     *data_size = 0;
1116     ret = GNUNET_YES;
1117     break;
1118
1119   default:
1120     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1121                 "%p psyc_transmit_notify_mod: unexpected message part of type %u.\n",
1122                 plc, ptype);
1123     ret = GNUNET_SYSERR;
1124   }
1125
1126   if (GNUNET_SYSERR == ret)
1127   {
1128     *data_size = 0;
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);
1134   }
1135   else
1136   {
1137     if (GNUNET_YES != ret)
1138       psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1139
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);
1144     }
1145   }
1146   return ret;
1147 }
1148
1149 /**
1150  * Callback for data transmission from a host to PSYC.
1151  */
1152 static int
1153 host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1154 {
1155   int ret = psyc_transmit_notify_data (cls, data_size, data);
1156
1157   if (GNUNET_NO != ret)
1158   {
1159     struct Host *hst = cls;
1160     hst->tmit_handle = NULL;
1161   }
1162   return ret;
1163 }
1164
1165
1166 /**
1167  * Callback for the transmit functions of multicast.
1168  */
1169 static int
1170 guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1171 {
1172   int ret = psyc_transmit_notify_data (cls, data_size, data);
1173
1174   if (GNUNET_NO != ret)
1175   {
1176     struct Guest *gst = cls;
1177     gst->tmit_handle = NULL;
1178   }
1179   return ret;
1180 }
1181
1182
1183 /**
1184  * Callback for modifier transmission from a host to PSYC.
1185  */
1186 static int
1187 host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1188                           uint8_t *oper, uint32_t *full_value_size)
1189 {
1190   int ret = psyc_transmit_notify_mod (cls, data_size, data,
1191                                       oper, full_value_size);
1192   if (GNUNET_SYSERR == ret)
1193   {
1194     struct Host *hst = cls;
1195     hst->tmit_handle = NULL;
1196   }
1197   return ret;
1198 }
1199
1200
1201 /**
1202  * Callback for modifier transmission from a guest to PSYC.
1203  */
1204 static int
1205 guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1206                            uint8_t *oper, uint32_t *full_value_size)
1207 {
1208   int ret = psyc_transmit_notify_mod (cls, data_size, data,
1209                                       oper, full_value_size);
1210   if (GNUNET_SYSERR == ret)
1211   {
1212     struct Guest *gst = cls;
1213     gst->tmit_handle = NULL;
1214   }
1215   return ret;
1216 }
1217
1218
1219 /**
1220  * Get method part of next message from transmission queue.
1221  *
1222  * @param tmit_msg
1223  *        Next item in message transmission queue.
1224  * @param[out] pmeth
1225  *        The message method is returned here.
1226  *
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.
1230  */
1231 static int
1232 psyc_transmit_queue_next_method (struct Place *plc,
1233                                  struct GNUNET_PSYC_MessageMethod **pmeth)
1234 {
1235   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1236   if (NULL == tmit_msg)
1237     return GNUNET_NO;
1238
1239   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1240   if (NULL == tmit_frag)
1241   {
1242     GNUNET_break (0);
1243     return GNUNET_NO;
1244   }
1245
1246   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1247   if (NULL == pmsg
1248       || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
1249   {
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));
1253     GNUNET_break (0);
1254     return GNUNET_SYSERR;
1255   }
1256
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))
1260   {
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));
1267     GNUNET_break (0);
1268     return GNUNET_SYSERR;
1269   }
1270
1271   psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1272   return GNUNET_OK;
1273 }
1274
1275
1276 /**
1277  * Transmit the next message in queue from the host to the PSYC channel.
1278  */
1279 static int
1280 psyc_master_transmit_message (struct Host *hst)
1281 {
1282
1283   if (NULL == hst->tmit_handle)
1284   {
1285     struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1286     int ret = psyc_transmit_queue_next_method (&hst->plc, &pmeth);
1287     if (GNUNET_OK != ret)
1288       return ret;
1289
1290     hst->tmit_handle
1291       = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
1292                                      &host_transmit_notify_mod,
1293                                      &host_transmit_notify_data, hst,
1294                                      pmeth->flags);
1295   }
1296   else
1297   {
1298     GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
1299   }
1300   return GNUNET_OK;
1301 }
1302
1303
1304 /**
1305  * Transmit the next message in queue from a guest to the PSYC channel.
1306  */
1307 static int
1308 psyc_slave_transmit_message (struct Guest *gst)
1309 {
1310   if (NULL == gst->tmit_handle)
1311   {
1312     struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1313     int ret = psyc_transmit_queue_next_method (&gst->plc, &pmeth);
1314     if (GNUNET_OK != ret)
1315       return ret;
1316
1317     gst->tmit_handle
1318       = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
1319                                     &guest_transmit_notify_mod,
1320                                     &guest_transmit_notify_data, gst,
1321                                     pmeth->flags);
1322   }
1323   else
1324   {
1325     GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
1326   }
1327   return GNUNET_OK;
1328 }
1329
1330
1331 /**
1332  * Transmit a message to PSYC.
1333  */
1334 static int
1335 psyc_transmit_message (struct Place *plc)
1336 {
1337   return
1338     (plc->is_host)
1339     ? psyc_master_transmit_message ((struct Host *) plc)
1340     : psyc_slave_transmit_message ((struct Guest *) plc);
1341 }
1342
1343
1344 /**
1345  * Queue message parts for sending to PSYC.
1346  *
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.
1353  */
1354 static struct MessageTransmitQueue *
1355 psyc_transmit_queue_message (struct Place *plc,
1356                              struct GNUNET_SERVER_Client *client,
1357                              size_t data_size,
1358                              const void *data,
1359                              uint16_t first_ptype, uint16_t last_ptype,
1360                              struct MessageTransmitQueue *tmit_msg)
1361 {
1362   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
1363   {
1364     tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
1365     GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
1366   }
1367   else if (NULL == tmit_msg)
1368   {
1369     return NULL;
1370   }
1371
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;
1378
1379   GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
1380   tmit_msg->client = client;
1381   return tmit_msg;
1382 }
1383
1384
1385 /**
1386  * Cancel transmission of current message to PSYC.
1387  *
1388  * @param plc     Place to send to.
1389  * @param client  Client the message originates from.
1390  */
1391 static void
1392 psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVER_Client *client)
1393 {
1394   uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
1395
1396   struct GNUNET_MessageHeader msg;
1397   msg.size = htons (sizeof (msg));
1398   msg.type = htons (type);
1399
1400   psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, NULL);
1401   psyc_transmit_message (plc);
1402
1403   /* FIXME: cleanup */
1404 }
1405
1406
1407 /**
1408  * Handle an incoming message from a client, to be transmitted to the place.
1409  */
1410 static void
1411 client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
1412                           const struct GNUNET_MessageHeader *msg)
1413 {
1414   struct Client *
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;
1419
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);
1423
1424   if (GNUNET_YES != plc->is_ready)
1425   {
1426     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1427                 "%p Place is not ready yet, disconnecting client.\n", plc);
1428     GNUNET_break (0);
1429     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1430     return;
1431   }
1432
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)
1437   {
1438     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1439                 "%p Received message with invalid payload size (%u) from client.\n",
1440                 plc, psize);
1441     GNUNET_break (0);
1442     psyc_transmit_cancel (plc, client);
1443     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1444     return;
1445   }
1446
1447   uint16_t first_ptype = 0, last_ptype = 0;
1448   if (GNUNET_SYSERR
1449       == GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
1450                                           &first_ptype, &last_ptype))
1451   {
1452     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1453                 "%p Received invalid message part from client.\n", plc);
1454     GNUNET_break (0);
1455     psyc_transmit_cancel (plc, client);
1456     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1457     return;
1458   }
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);
1462
1463   ctx->tmit_msg
1464     = psyc_transmit_queue_message (plc, client, psize, &msg[1],
1465                                    first_ptype, last_ptype, ctx->tmit_msg);
1466   if (NULL != ctx->tmit_msg)
1467   {
1468     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
1469       ctx->tmit_msg = NULL;
1470     ret = psyc_transmit_message (plc);
1471   }
1472
1473   if (GNUNET_OK != ret)
1474   {
1475     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1476                 "%p Received invalid message part from client.\n", plc);
1477     GNUNET_break (0);
1478     psyc_transmit_cancel (plc, client);
1479     ret = GNUNET_SYSERR;
1480   }
1481   GNUNET_SERVER_receive_done (client, ret);
1482 }
1483
1484
1485 /**
1486  * Initialize the PSYC service.
1487  *
1488  * @param cls Closure.
1489  * @param server The initialized server.
1490  * @param c Configuration to use.
1491  */
1492 static void
1493 run (void *cls, struct GNUNET_SERVER_Handle *server,
1494      const struct GNUNET_CONFIGURATION_Handle *c)
1495 {
1496   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1497     { &client_recv_host_enter, NULL,
1498       GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER, 0 },
1499
1500     { &client_recv_guest_enter, NULL,
1501       GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER, 0 },
1502
1503     { &client_recv_join_decision, NULL,
1504       GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, 0 },
1505
1506     { &client_recv_psyc_message, NULL,
1507       GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 }
1508   };
1509
1510   cfg = c;
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);
1520 }
1521
1522
1523 /**
1524  * The main function for the service.
1525  *
1526  * @param argc number of arguments from the command line
1527  * @param argv command line arguments
1528  * @return 0 ok, 1 on error
1529  */
1530 int
1531 main (int argc, char *const *argv)
1532 {
1533   return (GNUNET_OK ==
1534           GNUNET_SERVICE_run (argc, argv, "social",
1535                               GNUNET_SERVICE_OPTION_NONE,
1536                               &run, NULL)) ? 0 : 1;
1537 }
1538
1539 /* end of gnunet-service-social.c */