de41637203c0d55c5f49e7fff8712046d6ffcd93
[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, 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 CountersResult 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 (GNUNET_OK);
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, 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 CountersResult 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 (GNUNET_OK);
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 CountersResult 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       return;
694     }
695     if (0 < gst->relay_count)
696     {
697       gst->relays = GNUNET_malloc (relay_size);
698       memcpy (gst->relays, &req[1], relay_size);
699     }
700
701     plc = &gst->plc;
702     plc->is_host = GNUNET_NO;
703     plc->pub_key = req->place_key;
704     plc->pub_key_hash = pub_key_hash;
705     place_init (plc);
706
707     if (NULL == plc_gst)
708     {
709       plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
710       GNUNET_CONTAINER_multihashmap_put (place_guests, &plc->pub_key_hash, plc_gst,
711                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
712     }
713     GNUNET_CONTAINER_multihashmap_put (plc_gst, &gst->pub_key_hash, plc,
714                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
715     GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, plc,
716                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
717     gst->slave
718       = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &gst->priv_key,
719                                 &gst->origin, gst->relay_count, gst->relays,
720                                 &psyc_recv_message, NULL, &psyc_slave_connected,
721                                 &psyc_recv_join_dcsn, gst, join_msg);
722   }
723   else
724   {
725     plc = &gst->plc;
726
727     struct CountersResult res;
728     res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
729     res.header.size = htons (sizeof (res));
730     res.result_code = htonl (GNUNET_OK);
731     res.max_message_id = GNUNET_htonll (plc->max_message_id);
732
733     GNUNET_SERVER_notification_context_add (nc, client);
734     GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
735                                                 GNUNET_NO);
736     if (NULL != gst->join_dcsn)
737     {
738       GNUNET_SERVER_notification_context_add (nc, client);
739       GNUNET_SERVER_notification_context_unicast (nc, client,
740                                                   &gst->join_dcsn->header,
741                                                   GNUNET_NO);
742     }
743   }
744
745   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
746               "%p Client connected as guest to place %s.\n",
747               gst, GNUNET_h2s (&plc->pub_key_hash));
748
749   struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
750   cli->client = client;
751   GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
752
753   struct Client *ctx = GNUNET_new (struct Client);
754   ctx->plc = plc;
755   GNUNET_SERVER_client_set_user_context (client, ctx);
756   GNUNET_SERVER_receive_done (client, GNUNET_OK);
757 }
758
759
760 struct JoinDecisionClosure
761 {
762   int32_t is_admitted;
763   struct GNUNET_PSYC_Message *msg;
764 };
765
766
767 /**
768  * Iterator callback for responding to join requests.
769  */
770 static int
771 psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
772                          void *value)
773 {
774   struct JoinDecisionClosure *jcls = cls;
775   struct GNUNET_PSYC_JoinHandle *jh = value;
776   // FIXME: add relays
777   GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
778   return GNUNET_YES;
779 }
780
781
782 /**
783  * Handle an entry decision from a host client.
784  */
785 static void
786 client_recv_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
787                            const struct GNUNET_MessageHeader *msg)
788 {
789   struct Client *
790     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
791   GNUNET_assert (NULL != ctx);
792   struct Place *plc = ctx->plc;
793   GNUNET_assert (GNUNET_YES == plc->is_host);
794   struct Host *hst = (struct Host *) plc;
795
796   struct GNUNET_PSYC_JoinDecisionMessage *
797     dcsn = (struct GNUNET_PSYC_JoinDecisionMessage *) msg;
798   struct JoinDecisionClosure jcls;
799   jcls.is_admitted = ntohl (dcsn->is_admitted);
800   jcls.msg
801     = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
802     ? (struct GNUNET_PSYC_Message *) &dcsn[1]
803     : NULL;
804
805   struct GNUNET_HashCode slave_key_hash;
806   GNUNET_CRYPTO_hash (&dcsn->slave_key, sizeof (dcsn->slave_key),
807                       &slave_key_hash);
808
809   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
810               "%p Got join decision (%d) from client for place %s..\n",
811               hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
812   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
813               "%p ..and slave %s.\n",
814               hst, GNUNET_h2s (&slave_key_hash));
815
816   GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_key_hash,
817                                               &psyc_send_join_decision, &jcls);
818   GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_key_hash);
819   GNUNET_SERVER_receive_done (client, GNUNET_OK);
820 }
821
822
823 /**
824  * Send acknowledgement to a client.
825  *
826  * Sent after a message fragment has been passed on to multicast.
827  *
828  * @param plc The place struct for the client.
829  */
830 static void
831 send_message_ack (struct Place *plc, struct GNUNET_SERVER_Client *client)
832 {
833   struct GNUNET_MessageHeader res;
834   res.size = htons (sizeof (res));
835   res.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
836
837   GNUNET_SERVER_notification_context_add (nc, client);
838   GNUNET_SERVER_notification_context_unicast (nc, client, &res, GNUNET_NO);
839 }
840
841
842 /**
843  * Proceed to the next message part in the transmission queue.
844  *
845  * @param plc
846  *        Place where the transmission is going on.
847  * @param tmit_msg
848  *        Currently transmitted message.
849  * @param tmit_frag
850  *        Currently transmitted message fragment.
851  *
852  * @return @a tmit_frag, or NULL if reached the end of fragment.
853  */
854 static struct FragmentTransmitQueue *
855 psyc_transmit_queue_next_part (struct Place *plc,
856                                struct MessageTransmitQueue *tmit_msg,
857                                struct FragmentTransmitQueue *tmit_frag)
858 {
859   uint16_t psize = ntohs (tmit_frag->next_part->size);
860   if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
861       < tmit_frag->size)
862   {
863     tmit_frag->next_part
864       = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + psize);
865   }
866   else /* Reached end of current fragment. */
867   {
868     if (NULL != tmit_frag->client)
869       send_message_ack (plc, tmit_frag->client);
870     GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
871     GNUNET_free (tmit_frag);
872     tmit_frag = NULL;
873   }
874   return tmit_frag;
875 }
876
877
878 /**
879  * Proceed to next message in transmission queue.
880  *
881  * @param plc
882  *        Place where the transmission is going on.
883  * @param tmit_msg
884  *        Currently transmitted message.
885  *
886  * @return The next message in queue, or NULL if queue is empty.
887  */
888 static struct MessageTransmitQueue *
889 psyc_transmit_queue_next_msg (struct Place *plc,
890                               struct MessageTransmitQueue *tmit_msg)
891 {
892   GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
893   GNUNET_free (tmit_msg);
894   return plc->tmit_msgs_head;
895 }
896
897
898 /**
899  * Callback for data transmission to PSYC.
900  */
901 static int
902 psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
903 {
904   struct Place *plc = cls;
905   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
906   GNUNET_assert (NULL != tmit_msg);
907   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
908   if (NULL == tmit_frag)
909   { /* Rest of the message have not arrived yet, pause transmission */
910     *data_size = 0;
911     return GNUNET_NO;
912   }
913   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
914   if (NULL == pmsg)
915   {
916     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
917                 "%p psyc_transmit_notify_data: nothing to send.\n", plc);
918     *data_size = 0;
919     return GNUNET_NO;
920   }
921
922   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923               "%p psyc_transmit_notify_data()\n", plc);
924   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
925
926   uint16_t ptype = ntohs (pmsg->type);
927   uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
928   int ret;
929
930   switch (ptype)
931   {
932   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
933     if (*data_size < pdata_size)
934     {
935       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
936                 "%p psyc_transmit_notify_data: buffer size too small for data.\n", plc);
937       *data_size = 0;
938       return GNUNET_NO;
939     }
940     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
941                 "%p psyc_transmit_notify_data: sending %u bytes.\n",
942                 plc, pdata_size);
943
944     *data_size = pdata_size;
945     memcpy (data, &pmsg[1], *data_size);
946     ret = GNUNET_NO;
947     break;
948
949   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
950     *data_size = 0;
951     ret = GNUNET_YES;
952     break;
953
954   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
955     *data_size = 0;
956     ret = GNUNET_SYSERR;
957     break;
958
959   default:
960     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
961                 "%p psyc_transmit_notify_data: unexpected message part of type %u.\n",
962                 plc, ptype);
963     ret = GNUNET_SYSERR;
964   }
965
966   if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
967   {
968     *data_size = 0;
969     tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
970     plc->is_disconnected = GNUNET_YES;
971     GNUNET_SERVER_client_disconnect (tmit_frag->client);
972     GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
973     return ret;
974   }
975   else
976   {
977     tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
978     if (NULL != tmit_frag)
979     {
980       struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
981       ptype = ntohs (pmsg->type);
982       switch (ptype)
983       {
984       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
985         ret = GNUNET_YES;
986         break;
987       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
988         ret = GNUNET_SYSERR;
989         break;
990       }
991       switch (ptype)
992       {
993       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
994       case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
995         tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
996       }
997     }
998
999     if (NULL == tmit_msg->frags_head
1000         && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1001     { /* Reached end of current message. */
1002       tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1003     }
1004   }
1005
1006   if (ret != GNUNET_NO)
1007   {
1008     if (NULL != tmit_msg)
1009     {
1010       psyc_transmit_message (plc);
1011     }
1012     else if (GNUNET_YES == plc->is_disconnected)
1013     {
1014       /* FIXME: handle partial message (when still in_transmit) */
1015       cleanup_place (plc);
1016     }
1017   }
1018   return ret;
1019 }
1020
1021
1022 /**
1023  * Callback for modifier transmission to PSYC.
1024  */
1025 static int
1026 psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1027                           uint8_t *oper, uint32_t *full_value_size)
1028 {
1029   struct Place *plc = cls;
1030   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1031   GNUNET_assert (NULL != tmit_msg);
1032   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1033   if (NULL == tmit_frag)
1034   { /* Rest of the message have not arrived yet, pause transmission */
1035     *data_size = 0;
1036     return GNUNET_NO;
1037   }
1038   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1039   if (NULL == pmsg)
1040   {
1041     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1042                 "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
1043     *data_size = 0;
1044     return GNUNET_NO;
1045   }
1046
1047   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1048               "%p psyc_transmit_notify_mod()\n", plc);
1049   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
1050
1051   uint16_t ptype = ntohs (pmsg->type);
1052   int ret;
1053
1054   switch (ptype)
1055   {
1056   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1057   {
1058     if (NULL == oper)
1059     {
1060       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1061                   "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
1062       ret = GNUNET_SYSERR;
1063       break;
1064     }
1065     struct GNUNET_PSYC_MessageModifier *
1066       pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
1067     uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
1068
1069     if (*data_size < mod_size)
1070     {
1071       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1072                 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1073       *data_size = 0;
1074       return GNUNET_NO;
1075     }
1076
1077     *full_value_size = ntohl (pmod->value_size);
1078     *oper = pmod->oper;
1079     *data_size = mod_size;
1080     memcpy (data, &pmod[1], mod_size);
1081     ret = GNUNET_NO;
1082     break;
1083   }
1084
1085   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1086   {
1087     if (NULL != oper)
1088     {
1089       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1090                   "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
1091       ret = GNUNET_SYSERR;
1092       break;
1093     }
1094     uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
1095     if (*data_size < mod_size)
1096     {
1097       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1098                 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
1099       *data_size = 0;
1100       return GNUNET_NO;
1101     }
1102     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1103                 "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, mod_size);
1104
1105     *data_size = mod_size;
1106     memcpy (data, &pmsg[1], *data_size);
1107     ret = GNUNET_NO;
1108     break;
1109   }
1110
1111   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1112   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1113   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1114     *data_size = 0;
1115     ret = GNUNET_YES;
1116     break;
1117
1118   default:
1119     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1120                 "%p psyc_transmit_notify_mod: unexpected message part of type %u.\n",
1121                 plc, ptype);
1122     ret = GNUNET_SYSERR;
1123   }
1124
1125   if (GNUNET_SYSERR == ret)
1126   {
1127     *data_size = 0;
1128     ret = GNUNET_SYSERR;
1129     tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1130     plc->is_disconnected = GNUNET_YES;
1131     GNUNET_SERVER_client_disconnect (tmit_frag->client);
1132     GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
1133   }
1134   else
1135   {
1136     if (GNUNET_YES != ret)
1137       psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1138
1139     if (NULL == tmit_msg->frags_head
1140         && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
1141     { /* Reached end of current message. */
1142       tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
1143     }
1144   }
1145   return ret;
1146 }
1147
1148 /**
1149  * Callback for data transmission from a host to PSYC.
1150  */
1151 static int
1152 host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1153 {
1154   int ret = psyc_transmit_notify_data (cls, data_size, data);
1155
1156   if (GNUNET_NO != ret)
1157   {
1158     struct Host *hst = cls;
1159     hst->tmit_handle = NULL;
1160   }
1161   return ret;
1162 }
1163
1164
1165 /**
1166  * Callback for the transmit functions of multicast.
1167  */
1168 static int
1169 guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
1170 {
1171   int ret = psyc_transmit_notify_data (cls, data_size, data);
1172
1173   if (GNUNET_NO != ret)
1174   {
1175     struct Guest *gst = cls;
1176     gst->tmit_handle = NULL;
1177   }
1178   return ret;
1179 }
1180
1181
1182 /**
1183  * Callback for modifier transmission from a host to PSYC.
1184  */
1185 static int
1186 host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1187                           uint8_t *oper, uint32_t *full_value_size)
1188 {
1189   int ret = psyc_transmit_notify_mod (cls, data_size, data,
1190                                       oper, full_value_size);
1191   if (GNUNET_SYSERR == ret)
1192   {
1193     struct Host *hst = cls;
1194     hst->tmit_handle = NULL;
1195   }
1196   return ret;
1197 }
1198
1199
1200 /**
1201  * Callback for modifier transmission from a guest to PSYC.
1202  */
1203 static int
1204 guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
1205                            uint8_t *oper, uint32_t *full_value_size)
1206 {
1207   int ret = psyc_transmit_notify_mod (cls, data_size, data,
1208                                       oper, full_value_size);
1209   if (GNUNET_SYSERR == ret)
1210   {
1211     struct Guest *gst = cls;
1212     gst->tmit_handle = NULL;
1213   }
1214   return ret;
1215 }
1216
1217
1218 /**
1219  * Get method part of next message from transmission queue.
1220  *
1221  * @param tmit_msg
1222  *        Next item in message transmission queue.
1223  * @param[out] pmeth
1224  *        The message method is returned here.
1225  *
1226  * @return #GNUNET_OK on success
1227  *         #GNUNET_NO if there are no more messages in queue.
1228  *         #GNUNET_SYSERR if the next message is malformed.
1229  */
1230 static int
1231 psyc_transmit_queue_next_method (struct Place *plc,
1232                                  struct GNUNET_PSYC_MessageMethod **pmeth)
1233 {
1234   struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
1235   if (NULL == tmit_msg)
1236     return GNUNET_NO;
1237
1238   struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
1239   if (NULL == tmit_frag)
1240   {
1241     GNUNET_break (0);
1242     return GNUNET_NO;
1243   }
1244
1245   struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
1246   if (NULL == pmsg
1247       || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
1248   {
1249     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1250                 "%p psyc_transmit_queue_next_method: unexpected message part of type %u.\n",
1251                 plc, ntohs (pmsg->type));
1252     GNUNET_break (0);
1253     return GNUNET_SYSERR;
1254   }
1255
1256   uint16_t psize = ntohs (pmsg->size);
1257   *pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
1258   if (psize < sizeof (**pmeth) + 1 || '\0' != *((char *) *pmeth + psize - 1))
1259   {
1260     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1261                 "%p psyc_transmit_queue_next_method: invalid method name.\n",
1262                 plc, ntohs (pmsg->type));
1263     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1264                 "%u <= %u || NUL != %u\n",
1265                 sizeof (**pmeth), psize, *((char *) *pmeth + psize - 1));
1266     GNUNET_break (0);
1267     return GNUNET_SYSERR;
1268   }
1269
1270   psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
1271   return GNUNET_OK;
1272 }
1273
1274
1275 /**
1276  * Transmit the next message in queue from the host to the PSYC channel.
1277  */
1278 static int
1279 psyc_master_transmit_message (struct Host *hst)
1280 {
1281
1282   if (NULL == hst->tmit_handle)
1283   {
1284     struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1285     int ret = psyc_transmit_queue_next_method (&hst->plc, &pmeth);
1286     if (GNUNET_OK != ret)
1287       return ret;
1288
1289     hst->tmit_handle
1290       = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
1291                                      &host_transmit_notify_mod,
1292                                      &host_transmit_notify_data, hst,
1293                                      pmeth->flags);
1294   }
1295   else
1296   {
1297     GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
1298   }
1299   return GNUNET_OK;
1300 }
1301
1302
1303 /**
1304  * Transmit the next message in queue from a guest to the PSYC channel.
1305  */
1306 static int
1307 psyc_slave_transmit_message (struct Guest *gst)
1308 {
1309   if (NULL == gst->tmit_handle)
1310   {
1311     struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
1312     int ret = psyc_transmit_queue_next_method (&gst->plc, &pmeth);
1313     if (GNUNET_OK != ret)
1314       return ret;
1315
1316     gst->tmit_handle
1317       = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
1318                                     &guest_transmit_notify_mod,
1319                                     &guest_transmit_notify_data, gst,
1320                                     pmeth->flags);
1321   }
1322   else
1323   {
1324     GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
1325   }
1326   return GNUNET_OK;
1327 }
1328
1329
1330 /**
1331  * Transmit a message to PSYC.
1332  */
1333 static int
1334 psyc_transmit_message (struct Place *plc)
1335 {
1336   return
1337     (plc->is_host)
1338     ? psyc_master_transmit_message ((struct Host *) plc)
1339     : psyc_slave_transmit_message ((struct Guest *) plc);
1340 }
1341
1342
1343 /**
1344  * Queue message parts for sending to PSYC.
1345  *
1346  * @param plc          Place to send to.
1347  * @param client       Client the message originates from.
1348  * @param data_size    Size of @a data.
1349  * @param data         Concatenated message parts.
1350  * @param first_ptype  First message part type in @a data.
1351  * @param last_ptype   Last message part type in @a data.
1352  */
1353 static struct MessageTransmitQueue *
1354 psyc_transmit_queue_message (struct Place *plc,
1355                              struct GNUNET_SERVER_Client *client,
1356                              size_t data_size,
1357                              const void *data,
1358                              uint16_t first_ptype, uint16_t last_ptype,
1359                              struct MessageTransmitQueue *tmit_msg)
1360 {
1361   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
1362   {
1363     tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
1364     GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
1365   }
1366   else if (NULL == tmit_msg)
1367   {
1368     return NULL;
1369   }
1370
1371   struct FragmentTransmitQueue *
1372     tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
1373   memcpy (&tmit_frag[1], data, data_size);
1374   tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
1375   tmit_frag->client = client;
1376   tmit_frag->size = data_size;
1377
1378   GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
1379   tmit_msg->client = client;
1380   return tmit_msg;
1381 }
1382
1383
1384 /**
1385  * Cancel transmission of current message to PSYC.
1386  *
1387  * @param plc     Place to send to.
1388  * @param client  Client the message originates from.
1389  */
1390 static void
1391 psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVER_Client *client)
1392 {
1393   uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
1394
1395   struct GNUNET_MessageHeader msg;
1396   msg.size = htons (sizeof (msg));
1397   msg.type = htons (type);
1398
1399   psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, NULL);
1400   psyc_transmit_message (plc);
1401
1402   /* FIXME: cleanup */
1403 }
1404
1405
1406 /**
1407  * Handle an incoming message from a client, to be transmitted to the place.
1408  */
1409 static void
1410 client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
1411                           const struct GNUNET_MessageHeader *msg)
1412 {
1413   struct Client *
1414     ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
1415   GNUNET_assert (NULL != ctx);
1416   struct Place *plc = ctx->plc;
1417   int ret = GNUNET_SYSERR;
1418
1419   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1420               "%p Received message from client.\n", plc);
1421   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
1422
1423   if (GNUNET_YES != plc->is_ready)
1424   {
1425     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1426                 "%p Place is not ready yet, disconnecting client.\n", plc);
1427     GNUNET_break (0);
1428     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1429     return;
1430   }
1431
1432   uint16_t size = ntohs (msg->size);
1433   uint16_t psize = size - sizeof (*msg);
1434   if (psize < sizeof (struct GNUNET_MessageHeader)
1435       || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
1436   {
1437     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1438                 "%p Received message with invalid payload size (%u) from client.\n",
1439                 plc, psize);
1440     GNUNET_break (0);
1441     psyc_transmit_cancel (plc, client);
1442     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1443     return;
1444   }
1445
1446   uint16_t first_ptype = 0, last_ptype = 0;
1447   if (GNUNET_SYSERR
1448       == GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
1449                                           &first_ptype, &last_ptype))
1450   {
1451     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1452                 "%p Received invalid message part from client.\n", plc);
1453     GNUNET_break (0);
1454     psyc_transmit_cancel (plc, client);
1455     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1456     return;
1457   }
1458   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459               "%p Received message with first part type %u and last part type %u.\n",
1460               plc, first_ptype, last_ptype);
1461
1462   ctx->tmit_msg
1463     = psyc_transmit_queue_message (plc, client, psize, &msg[1],
1464                                    first_ptype, last_ptype, ctx->tmit_msg);
1465   if (NULL != ctx->tmit_msg)
1466   {
1467     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
1468       ctx->tmit_msg = NULL;
1469     ret = psyc_transmit_message (plc);
1470   }
1471
1472   if (GNUNET_OK != ret)
1473   {
1474     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1475                 "%p Received invalid message part from client.\n", plc);
1476     GNUNET_break (0);
1477     psyc_transmit_cancel (plc, client);
1478     ret = GNUNET_SYSERR;
1479   }
1480   GNUNET_SERVER_receive_done (client, ret);
1481 }
1482
1483
1484 /**
1485  * Initialize the PSYC service.
1486  *
1487  * @param cls Closure.
1488  * @param server The initialized server.
1489  * @param c Configuration to use.
1490  */
1491 static void
1492 run (void *cls, struct GNUNET_SERVER_Handle *server,
1493      const struct GNUNET_CONFIGURATION_Handle *c)
1494 {
1495   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1496     { &client_recv_host_enter, NULL,
1497       GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER, 0 },
1498
1499     { &client_recv_guest_enter, NULL,
1500       GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER, 0 },
1501
1502     { &client_recv_join_decision, NULL,
1503       GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, 0 },
1504
1505     { &client_recv_psyc_message, NULL,
1506       GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 }
1507   };
1508
1509   cfg = c;
1510   stats = GNUNET_STATISTICS_create ("social", cfg);
1511   hosts = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1512   guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1513   place_guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1514   nc = GNUNET_SERVER_notification_context_create (server, 1);
1515   GNUNET_SERVER_add_handlers (server, handlers);
1516   GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
1517   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1518                                 &shutdown_task, NULL);
1519 }
1520
1521
1522 /**
1523  * The main function for the service.
1524  *
1525  * @param argc number of arguments from the command line
1526  * @param argv command line arguments
1527  * @return 0 ok, 1 on error
1528  */
1529 int
1530 main (int argc, char *const *argv)
1531 {
1532   return (GNUNET_OK ==
1533           GNUNET_SERVICE_run (argc, argv, "social",
1534                               GNUNET_SERVICE_OPTION_NONE,
1535                               &run, NULL)) ? 0 : 1;
1536 }
1537
1538 /* end of gnunet-service-social.c */