4aed3755e33d747c2f794ad3231a7abf73762290
[oweals/gnunet.git] / src / social / social_api.c
1  /*
2  * This file is part of GNUnet
3  * Copyright (C) 2013 Christian Grothoff (and other contributing authors)
4  *
5  * GNUnet is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published
7  * by the Free Software Foundation; either version 3, or (at your
8  * option) any later version.
9  *
10  * GNUnet is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with GNUnet; see the file COPYING.  If not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file social/social_api.c
23  * @brief Social service; implements social interactions using the PSYC service.
24  * @author Gabor X Toth
25  */
26
27 #include <inttypes.h>
28 #include <string.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_env_lib.h"
33 #include "gnunet_core_service.h"
34 #include "gnunet_identity_service.h"
35 #include "gnunet_namestore_service.h"
36 #include "gnunet_gns_service.h"
37 #include "gnunet_psyc_service.h"
38 #include "gnunet_psyc_util_lib.h"
39 #include "gnunet_social_service.h"
40 #include "social.h"
41
42 #define LOG(kind,...) GNUNET_log_from (kind, "social-api",__VA_ARGS__)
43
44
45 static struct GNUNET_CORE_Handle *core;
46 static struct GNUNET_GNS_Handle *gns;
47 static struct GNUNET_NAMESTORE_Handle *namestore;
48 static struct GNUNET_PeerIdentity this_peer;
49
50
51 /**
52  * Handle for a pseudonym of another user in the network.
53  */
54 struct GNUNET_SOCIAL_Nym
55 {
56   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
57   struct GNUNET_HashCode pub_key_hash;
58 };
59
60
61 /**
62  * Handle for a place where social interactions happen.
63  */
64 struct GNUNET_SOCIAL_Place
65 {
66   /**
67    * Configuration to use.
68    */
69   const struct GNUNET_CONFIGURATION_Handle *cfg;
70
71   /**
72    * Client connection to the service.
73    */
74   struct GNUNET_CLIENT_MANAGER_Connection *client;
75
76   /**
77    * Transmission handle.
78    */
79   struct GNUNET_PSYC_TransmitHandle *tmit;
80
81   /**
82    * Receipt handle.
83    */
84   struct GNUNET_PSYC_ReceiveHandle *recv;
85
86   /**
87    * Slicer for processing incoming methods.
88    */
89   struct GNUNET_SOCIAL_Slicer *slicer;
90
91   /**
92    * Message to send on reconnect.
93    */
94   struct GNUNET_MessageHeader *connect_msg;
95
96   /**
97    * Function called after disconnected from the service.
98    */
99   GNUNET_ContinuationCallback disconnect_cb;
100
101   /**
102    * Closure for @a disconnect_cb.
103    */
104   void *disconnect_cls;
105
106   /**
107    * Public key of the place.
108    */
109   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
110
111   /**
112    * Private key of the ego.
113    */
114   struct GNUNET_CRYPTO_EcdsaPrivateKey ego_key;
115
116   /**
117    * Does this place belong to a host (#GNUNET_YES) or guest (#GNUNET_NO)?
118    */
119   uint8_t is_host;
120
121   /**
122    * Is this place in the process of disconnecting from the service?
123    * #GNUNET_YES or #GNUNET_NO
124    */
125   uint8_t is_disconnecting;
126 };
127
128
129 /**
130  * Host handle for a place that we entered.
131  */
132 struct GNUNET_SOCIAL_Host
133 {
134   struct GNUNET_SOCIAL_Place plc;
135
136   struct GNUNET_CRYPTO_EddsaPrivateKey place_key;
137
138   /**
139    * Receipt handle.
140    */
141   struct GNUNET_PSYC_ReceiveHandle *recv;
142
143   /**
144    * Slicer for processing incoming methods.
145    */
146   struct GNUNET_SOCIAL_Slicer *slicer;
147
148   GNUNET_SOCIAL_HostEnterCallback enter_cb;
149
150   GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb;
151
152   GNUNET_SOCIAL_FarewellCallback farewell_cb;
153
154   /**
155    * Closure for callbacks.
156    */
157   void *cb_cls;
158
159   struct GNUNET_SOCIAL_Nym *notice_place_leave_nym;
160   struct GNUNET_ENV_Environment *notice_place_leave_env;
161 };
162
163
164 /**
165  * Guest handle for place that we entered.
166  */
167 struct GNUNET_SOCIAL_Guest
168 {
169   struct GNUNET_SOCIAL_Place plc;
170
171   /**
172    * Receipt handle.
173    */
174   struct GNUNET_PSYC_ReceiveHandle *recv;
175
176   /**
177    * Slicer for processing incoming methods.
178    */
179   struct GNUNET_SOCIAL_Slicer *slicer;
180
181   GNUNET_SOCIAL_GuestEnterCallback enter_cb;
182
183   GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb;
184
185   /**
186    * Closure for callbacks.
187    */
188   void *cb_cls;
189 };
190
191
192 /**
193  * Hash map of all nyms.
194  * pub_key_hash -> struct GNUNET_SOCIAL_Nym *
195  */
196 struct GNUNET_CONTAINER_MultiHashMap *nyms;
197
198
199 /**
200  * Handle for a try-and-slice instance.
201  */
202 struct GNUNET_SOCIAL_Slicer
203 {
204   /**
205    * Method handlers: method_name -> SlicerMethodCallbacks
206    */
207   struct GNUNET_CONTAINER_MultiHashMap *method_handlers;
208
209   /**
210    * Modifier handlers: modifier name -> SlicerModifierCallbacks
211    */
212   struct GNUNET_CONTAINER_MultiHashMap *modifier_handlers;
213
214   /**
215    * Currently being processed message part.
216    */
217   const struct GNUNET_MessageHeader *msg;
218
219   /**
220    * ID of currently being received message.
221    */
222   uint64_t message_id;
223
224   /**
225    * Method name of currently being received message.
226    */
227   char *method_name;
228
229   /**
230    * Name of currently processed modifier.
231    */
232   char *mod_name;
233
234   /**
235    * Value of currently processed modifier.
236    */
237   char *mod_value;
238
239   /**
240    * Public key of the nym the current message originates from.
241    */
242   struct GNUNET_CRYPTO_EcdsaPublicKey nym_key;
243
244   /**
245    * Size of @a method_name (including terminating \0).
246    */
247   uint16_t method_name_size;
248
249   /**
250    * Size of @a modifier_name (including terminating \0).
251    */
252   uint16_t mod_name_size;
253
254   /**
255    * Size of modifier value fragment.
256    */
257   uint16_t mod_value_size;
258
259   /**
260    * Full size of modifier value.
261    */
262   uint16_t mod_full_value_size;
263
264   /**
265    * Remaining bytes from the value of the current modifier.
266    */
267   uint16_t mod_value_remaining;
268
269   /**
270    * Operator of currently processed modifier.
271    */
272   uint8_t mod_oper;
273 };
274
275
276 /**
277  * Callbacks for a slicer method handler.
278  */
279 struct SlicerMethodCallbacks
280 {
281   GNUNET_SOCIAL_MethodCallback method_cb;
282   GNUNET_SOCIAL_ModifierCallback modifier_cb;
283   GNUNET_SOCIAL_DataCallback data_cb;
284   GNUNET_SOCIAL_EndOfMessageCallback eom_cb;
285   void *cls;
286 };
287
288
289 struct SlicerMethodRemoveClosure
290 {
291   struct GNUNET_SOCIAL_Slicer *slicer;
292   struct SlicerMethodCallbacks rm_cbs;
293 };
294
295
296 /**
297  * Callbacks for a slicer method handler.
298  */
299 struct SlicerModifierCallbacks
300 {
301   GNUNET_SOCIAL_ModifierCallback modifier_cb;
302   void *cls;
303 };
304
305
306 struct SlicerModifierRemoveClosure
307 {
308   struct GNUNET_SOCIAL_Slicer *slicer;
309   struct SlicerModifierCallbacks rm_cbs;
310 };
311
312
313 /**
314  * Handle for an announcement request.
315  */
316 struct GNUNET_SOCIAL_Announcement
317 {
318
319 };
320
321
322 /**
323  * A talk request.
324  */
325 struct GNUNET_SOCIAL_TalkRequest
326 {
327
328 };
329
330
331 /**
332  * A history lesson.
333  */
334 struct GNUNET_SOCIAL_HistoryRequest
335 {
336   /**
337    * Place.
338    */
339   struct GNUNET_SOCIAL_Place *plc;
340
341   /**
342    * Operation ID.
343    */
344   uint64_t op_id;
345
346   /**
347    * Message handler.
348    */
349   struct GNUNET_PSYC_ReceiveHandle *recv;
350
351   /**
352    * Function to call when the operation finished.
353    */
354   GNUNET_ResultCallback result_cb;
355
356   /**
357    * Closure for @a result_cb.
358    */
359   void *cls;
360 };
361
362
363 struct GNUNET_SOCIAL_LookHandle
364 {
365   /**
366    * Place.
367    */
368   struct GNUNET_SOCIAL_Place *plc;
369
370   /**
371    * Operation ID.
372    */
373   uint64_t op_id;
374
375   /**
376    * State variable result callback.
377    */
378   GNUNET_PSYC_StateVarCallback var_cb;
379
380   /**
381    * Function to call when the operation finished.
382    */
383   GNUNET_ResultCallback result_cb;
384
385   /**
386    * Name of current modifier being received.
387    */
388   char *mod_name;
389
390   /**
391    * Size of current modifier value being received.
392    */
393   size_t mod_value_size;
394
395   /**
396    * Remaining size of current modifier value still to be received.
397    */
398   size_t mod_value_remaining;
399
400   /**
401    * Closure for @a result_cb.
402    */
403   void *cls;
404 };
405
406
407 /*** NYM ***/
408
409 static struct GNUNET_SOCIAL_Nym *
410 nym_get_or_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key)
411 {
412   struct GNUNET_SOCIAL_Nym *nym = NULL;
413   struct GNUNET_HashCode pub_key_hash;
414
415   if (NULL == pub_key)
416     return NULL;
417
418   GNUNET_CRYPTO_hash (pub_key, sizeof (*pub_key), &pub_key_hash);
419
420   if (NULL == nyms)
421     nyms = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
422   else
423     nym = GNUNET_CONTAINER_multihashmap_get (nyms, &pub_key_hash);
424
425   if (NULL == nym)
426   {
427     nym = GNUNET_new (struct GNUNET_SOCIAL_Nym);
428     nym->pub_key = *pub_key;
429     nym->pub_key_hash = pub_key_hash;
430     GNUNET_CONTAINER_multihashmap_put (nyms, &nym->pub_key_hash, nym,
431                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
432   }
433   return nym;
434 }
435
436
437 static void
438 nym_destroy (struct GNUNET_SOCIAL_Nym *nym)
439 {
440   GNUNET_CONTAINER_multihashmap_remove (nyms, &nym->pub_key_hash, nym);
441   GNUNET_free (nym);
442 }
443
444
445 /*** MESSAGE HANDLERS ***/
446
447 /** _notice_place_leave from guests */
448
449 static void
450 host_recv_notice_place_leave_method (void *cls,
451                                      const struct GNUNET_PSYC_MessageMethod *meth,
452                                      uint64_t message_id,
453                                      uint32_t flags,
454                                      const struct GNUNET_SOCIAL_Nym *nym,
455                                      const char *method_name)
456 {
457   struct GNUNET_SOCIAL_Host *hst = cls;
458   if (0 == memcmp (&(struct GNUNET_CRYPTO_EcdsaPublicKey) {},
459                    &nym->pub_key, sizeof (nym->pub_key)))
460     return;
461
462   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
463               "Host received method for message ID %" PRIu64 " from nym %s: %s\n",
464               message_id, GNUNET_h2s (&nym->pub_key_hash), method_name);
465
466   hst->notice_place_leave_nym = (struct GNUNET_SOCIAL_Nym *) nym;
467   hst->notice_place_leave_env = GNUNET_ENV_environment_create ();
468
469   char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key);
470   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
471               "_notice_place_leave: got method from nym %s (%s).\n",
472               GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
473 }
474
475
476 static void
477 host_recv_notice_place_leave_modifier (void *cls,
478                                        const struct GNUNET_MessageHeader *msg,
479                                        uint64_t message_id,
480                                        enum GNUNET_ENV_Operator oper,
481                                        const char *name,
482                                        const void *value,
483                                        uint16_t value_size,
484                                        uint16_t full_value_size)
485 {
486   struct GNUNET_SOCIAL_Host *hst = cls;
487   if (NULL == hst->notice_place_leave_env)
488     return;
489
490   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
491               "Host received modifier for _notice_place_leave message with ID %" PRIu64 ":\n"
492               "%c%s: %.*s\n",
493               message_id, oper, name, value_size, value);
494
495   /* skip _nym, it's added later in eom() */
496   if (0 == memcmp (name, "_nym", sizeof ("_nym"))
497       || 0 == memcmp (name, "_nym_", sizeof ("_nym_") - 1))
498     return;
499
500   GNUNET_ENV_environment_add (hst->notice_place_leave_env,
501                               GNUNET_ENV_OP_SET, name, value, value_size);
502 }
503
504
505 static void
506 host_recv_notice_place_leave_eom (void *cls,
507                                   const struct GNUNET_MessageHeader *msg,
508                                   uint64_t message_id,
509                                   uint8_t cancelled)
510 {
511   struct GNUNET_SOCIAL_Host *hst = cls;
512   if (NULL == hst->notice_place_leave_env)
513     return;
514
515   char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key);
516   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
517               "_notice_place_leave: got EOM from nym %s (%s).\n",
518               GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
519
520   if (GNUNET_YES != cancelled)
521   {
522     if (NULL != hst->farewell_cb)
523       hst->farewell_cb (hst->cb_cls, hst->notice_place_leave_nym,
524                         hst->notice_place_leave_env);
525     /* announce leaving guest to place */
526     GNUNET_ENV_environment_add (hst->notice_place_leave_env, GNUNET_ENV_OP_SET,
527                                 "_nym", hst->notice_place_leave_nym,
528                                 sizeof (*hst->notice_place_leave_nym));
529     GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave",
530                                  hst->notice_place_leave_env,
531                                  NULL, NULL, GNUNET_SOCIAL_ANNOUNCE_NONE);
532     nym_destroy (hst->notice_place_leave_nym);
533   }
534   GNUNET_ENV_environment_destroy (hst->notice_place_leave_env);
535   hst->notice_place_leave_env = NULL;
536 }
537
538
539 /*** SLICER ***/
540
541 /**
542  * Call a method handler for an incoming message part.
543  */
544 int
545 slicer_method_handler_notify (void *cls, const struct GNUNET_HashCode *key,
546                               void *value)
547 {
548   struct GNUNET_SOCIAL_Slicer *slicer = cls;
549   const struct GNUNET_MessageHeader *msg = slicer->msg;
550   struct SlicerMethodCallbacks *cbs = value;
551   uint16_t ptype = ntohs (msg->type);
552
553   switch (ptype)
554   {
555   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
556   {
557     if (NULL == cbs->method_cb)
558       break;
559     struct GNUNET_PSYC_MessageMethod *
560       meth = (struct GNUNET_PSYC_MessageMethod *) msg;
561     cbs->method_cb (cbs->cls, meth, slicer->message_id,
562                     ntohl (meth->flags),
563                     nym_get_or_create (&slicer->nym_key),
564                     slicer->method_name);
565     break;
566   }
567
568   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
569   {
570     if (NULL == cbs->modifier_cb)
571       break;
572     struct GNUNET_PSYC_MessageModifier *
573       mod = (struct GNUNET_PSYC_MessageModifier *) msg;
574     cbs->modifier_cb (cbs->cls, &mod->header, slicer->message_id,
575                       mod->oper, (const char *) &mod[1],
576                       (const void *) &mod[1] + ntohs (mod->name_size),
577                       ntohs (mod->header.size) - sizeof (*mod) - ntohs (mod->name_size),
578                       ntohs (mod->value_size));
579     break;
580   }
581
582   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
583   {
584     if (NULL == cbs->modifier_cb)
585       break;
586     cbs->modifier_cb (cbs->cls, msg, slicer->message_id,
587                       slicer->mod_oper, slicer->mod_name, &msg[1],
588                       ntohs (msg->size) - sizeof (*msg),
589                       slicer->mod_full_value_size);
590     break;
591   }
592
593   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
594   {
595     if (NULL == cbs->data_cb)
596       break;
597     uint64_t data_offset = 0; // FIXME
598     cbs->data_cb (cbs->cls, msg, slicer->message_id,
599                   data_offset, &msg[1], ntohs (msg->size) - sizeof (*msg));
600     break;
601   }
602
603   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
604     if (NULL == cbs->eom_cb)
605       break;
606     cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_NO);
607     break;
608
609   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
610     if (NULL == cbs->eom_cb)
611       break;
612     cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_YES);
613     break;
614   }
615   return GNUNET_YES;
616 }
617
618
619 /**
620  * Call a method handler for an incoming message part.
621  */
622 int
623 slicer_modifier_handler_notify (void *cls, const struct GNUNET_HashCode *key,
624                                 void *value)
625 {
626   struct GNUNET_SOCIAL_Slicer *slicer = cls;
627   struct SlicerModifierCallbacks *cbs = value;
628
629   cbs->modifier_cb (cbs->cls, slicer->msg, slicer->message_id, slicer->mod_oper,
630                     slicer->mod_name, slicer->mod_value,
631                     slicer->mod_value_size, slicer->mod_full_value_size);
632   return GNUNET_YES;
633 }
634
635
636 /**
637  * Process an incoming message part and call matching handlers.
638  *
639  * @param cls
640  *        Closure.
641  * @param message_id
642  *        ID of the message.
643  * @param flags
644  *        Flags for the message.
645  *        @see enum GNUNET_PSYC_MessageFlags
646  * @param msg
647  *        The message part. as it arrived from the network.
648  */
649 static void
650 slicer_message (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
651                 uint64_t message_id, uint32_t flags, uint64_t fragment_offset,
652                 const struct GNUNET_MessageHeader *msg)
653 {
654   struct GNUNET_SOCIAL_Slicer *slicer = cls;
655   slicer->nym_key = *slave_key;
656
657   uint16_t ptype = ntohs (msg->type);
658   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
659   {
660     struct GNUNET_PSYC_MessageMethod *
661       meth = (struct GNUNET_PSYC_MessageMethod *) msg;
662     slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth);
663     slicer->method_name = GNUNET_malloc (slicer->method_name_size);
664     memcpy (slicer->method_name, &meth[1], slicer->method_name_size);
665     slicer->message_id = message_id;
666   }
667   else
668   {
669     GNUNET_assert (message_id == slicer->message_id);
670   }
671
672   char *nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (slave_key);
673   LOG (GNUNET_ERROR_TYPE_DEBUG,
674        "Slicer received message of type %u and size %u, "
675        "with ID %" PRIu64 " and method %s from %s\n",
676        ptype, ntohs (msg->size), message_id, slicer->method_name, nym_str);
677   GNUNET_free (nym_str);
678
679   slicer->msg = msg;
680
681   /* try-and-slice modifier */
682
683   switch (ptype)
684   {
685   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
686   {
687     struct GNUNET_PSYC_MessageModifier *
688       mod = (struct GNUNET_PSYC_MessageModifier *) msg;
689     slicer->mod_oper = mod->oper;
690     slicer->mod_name_size = ntohs (mod->name_size);
691     slicer->mod_name = GNUNET_malloc (slicer->mod_name_size);
692     memcpy (slicer->mod_name, &mod[1], slicer->mod_name_size);
693     slicer->mod_value = (char *) &mod[1] + slicer->mod_name_size;
694     slicer->mod_full_value_size = ntohs (mod->value_size);
695     slicer->mod_value_remaining = slicer->mod_full_value_size;
696     slicer->mod_value_size
697       = ntohs (mod->header.size) - sizeof (*mod) - slicer->mod_name_size;
698   }
699   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
700     if (ptype == GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT)
701     {
702       slicer->mod_value = (char *) &msg[1];
703       slicer->mod_value_size = ntohs (msg->size) - sizeof (*msg);
704     }
705     slicer->mod_value_remaining -= slicer->mod_value_size;
706     char *name = GNUNET_malloc (slicer->mod_name_size);
707     memcpy (name, slicer->mod_name, slicer->mod_name_size);
708     do
709     {
710       struct GNUNET_HashCode key;
711       uint16_t name_len = strlen (name);
712       GNUNET_CRYPTO_hash (name, name_len, &key);
713       GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key,
714                                                   slicer_modifier_handler_notify,
715                                                   slicer);
716       char *p = strrchr (name, '_');
717       if (NULL == p)
718         break;
719       *p = '\0';
720     } while (1);
721     GNUNET_free (name);
722   }
723
724   /* try-and-slice method */
725
726   char *name = GNUNET_malloc (slicer->method_name_size);
727   memcpy (name, slicer->method_name, slicer->method_name_size);
728   do
729   {
730     struct GNUNET_HashCode key;
731     uint16_t name_len = strlen (name);
732     GNUNET_CRYPTO_hash (name, name_len, &key);
733     GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
734                                                 slicer_method_handler_notify,
735                                                 slicer);
736     char *p = strrchr (name, '_');
737     if (NULL == p)
738       break;
739     *p = '\0';
740   } while (1);
741   GNUNET_free (name);
742
743   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
744     GNUNET_free (slicer->method_name);
745
746   if (0 == slicer->mod_value_remaining && NULL != slicer->mod_name)
747   {
748     GNUNET_free (slicer->mod_name);
749     slicer->mod_name = NULL;
750     slicer->mod_name_size = 0;
751     slicer->mod_value_size = 0;
752     slicer->mod_full_value_size = 0;
753     slicer->mod_oper = 0;
754   }
755
756   slicer->msg = NULL;
757 }
758
759
760 /**
761  * Create a try-and-slice instance.
762  *
763  * A slicer processes incoming messages and notifies callbacks about matching
764  * methods or modifiers encountered.
765  *
766  * @return A new try-and-slice construct.
767  */
768 struct GNUNET_SOCIAL_Slicer *
769 GNUNET_SOCIAL_slicer_create (void)
770 {
771   struct GNUNET_SOCIAL_Slicer *slicer = GNUNET_malloc (sizeof (*slicer));
772   slicer->method_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
773   slicer->modifier_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
774   return slicer;
775 }
776
777
778 /**
779  * Add a method to the try-and-slice instance.
780  *
781  * The callbacks are called for messages with a matching @a method_name prefix.
782  *
783  * @param slicer
784  *        The try-and-slice instance to extend.
785  * @param method_name
786  *        Name of the given method, use empty string to match all.
787  * @param method_cb
788  *        Method handler invoked upon a matching message.
789  * @param modifier_cb
790  *        Modifier handler, invoked after @a method_cb
791  *        for each modifier in the message.
792  * @param data_cb
793  *        Data handler, invoked after @a modifier_cb for each data fragment.
794  * @param eom_cb
795  *        Invoked upon reaching the end of a matching message.
796  * @param cls
797  *        Closure for the callbacks.
798  */
799 void
800 GNUNET_SOCIAL_slicer_method_add (struct GNUNET_SOCIAL_Slicer *slicer,
801                                  const char *method_name,
802                                  GNUNET_SOCIAL_MethodCallback method_cb,
803                                  GNUNET_SOCIAL_ModifierCallback modifier_cb,
804                                  GNUNET_SOCIAL_DataCallback data_cb,
805                                  GNUNET_SOCIAL_EndOfMessageCallback eom_cb,
806                                  void *cls)
807 {
808   struct GNUNET_HashCode key;
809   GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
810
811   struct SlicerMethodCallbacks *cbs = GNUNET_malloc (sizeof (*cbs));
812   cbs->method_cb = method_cb;
813   cbs->modifier_cb = modifier_cb;
814   cbs->data_cb = data_cb;
815   cbs->eom_cb = eom_cb;
816   cbs->cls = cls;
817
818   GNUNET_CONTAINER_multihashmap_put (slicer->method_handlers, &key, cbs,
819                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
820 }
821
822
823 int
824 slicer_method_remove (void *cls, const struct GNUNET_HashCode *key, void *value)
825 {
826   struct SlicerMethodRemoveClosure *rm_cls = cls;
827   struct GNUNET_SOCIAL_Slicer *slicer = rm_cls->slicer;
828   struct SlicerMethodCallbacks *rm_cbs = &rm_cls->rm_cbs;
829   struct SlicerMethodCallbacks *cbs = value;
830
831   if (cbs->method_cb == rm_cbs->method_cb
832       && cbs->modifier_cb == rm_cbs->modifier_cb
833       && cbs->data_cb == rm_cbs->data_cb
834       && cbs->eom_cb == rm_cbs->eom_cb)
835   {
836     GNUNET_CONTAINER_multihashmap_remove (slicer->method_handlers, key, cbs);
837     GNUNET_free (cbs);
838     return GNUNET_NO;
839   }
840   return GNUNET_YES;
841 }
842
843
844 /**
845  * Remove a registered method from the try-and-slice instance.
846  *
847  * Removes one matching handler registered with the given
848  * @a method_name and  callbacks.
849  *
850  * @param slicer
851  *        The try-and-slice instance.
852  * @param method_name
853  *        Name of the method to remove.
854  * @param method_cb
855  *        Method handler.
856  * @param modifier_cb
857  *        Modifier handler.
858  * @param data_cb
859  *        Data handler.
860  * @param eom_cb
861  *        End of message handler.
862  *
863  * @return #GNUNET_OK if a method handler was removed,
864  *         #GNUNET_NO if no handler matched the given method name and callbacks.
865  */
866 int
867 GNUNET_SOCIAL_slicer_method_remove (struct GNUNET_SOCIAL_Slicer *slicer,
868                                     const char *method_name,
869                                     GNUNET_SOCIAL_MethodCallback method_cb,
870                                     GNUNET_SOCIAL_ModifierCallback modifier_cb,
871                                     GNUNET_SOCIAL_DataCallback data_cb,
872                                     GNUNET_SOCIAL_EndOfMessageCallback eom_cb)
873 {
874   struct GNUNET_HashCode key;
875   GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
876
877   struct SlicerMethodRemoveClosure rm_cls;
878   rm_cls.slicer = slicer;
879   struct SlicerMethodCallbacks *rm_cbs = &rm_cls.rm_cbs;
880   rm_cbs->method_cb = method_cb;
881   rm_cbs->modifier_cb = modifier_cb;
882   rm_cbs->data_cb = data_cb;
883   rm_cbs->eom_cb = eom_cb;
884
885   return
886     (GNUNET_SYSERR
887      == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
888                                                     slicer_method_remove,
889                                                     &rm_cls))
890     ? GNUNET_NO
891     : GNUNET_OK;
892 }
893
894
895 /**
896  * Watch a place for changed objects.
897  *
898  * @param slicer
899  *        The try-and-slice instance.
900  * @param object_filter
901  *        Object prefix to match.
902  * @param modifier_cb
903  *        Function to call when encountering a state modifier.
904  * @param cls
905  *        Closure for callback.
906  */
907 void
908 GNUNET_SOCIAL_slicer_modifier_add (struct GNUNET_SOCIAL_Slicer *slicer,
909                                    const char *object_filter,
910                                    GNUNET_SOCIAL_ModifierCallback modifier_cb,
911                                    void *cls)
912 {
913   struct SlicerModifierCallbacks *cbs = GNUNET_malloc (sizeof *cbs);
914   cbs->modifier_cb = modifier_cb;
915   cbs->cls = cls;
916
917   struct GNUNET_HashCode key;
918   GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
919   GNUNET_CONTAINER_multihashmap_put (slicer->modifier_handlers, &key, cbs,
920                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
921 }
922
923
924 int
925 slicer_modifier_remove (void *cls, const struct GNUNET_HashCode *key, void *value)
926 {
927   struct SlicerModifierRemoveClosure *rm_cls = cls;
928   struct GNUNET_SOCIAL_Slicer *slicer = rm_cls->slicer;
929   struct SlicerModifierCallbacks *rm_cbs = &rm_cls->rm_cbs;
930   struct SlicerModifierCallbacks *cbs = value;
931
932   if (cbs->modifier_cb == rm_cbs->modifier_cb)
933   {
934     GNUNET_CONTAINER_multihashmap_remove (slicer->modifier_handlers, key, cbs);
935     GNUNET_free (cbs);
936     return GNUNET_NO;
937   }
938   return GNUNET_YES;
939 }
940
941
942 /**
943  * Remove a registered modifier from the try-and-slice instance.
944  *
945  * Removes one matching handler registered with the given
946  * @a object_filter and @a modifier_cb.
947  *
948  * @param slicer
949  *        The try-and-slice instance.
950  * @param object_filter
951  *        Object prefix to match.
952  * @param modifier_cb
953  *        Function to call when encountering a state modifier changes.
954  */
955 int
956 GNUNET_SOCIAL_slicer_modifier_remove (struct GNUNET_SOCIAL_Slicer *slicer,
957                                       const char *object_filter,
958                                       GNUNET_SOCIAL_ModifierCallback modifier_cb)
959 {
960   struct GNUNET_HashCode key;
961   GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
962
963   struct SlicerModifierRemoveClosure rm_cls;
964   rm_cls.slicer = slicer;
965   struct SlicerModifierCallbacks *rm_cbs = &rm_cls.rm_cbs;
966   rm_cbs->modifier_cb = modifier_cb;
967
968   return
969     (GNUNET_SYSERR
970      == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key,
971                                                     slicer_modifier_remove,
972                                                     &rm_cls))
973     ? GNUNET_NO
974     : GNUNET_OK;
975  }
976
977
978 int
979 slicer_method_free (void *cls, const struct GNUNET_HashCode *key, void *value)
980 {
981   struct SlicerMethodCallbacks *cbs = value;
982   GNUNET_free (cbs);
983   return GNUNET_YES;
984 }
985
986
987 /**
988  * Destroy a given try-and-slice instance.
989  *
990  * @param slicer
991  *        Slicer to destroy
992  */
993 void
994 GNUNET_SOCIAL_slicer_destroy (struct GNUNET_SOCIAL_Slicer *slicer)
995 {
996   GNUNET_CONTAINER_multihashmap_iterate (slicer->method_handlers,
997                                          slicer_method_free, NULL);
998   GNUNET_CONTAINER_multihashmap_destroy (slicer->method_handlers);
999   GNUNET_free (slicer);
1000 }
1001
1002
1003 /*** PLACE ***/
1004
1005
1006 static void
1007 place_send_connect_msg (struct GNUNET_SOCIAL_Place *plc)
1008 {
1009   uint16_t cmsg_size = ntohs (plc->connect_msg->size);
1010   struct GNUNET_MessageHeader * cmsg = GNUNET_malloc (cmsg_size);
1011   memcpy (cmsg, plc->connect_msg, cmsg_size);
1012   GNUNET_CLIENT_MANAGER_transmit_now (plc->client, cmsg);
1013 }
1014
1015
1016 static void
1017 place_recv_disconnect (void *cls,
1018                        struct GNUNET_CLIENT_MANAGER_Connection *client,
1019                        const struct GNUNET_MessageHeader *msg)
1020 {
1021   struct GNUNET_SOCIAL_Place *
1022     plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
1023
1024   GNUNET_CLIENT_MANAGER_reconnect (client);
1025   place_send_connect_msg (plc);
1026 }
1027
1028
1029 static void
1030 place_recv_result (void *cls,
1031                    struct GNUNET_CLIENT_MANAGER_Connection *client,
1032                    const struct GNUNET_MessageHeader *msg)
1033 {
1034   struct GNUNET_SOCIAL_Place *
1035     plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
1036
1037   const struct GNUNET_OperationResultMessage *
1038     res = (const struct GNUNET_OperationResultMessage *) msg;
1039
1040   uint16_t size = ntohs (msg->size);
1041   if (size < sizeof (*res))
1042   { /* Error, message too small. */
1043     GNUNET_break (0);
1044     return;
1045   }
1046
1047   uint16_t data_size = size - sizeof (*res);
1048   const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
1049   GNUNET_CLIENT_MANAGER_op_result (plc->client, GNUNET_ntohll (res->op_id),
1050                                    GNUNET_ntohll (res->result_code),
1051                                    data, data_size);
1052 }
1053
1054
1055 static void
1056 op_recv_history_result (void *cls, int64_t result,
1057                         const void *err_msg, uint16_t err_msg_size)
1058 {
1059   LOG (GNUNET_ERROR_TYPE_DEBUG,
1060        "Received history replay result: %" PRId64 ".\n", result);
1061
1062   struct GNUNET_SOCIAL_HistoryRequest *hist = cls;
1063
1064   if (NULL != hist->result_cb)
1065     hist->result_cb (hist->cls, result, err_msg, err_msg_size);
1066
1067   GNUNET_PSYC_receive_destroy (hist->recv);
1068   GNUNET_free (hist);
1069 }
1070
1071
1072 static void
1073 op_recv_state_result (void *cls, int64_t result,
1074                       const void *err_msg, uint16_t err_msg_size)
1075 {
1076   LOG (GNUNET_ERROR_TYPE_DEBUG,
1077        "Received state request result: %" PRId64 ".\n", result);
1078
1079   struct GNUNET_SOCIAL_LookHandle *look = cls;
1080
1081   if (NULL != look->result_cb)
1082     look->result_cb (look->cls, result, err_msg, err_msg_size);
1083
1084   GNUNET_free (look);
1085 }
1086
1087
1088 static void
1089 place_recv_history_result (void *cls,
1090                            struct GNUNET_CLIENT_MANAGER_Connection *client,
1091                            const struct GNUNET_MessageHeader *msg)
1092 {
1093   struct GNUNET_SOCIAL_Place *
1094     plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
1095
1096   const struct GNUNET_OperationResultMessage *
1097     res = (const struct GNUNET_OperationResultMessage *) msg;
1098   struct GNUNET_PSYC_MessageHeader *
1099     pmsg = (struct GNUNET_PSYC_MessageHeader *) &res[1];
1100
1101   LOG (GNUNET_ERROR_TYPE_DEBUG,
1102        "%p Received historic fragment for message #%" PRIu64 ".\n",
1103        plc, GNUNET_ntohll (pmsg->message_id));
1104
1105   GNUNET_ResultCallback result_cb = NULL;
1106   struct GNUNET_SOCIAL_HistoryRequest *hist = NULL;
1107
1108   if (GNUNET_YES != GNUNET_CLIENT_MANAGER_op_find (plc->client,
1109                                                    GNUNET_ntohll (res->op_id),
1110                                                    &result_cb, (void *) &hist))
1111   { /* Operation not found. */
1112     LOG (GNUNET_ERROR_TYPE_WARNING,
1113          "%p Replay operation not found for historic fragment of message #%"
1114          PRIu64 ".\n",
1115          plc, GNUNET_ntohll (pmsg->message_id));
1116     return;
1117   }
1118
1119   uint16_t size = ntohs (msg->size);
1120   if (size < sizeof (*res) + sizeof (*pmsg))
1121   { /* Error, message too small. */
1122     GNUNET_break (0);
1123     return;
1124   }
1125
1126   GNUNET_PSYC_receive_message (hist->recv,
1127                                (const struct GNUNET_PSYC_MessageHeader *) pmsg);
1128 }
1129
1130
1131 static void
1132 place_recv_state_result (void *cls,
1133                          struct GNUNET_CLIENT_MANAGER_Connection *client,
1134                          const struct GNUNET_MessageHeader *msg)
1135 {
1136   struct GNUNET_SOCIAL_Place *
1137     plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
1138
1139   const struct GNUNET_OperationResultMessage *
1140     res = (const struct GNUNET_OperationResultMessage *) msg;
1141
1142   GNUNET_ResultCallback result_cb = NULL;
1143   struct GNUNET_SOCIAL_LookHandle *look = NULL;
1144
1145   if (GNUNET_YES != GNUNET_CLIENT_MANAGER_op_find (plc->client,
1146                                                    GNUNET_ntohll (res->op_id),
1147                                                    &result_cb, (void *) &look))
1148   { /* Operation not found. */
1149     return;
1150   }
1151
1152   const struct GNUNET_MessageHeader *
1153     mod = (struct GNUNET_MessageHeader *) &res[1];
1154   uint16_t mod_size = ntohs (mod->size);
1155   if (ntohs (msg->size) - sizeof (*res) != mod_size)
1156   {
1157     GNUNET_break_op (0);
1158     LOG (GNUNET_ERROR_TYPE_WARNING,
1159          "Invalid modifier size in state result: %u - %u != %u\n",
1160          ntohs (msg->size), sizeof (*res), mod_size);
1161     return;
1162   }
1163   switch (ntohs (mod->type))
1164   {
1165   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1166   {
1167     const struct GNUNET_PSYC_MessageModifier *
1168       pmod = (const struct GNUNET_PSYC_MessageModifier *) mod;
1169
1170     const char *name = (const char *) &pmod[1];
1171     uint16_t name_size = ntohs (pmod->name_size);
1172     if ('\0' != name[name_size - 1])
1173     {
1174       GNUNET_break_op (0);
1175       LOG (GNUNET_ERROR_TYPE_WARNING,
1176            "Invalid modifier name in state result\n");
1177       return;
1178     }
1179     look->mod_value_size = ntohs (pmod->value_size);
1180     look->var_cb (look->cls, mod, name, name + name_size,
1181                   mod_size - sizeof (*mod) - name_size,
1182                   look->mod_value_size);
1183     if (look->mod_value_size > mod_size - sizeof (*mod) - name_size)
1184     {
1185         look->mod_value_remaining = look->mod_value_size;
1186         look->mod_name = GNUNET_malloc (name_size);
1187         memcpy (look->mod_name, name, name_size);
1188     }
1189     break;
1190   }
1191
1192   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1193     look->var_cb (look->cls, mod, look->mod_name, (const char *) &mod[1],
1194                   mod_size - sizeof (*mod), look->mod_value_size);
1195     look->mod_value_remaining -= mod_size - sizeof (*mod);
1196     if (0 == look->mod_value_remaining)
1197     {
1198         GNUNET_free (look->mod_name);
1199     }
1200     break;
1201   }
1202 }
1203
1204
1205 static void
1206 place_recv_message_ack (void *cls,
1207                         struct GNUNET_CLIENT_MANAGER_Connection *client,
1208                         const struct GNUNET_MessageHeader *msg)
1209 {
1210   struct GNUNET_SOCIAL_Place *
1211     plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
1212   GNUNET_PSYC_transmit_got_ack (plc->tmit);
1213 }
1214
1215
1216 static void
1217 place_recv_message (void *cls,
1218                     struct GNUNET_CLIENT_MANAGER_Connection *client,
1219                     const struct GNUNET_MessageHeader *msg)
1220 {
1221   struct GNUNET_SOCIAL_Place *
1222     plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
1223   GNUNET_PSYC_receive_message (plc->recv,
1224                                (const struct GNUNET_PSYC_MessageHeader *) msg);
1225 }
1226
1227
1228 static void
1229 host_recv_message (void *cls,
1230                    struct GNUNET_CLIENT_MANAGER_Connection *client,
1231                    const struct GNUNET_MessageHeader *msg)
1232 {
1233   struct GNUNET_SOCIAL_Host *
1234     hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (hst->plc));
1235   GNUNET_PSYC_receive_message (hst->recv,
1236                                (const struct GNUNET_PSYC_MessageHeader *) msg);
1237   GNUNET_PSYC_receive_message (hst->plc.recv,
1238                                (const struct GNUNET_PSYC_MessageHeader *) msg);
1239 }
1240
1241
1242 static void
1243 host_recv_enter_ack (void *cls,
1244                      struct GNUNET_CLIENT_MANAGER_Connection *client,
1245                      const struct GNUNET_MessageHeader *msg)
1246 {
1247   struct GNUNET_SOCIAL_Host *
1248     hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
1249                                                    sizeof (struct GNUNET_SOCIAL_Place));
1250
1251   struct GNUNET_PSYC_CountersResultMessage *
1252     cres = (struct GNUNET_PSYC_CountersResultMessage *) msg;
1253   int32_t result = ntohl (cres->result_code);
1254   if (NULL != hst->enter_cb)
1255     hst->enter_cb (hst->cb_cls, result, GNUNET_ntohll (cres->max_message_id));
1256 }
1257
1258
1259 static void
1260 host_recv_enter_request (void *cls,
1261                          struct GNUNET_CLIENT_MANAGER_Connection *client,
1262                          const struct GNUNET_MessageHeader *msg)
1263 {
1264   struct GNUNET_SOCIAL_Host *
1265     hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
1266                                                    sizeof (struct GNUNET_SOCIAL_Place));
1267   if (NULL == hst->answer_door_cb)
1268      return;
1269
1270   const char *method_name = NULL;
1271   struct GNUNET_ENV_Environment *env = NULL;
1272   struct GNUNET_PSYC_MessageHeader *entry_pmsg;
1273   const void *data = NULL;
1274   uint16_t data_size = 0;
1275   char *str;
1276   const struct GNUNET_PSYC_JoinRequestMessage *
1277     req = (const struct GNUNET_PSYC_JoinRequestMessage *) msg;
1278   const struct GNUNET_PSYC_Message *entry_msg = NULL;
1279
1280   do
1281   {
1282     if (sizeof (*req) + sizeof (*entry_msg) <= ntohs (req->header.size))
1283     {
1284       entry_msg = (struct GNUNET_PSYC_Message *) &req[1];
1285       LOG (GNUNET_ERROR_TYPE_DEBUG,
1286            "Received entry_msg of type %u and size %u.\n",
1287            ntohs (entry_msg->header.type), ntohs (entry_msg->header.size));
1288
1289       env = GNUNET_ENV_environment_create ();
1290       entry_pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
1291       if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_pmsg, &method_name, env,
1292                                                   &data, &data_size))
1293       {
1294         GNUNET_break_op (0);
1295         str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_key);
1296         LOG (GNUNET_ERROR_TYPE_WARNING,
1297              "Ignoring invalid entry request from nym %s.\n",
1298              str);
1299         GNUNET_free (str);
1300         break;
1301       }
1302     }
1303
1304     struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_key);
1305     hst->answer_door_cb (hst->cb_cls, nym, method_name, env,
1306                          data_size, data);
1307   } while (0);
1308
1309   if (NULL != env)
1310     GNUNET_ENV_environment_destroy (env);
1311   GNUNET_free (entry_pmsg);
1312 }
1313
1314
1315 static void
1316 guest_recv_enter_ack (void *cls,
1317                      struct GNUNET_CLIENT_MANAGER_Connection *client,
1318                      const struct GNUNET_MessageHeader *msg)
1319 {
1320   struct GNUNET_SOCIAL_Guest *
1321     gst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
1322                                                    sizeof (struct GNUNET_SOCIAL_Place));
1323
1324   struct GNUNET_PSYC_CountersResultMessage *
1325     cres = (struct GNUNET_PSYC_CountersResultMessage *) msg;
1326   int32_t result = ntohl (cres->result_code);
1327   if (NULL != gst->enter_cb)
1328     gst->enter_cb (gst->cb_cls, result, GNUNET_ntohll (cres->max_message_id));
1329 }
1330
1331
1332 static void
1333 guest_recv_join_decision (void *cls,
1334                           struct GNUNET_CLIENT_MANAGER_Connection *client,
1335                           const struct GNUNET_MessageHeader *msg)
1336 {
1337   struct GNUNET_SOCIAL_Guest *
1338     gst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
1339                                                    sizeof (struct GNUNET_SOCIAL_Place));
1340   const struct GNUNET_PSYC_JoinDecisionMessage *
1341     dcsn = (const struct GNUNET_PSYC_JoinDecisionMessage *) msg;
1342
1343   struct GNUNET_PSYC_Message *pmsg = NULL;
1344   if (ntohs (dcsn->header.size) <= sizeof (*dcsn) + sizeof (*pmsg))
1345     pmsg = (struct GNUNET_PSYC_Message *) &dcsn[1];
1346
1347   if (NULL != gst->entry_dcsn_cb)
1348     gst->entry_dcsn_cb (gst->cb_cls, ntohl (dcsn->is_admitted), pmsg);
1349 }
1350
1351
1352 static struct GNUNET_CLIENT_MANAGER_MessageHandler host_handlers[] =
1353 {
1354   { host_recv_enter_ack, NULL,
1355     GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK,
1356     sizeof (struct GNUNET_PSYC_CountersResultMessage), GNUNET_NO },
1357
1358   { host_recv_enter_request, NULL,
1359     GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
1360     sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES },
1361
1362   { host_recv_message, NULL,
1363     GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
1364     sizeof (struct GNUNET_PSYC_MessageHeader), GNUNET_YES },
1365
1366   { place_recv_message_ack, NULL,
1367     GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
1368     sizeof (struct GNUNET_MessageHeader), GNUNET_NO },
1369
1370   { place_recv_history_result, NULL,
1371     GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
1372     sizeof (struct GNUNET_OperationResultMessage), GNUNET_YES },
1373
1374   { place_recv_state_result, NULL,
1375     GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
1376     sizeof (struct GNUNET_OperationResultMessage), GNUNET_YES },
1377
1378   { place_recv_result, NULL,
1379     GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
1380     sizeof (struct GNUNET_OperationResultMessage), GNUNET_YES },
1381
1382   { place_recv_disconnect, NULL, 0, 0, GNUNET_NO },
1383
1384   { NULL, NULL, 0, 0, GNUNET_NO }
1385 };
1386
1387
1388 static struct GNUNET_CLIENT_MANAGER_MessageHandler guest_handlers[] =
1389 {
1390   { guest_recv_enter_ack, NULL,
1391     GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK,
1392     sizeof (struct GNUNET_PSYC_CountersResultMessage), GNUNET_NO },
1393
1394   { host_recv_enter_request, NULL,
1395     GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
1396     sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES },
1397
1398   { place_recv_message, NULL,
1399     GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
1400     sizeof (struct GNUNET_PSYC_MessageHeader), GNUNET_YES },
1401
1402   { place_recv_message_ack, NULL,
1403     GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
1404     sizeof (struct GNUNET_MessageHeader), GNUNET_NO },
1405
1406   { guest_recv_join_decision, NULL,
1407     GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
1408     sizeof (struct GNUNET_PSYC_JoinDecisionMessage), GNUNET_YES },
1409
1410   { place_recv_history_result, NULL,
1411     GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
1412     sizeof (struct GNUNET_OperationResultMessage), GNUNET_YES },
1413
1414   { place_recv_state_result, NULL,
1415     GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
1416     sizeof (struct GNUNET_OperationResultMessage), GNUNET_YES },
1417
1418   { place_recv_result, NULL,
1419     GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
1420     sizeof (struct GNUNET_OperationResultMessage), GNUNET_YES },
1421
1422   { place_recv_disconnect, NULL, 0, 0, GNUNET_NO },
1423
1424   { NULL, NULL, 0, 0, GNUNET_NO }
1425 };
1426
1427
1428 static void
1429 place_cleanup (struct GNUNET_SOCIAL_Place *plc)
1430 {
1431   if (NULL != plc->tmit)
1432     GNUNET_PSYC_transmit_destroy (plc->tmit);
1433   if (NULL != plc->recv)
1434     GNUNET_PSYC_receive_destroy (plc->recv);
1435   if (NULL != plc->connect_msg)
1436     GNUNET_free (plc->connect_msg);
1437   if (NULL != plc->disconnect_cb)
1438     plc->disconnect_cb (plc->disconnect_cls);
1439
1440   if (NULL != core)
1441   {
1442     GNUNET_CORE_disconnect (core);
1443     core = NULL;
1444   }
1445   if (NULL != namestore)
1446   {
1447     GNUNET_NAMESTORE_disconnect (namestore);
1448     namestore = NULL;
1449   }
1450   if (NULL != gns)
1451   {
1452     GNUNET_GNS_disconnect (gns);
1453     gns = NULL;
1454   }
1455 }
1456
1457
1458 static void
1459 host_cleanup (void *cls)
1460 {
1461   struct GNUNET_SOCIAL_Host *hst = cls;
1462   place_cleanup (&hst->plc);
1463   GNUNET_PSYC_receive_destroy (hst->recv);
1464   GNUNET_SOCIAL_slicer_destroy (hst->slicer);
1465   GNUNET_free (hst);
1466 }
1467
1468
1469 static void
1470 guest_cleanup (void *cls)
1471 {
1472   struct GNUNET_SOCIAL_Guest *gst = cls;
1473   place_cleanup (&gst->plc);
1474   GNUNET_free (gst);
1475 }
1476
1477
1478 /*** HOST ***/
1479
1480 /**
1481  * Enter a place as host.
1482  *
1483  * A place is created upon first entering, and it is active until permanently
1484  * left using GNUNET_SOCIAL_host_leave().
1485  *
1486  * @param cfg
1487  *        Configuration to contact the social service.
1488  * @param ego
1489  *        Identity of the host.
1490  * @param place_key
1491  *        Private-public key pair of the place.
1492  *        NULL for ephemeral places.
1493  * @param policy
1494  *        Policy specifying entry and history restrictions for the place.
1495  * @param slicer
1496  *        Slicer to handle incoming messages.
1497  * @param answer_door_cb
1498  *        Function to handle new nyms that want to enter.
1499  * @param farewell_cb
1500  *        Function to handle departing nyms.
1501  * @param cls
1502  *        Closure for the callbacks.
1503  *
1504  * @return Handle for the host.
1505  */
1506 struct GNUNET_SOCIAL_Host *
1507 GNUNET_SOCIAL_host_enter (const struct GNUNET_CONFIGURATION_Handle *cfg,
1508                           const struct GNUNET_IDENTITY_Ego *ego,
1509                           const struct GNUNET_CRYPTO_EddsaPrivateKey *place_key,
1510                           enum GNUNET_PSYC_Policy policy,
1511                           struct GNUNET_SOCIAL_Slicer *slicer,
1512                           GNUNET_SOCIAL_HostEnterCallback enter_cb,
1513                           GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
1514                           GNUNET_SOCIAL_FarewellCallback farewell_cb,
1515                           void *cls)
1516 {
1517   struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
1518   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1519   struct HostEnterRequest *req = GNUNET_malloc (sizeof (*req));
1520
1521   if (NULL != place_key)
1522   {
1523     hst->place_key = *place_key;
1524   }
1525   else
1526   {
1527     struct GNUNET_CRYPTO_EddsaPrivateKey *
1528       ephemeral_key = GNUNET_CRYPTO_eddsa_key_create ();
1529     hst->place_key = *ephemeral_key;
1530     GNUNET_CRYPTO_eddsa_key_get_public (&hst->place_key, &plc->pub_key);
1531     GNUNET_CRYPTO_eddsa_key_clear (ephemeral_key);
1532     GNUNET_free (ephemeral_key);
1533   }
1534
1535   req->header.size = htons (sizeof (*req));
1536   req->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
1537   req->policy = policy;
1538   req->place_key = hst->place_key;
1539   req->host_key = plc->ego_key;
1540
1541   plc->connect_msg = (struct GNUNET_MessageHeader *) req;
1542   plc->cfg = cfg;
1543   plc->is_host = GNUNET_YES;
1544   plc->slicer = slicer;
1545
1546   plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
1547   GNUNET_CRYPTO_eddsa_key_get_public (place_key, &plc->pub_key);
1548
1549   hst->enter_cb = enter_cb;
1550   hst->answer_door_cb = answer_door_cb;
1551   hst->farewell_cb = farewell_cb;
1552   hst->cb_cls = cls;
1553
1554   plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", host_handlers);
1555   GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, hst, sizeof (*plc));
1556
1557   plc->tmit = GNUNET_PSYC_transmit_create (plc->client);
1558   plc->recv = GNUNET_PSYC_receive_create (NULL, slicer_message, plc->slicer);
1559
1560   hst->slicer = GNUNET_SOCIAL_slicer_create ();
1561   GNUNET_SOCIAL_slicer_method_add (hst->slicer, "_notice_place_leave",
1562                                    host_recv_notice_place_leave_method,
1563                                    host_recv_notice_place_leave_modifier,
1564                                    NULL, host_recv_notice_place_leave_eom, hst);
1565   hst->recv = GNUNET_PSYC_receive_create (NULL, slicer_message, hst->slicer);
1566
1567   place_send_connect_msg (plc);
1568   return hst;
1569 }
1570
1571
1572 /**
1573  * Enter a place as host.
1574  *
1575  * A place is created upon first entering, and it is active until permanently
1576  * left using GNUNET_SOCIAL_host_leave().
1577  *
1578  * @param cfg
1579  *        Configuration to contact the social service.
1580  * @param ego
1581  *        Identity of the host.
1582  * @param gns_name
1583  *        GNS name in the zone of the @a ego that contains the
1584  *        public key of the place in a PLACE record.
1585  * @param policy
1586  *        Policy specifying entry and history restrictions for the place.
1587  * @param slicer
1588  *        Slicer to handle incoming messages.
1589  * @param answer_door_cb
1590  *        Function to handle new nyms that want to enter.
1591  * @param farewell_cb
1592  *        Function to handle departing nyms.
1593  * @param cls
1594  *        Closure for the callbacks.
1595  *
1596  * @return Handle for the host.
1597  */
1598 struct GNUNET_SOCIAL_Host *
1599 GNUNET_SOCIAL_host_enter_by_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
1600                                   struct GNUNET_IDENTITY_Ego *ego,
1601                                   const char *gns_name,
1602                                   enum GNUNET_PSYC_Policy policy,
1603                                   struct GNUNET_SOCIAL_Slicer *slicer,
1604                                   GNUNET_SOCIAL_HostEnterCallback enter_cb,
1605                                   GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
1606                                   GNUNET_SOCIAL_FarewellCallback farewell_cb,
1607                                   void *cls)
1608 {
1609   struct GNUNET_CRYPTO_EddsaPrivateKey place_key = {};
1610
1611   /* FIXME:
1612    * 1. get public key by looking up PLACE entry under gns_name
1613    *    in the zone of the ego.
1614    * 2. get private key from $GNUNET_DATA_HOME/social/places/PUB_KEY_HASH
1615    */
1616
1617   return GNUNET_SOCIAL_host_enter (cfg, ego, &place_key, policy, slicer,
1618                                    enter_cb, answer_door_cb, farewell_cb, cls);
1619 }
1620
1621
1622 /**
1623  * Decision whether to admit @a nym into the place or refuse entry.
1624  *
1625  * @param hst
1626  *        Host of the place.
1627  * @param nym
1628  *        Handle for the entity that wanted to enter.
1629  * @param is_admitted
1630  *        #GNUNET_YES    if @a nym is admitted,
1631  *        #GNUNET_NO     if @a nym is refused entry,
1632  *        #GNUNET_SYSERR if we cannot answer the request.
1633  * @param method_name
1634  *        Method name for the rejection message.
1635  * @param env
1636  *        Environment containing variables for the message, or NULL.
1637  * @param data
1638  *        Data for the rejection message to send back.
1639  * @param data_size
1640  *        Number of bytes in @a data for method.
1641  * @return #GNUNET_OK on success,
1642  *         #GNUNET_SYSERR if the message is too large.
1643  */
1644 int
1645 GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
1646                                    struct GNUNET_SOCIAL_Nym *nym,
1647                                    int is_admitted,
1648                                    const struct GNUNET_PSYC_Message *entry_resp)
1649 {
1650   struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
1651   uint16_t entry_resp_size
1652     = (NULL != entry_resp) ? ntohs (entry_resp->header.size) : 0;
1653
1654   if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < sizeof (*dcsn) + entry_resp_size)
1655     return GNUNET_SYSERR;
1656
1657   dcsn = GNUNET_malloc (sizeof (*dcsn) + entry_resp_size);
1658   dcsn->header.size = htons (sizeof (*dcsn) + entry_resp_size);
1659   dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
1660   dcsn->is_admitted = htonl (is_admitted);
1661   dcsn->slave_key = nym->pub_key;
1662
1663   if (0 < entry_resp_size)
1664     memcpy (&dcsn[1], entry_resp, entry_resp_size);
1665
1666   GNUNET_CLIENT_MANAGER_transmit (hst->plc.client, &dcsn->header);
1667   return GNUNET_OK;
1668 }
1669
1670
1671 /**
1672  * Throw @a nym out of the place.
1673  *
1674  * The @a nym reference will remain valid until the
1675  * #GNUNET_SOCIAL_FarewellCallback is invoked,
1676  * which should be very soon after this call.
1677  *
1678  * @param host
1679  *        Host of the place.
1680  * @param nym
1681  *        Handle for the entity to be ejected.
1682  */
1683 void
1684 GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *hst,
1685                           const struct GNUNET_SOCIAL_Nym *nym)
1686 {
1687   struct GNUNET_ENV_Environment *env = GNUNET_ENV_environment_create ();
1688   GNUNET_ENV_environment_add (env, GNUNET_ENV_OP_SET,
1689                               "_nym", &nym->pub_key, sizeof (nym->pub_key));
1690   GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave", env, NULL, NULL,
1691                                GNUNET_SOCIAL_ANNOUNCE_NONE);
1692 }
1693
1694
1695 /**
1696  * Get the public key of a @a nym.
1697  *
1698  * Suitable, for example, to be used with GNUNET_NAMESTORE_zone_to_name().
1699  *
1700  * @param nym
1701  *        Pseudonym to map to a cryptographic identifier.
1702  *
1703  * @return Public key of nym.
1704  */
1705 const struct GNUNET_CRYPTO_EcdsaPublicKey *
1706 GNUNET_SOCIAL_nym_get_key (const struct GNUNET_SOCIAL_Nym *nym)
1707 {
1708   return &nym->pub_key;
1709 }
1710
1711
1712 /**
1713  * Get the hash of the public key of a @a nym.
1714  *
1715  * @param nym
1716  *        Pseudonym to map to a cryptographic identifier.
1717  *
1718  * @return Hash of the public key of nym.
1719  */
1720 const struct GNUNET_HashCode *
1721 GNUNET_SOCIAL_nym_get_key_hash (const struct GNUNET_SOCIAL_Nym *nym)
1722 {
1723   return &nym->pub_key_hash;
1724 }
1725
1726
1727 /**
1728  * Obtain the private-public key pair of the hosted place.
1729  *
1730  * The public part is suitable for storing in GNS within a PLACE record,
1731  * along with peer IDs to join at.
1732  *
1733  * @param host
1734  *        Host of the place.
1735  *
1736  * @return Private-public key pair of the hosted place.
1737  */
1738 const struct GNUNET_CRYPTO_EddsaPrivateKey *
1739 GNUNET_SOCIAL_host_get_place_key (struct GNUNET_SOCIAL_Host *hst)
1740 {
1741   return &hst->place_key;
1742 }
1743
1744
1745 /**
1746  * Connected to core service.
1747  */
1748 static void
1749 core_connected_cb  (void *cls, const struct GNUNET_PeerIdentity *my_identity)
1750 {
1751   this_peer = *my_identity;
1752   // FIXME
1753 }
1754
1755
1756 /**
1757  * Advertise the place in the GNS zone of the @e ego of the @a host.
1758  *
1759  * @param hst
1760  *        Host of the place.
1761  * @param name
1762  *        The name for the PLACE record to put in the zone.
1763  * @param peer_count
1764  *        Number of elements in the @a peers array.
1765  * @param peers
1766  *        List of peers to put in the PLACE record to advertise
1767  *        as entry points to the place in addition to the origin.
1768  * @param expiration_time
1769  *        Expiration time of the record, use 0 to remove the record.
1770  * @param password
1771  *        Password used to encrypt the record or NULL to keep it cleartext.
1772  * @param result_cb
1773  *        Function called with the result of the operation.
1774  * @param result_cls
1775  *        Closure for @a result_cb
1776  */
1777 void
1778 GNUNET_SOCIAL_host_advertise (struct GNUNET_SOCIAL_Host *hst,
1779                               const char *name,
1780                               uint32_t peer_count,
1781                               const struct GNUNET_PeerIdentity *peers,
1782                               struct GNUNET_TIME_Absolute expiration_time,
1783                               const char *password,
1784                               GNUNET_NAMESTORE_ContinuationWithStatus result_cb,
1785                               void *result_cls)
1786 {
1787   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1788   if (NULL == namestore)
1789     namestore = GNUNET_NAMESTORE_connect (plc->cfg);
1790   if (NULL == core)
1791     core = GNUNET_CORE_connect (plc->cfg, NULL, core_connected_cb, NULL, NULL,
1792                                 NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
1793
1794   struct GNUNET_GNSRECORD_Data rd = { };
1795   rd.record_type = GNUNET_GNSRECORD_TYPE_PLACE;
1796   rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1797   rd.expiration_time = expiration_time.abs_value_us;
1798
1799   struct GNUNET_GNSRECORD_PlaceData *
1800     rec = GNUNET_malloc (sizeof (*rec) + peer_count * sizeof (*peers));
1801   rec->place_key = plc->pub_key;
1802   rec->origin = this_peer;
1803   rec->relay_count = htonl (peer_count);
1804   memcpy (&rec[1], peers, peer_count * sizeof (*peers));
1805
1806   rd.data = rec;
1807   rd.data_size = sizeof (*rec) + peer_count * sizeof (*peers);
1808
1809   GNUNET_NAMESTORE_records_store (namestore, &hst->plc.ego_key,
1810                                   name, 1, &rd, result_cb, result_cls);
1811 }
1812
1813
1814 /**
1815  * Send a message to all nyms that are present in the place.
1816  *
1817  * This function is restricted to the host.  Nyms can only send requests
1818  * to the host who can decide to relay it to everyone in the place.
1819  *
1820  * @param host  Host of the place.
1821  * @param method_name Method to use for the announcement.
1822  * @param env  Environment containing variables for the message and operations
1823  *          on objects of the place.  Can be NULL.
1824  * @param notify Function to call to get the payload of the announcement.
1825  * @param notify_cls Closure for @a notify.
1826  * @param flags Flags for this announcement.
1827  *
1828  * @return NULL on error (announcement already in progress?).
1829  */
1830 struct GNUNET_SOCIAL_Announcement *
1831 GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *hst,
1832                              const char *method_name,
1833                              const struct GNUNET_ENV_Environment *env,
1834                              GNUNET_PSYC_TransmitNotifyData notify_data,
1835                              void *notify_data_cls,
1836                              enum GNUNET_SOCIAL_AnnounceFlags flags)
1837 {
1838   if (GNUNET_OK ==
1839       GNUNET_PSYC_transmit_message (hst->plc.tmit, method_name, env,
1840                                     NULL, notify_data, notify_data_cls, flags))
1841     return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit;
1842   else
1843     return NULL;
1844 }
1845
1846
1847 /**
1848  * Resume transmitting announcement.
1849  *
1850  * @param a
1851  *        The announcement to resume.
1852  */
1853 void
1854 GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a)
1855 {
1856   GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) a);
1857 }
1858
1859
1860 /**
1861  * Cancel announcement.
1862  *
1863  * @param a
1864  *        The announcement to cancel.
1865  */
1866 void
1867 GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a)
1868 {
1869   GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) a);
1870 }
1871
1872
1873 /**
1874  * Obtain handle for a hosted place.
1875  *
1876  * The returned handle can be used to access the place API.
1877  *
1878  * @param host  Handle for the host.
1879  *
1880  * @return Handle for the hosted place, valid as long as @a host is valid.
1881  */
1882 struct GNUNET_SOCIAL_Place *
1883 GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *hst)
1884 {
1885   return &hst->plc;
1886 }
1887
1888
1889 /**
1890  * Stop hosting a place.
1891  *
1892  * Invalidates host handle.
1893  *
1894  * @param host  Host leaving the place.
1895  * @param keep_active  Keep the place active after last host disconnected.
1896  */
1897 void
1898 GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst,
1899                           int keep_active,
1900                           GNUNET_ContinuationCallback leave_cb,
1901                           void *leave_cls)
1902 {
1903   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1904
1905  /* FIXME: send msg to service */
1906
1907   plc->is_disconnecting = GNUNET_YES;
1908   plc->disconnect_cb = leave_cb;
1909   plc->disconnect_cls = leave_cls;
1910
1911   GNUNET_CLIENT_MANAGER_disconnect (plc->client, GNUNET_YES,
1912                                     &host_cleanup, hst);
1913 }
1914
1915
1916 /*** GUEST ***/
1917
1918 static struct GuestEnterRequest *
1919 guest_enter_request_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *guest_key,
1920                             const struct GNUNET_CRYPTO_EddsaPublicKey *place_key,
1921                             const struct GNUNET_PeerIdentity *origin,
1922                             size_t relay_count,
1923                             const struct GNUNET_PeerIdentity *relays,
1924                             const struct GNUNET_PSYC_Message *join_msg)
1925 {
1926   uint16_t join_msg_size = ntohs (join_msg->header.size);
1927   uint16_t relay_size = relay_count * sizeof (*relays);
1928
1929   struct GuestEnterRequest *
1930     req = GNUNET_malloc (sizeof (*req) + relay_size + join_msg_size);
1931
1932   req->header.size = htons (sizeof (*req) + relay_size + join_msg_size);
1933   req->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
1934   req->place_key = *place_key;
1935   req->guest_key = *guest_key;
1936   req->origin = *origin;
1937   req->relay_count = htonl (relay_count);
1938
1939   uint16_t p = sizeof (*req);
1940   if (0 < relay_size)
1941   {
1942     memcpy ((char *) req + p, relays, relay_size);
1943     p += relay_size;
1944   }
1945
1946   memcpy ((char *) req + p, join_msg, join_msg_size);
1947   return req;
1948 }
1949
1950 /**
1951  * Request entry to a place as a guest.
1952  *
1953  * @param cfg Configuration to contact the social service.
1954  * @param ego  Identity of the guest.
1955  * @param crypto_address Public key of the place to enter.
1956  * @param origin Peer identity of the origin of the underlying multicast group.
1957  * @param relay_count Number of elements in the @a relays array.
1958  * @param relays Relays for the underlying multicast group.
1959  * @param method_name Method name for the message.
1960  * @param env Environment containing variables for the message, or NULL.
1961  * @param data Payload for the message to give to the enter callback.
1962  * @param data_size Number of bytes in @a data.
1963  * @param slicer Slicer to use for processing incoming requests from guests.
1964  *
1965  * @return NULL on errors, otherwise handle for the guest.
1966  */
1967 struct GNUNET_SOCIAL_Guest *
1968 GNUNET_SOCIAL_guest_enter (const struct GNUNET_CONFIGURATION_Handle *cfg,
1969                            const struct GNUNET_IDENTITY_Ego *ego,
1970                            const struct GNUNET_CRYPTO_EddsaPublicKey *place_key,
1971                            const struct GNUNET_PeerIdentity *origin,
1972                            uint32_t relay_count,
1973                            const struct GNUNET_PeerIdentity *relays,
1974                            const struct GNUNET_PSYC_Message *entry_msg,
1975                            struct GNUNET_SOCIAL_Slicer *slicer,
1976                            GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
1977                            GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb,
1978                            void *cls)
1979 {
1980   struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1981   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1982
1983   plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
1984   plc->pub_key = *place_key;
1985   plc->cfg = cfg;
1986   plc->is_host = GNUNET_YES;
1987   plc->slicer = slicer;
1988
1989   gst->enter_cb = local_enter_cb;
1990   gst->entry_dcsn_cb = entry_dcsn_cb;
1991   gst->cb_cls = cls;
1992
1993   plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", guest_handlers);
1994   GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, gst, sizeof (*plc));
1995
1996   plc->tmit = GNUNET_PSYC_transmit_create (plc->client);
1997   plc->recv = GNUNET_PSYC_receive_create (NULL, slicer_message, plc->slicer);
1998
1999   struct GuestEnterRequest *
2000     req = guest_enter_request_create (&plc->ego_key, place_key, origin,
2001                                       relay_count, relays, entry_msg);
2002   plc->connect_msg = &req->header;
2003   place_send_connect_msg (plc);
2004   return gst;
2005 }
2006
2007
2008 /**
2009  * Result of a GNS name lookup for entering a place.
2010  *
2011  * @see GNUNET_SOCIAL_guest_enter_by_name
2012  */
2013 static void
2014 gns_result_guest_enter (void *cls, uint32_t rd_count,
2015                         const struct GNUNET_GNSRECORD_Data *rd)
2016 {
2017   struct GNUNET_SOCIAL_Guest *gst = cls;
2018   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2019
2020   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2021               "%p GNS result: %u records.\n", gst, rd_count);
2022
2023   const struct GNUNET_GNSRECORD_PlaceData *
2024     rec = (const struct GNUNET_GNSRECORD_PlaceData *) rd->data;
2025
2026   if (0 == rd_count)
2027   {
2028     if (NULL != gst->enter_cb)
2029       gst->enter_cb (gst->cb_cls, GNUNET_SYSERR, 0);
2030     return;
2031   }
2032
2033   if (rd->data_size < sizeof (*rec))
2034   {
2035     GNUNET_break_op (0);
2036     if (NULL != gst->enter_cb)
2037       gst->enter_cb (gst->cb_cls, GNUNET_SYSERR, 0);
2038     return;
2039   }
2040
2041   uint16_t relay_count = ntohl (rec->relay_count);
2042   struct GNUNET_PeerIdentity *relays = NULL;
2043
2044   if (0 < relay_count)
2045   {
2046     if (rd->data_size == sizeof (*rec) + relay_count * sizeof (struct GNUNET_PeerIdentity))
2047     {
2048       relays = (struct GNUNET_PeerIdentity *) &rec[1];
2049     }
2050     else
2051     {
2052       relay_count = 0;
2053       GNUNET_break_op (0);
2054     }
2055   }
2056
2057   struct GuestEnterRequest *
2058     req = guest_enter_request_create (&plc->ego_key, &rec->place_key,
2059                                       &rec->origin, relay_count, relays,
2060                                       (struct GNUNET_PSYC_Message *) plc->connect_msg);
2061   GNUNET_free (plc->connect_msg);
2062   plc->connect_msg = &req->header;
2063   plc->pub_key = req->place_key;
2064
2065   plc->tmit = GNUNET_PSYC_transmit_create (plc->client);
2066   plc->recv = GNUNET_PSYC_receive_create (NULL, slicer_message, plc->slicer);
2067
2068   place_send_connect_msg (plc);
2069 }
2070
2071
2072 /**
2073  * Request entry to a place by name as a guest.
2074  *
2075  * @param cfg
2076  *        Configuration to contact the social service.
2077  * @param ego
2078  *        Identity of the guest.
2079  * @param gns_name
2080  *        GNS name of the place to enter.  Either in the form of
2081  *        'room.friend.gnu', or 'NYMPUBKEY.zkey'.  This latter case refers to
2082  *        the 'PLACE' record of the empty label ("+") in the GNS zone with the
2083  *        nym's public key 'NYMPUBKEY', and can be used to request entry to a
2084  *        pseudonym's place directly.
2085  * @param password
2086  *        Password to decrypt the record, or NULL for cleartext records.
2087  * @param join_msg
2088  *        Entry request message.
2089  * @param slicer
2090  *        Slicer to use for processing incoming requests from guests.
2091  * @param local_enter_cb
2092  *        Called upon connection established to the social service.
2093  * @param entry_decision_cb
2094  *        Called upon receiving entry decision.
2095  *
2096  * @return NULL on errors, otherwise handle for the guest.
2097  */
2098 struct GNUNET_SOCIAL_Guest *
2099 GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
2100                                    const struct GNUNET_IDENTITY_Ego *ego,
2101                                    const char *gns_name, const char *password,
2102                                    const struct GNUNET_PSYC_Message *join_msg,
2103                                    struct GNUNET_SOCIAL_Slicer *slicer,
2104                                    GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
2105                                    GNUNET_SOCIAL_EntryDecisionCallback entry_decision_cb,
2106                                    void *cls)
2107 {
2108   struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
2109   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2110
2111   GNUNET_assert (NULL != join_msg);
2112
2113   gst->enter_cb = local_enter_cb;
2114   gst->entry_dcsn_cb = entry_decision_cb;
2115   gst->cb_cls = cls;
2116
2117   plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
2118   plc->cfg = cfg;
2119   plc->is_host = GNUNET_NO;
2120   plc->slicer = slicer;
2121
2122   uint16_t join_msg_size = ntohs (join_msg->header.size);
2123   plc->connect_msg = GNUNET_malloc (join_msg_size);
2124   memcpy (plc->connect_msg, join_msg, join_msg_size);
2125
2126   if (NULL == gns)
2127     gns = GNUNET_GNS_connect (cfg);
2128
2129   plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", guest_handlers);
2130   GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, gst, sizeof (*plc));
2131
2132   struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
2133   GNUNET_IDENTITY_ego_get_public_key (ego, &ego_pub_key);
2134   GNUNET_GNS_lookup (gns, gns_name, &ego_pub_key,
2135                      GNUNET_GNSRECORD_TYPE_PLACE, GNUNET_GNS_LO_DEFAULT,
2136                      NULL, gns_result_guest_enter, gst);
2137   return gst;
2138 }
2139
2140
2141 /**
2142  * Talk to the host of the place.
2143  *
2144  * @param place
2145  *        Place where we want to talk to the host.
2146  * @param method_name
2147  *        Method to invoke on the host.
2148  * @param env
2149  *        Environment containing variables for the message, or NULL.
2150  * @param notify_data
2151  *        Function to use to get the payload for the method.
2152  * @param notify_data_cls
2153  *        Closure for @a notify_data.
2154  * @param flags
2155  *        Flags for the message being sent.
2156  *
2157  * @return NULL if we are already trying to talk to the host,
2158  *         otherwise handle to cancel the request.
2159  */
2160 struct GNUNET_SOCIAL_TalkRequest *
2161 GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *gst,
2162                           const char *method_name,
2163                           const struct GNUNET_ENV_Environment *env,
2164                           GNUNET_PSYC_TransmitNotifyData notify_data,
2165                           void *notify_data_cls,
2166                           enum GNUNET_SOCIAL_TalkFlags flags)
2167 {
2168   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2169   GNUNET_assert (NULL != plc->tmit);
2170
2171   if (GNUNET_OK ==
2172       GNUNET_PSYC_transmit_message (plc->tmit, method_name, env,
2173                                     NULL, notify_data, notify_data_cls, flags))
2174     return (struct GNUNET_SOCIAL_TalkRequest *) plc->tmit;
2175   else
2176     return NULL;
2177 }
2178
2179
2180 /**
2181  * Resume talking to the host of the place.
2182  *
2183  * @param tr
2184  *        Talk request to resume.
2185  */
2186 void
2187 GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr)
2188 {
2189   GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tr);
2190 }
2191
2192
2193 /**
2194  * Cancel talking to the host of the place.
2195  *
2196  * @param tr
2197  *        Talk request to cancel.
2198  */
2199 void
2200 GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr)
2201 {
2202   GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) tr);
2203 }
2204
2205
2206 /**
2207  * Leave a place temporarily or permanently.
2208  *
2209  * Notifies the owner of the place about leaving, and destroys the place handle.
2210  *
2211  * @param place
2212  *        Place to leave.
2213  * @param keep_active
2214  *        Keep place active after last application disconnected.
2215  *        #GNUNET_YES or #GNUNET_NO
2216  * @param env
2217  *        Optional environment for the leave message if @a keep_active
2218  *        is #GNUNET_NO.  NULL if not needed.
2219  * @param leave_cb
2220  *        Called upon disconnecting from the social service.
2221  */
2222 void
2223 GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst,
2224                            int keep_active,
2225                            struct GNUNET_ENV_Environment *env,
2226                            GNUNET_ContinuationCallback leave_cb,
2227                            void *leave_cls)
2228 {
2229   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2230
2231   plc->is_disconnecting = GNUNET_YES;
2232   plc->disconnect_cb = leave_cb;
2233   plc->disconnect_cls = leave_cls;
2234
2235   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2236               "Guest: leaving place.\n");
2237
2238   if (GNUNET_NO == keep_active && NULL != plc->tmit)
2239   {
2240     GNUNET_SOCIAL_guest_talk (gst, "_notice_place_leave", env, NULL, NULL,
2241                               GNUNET_SOCIAL_TALK_NONE);
2242   }
2243
2244   GNUNET_CLIENT_MANAGER_disconnect (plc->client, GNUNET_YES,
2245                                     &guest_cleanup, gst);
2246 }
2247
2248
2249 /**
2250  * Obtain handle for a place entered as guest.
2251  *
2252  * The returned handle can be used to access the place API.
2253  *
2254  * @param guest  Handle for the guest.
2255  *
2256  * @return Handle for the place, valid as long as @a guest is valid.
2257  */
2258 struct GNUNET_SOCIAL_Place *
2259 GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *gst)
2260 {
2261   return &gst->plc;
2262 }
2263
2264
2265 static struct GNUNET_SOCIAL_HistoryRequest *
2266 place_history_replay (struct GNUNET_SOCIAL_Place *plc,
2267                       uint64_t start_message_id,
2268                       uint64_t end_message_id,
2269                       uint64_t message_limit,
2270                       const char *method_prefix,
2271                       uint32_t flags,
2272                       struct GNUNET_SOCIAL_Slicer *slicer,
2273                       GNUNET_ResultCallback result_cb,
2274                       void *cls)
2275 {
2276   struct GNUNET_PSYC_HistoryRequestMessage *req;
2277   struct GNUNET_SOCIAL_HistoryRequest *hist = GNUNET_malloc (sizeof (*hist));
2278   hist->plc = plc;
2279   hist->recv = GNUNET_PSYC_receive_create (NULL, slicer_message, slicer);
2280   hist->result_cb = result_cb;
2281   hist->cls = cls;
2282   hist->op_id = GNUNET_CLIENT_MANAGER_op_add (plc->client,
2283                                               &op_recv_history_result, hist);
2284
2285   GNUNET_assert (NULL != method_prefix);
2286   uint16_t method_size = strnlen (method_prefix,
2287                                   GNUNET_SERVER_MAX_MESSAGE_SIZE
2288                                   - sizeof (*req)) + 1;
2289   GNUNET_assert ('\0' == method_prefix[method_size - 1]);
2290   req = GNUNET_malloc (sizeof (*req) + method_size);
2291   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY);
2292   req->header.size = htons (sizeof (*req) + method_size);
2293   req->start_message_id = GNUNET_htonll (start_message_id);
2294   req->end_message_id = GNUNET_htonll (end_message_id);
2295   req->message_limit = GNUNET_htonll (message_limit);
2296   req->flags = htonl (flags);
2297   req->op_id = GNUNET_htonll (hist->op_id);
2298   memcpy (&req[1], method_prefix, method_size);
2299
2300   GNUNET_CLIENT_MANAGER_transmit (plc->client, &req->header);
2301   return hist;
2302 }
2303
2304
2305 /**
2306  * Learn about the history of a place.
2307  *
2308  * Messages are returned through the @a slicer function
2309  * and have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
2310  *
2311  * @param place
2312  *        Place we want to learn more about.
2313  * @param start_message_id
2314  *        First historic message we are interested in.
2315  * @param end_message_id
2316  *        Last historic message we are interested in (inclusive).
2317  * @param method_prefix
2318  *        Only retrieve messages with this method prefix.
2319  * @param flags
2320  *        OR'ed GNUNET_PSYC_HistoryReplayFlags
2321  * @param slicer
2322  *        Slicer to use for retrieved messages.
2323  *        Can be the same as the slicer of the place.
2324  * @param result_cb
2325  *        Function called after all messages retrieved.
2326  *        NULL if not needed.
2327  * @param cls Closure for @a result_cb.
2328  */
2329 struct GNUNET_SOCIAL_HistoryRequest *
2330 GNUNET_SOCIAL_place_history_replay (struct GNUNET_SOCIAL_Place *plc,
2331                                     uint64_t start_message_id,
2332                                     uint64_t end_message_id,
2333                                     const char *method_prefix,
2334                                     uint32_t flags,
2335                                     struct GNUNET_SOCIAL_Slicer *slicer,
2336                                     GNUNET_ResultCallback result_cb,
2337                                     void *cls)
2338 {
2339   return place_history_replay (plc, start_message_id, end_message_id, 0,
2340                                method_prefix, flags, slicer, result_cb, cls);
2341 }
2342
2343
2344 /**
2345  * Learn about the history of a place.
2346  *
2347  * Sends messages through the slicer function of the place where
2348  * start_message_id <= message_id <= end_message_id.
2349  * The messages will have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
2350  *
2351  * To get the latest message, use 0 for both the start and end message ID.
2352  *
2353  * @param place
2354  *        Place we want to learn more about.
2355  * @param message_limit
2356  *        Maximum number of historic messages we are interested in.
2357  * @param method_prefix
2358  *        Only retrieve messages with this method prefix.
2359  * @param flags
2360  *        OR'ed GNUNET_PSYC_HistoryReplayFlags
2361  * @param result_cb
2362  *        Function called after all messages retrieved.
2363  *        NULL if not needed.
2364  * @param cls Closure for @a result_cb.
2365  */
2366 struct GNUNET_SOCIAL_HistoryRequest *
2367 GNUNET_SOCIAL_place_history_replay_latest (struct GNUNET_SOCIAL_Place *plc,
2368                                            uint64_t message_limit,
2369                                            const char *method_prefix,
2370                                            uint32_t flags,
2371                                            struct GNUNET_SOCIAL_Slicer *slicer,
2372                                            GNUNET_ResultCallback result_cb,
2373                                            void *cls)
2374 {
2375   return place_history_replay (plc, 0, 0, message_limit, method_prefix, flags,
2376                                slicer, result_cb, cls);
2377 }
2378
2379
2380 /**
2381  * Cancel learning about the history of a place.
2382  *
2383  * @param hist
2384  *        History lesson to cancel.
2385  */
2386 void
2387 GNUNET_SOCIAL_place_history_replay_cancel (struct GNUNET_SOCIAL_HistoryRequest *hist)
2388 {
2389   GNUNET_PSYC_receive_destroy (hist->recv);
2390   GNUNET_CLIENT_MANAGER_op_cancel (hist->plc->client, hist->op_id);
2391   GNUNET_free (hist);
2392 }
2393
2394
2395 /**
2396  * Request matching state variables.
2397  */
2398 static struct GNUNET_SOCIAL_LookHandle *
2399 place_state_get (struct GNUNET_SOCIAL_Place *plc,
2400                  uint16_t type, const char *name,
2401                  GNUNET_PSYC_StateVarCallback var_cb,
2402                  GNUNET_ResultCallback result_cb, void *cls)
2403 {
2404   struct GNUNET_PSYC_StateRequestMessage *req;
2405   struct GNUNET_SOCIAL_LookHandle *look = GNUNET_malloc (sizeof (*look));
2406   look->plc = plc;
2407   look->var_cb = var_cb;
2408   look->result_cb = result_cb;
2409   look->cls = cls;
2410   look->op_id = GNUNET_CLIENT_MANAGER_op_add (plc->client,
2411                                               &op_recv_state_result, look);
2412
2413   GNUNET_assert (NULL != name);
2414   size_t name_size = strnlen (name, GNUNET_SERVER_MAX_MESSAGE_SIZE
2415                               - sizeof (*req)) + 1;
2416   req = GNUNET_malloc (sizeof (*req) + name_size);
2417   req->header.type = htons (type);
2418   req->header.size = htons (sizeof (*req) + name_size);
2419   req->op_id = GNUNET_htonll (look->op_id);
2420   memcpy (&req[1], name, name_size);
2421
2422   GNUNET_CLIENT_MANAGER_transmit (plc->client, &req->header);
2423   return look;
2424 }
2425
2426
2427 /**
2428  * Look at a particular object in the place.
2429  *
2430  * The best matching object is returned (its name might be less specific than
2431  * what was requested).
2432  *
2433  * @param place
2434  *        The place where to look.
2435  * @param full_name
2436  *        Full name of the object.
2437  * @param value_size
2438  *        Set to the size of the returned value.
2439  *
2440  * @return NULL if there is no such object at this place.
2441  */
2442 struct GNUNET_SOCIAL_LookHandle *
2443 GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *plc,
2444                              const char *full_name,
2445                              GNUNET_PSYC_StateVarCallback var_cb,
2446                              GNUNET_ResultCallback result_cb,
2447                              void *cls)
2448 {
2449   return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
2450                           full_name, var_cb, result_cb, cls);
2451 }
2452
2453
2454 /**
2455  * Look for objects in the place with a matching name prefix.
2456  *
2457  * @param place
2458  *        The place where to look.
2459  * @param name_prefix
2460  *        Look at objects with names beginning with this value.
2461  * @param var_cb
2462  *        Function to call for each object found.
2463  * @param cls
2464  *        Closure for callback function.
2465  *
2466  * @return Handle that can be used to stop looking at objects.
2467  */
2468 struct GNUNET_SOCIAL_LookHandle *
2469 GNUNET_SOCIAL_place_look_for (struct GNUNET_SOCIAL_Place *plc,
2470                               const char *name_prefix,
2471                               GNUNET_PSYC_StateVarCallback var_cb,
2472                               GNUNET_ResultCallback result_cb,
2473                               void *cls)
2474 {
2475   return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
2476                           name_prefix, var_cb, result_cb, cls);
2477 }
2478
2479
2480 /**
2481  * Cancel a state request operation.
2482  *
2483  * @param sr
2484  *        Handle for the operation to cancel.
2485  */
2486 void
2487 GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *look)
2488 {
2489   GNUNET_CLIENT_MANAGER_op_cancel (look->plc->client, look->op_id);
2490   GNUNET_free (look);
2491 }
2492
2493
2494 /**
2495  * Add public key to the GNS zone of the @e ego.
2496  *
2497  * @param cfg
2498  *        Configuration.
2499  * @param ego
2500  *        Ego.
2501  * @param name
2502  *        The name for the PKEY record to put in the zone.
2503  * @param pub_key
2504  *        Public key to add.
2505  * @param expiration_time
2506  *        Expiration time of the record, use 0 to remove the record.
2507  * @param result_cb
2508  *        Function called with the result of the operation.
2509  * @param result_cls
2510  *        Closure for @a result_cb
2511  */
2512 void
2513 GNUNET_SOCIAL_zone_add_pkey (const struct GNUNET_CONFIGURATION_Handle *cfg,
2514                              const struct GNUNET_IDENTITY_Ego *ego,
2515                              const char *name,
2516                              const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
2517                              struct GNUNET_TIME_Absolute expiration_time,
2518                              GNUNET_NAMESTORE_ContinuationWithStatus result_cb,
2519                              void *result_cls)
2520 {
2521   if (NULL == namestore)
2522     namestore = GNUNET_NAMESTORE_connect (cfg);
2523
2524   struct GNUNET_GNSRECORD_Data rd = { };
2525   rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
2526   rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
2527   rd.expiration_time = expiration_time.abs_value_us;
2528   rd.data = pub_key;
2529   rd.data_size = sizeof (*pub_key);
2530
2531   GNUNET_NAMESTORE_records_store (namestore,
2532                                   GNUNET_IDENTITY_ego_get_private_key (ego),
2533                                   name, 1, &rd, result_cb, result_cls);
2534 }
2535
2536
2537 /* end of social_api.c */