Merge branch 'fix_social' of gnunet.org:gnunet into fix_social
[oweals/gnunet.git] / src / social / social_api.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2013 GNUnet e.V.
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  * @author Gabor X Toth
23  *
24  * @file
25  * Social service; implements social interactions using the PSYC service.
26  */
27
28 #include <inttypes.h>
29 #include <string.h>
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_psyc_service.h"
34 #include "gnunet_psyc_util_lib.h"
35 #include "gnunet_social_service.h"
36 #include "social.h"
37
38 #define LOG(kind,...) GNUNET_log_from (kind, "social-api",__VA_ARGS__)
39
40 /**
41  * Handle for an ego.
42  */
43 struct GNUNET_SOCIAL_Ego
44 {
45   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
46   struct GNUNET_HashCode pub_key_hash;
47   char *name;
48 };
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 an application.
63  */
64 struct GNUNET_SOCIAL_App
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_MQ_Handle *mq;
75
76   /**
77    * Message to send on connect.
78    */
79   struct GNUNET_MQ_Envelope *connect_env;
80
81   /**
82    * Time to wait until we try to reconnect on failure.
83    */
84   struct GNUNET_TIME_Relative reconnect_delay;
85
86   /**
87    * Task for reconnecting when the listener fails.
88    */
89   struct GNUNET_SCHEDULER_Task *reconnect_task;
90
91   /**
92    * Async operations.
93    */
94   struct GNUNET_OP_Handle *op;
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    * Application ID.
108    */
109   char *id;
110
111   /**
112    * Hash map of all egos.
113    * pub_key_hash -> struct GNUNET_SOCIAL_Ego *
114    */
115   struct GNUNET_CONTAINER_MultiHashMap *egos;
116
117   GNUNET_SOCIAL_AppEgoCallback ego_cb;
118   GNUNET_SOCIAL_AppHostPlaceCallback host_cb;
119   GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb;
120   GNUNET_SOCIAL_AppConnectedCallback connected_cb;
121   void *cb_cls;
122 };
123
124
125 struct GNUNET_SOCIAL_HostConnection
126 {
127   struct GNUNET_SOCIAL_App *app;
128
129   struct AppPlaceMessage plc_msg;
130 };
131
132
133 struct GNUNET_SOCIAL_GuestConnection
134 {
135   struct GNUNET_SOCIAL_App *app;
136
137   struct AppPlaceMessage plc_msg;
138 };
139
140
141 /**
142  * Handle for a place where social interactions happen.
143  */
144 struct GNUNET_SOCIAL_Place
145 {
146   /**
147    * Configuration to use.
148    */
149   const struct GNUNET_CONFIGURATION_Handle *cfg;
150
151   /**
152    * Client connection to the service.
153    */
154   struct GNUNET_MQ_Handle *mq;
155
156   /**
157    * Message to send on connect.
158    */
159   struct GNUNET_MQ_Envelope *connect_env;
160
161   /**
162    * Time to wait until we try to reconnect on failure.
163    */
164   struct GNUNET_TIME_Relative reconnect_delay;
165
166   /**
167    * Task for reconnecting when the listener fails.
168    */
169   struct GNUNET_SCHEDULER_Task *reconnect_task;
170
171   /**
172    * Async operations.
173    */
174   struct GNUNET_OP_Handle *op;
175
176   /**
177    * Transmission handle.
178    */
179   struct GNUNET_PSYC_TransmitHandle *tmit;
180
181   /**
182    * Slicer for processing incoming messages.
183    */
184   struct GNUNET_PSYC_Slicer *slicer;
185
186   /**
187    * Function called after disconnected from the service.
188    */
189   GNUNET_ContinuationCallback disconnect_cb;
190
191   /**
192    * Closure for @a disconnect_cb.
193    */
194   void *disconnect_cls;
195
196   /**
197    * Public key of the place.
198    */
199   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
200
201   /**
202    * Public key of the ego.
203    */
204   struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
205
206   /**
207    * Does this place belong to a host (#GNUNET_YES) or guest (#GNUNET_NO)?
208    */
209   uint8_t is_host;
210 };
211
212
213 /**
214  * Host handle for a place that we entered.
215  */
216 struct GNUNET_SOCIAL_Host
217 {
218   struct GNUNET_SOCIAL_Place plc;
219
220   /**
221    * Slicer for processing incoming messages from guests.
222    */
223   struct GNUNET_PSYC_Slicer *slicer;
224
225   GNUNET_SOCIAL_HostEnterCallback enter_cb;
226
227   GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb;
228
229   GNUNET_SOCIAL_FarewellCallback farewell_cb;
230
231   /**
232    * Closure for callbacks.
233    */
234   void *cb_cls;
235
236   struct GNUNET_SOCIAL_Nym *notice_place_leave_nym;
237   struct GNUNET_PSYC_Environment *notice_place_leave_env;
238 };
239
240
241 /**
242  * Guest handle for place that we entered.
243  */
244 struct GNUNET_SOCIAL_Guest
245 {
246   struct GNUNET_SOCIAL_Place plc;
247
248   GNUNET_SOCIAL_GuestEnterCallback enter_cb;
249
250   GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb;
251
252   /**
253    * Closure for callbacks.
254    */
255   void *cb_cls;
256 };
257
258
259 /**
260  * Hash map of all nyms.
261  * pub_key_hash -> struct GNUNET_SOCIAL_Nym *
262  */
263 struct GNUNET_CONTAINER_MultiHashMap *nyms;
264
265
266 /**
267  * Handle for an announcement request.
268  */
269 struct GNUNET_SOCIAL_Announcement
270 {
271
272 };
273
274
275 /**
276  * A talk request.
277  */
278 struct GNUNET_SOCIAL_TalkRequest
279 {
280
281 };
282
283
284 /**
285  * A history lesson.
286  */
287 struct GNUNET_SOCIAL_HistoryRequest
288 {
289   /**
290    * Place.
291    */
292   struct GNUNET_SOCIAL_Place *plc;
293
294   /**
295    * Operation ID.
296    */
297   uint64_t op_id;
298
299   /**
300    * Slicer for processing incoming messages.
301    */
302   struct GNUNET_PSYC_Slicer *slicer;
303
304   /**
305    * Function to call when the operation finished.
306    */
307   GNUNET_ResultCallback result_cb;
308
309   /**
310    * Closure for @a result_cb.
311    */
312   void *cls;
313 };
314
315
316 struct GNUNET_SOCIAL_LookHandle
317 {
318   /**
319    * Place.
320    */
321   struct GNUNET_SOCIAL_Place *plc;
322
323   /**
324    * Operation ID.
325    */
326   uint64_t op_id;
327
328   /**
329    * State variable result callback.
330    */
331   GNUNET_PSYC_StateVarCallback var_cb;
332
333   /**
334    * Function to call when the operation finished.
335    */
336   GNUNET_ResultCallback result_cb;
337
338   /**
339    * Name of current modifier being received.
340    */
341   char *mod_name;
342
343   /**
344    * Size of current modifier value being received.
345    */
346   size_t mod_value_size;
347
348   /**
349    * Remaining size of current modifier value still to be received.
350    */
351   size_t mod_value_remaining;
352
353   /**
354    * Closure for @a result_cb.
355    */
356   void *cls;
357 };
358
359
360 struct ZoneAddPlaceHandle
361 {
362   GNUNET_ResultCallback result_cb;
363   void *result_cls;
364 };
365
366
367 struct ZoneAddNymHandle
368 {
369   GNUNET_ResultCallback result_cb;
370   void *result_cls;
371 };
372
373
374 /*** CLEANUP / DISCONNECT ***/
375
376
377 static void
378 host_cleanup (struct GNUNET_SOCIAL_Host *hst)
379 {
380   if (NULL != hst->slicer)
381   {
382     GNUNET_PSYC_slicer_destroy (hst->slicer);
383     hst->slicer = NULL;
384   }
385   GNUNET_free (hst);
386 }
387
388
389 static void
390 guest_cleanup (struct GNUNET_SOCIAL_Guest *gst)
391 {
392   GNUNET_free (gst);
393 }
394
395
396 static void
397 place_cleanup (struct GNUNET_SOCIAL_Place *plc)
398 {
399   struct GNUNET_HashCode place_pub_hash;
400
401   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
402               "place_cleanup\n");
403
404   GNUNET_CRYPTO_hash (&plc->pub_key, sizeof (plc->pub_key), &place_pub_hash);
405   if (NULL != plc->tmit)
406   {
407     GNUNET_PSYC_transmit_destroy (plc->tmit);
408     plc->tmit = NULL;
409   }
410   if (NULL != plc->connect_env)
411   {
412     GNUNET_MQ_discard (plc->connect_env);
413     plc->connect_env = NULL;
414   }
415   if (NULL != plc->mq)
416   {
417     GNUNET_MQ_destroy (plc->mq);
418     plc->mq = NULL;
419   }
420   if (NULL != plc->disconnect_cb)
421   {
422     plc->disconnect_cb (plc->disconnect_cls);
423     plc->disconnect_cb = NULL;
424   }
425
426   (GNUNET_YES == plc->is_host)
427     ? host_cleanup ((struct GNUNET_SOCIAL_Host *) plc)
428     : guest_cleanup ((struct GNUNET_SOCIAL_Guest *) plc);
429 }
430
431
432 static void
433 place_disconnect (struct GNUNET_SOCIAL_Place *plc)
434 {
435   struct GNUNET_HashCode place_pub_hash;
436
437   GNUNET_CRYPTO_hash (&plc->pub_key,
438                       sizeof (plc->pub_key),
439                       &place_pub_hash);
440   place_cleanup (plc);
441 }
442
443
444 /*** NYM ***/
445
446 static struct GNUNET_SOCIAL_Nym *
447 nym_get_or_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key)
448 {
449   struct GNUNET_SOCIAL_Nym *nym = NULL;
450   struct GNUNET_HashCode pub_key_hash;
451
452   if (NULL == pub_key)
453     return NULL;
454
455   GNUNET_CRYPTO_hash (pub_key, sizeof (*pub_key), &pub_key_hash);
456
457   if (NULL == nyms)
458     nyms = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
459   else
460     nym = GNUNET_CONTAINER_multihashmap_get (nyms, &pub_key_hash);
461
462   if (NULL == nym)
463   {
464     nym = GNUNET_new (struct GNUNET_SOCIAL_Nym);
465     nym->pub_key = *pub_key;
466     nym->pub_key_hash = pub_key_hash;
467     GNUNET_CONTAINER_multihashmap_put (nyms, &nym->pub_key_hash, nym,
468                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
469   }
470   return nym;
471 }
472
473
474 static void
475 nym_destroy (struct GNUNET_SOCIAL_Nym *nym)
476 {
477   GNUNET_CONTAINER_multihashmap_remove (nyms, &nym->pub_key_hash, nym);
478   GNUNET_free (nym);
479 }
480
481
482 /*** MESSAGE HANDLERS ***/
483
484 /** _notice_place_leave from guests */
485
486 static void
487 host_recv_notice_place_leave_method (void *cls,
488                                      const struct GNUNET_PSYC_MessageHeader *msg,
489                                      const struct GNUNET_PSYC_MessageMethod *meth,
490                                      uint64_t message_id,
491                                      const char *method_name)
492 {
493   struct GNUNET_SOCIAL_Host *hst = cls;
494
495   if (0 == memcmp (&(struct GNUNET_CRYPTO_EcdsaPublicKey) {},
496                    &msg->slave_pub_key, sizeof (msg->slave_pub_key)))
497     return;
498
499   struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&msg->slave_pub_key);
500
501   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502               "Host received method for message ID %" PRIu64 " from nym %s: %s\n",
503               message_id, GNUNET_h2s (&nym->pub_key_hash), method_name);
504
505   hst->notice_place_leave_nym = (struct GNUNET_SOCIAL_Nym *) nym;
506   hst->notice_place_leave_env = GNUNET_PSYC_env_create ();
507
508   char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key);
509   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510               "_notice_place_leave: got method from nym %s (%s).\n",
511               GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
512   GNUNET_free (str);
513 }
514
515
516 static void
517 host_recv_notice_place_leave_modifier (void *cls,
518                                        const struct GNUNET_PSYC_MessageHeader *msg,
519                                        const struct GNUNET_MessageHeader *pmsg,
520                                        uint64_t message_id,
521                                        enum GNUNET_PSYC_Operator oper,
522                                        const char *name,
523                                        const void *value,
524                                        uint16_t value_size,
525                                        uint16_t full_value_size)
526 {
527   struct GNUNET_SOCIAL_Host *hst = cls;
528   if (NULL == hst->notice_place_leave_env)
529     return;
530
531   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532               "Host received modifier for _notice_place_leave message with ID %" PRIu64 ":\n"
533               "%c%s: %.*s\n",
534               message_id, oper, name, value_size, (const char *) value);
535
536   /* skip _nym, it's added later in eom() */
537   if (0 == memcmp (name, "_nym", sizeof ("_nym"))
538       || 0 == memcmp (name, "_nym_", sizeof ("_nym_") - 1))
539     return;
540
541   GNUNET_PSYC_env_add (hst->notice_place_leave_env,
542                        GNUNET_PSYC_OP_SET, name, value, value_size);
543 }
544
545
546 static void
547 host_recv_notice_place_leave_eom (void *cls,
548                                   const struct GNUNET_PSYC_MessageHeader *msg,
549                                   const struct GNUNET_MessageHeader *pmsg,
550                                   uint64_t message_id,
551                                   uint8_t is_cancelled)
552 {
553   struct GNUNET_SOCIAL_Host *hst = cls;
554   if (NULL == hst->notice_place_leave_env)
555     return;
556
557   char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key);
558   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559               "_notice_place_leave: got EOM from nym %s (%s).\n",
560               GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
561   GNUNET_free (str);
562
563   if (GNUNET_YES != is_cancelled)
564   {
565     if (NULL != hst->farewell_cb)
566       hst->farewell_cb (hst->cb_cls, hst->notice_place_leave_nym,
567                         hst->notice_place_leave_env);
568     /* announce leaving guest to place */
569     GNUNET_PSYC_env_add (hst->notice_place_leave_env, GNUNET_PSYC_OP_SET,
570                          "_nym", hst->notice_place_leave_nym,
571                          sizeof (*hst->notice_place_leave_nym));
572     GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave",
573                                  hst->notice_place_leave_env,
574                                  NULL, NULL, GNUNET_SOCIAL_ANNOUNCE_NONE);
575     nym_destroy (hst->notice_place_leave_nym);
576   }
577   GNUNET_PSYC_env_destroy (hst->notice_place_leave_env);
578   hst->notice_place_leave_env = NULL;
579 }
580
581
582 /*** PLACE ***/
583
584
585 static int
586 check_place_result (void *cls,
587                     const struct GNUNET_OperationResultMessage *res)
588 {
589   uint16_t size = ntohs (res->header.size);
590   if (size < sizeof (*res))
591   { /* Error, message too small. */
592     GNUNET_break (0);
593     return GNUNET_SYSERR;
594   }
595   return GNUNET_OK;
596 }
597
598
599 static void
600 handle_place_result (void *cls,
601                      const struct GNUNET_OperationResultMessage *res)
602 {
603   struct GNUNET_SOCIAL_Place *plc = cls;
604
605   uint16_t size = ntohs (res->header.size);
606   uint16_t data_size = size - sizeof (*res);
607   const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
608
609   GNUNET_OP_result (plc->op, GNUNET_ntohll (res->op_id),
610                     GNUNET_ntohll (res->result_code),
611                     data, data_size, NULL);
612 }
613
614
615 static int
616 check_app_result (void *cls,
617                   const struct GNUNET_OperationResultMessage *res)
618 {
619   uint16_t size = ntohs (res->header.size);
620   if (size < sizeof (*res))
621   { /* Error, message too small. */
622     GNUNET_break (0);
623     return GNUNET_SYSERR;
624   }
625   return GNUNET_OK;
626 }
627
628
629 static void
630 handle_app_result (void *cls,
631                    const struct GNUNET_OperationResultMessage *res)
632 {
633   struct GNUNET_SOCIAL_App *app = cls;
634
635   uint16_t size = ntohs (res->header.size);
636   uint16_t data_size = size - sizeof (*res);
637   const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
638
639   GNUNET_OP_result (app->op, GNUNET_ntohll (res->op_id),
640                     GNUNET_ntohll (res->result_code),
641                     data, data_size, NULL);
642 }
643
644
645 static void
646 op_recv_history_result (void *cls, int64_t result,
647                         const void *err_msg, uint16_t err_msg_size)
648 {
649   LOG (GNUNET_ERROR_TYPE_DEBUG,
650        "Received history replay result: %" PRId64 ".\n", result);
651
652   struct GNUNET_SOCIAL_HistoryRequest *hist = cls;
653
654   if (NULL != hist->result_cb)
655     hist->result_cb (hist->cls, result, err_msg, err_msg_size);
656
657   GNUNET_free (hist);
658 }
659
660
661 static void
662 op_recv_state_result (void *cls, int64_t result,
663                       const void *err_msg, uint16_t err_msg_size)
664 {
665   LOG (GNUNET_ERROR_TYPE_DEBUG,
666        "Received state request result: %" PRId64 ".\n", result);
667
668   struct GNUNET_SOCIAL_LookHandle *look = cls;
669
670   if (NULL != look->result_cb)
671     look->result_cb (look->cls, result, err_msg, err_msg_size);
672
673   GNUNET_free (look);
674 }
675
676
677 static int
678 check_place_history_result (void *cls,
679                             const struct GNUNET_OperationResultMessage *res)
680 {
681   struct GNUNET_PSYC_MessageHeader *
682     pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh (res);
683   uint16_t size = ntohs (res->header.size);
684
685   if (NULL == pmsg || size < sizeof (*res) + sizeof (*pmsg))
686   { /* Error, message too small. */
687     GNUNET_break (0);
688     return GNUNET_SYSERR;
689   }
690   return GNUNET_OK;
691 }
692
693
694 static void
695 handle_place_history_result (void *cls,
696                              const struct GNUNET_OperationResultMessage *res)
697 {
698   struct GNUNET_SOCIAL_Place *plc = cls;
699   struct GNUNET_PSYC_MessageHeader *
700     pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh (res);
701
702   LOG (GNUNET_ERROR_TYPE_DEBUG,
703        "%p Received historic fragment for message #%" PRIu64 ".\n",
704        plc, GNUNET_ntohll (pmsg->message_id));
705
706   GNUNET_ResultCallback result_cb = NULL;
707   struct GNUNET_SOCIAL_HistoryRequest *hist = NULL;
708
709   if (GNUNET_YES != GNUNET_OP_get (plc->op,
710                                    GNUNET_ntohll (res->op_id),
711                                    &result_cb, (void *) &hist, NULL))
712   { /* Operation not found. */
713     LOG (GNUNET_ERROR_TYPE_WARNING,
714          "%p Replay operation not found for historic fragment of message #%"
715          PRIu64 ".\n",
716          plc, GNUNET_ntohll (pmsg->message_id));
717     return;
718   }
719
720   GNUNET_PSYC_slicer_message (hist->slicer,
721                               (const struct GNUNET_PSYC_MessageHeader *) pmsg);
722 }
723
724
725 static int
726 check_place_state_result (void *cls,
727                           const struct GNUNET_OperationResultMessage *res)
728 {
729   const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
730   if (NULL == mod)
731   {
732     GNUNET_break_op (0);
733     LOG (GNUNET_ERROR_TYPE_WARNING,
734          "Invalid modifier in state result\n");
735     return GNUNET_SYSERR;
736   }
737
738   uint16_t size = ntohs (res->header.size);
739   uint16_t mod_size = ntohs (mod->size);
740   if (size - sizeof (*res) != mod_size)
741   {
742     GNUNET_break_op (0);
743     LOG (GNUNET_ERROR_TYPE_WARNING,
744          "Invalid modifier size in state result: %u - %u != %u\n",
745          ntohs (res->header.size), sizeof (*res), mod_size);
746     return GNUNET_SYSERR;
747   }
748   return GNUNET_OK;
749 }
750
751
752 static void
753 handle_place_state_result (void *cls,
754                            const struct GNUNET_OperationResultMessage *res)
755 {
756   struct GNUNET_SOCIAL_Place *plc = cls;
757
758   GNUNET_ResultCallback result_cb = NULL;
759   struct GNUNET_SOCIAL_LookHandle *look = NULL;
760
761   if (GNUNET_YES != GNUNET_OP_get (plc->op,
762                                    GNUNET_ntohll (res->op_id),
763                                    &result_cb, (void *) &look, NULL))
764   { /* Operation not found. */
765     return;
766   }
767
768   const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
769   uint16_t mod_size = ntohs (mod->size);
770
771   switch (ntohs (mod->type))
772   {
773   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
774   {
775     const struct GNUNET_PSYC_MessageModifier *
776       pmod = (const struct GNUNET_PSYC_MessageModifier *) mod;
777
778     const char *name = (const char *) &pmod[1];
779     uint16_t name_size = ntohs (pmod->name_size);
780     if (0 == name_size
781         || mod_size - sizeof (*pmod) < name_size
782         || '\0' != name[name_size - 1])
783     {
784       GNUNET_break_op (0);
785       LOG (GNUNET_ERROR_TYPE_WARNING,
786            "Invalid modifier name in state result\n");
787       return;
788     }
789     look->mod_value_size = ntohs (pmod->value_size);
790     look->var_cb (look->cls, mod, name, name + name_size,
791                   mod_size - sizeof (*mod) - name_size,
792                   look->mod_value_size);
793     if (look->mod_value_size > mod_size - sizeof (*mod) - name_size)
794     {
795         look->mod_value_remaining = look->mod_value_size;
796         look->mod_name = GNUNET_malloc (name_size);
797         GNUNET_memcpy (look->mod_name, name, name_size);
798     }
799     break;
800   }
801
802   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
803     look->var_cb (look->cls, mod, look->mod_name, (const char *) &mod[1],
804                   mod_size - sizeof (*mod), look->mod_value_size);
805     look->mod_value_remaining -= mod_size - sizeof (*mod);
806     if (0 == look->mod_value_remaining)
807     {
808         GNUNET_free (look->mod_name);
809     }
810     break;
811   }
812 }
813
814
815 static void
816 handle_place_message_ack (void *cls,
817                           const struct GNUNET_MessageHeader *msg)
818 {
819   struct GNUNET_SOCIAL_Place *plc = cls;
820
821   GNUNET_PSYC_transmit_got_ack (plc->tmit);
822 }
823
824
825 static int
826 check_place_message (void *cls,
827                      const struct GNUNET_PSYC_MessageHeader *pmsg)
828 {
829   return GNUNET_OK;
830 }
831
832
833 static void
834 handle_place_message (void *cls,
835                       const struct GNUNET_PSYC_MessageHeader *pmsg)
836 {
837   struct GNUNET_SOCIAL_Place *plc = cls;
838
839   GNUNET_PSYC_slicer_message (plc->slicer, pmsg);
840 }
841
842
843 static int
844 check_host_message (void *cls,
845                     const struct GNUNET_PSYC_MessageHeader *pmsg)
846 {
847   return GNUNET_OK;
848 }
849
850
851 static void
852 handle_host_message (void *cls,
853                      const struct GNUNET_PSYC_MessageHeader *pmsg)
854 {
855   struct GNUNET_SOCIAL_Host *hst = cls;
856
857   GNUNET_PSYC_slicer_message (hst->slicer, pmsg);
858   GNUNET_PSYC_slicer_message (hst->plc.slicer, pmsg);
859 }
860
861
862 static void
863 handle_host_enter_ack (void *cls,
864                        const struct HostEnterAck *hack)
865 {
866   struct GNUNET_SOCIAL_Host *hst = cls;
867
868   hst->plc.pub_key = hack->place_pub_key;
869
870   int32_t result = ntohl (hack->result_code);
871   if (NULL != hst->enter_cb)
872     hst->enter_cb (hst->cb_cls, result, &hack->place_pub_key,
873                    GNUNET_ntohll (hack->max_message_id));
874 }
875
876
877 static int
878 check_host_enter_request (void *cls,
879                           const struct GNUNET_PSYC_JoinRequestMessage *req)
880 {
881   return GNUNET_OK;
882 }
883
884
885 static void
886 handle_host_enter_request (void *cls,
887                            const struct GNUNET_PSYC_JoinRequestMessage *req)
888 {
889   struct GNUNET_SOCIAL_Host *hst = cls;
890
891   if (NULL == hst->answer_door_cb)
892      return;
893
894   const char *method_name = NULL;
895   struct GNUNET_PSYC_Environment *env = NULL;
896   struct GNUNET_PSYC_MessageHeader *entry_pmsg = NULL;
897   const void *data = NULL;
898   uint16_t data_size = 0;
899   char *str;
900   const struct GNUNET_PSYC_Message *join_msg = NULL;
901
902   do
903   {
904     if (sizeof (*req) + sizeof (*join_msg) <= ntohs (req->header.size))
905     {
906       join_msg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (req);
907       LOG (GNUNET_ERROR_TYPE_DEBUG,
908            "Received join_msg of type %u and size %u.\n",
909            ntohs (join_msg->header.type), ntohs (join_msg->header.size));
910
911       env = GNUNET_PSYC_env_create ();
912       entry_pmsg = GNUNET_PSYC_message_header_create_from_psyc (join_msg);
913       if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_pmsg, &method_name, env,
914                                                   &data, &data_size))
915       {
916         GNUNET_break_op (0);
917         str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_pub_key);
918         LOG (GNUNET_ERROR_TYPE_WARNING,
919              "Ignoring invalid entry request from nym %s.\n",
920              str);
921         GNUNET_free (str);
922         break;
923       }
924     }
925
926     struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_pub_key);
927     hst->answer_door_cb (hst->cb_cls, nym, method_name, env,
928                          data, data_size);
929   } while (0);
930
931   if (NULL != env)
932     GNUNET_PSYC_env_destroy (env);
933   if (NULL != entry_pmsg)
934     GNUNET_free (entry_pmsg);
935 }
936
937
938 static void
939 handle_guest_enter_ack (void *cls,
940                         const struct GNUNET_PSYC_CountersResultMessage *cres)
941 {
942   struct GNUNET_SOCIAL_Guest *gst = cls;
943
944   int32_t result = ntohl (cres->result_code);
945   if (NULL != gst->enter_cb)
946     gst->enter_cb (gst->cb_cls, result, &gst->plc.pub_key,
947                    GNUNET_ntohll (cres->max_message_id));
948 }
949
950
951 static int
952 check_guest_enter_decision (void *cls,
953                             const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
954 {
955   return GNUNET_OK;
956 }
957
958
959 static void
960 handle_guest_enter_decision (void *cls,
961                              const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
962 {
963   struct GNUNET_SOCIAL_Guest *gst = cls;
964
965   struct GNUNET_PSYC_Message *pmsg = NULL;
966   if (ntohs (dcsn->header.size) > sizeof (*dcsn))
967     pmsg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (dcsn);
968
969   if (NULL != gst->entry_dcsn_cb)
970     gst->entry_dcsn_cb (gst->cb_cls, ntohl (dcsn->is_admitted), pmsg);
971 }
972
973
974 static int
975 check_app_ego (void *cls,
976                const struct AppEgoMessage *emsg)
977 {
978   return GNUNET_OK;
979 }
980
981
982 static void
983 handle_app_ego (void *cls,
984                 const struct AppEgoMessage *emsg)
985 {
986   struct GNUNET_SOCIAL_App *app = cls;
987
988   uint16_t name_size = ntohs (emsg->header.size) - sizeof (*emsg);
989
990   struct GNUNET_HashCode ego_pub_hash;
991   GNUNET_CRYPTO_hash (&emsg->ego_pub_key, sizeof (emsg->ego_pub_key),
992                       &ego_pub_hash);
993
994   struct GNUNET_SOCIAL_Ego *
995     ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash);
996   if (NULL == ego)
997   {
998     ego = GNUNET_malloc (sizeof (*ego));
999     ego->pub_key = emsg->ego_pub_key;
1000     ego->name = GNUNET_malloc (name_size);
1001     GNUNET_memcpy (ego->name, &emsg[1], name_size);
1002   }
1003   else
1004   {
1005     ego->name = GNUNET_realloc (ego->name, name_size);
1006     GNUNET_memcpy (ego->name, &emsg[1], name_size);
1007   }
1008
1009   GNUNET_CONTAINER_multihashmap_put (app->egos, &ego_pub_hash, ego,
1010                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1011
1012   if (NULL != app->ego_cb)
1013     app->ego_cb (app->cb_cls, ego, &ego->pub_key, ego->name);
1014 }
1015
1016
1017 static void
1018 handle_app_ego_end (void *cls,
1019                     const struct GNUNET_MessageHeader *msg)
1020 {
1021   //struct GNUNET_SOCIAL_App *app = cls;
1022 }
1023
1024
1025 static int
1026 check_app_place (void *cls,
1027                  const struct AppPlaceMessage *pmsg)
1028 {
1029   return GNUNET_OK;
1030 }
1031
1032
1033 static void
1034 handle_app_place (void *cls,
1035                   const struct AppPlaceMessage *pmsg)
1036 {
1037   struct GNUNET_SOCIAL_App *app = cls;
1038
1039   if ((GNUNET_YES == pmsg->is_host && NULL == app->host_cb)
1040       || (GNUNET_NO == pmsg->is_host && NULL == app->guest_cb))
1041     return;
1042
1043   struct GNUNET_HashCode ego_pub_hash;
1044   GNUNET_CRYPTO_hash (&pmsg->ego_pub_key, sizeof (pmsg->ego_pub_key),
1045                       &ego_pub_hash);
1046   struct GNUNET_SOCIAL_Ego *
1047     ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash);
1048   if (NULL == ego)
1049   {
1050     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failure to obtain ego %s.\n",
1051                 GNUNET_h2s (&ego_pub_hash));
1052     GNUNET_break (0);
1053     return;
1054   }
1055
1056   if (GNUNET_YES == pmsg->is_host)
1057   {
1058     if (NULL != app->host_cb) {
1059       struct GNUNET_SOCIAL_HostConnection *hconn = GNUNET_malloc (sizeof (*hconn));
1060       hconn->app = app;
1061       hconn->plc_msg = *pmsg;
1062       app->host_cb (app->cb_cls, hconn, ego, &pmsg->place_pub_key, pmsg->place_state);
1063       GNUNET_free (hconn);
1064     }
1065   }
1066   else if (NULL != app->guest_cb)
1067   {
1068     struct GNUNET_SOCIAL_GuestConnection *gconn = GNUNET_malloc (sizeof (*gconn));
1069     gconn->app = app;
1070     gconn->plc_msg = *pmsg;
1071     app->guest_cb (app->cb_cls, gconn, ego, &pmsg->place_pub_key, pmsg->place_state);
1072     GNUNET_free (gconn);
1073   }
1074 }
1075
1076
1077 static void
1078 handle_app_place_end (void *cls,
1079                       const struct GNUNET_MessageHeader *msg)
1080 {
1081   struct GNUNET_SOCIAL_App *app = cls;
1082
1083   if (NULL != app->connected_cb)
1084     app->connected_cb (app->cb_cls);
1085 }
1086
1087
1088 /**
1089  * Handler for a #GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK message received
1090  * from the social service.
1091  *
1092  * @param cls the place of type `struct GNUNET_SOCIAL_Place`
1093  * @param msg the message received from the service
1094  */
1095 static void
1096 handle_place_leave_ack (void *cls,
1097                         const struct GNUNET_MessageHeader *msg)
1098 {
1099   struct GNUNET_SOCIAL_Place *plc = cls;
1100
1101   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102               "%s left place %p\n",
1103               plc->is_host ? "host" : "guest", 
1104               plc);
1105   place_disconnect (plc);  
1106 }
1107
1108
1109 /*** HOST ***/
1110
1111
1112 static void
1113 host_connect (struct GNUNET_SOCIAL_Host *hst);
1114
1115
1116 static void
1117 host_reconnect (void *cls)
1118 {
1119   host_connect (cls);
1120 }
1121
1122
1123 /**
1124  * Host client disconnected from service.
1125  *
1126  * Reconnect after backoff period.
1127  */
1128 static void
1129 host_disconnected (void *cls, enum GNUNET_MQ_Error error)
1130 {
1131   struct GNUNET_SOCIAL_Host *hst = cls;
1132   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1133
1134   LOG (GNUNET_ERROR_TYPE_DEBUG,
1135        "Host client disconnected (%d), re-connecting\n",
1136        (int) error);
1137   if (NULL != plc->tmit)
1138   {
1139     GNUNET_PSYC_transmit_destroy (plc->tmit);
1140     plc->tmit = NULL;
1141   }
1142   if (NULL != plc->mq)
1143   {
1144     GNUNET_MQ_destroy (plc->mq);
1145     plc->mq = NULL;
1146   }
1147
1148   plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay,
1149                                                       host_reconnect,
1150                                                       hst);
1151   plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay);
1152 }
1153
1154
1155 static void
1156 host_connect (struct GNUNET_SOCIAL_Host *hst)
1157 {
1158   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1159
1160   struct GNUNET_MQ_MessageHandler handlers[] = {
1161     GNUNET_MQ_hd_fixed_size (host_enter_ack,
1162                              GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK,
1163                              struct HostEnterAck,
1164                              hst),
1165     GNUNET_MQ_hd_fixed_size (place_leave_ack,
1166                              GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK,
1167                              struct GNUNET_MessageHeader,
1168                              plc),
1169     GNUNET_MQ_hd_var_size (host_enter_request,
1170                            GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
1171                            struct GNUNET_PSYC_JoinRequestMessage,
1172                            hst),
1173     GNUNET_MQ_hd_var_size (host_message,
1174                            GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
1175                            struct GNUNET_PSYC_MessageHeader,
1176                            hst),
1177     GNUNET_MQ_hd_fixed_size (place_message_ack,
1178                              GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
1179                              struct GNUNET_MessageHeader,
1180                              plc),
1181     GNUNET_MQ_hd_var_size (place_history_result,
1182                            GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
1183                            struct GNUNET_OperationResultMessage,
1184                            plc),
1185     GNUNET_MQ_hd_var_size (place_state_result,
1186                            GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
1187                            struct GNUNET_OperationResultMessage,
1188                            plc),
1189     GNUNET_MQ_hd_var_size (place_result,
1190                            GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
1191                            struct GNUNET_OperationResultMessage,
1192                            plc),
1193     GNUNET_MQ_handler_end ()
1194   };
1195
1196   plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social",
1197                                    handlers, host_disconnected, hst);
1198   GNUNET_assert (NULL != plc->mq);
1199   plc->tmit = GNUNET_PSYC_transmit_create (plc->mq);
1200
1201   GNUNET_MQ_send_copy (plc->mq, plc->connect_env);
1202 }
1203
1204
1205 /**
1206  * Enter a place as host.
1207  *
1208  * A place is created upon first entering, and it is active until permanently
1209  * left using GNUNET_SOCIAL_host_leave().
1210  *
1211  * @param app
1212  *        Application handle.
1213  * @param ego
1214  *        Identity of the host.
1215  * @param policy
1216  *        Policy specifying entry and history restrictions for the place.
1217  * @param slicer
1218  *        Slicer to handle incoming messages.
1219  * @param enter_cb
1220  *        Function called when the place is entered and ready to use.
1221  * @param answer_door_cb
1222  *        Function to handle new nyms that want to enter.
1223  * @param farewell_cb
1224  *        Function to handle departing nyms.
1225  * @param cls
1226  *        Closure for the callbacks.
1227  *
1228  * @return Handle for the host. This handle contains the pubkey.
1229  */
1230 struct GNUNET_SOCIAL_Host *
1231 GNUNET_SOCIAL_host_enter (const struct GNUNET_SOCIAL_App *app,
1232                           const struct GNUNET_SOCIAL_Ego *ego,
1233                           enum GNUNET_PSYC_Policy policy,
1234                           struct GNUNET_PSYC_Slicer *slicer,
1235                           GNUNET_SOCIAL_HostEnterCallback enter_cb,
1236                           GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
1237                           GNUNET_SOCIAL_FarewellCallback farewell_cb,
1238                           void *cls)
1239 {
1240   struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
1241   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1242
1243   plc->cfg = app->cfg;
1244   plc->is_host = GNUNET_YES;
1245   plc->slicer = slicer;
1246
1247   hst->enter_cb = enter_cb;
1248   hst->answer_door_cb = answer_door_cb;
1249   hst->farewell_cb = farewell_cb;
1250   hst->cb_cls = cls;
1251
1252   plc->op = GNUNET_OP_create ();
1253
1254   hst->slicer = GNUNET_PSYC_slicer_create ();
1255   GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL,
1256                                  host_recv_notice_place_leave_method,
1257                                  host_recv_notice_place_leave_modifier,
1258                                  NULL, host_recv_notice_place_leave_eom, hst);
1259
1260   uint16_t app_id_size = strlen (app->id) + 1;
1261   struct HostEnterRequest *hreq;
1262   plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size,
1263                                           GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
1264   hreq->policy = policy;
1265   hreq->ego_pub_key = ego->pub_key;
1266   GNUNET_memcpy (&hreq[1], app->id, app_id_size);
1267
1268   host_connect (hst);
1269   return hst;
1270 }
1271
1272
1273 /**
1274  * Reconnect to an already entered place as host.
1275  *
1276  * @param hconn
1277  *        Host connection handle.
1278  *        @see GNUNET_SOCIAL_app_connect() & GNUNET_SOCIAL_AppHostPlaceCallback()
1279  * @param slicer
1280  *        Slicer to handle incoming messages.
1281  * @param enter_cb
1282  *        Function called when the place is entered and ready to use.
1283  * @param answer_door_cb
1284  *        Function to handle new nyms that want to enter.
1285  * @param farewell_cb
1286  *        Function to handle departing nyms.
1287  * @param cls
1288  *        Closure for the callbacks.
1289  *
1290  * @return Handle for the host.
1291  */
1292  struct GNUNET_SOCIAL_Host *
1293 GNUNET_SOCIAL_host_enter_reconnect (struct GNUNET_SOCIAL_HostConnection *hconn,
1294                                     struct GNUNET_PSYC_Slicer *slicer,
1295                                     GNUNET_SOCIAL_HostEnterCallback enter_cb,
1296                                     GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
1297                                     GNUNET_SOCIAL_FarewellCallback farewell_cb,
1298                                     void *cls)
1299 {
1300   struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
1301   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1302
1303   hst->enter_cb = enter_cb;
1304   hst->answer_door_cb = answer_door_cb;
1305   hst->farewell_cb = farewell_cb;
1306   hst->cb_cls = cls;
1307
1308   plc->cfg = hconn->app->cfg;
1309   plc->is_host = GNUNET_YES;
1310   plc->slicer = slicer;
1311   plc->pub_key = hconn->plc_msg.place_pub_key;
1312   plc->ego_pub_key = hconn->plc_msg.ego_pub_key;
1313
1314   plc->op = GNUNET_OP_create ();
1315
1316   hst->slicer = GNUNET_PSYC_slicer_create ();
1317   GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL,
1318                                  host_recv_notice_place_leave_method,
1319                                  host_recv_notice_place_leave_modifier,
1320                                  NULL, host_recv_notice_place_leave_eom, hst);
1321
1322   size_t app_id_size = strlen (hconn->app->id) + 1;
1323   struct HostEnterRequest *hreq;
1324   plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size,
1325                                           GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
1326   hreq->place_pub_key = hconn->plc_msg.place_pub_key;
1327   hreq->ego_pub_key = hconn->plc_msg.ego_pub_key;
1328   GNUNET_memcpy (&hreq[1], hconn->app->id, app_id_size);
1329
1330   host_connect (hst);
1331   return hst;
1332 }
1333
1334
1335 /**
1336  * Decision whether to admit @a nym into the place or refuse entry.
1337  *
1338  * @param hst
1339  *        Host of the place.
1340  * @param nym
1341  *        Handle for the entity that wanted to enter.
1342  * @param is_admitted
1343  *        #GNUNET_YES    if @a nym is admitted,
1344  *        #GNUNET_NO     if @a nym is refused entry,
1345  *        #GNUNET_SYSERR if we cannot answer the request.
1346  * @param method_name
1347  *        Method name for the rejection message.
1348  * @param env
1349  *        Environment containing variables for the message, or NULL.
1350  * @param data
1351  *        Data for the rejection message to send back.
1352  * @param data_size
1353  *        Number of bytes in @a data for method.
1354  * @return #GNUNET_OK on success,
1355  *         #GNUNET_SYSERR if the message is too large.
1356  */
1357 int
1358 GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
1359                                    struct GNUNET_SOCIAL_Nym *nym,
1360                                    int is_admitted,
1361                                    const struct GNUNET_PSYC_Message *entry_resp)
1362 {
1363   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1364   struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
1365   uint16_t entry_resp_size
1366     = (NULL != entry_resp) ? ntohs (entry_resp->header.size) : 0;
1367
1368   if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < sizeof (*dcsn) + entry_resp_size)
1369     return GNUNET_SYSERR;
1370
1371   struct GNUNET_MQ_Envelope *
1372     env = GNUNET_MQ_msg_extra (dcsn, entry_resp_size,
1373                                GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
1374   dcsn->is_admitted = htonl (is_admitted);
1375   dcsn->slave_pub_key = nym->pub_key;
1376
1377   if (0 < entry_resp_size)
1378     GNUNET_memcpy (&dcsn[1], entry_resp, entry_resp_size);
1379
1380   GNUNET_MQ_send (plc->mq, env);
1381   return GNUNET_OK;
1382 }
1383
1384
1385 /**
1386  * Throw @a nym out of the place.
1387  *
1388  * The @a nym reference will remain valid until the
1389  * #GNUNET_SOCIAL_FarewellCallback is invoked,
1390  * which should be very soon after this call.
1391  *
1392  * @param host
1393  *        Host of the place.
1394  * @param nym
1395  *        Handle for the entity to be ejected.
1396  * @param env
1397  *        Environment for the message or NULL.
1398  */
1399 void
1400 GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *hst,
1401                           const struct GNUNET_SOCIAL_Nym *nym,
1402                           struct GNUNET_PSYC_Environment *e)
1403 {
1404   struct GNUNET_PSYC_Environment *env = e;
1405   if (NULL == env)
1406     env = GNUNET_PSYC_env_create ();
1407   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
1408                        "_nym", &nym->pub_key, sizeof (nym->pub_key));
1409   GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave", env, NULL, NULL,
1410                                GNUNET_SOCIAL_ANNOUNCE_NONE);
1411   if (NULL == e)
1412     GNUNET_PSYC_env_destroy (env);
1413 }
1414
1415
1416 /**
1417  * Get the public key of @a ego.
1418  *
1419  * @param ego
1420  *        Ego.
1421  *
1422  * @return Public key of ego.
1423  */
1424 const struct GNUNET_CRYPTO_EcdsaPublicKey *
1425 GNUNET_SOCIAL_ego_get_pub_key (const struct GNUNET_SOCIAL_Ego *ego)
1426 {
1427   return &ego->pub_key;
1428 }
1429
1430
1431 /**
1432  * Get the hash of the public key of @a ego.
1433  *
1434  * @param ego
1435  *        Ego.
1436  *
1437  * @return Hash of the public key of @a ego.
1438  */
1439 const struct GNUNET_HashCode *
1440 GNUNET_SOCIAL_ego_get_pub_key_hash (const struct GNUNET_SOCIAL_Ego *ego)
1441 {
1442   return &ego->pub_key_hash;
1443 }
1444
1445
1446 /**
1447  * Get the name of @a ego.
1448  *
1449  * @param ego
1450  *        Ego.
1451  *
1452  * @return Public key of @a ego.
1453  */
1454 const char *
1455 GNUNET_SOCIAL_ego_get_name (const struct GNUNET_SOCIAL_Ego *ego)
1456 {
1457   return ego->name;
1458 }
1459
1460
1461 /**
1462  * Get the public key of @a nym.
1463  *
1464  * Suitable, for example, to be used with GNUNET_SOCIAL_zone_add_nym().
1465  *
1466  * @param nym
1467  *        Pseudonym.
1468  *
1469  * @return Public key of @a nym.
1470  */
1471 const struct GNUNET_CRYPTO_EcdsaPublicKey *
1472 GNUNET_SOCIAL_nym_get_pub_key (const struct GNUNET_SOCIAL_Nym *nym)
1473 {
1474   return &nym->pub_key;
1475 }
1476
1477
1478 /**
1479  * Get the hash of the public key of @a nym.
1480  *
1481  * @param nym
1482  *        Pseudonym.
1483  *
1484  * @return Hash of the public key of @a nym.
1485  */
1486 const struct GNUNET_HashCode *
1487 GNUNET_SOCIAL_nym_get_pub_key_hash (const struct GNUNET_SOCIAL_Nym *nym)
1488 {
1489   return &nym->pub_key_hash;
1490 }
1491
1492
1493 /**
1494  * Send a message to all nyms that are present in the place.
1495  *
1496  * This function is restricted to the host.  Nyms can only send requests
1497  * to the host who can decide to relay it to everyone in the place.
1498  *
1499  * @param host  Host of the place.
1500  * @param method_name Method to use for the announcement.
1501  * @param env  Environment containing variables for the message and operations
1502  *          on objects of the place.  Can be NULL.
1503  * @param notify Function to call to get the payload of the announcement.
1504  * @param notify_cls Closure for @a notify.
1505  * @param flags Flags for this announcement.
1506  *
1507  * @return NULL on error (announcement already in progress?).
1508  */
1509 struct GNUNET_SOCIAL_Announcement *
1510 GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *hst,
1511                              const char *method_name,
1512                              const struct GNUNET_PSYC_Environment *env,
1513                              GNUNET_PSYC_TransmitNotifyData notify_data,
1514                              void *notify_data_cls,
1515                              enum GNUNET_SOCIAL_AnnounceFlags flags)
1516 {
1517   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1518               "PSYC_transmit_message for host\n");
1519   if (GNUNET_OK ==
1520       GNUNET_PSYC_transmit_message (hst->plc.tmit, method_name, env,
1521                                     NULL, notify_data, notify_data_cls, flags))
1522     return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit;
1523   else
1524     return NULL;
1525 }
1526
1527
1528 /**
1529  * Resume transmitting announcement.
1530  *
1531  * @param a
1532  *        The announcement to resume.
1533  */
1534 void
1535 GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a)
1536 {
1537   GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) a);
1538 }
1539
1540
1541 /**
1542  * Cancel announcement.
1543  *
1544  * @param a
1545  *        The announcement to cancel.
1546  */
1547 void
1548 GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a)
1549 {
1550   GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) a);
1551 }
1552
1553
1554 /**
1555  * Obtain handle for a hosted place.
1556  *
1557  * The returned handle can be used to access the place API.
1558  *
1559  * @param host  Handle for the host.
1560  *
1561  * @return Handle for the hosted place, valid as long as @a host is valid.
1562  */
1563 struct GNUNET_SOCIAL_Place *
1564 GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *hst)
1565 {
1566   return &hst->plc;
1567 }
1568
1569
1570 /**
1571  * Disconnect from a home.
1572  *
1573  * Invalidates host handle.
1574  *
1575  * @param hst
1576  *        The host to disconnect.
1577  */
1578 void
1579 GNUNET_SOCIAL_host_disconnect (struct GNUNET_SOCIAL_Host *hst,
1580                                GNUNET_ContinuationCallback disconnect_cb,
1581                                void *cls)
1582 {
1583   struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1584
1585   plc->disconnect_cb = disconnect_cb;
1586   plc->disconnect_cls = cls;
1587   place_disconnect (plc);
1588 }
1589
1590
1591 /**
1592  * Stop hosting the home.
1593  *
1594  * Sends a _notice_place_closing announcement to the home.
1595  * Invalidates host handle.
1596  *
1597  * @param hst
1598  *        The host leaving.
1599  * @param env
1600  *        Environment for the message or NULL.
1601  *        _nym is set to @e nym regardless whether an @e env is provided.
1602  * @param disconnect_cb
1603  *        Function called after the host left the place
1604  *        and disconnected from the social service.
1605  * @param cls
1606  *        Closure for @a disconnect_cb.
1607  */
1608 void
1609 GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst,
1610                           const struct GNUNET_PSYC_Environment *env,
1611                           GNUNET_ContinuationCallback disconnect_cb,
1612                           void *cls)
1613 {
1614   struct GNUNET_MQ_Envelope *envelope;
1615
1616   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1617               "sending _notice_place_closing\n");
1618   GNUNET_SOCIAL_host_announce (hst, "_notice_place_closing", env, NULL, NULL,
1619                                GNUNET_SOCIAL_ANNOUNCE_NONE);
1620   hst->plc.disconnect_cb = disconnect_cb;
1621   hst->plc.disconnect_cls = cls;
1622   envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE);
1623   GNUNET_MQ_send (hst->plc.mq,
1624                   envelope);
1625 }
1626
1627
1628 /*** GUEST ***/
1629
1630
1631 static void
1632 guest_connect (struct GNUNET_SOCIAL_Guest *gst);
1633
1634
1635 static void
1636 guest_reconnect (void *cls)
1637 {
1638   guest_connect (cls);
1639 }
1640
1641
1642 /**
1643  * Guest client disconnected from service.
1644  *
1645  * Reconnect after backoff period.
1646  */
1647 static void
1648 guest_disconnected (void *cls, enum GNUNET_MQ_Error error)
1649 {
1650   struct GNUNET_SOCIAL_Guest *gst = cls;
1651   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1652
1653   LOG (GNUNET_ERROR_TYPE_DEBUG,
1654        "Guest client disconnected (%d), re-connecting\n",
1655        (int) error);
1656   if (NULL != plc->tmit)
1657   {
1658     GNUNET_PSYC_transmit_destroy (plc->tmit);
1659     plc->tmit = NULL;
1660   }
1661   if (NULL != plc->mq)
1662   {
1663     GNUNET_MQ_destroy (plc->mq);
1664     plc->mq = NULL;
1665   }
1666
1667   plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay,
1668                                                       guest_reconnect,
1669                                                       gst);
1670   plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay);
1671 }
1672
1673
1674 static void
1675 guest_connect (struct GNUNET_SOCIAL_Guest *gst)
1676 {
1677   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1678
1679   struct GNUNET_MQ_MessageHandler handlers[] = {
1680     GNUNET_MQ_hd_fixed_size (guest_enter_ack,
1681                              GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK,
1682                              struct GNUNET_PSYC_CountersResultMessage,
1683                              gst),
1684     GNUNET_MQ_hd_fixed_size (place_leave_ack,
1685                              GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK,
1686                              struct GNUNET_MessageHeader,
1687                              plc),
1688     GNUNET_MQ_hd_var_size (guest_enter_decision,
1689                            GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
1690                            struct GNUNET_PSYC_JoinDecisionMessage,
1691                            gst),
1692     GNUNET_MQ_hd_var_size (place_message,
1693                            GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
1694                            struct GNUNET_PSYC_MessageHeader,
1695                            plc),
1696     GNUNET_MQ_hd_fixed_size (place_message_ack,
1697                              GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
1698                              struct GNUNET_MessageHeader,
1699                              plc),
1700     GNUNET_MQ_hd_var_size (place_history_result,
1701                            GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
1702                            struct GNUNET_OperationResultMessage,
1703                            plc),
1704     GNUNET_MQ_hd_var_size (place_state_result,
1705                            GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
1706                            struct GNUNET_OperationResultMessage,
1707                            plc),
1708     GNUNET_MQ_hd_var_size (place_result,
1709                            GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
1710                            struct GNUNET_OperationResultMessage,
1711                            plc),
1712     GNUNET_MQ_handler_end ()
1713   };
1714
1715   plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social",
1716                                    handlers, guest_disconnected, gst);
1717   GNUNET_assert (NULL != plc->mq);
1718   plc->tmit = GNUNET_PSYC_transmit_create (plc->mq);
1719
1720   GNUNET_MQ_send_copy (plc->mq, plc->connect_env);
1721 }
1722
1723
1724 static struct GNUNET_MQ_Envelope *
1725 guest_enter_request_create (const char *app_id,
1726                             const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
1727                             const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
1728                             const struct GNUNET_PeerIdentity *origin,
1729                             size_t relay_count,
1730                             const struct GNUNET_PeerIdentity *relays,
1731                             const struct GNUNET_PSYC_Message *join_msg)
1732 {
1733   uint16_t app_id_size = strlen (app_id) + 1;
1734   uint16_t join_msg_size = ntohs (join_msg->header.size);
1735   uint16_t relay_size = relay_count * sizeof (*relays);
1736
1737   struct GuestEnterRequest *greq;
1738   struct GNUNET_MQ_Envelope *
1739     env = GNUNET_MQ_msg_extra (greq, app_id_size + relay_size + join_msg_size,
1740                                GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
1741   greq->place_pub_key = *place_pub_key;
1742   greq->ego_pub_key = *ego_pub_key;
1743   greq->origin = *origin;
1744   greq->relay_count = htonl (relay_count);
1745
1746   char *p = (char *) &greq[1];
1747   GNUNET_memcpy (p, app_id, app_id_size);
1748   p += app_id_size;
1749
1750   if (0 < relay_size)
1751   {
1752     GNUNET_memcpy (p, relays, relay_size);
1753     p += relay_size;
1754   }
1755
1756   GNUNET_memcpy (p, join_msg, join_msg_size);
1757   return env;
1758 }
1759
1760
1761 /**
1762  * Request entry to a place as a guest.
1763  *
1764  * @param app
1765  *        Application handle.
1766  * @param ego
1767  *        Identity of the guest.
1768  * @param place_pub_key
1769  *        Public key of the place to enter.
1770  * @param flags
1771  *        Flags for the entry.
1772  * @param origin
1773  *        Peer identity of the origin of the underlying multicast group.
1774  * @param relay_count
1775  *        Number of elements in the @a relays array.
1776  * @param relays
1777  *        Relays for the underlying multicast group.
1778  * @param method_name
1779  *        Method name for the message.
1780  * @param env
1781  *        Environment containing variables for the message, or NULL.
1782  * @param data
1783  *        Payload for the message to give to the enter callback.
1784  * @param data_size
1785  *        Number of bytes in @a data.
1786  * @param slicer
1787  *        Slicer to use for processing incoming requests from guests.
1788  *
1789  * @return NULL on errors, otherwise handle for the guest.
1790  */
1791 struct GNUNET_SOCIAL_Guest *
1792 GNUNET_SOCIAL_guest_enter (const struct GNUNET_SOCIAL_App *app,
1793                            const struct GNUNET_SOCIAL_Ego *ego,
1794                            const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
1795                            enum GNUNET_PSYC_SlaveJoinFlags flags,
1796                            const struct GNUNET_PeerIdentity *origin,
1797                            uint32_t relay_count,
1798                            const struct GNUNET_PeerIdentity *relays,
1799                            const struct GNUNET_PSYC_Message *entry_msg,
1800                            struct GNUNET_PSYC_Slicer *slicer,
1801                            GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
1802                            GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb,
1803                            void *cls)
1804 {
1805   struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1806   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1807
1808   plc->ego_pub_key = ego->pub_key;
1809   plc->pub_key = *place_pub_key;
1810   plc->cfg = app->cfg;
1811   plc->is_host = GNUNET_NO;
1812   plc->slicer = slicer;
1813
1814   plc->op = GNUNET_OP_create ();
1815
1816   plc->connect_env
1817     = guest_enter_request_create (app->id, &ego->pub_key, &plc->pub_key,
1818                                   origin, relay_count, relays, entry_msg);
1819
1820   gst->enter_cb = local_enter_cb;
1821   gst->entry_dcsn_cb = entry_dcsn_cb;
1822   gst->cb_cls = cls;
1823
1824   guest_connect (gst);
1825   return gst;
1826 }
1827
1828
1829 /**
1830  * Request entry to a place by name as a guest.
1831  *
1832  * @param app
1833  *        Application handle.
1834  * @param ego
1835  *        Identity of the guest.
1836  * @param gns_name
1837  *        GNS name of the place to enter.  Either in the form of
1838  *        'room.friend.gnu', or 'NYMPUBKEY.zkey'.  This latter case refers to
1839  *        the 'PLACE' record of the empty label ("+") in the GNS zone with the
1840  *        nym's public key 'NYMPUBKEY', and can be used to request entry to a
1841  *        pseudonym's place directly.
1842  * @param password
1843  *        Password to decrypt the record, or NULL for cleartext records.
1844  * @param join_msg
1845  *        Entry request message or NULL.
1846  * @param slicer
1847  *        Slicer to use for processing incoming requests from guests.
1848  * @param local_enter_cb
1849  *        Called upon connection established to the social service.
1850  * @param entry_decision_cb
1851  *        Called upon receiving entry decision.
1852  *
1853  * @return NULL on errors, otherwise handle for the guest.
1854  */
1855 struct GNUNET_SOCIAL_Guest *
1856 GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_SOCIAL_App *app,
1857                                    const struct GNUNET_SOCIAL_Ego *ego,
1858                                    const char *gns_name,
1859                                    const char *password,
1860                                    const struct GNUNET_PSYC_Message *join_msg,
1861                                    struct GNUNET_PSYC_Slicer *slicer,
1862                                    GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
1863                                    GNUNET_SOCIAL_EntryDecisionCallback entry_decision_cb,
1864                                    void *cls)
1865 {
1866   struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1867   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1868
1869   if (NULL == password)
1870     password = "";
1871
1872   uint16_t app_id_size = strlen (app->id) + 1;
1873   uint16_t gns_name_size = strlen (gns_name) + 1;
1874   uint16_t password_size = strlen (password) + 1;
1875
1876   uint16_t join_msg_size = 0;
1877   if (NULL != join_msg)
1878     join_msg_size = ntohs (join_msg->header.size);
1879
1880   struct GuestEnterByNameRequest *greq;
1881   plc->connect_env
1882     = GNUNET_MQ_msg_extra (greq, app_id_size + gns_name_size
1883                            + password_size + join_msg_size,
1884                            GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME);
1885
1886   greq->ego_pub_key = ego->pub_key;
1887
1888   char *p = (char *) &greq[1];
1889   GNUNET_memcpy (p, app->id, app_id_size);
1890   p += app_id_size;
1891   GNUNET_memcpy (p, gns_name, gns_name_size);
1892   p += gns_name_size;
1893   GNUNET_memcpy (p, password, password_size);
1894   p += password_size;
1895   if (NULL != join_msg)
1896     GNUNET_memcpy (p, join_msg, join_msg_size);
1897
1898   plc->ego_pub_key = ego->pub_key;
1899   plc->cfg = app->cfg;
1900   plc->is_host = GNUNET_NO;
1901   plc->slicer = slicer;
1902
1903   plc->op = GNUNET_OP_create ();
1904
1905   gst->enter_cb = local_enter_cb;
1906   gst->entry_dcsn_cb = entry_decision_cb;
1907   gst->cb_cls = cls;
1908
1909   guest_connect (gst);
1910   return gst;
1911 }
1912
1913
1914 /**
1915  * Reconnect to an already entered place as guest.
1916  *
1917  * @param gconn
1918  *        Guest connection handle.
1919  *        @see GNUNET_SOCIAL_app_connect() & GNUNET_SOCIAL_AppGuestPlaceCallback()
1920  * @param flags
1921  *        Flags for the entry.
1922  * @param slicer
1923  *        Slicer to use for processing incoming requests from guests.
1924  * @param local_enter_cb
1925  *        Called upon connection established to the social service.
1926  * @param entry_decision_cb
1927  *        Called upon receiving entry decision.
1928  *
1929  * @return NULL on errors, otherwise handle for the guest.
1930  */
1931 struct GNUNET_SOCIAL_Guest *
1932 GNUNET_SOCIAL_guest_enter_reconnect (struct GNUNET_SOCIAL_GuestConnection *gconn,
1933                                      enum GNUNET_PSYC_SlaveJoinFlags flags,
1934                                      struct GNUNET_PSYC_Slicer *slicer,
1935                                      GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
1936                                      void *cls)
1937 {
1938   struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1939   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1940
1941   uint16_t app_id_size = strlen (gconn->app->id) + 1;
1942   struct GuestEnterRequest *greq;
1943   plc->connect_env
1944     = GNUNET_MQ_msg_extra (greq, app_id_size,
1945                            GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
1946   greq->ego_pub_key = gconn->plc_msg.ego_pub_key;
1947   greq->place_pub_key = gconn->plc_msg.place_pub_key;
1948   greq->flags = htonl (flags);
1949
1950   GNUNET_memcpy (&greq[1], gconn->app->id, app_id_size);
1951
1952   plc->cfg = gconn->app->cfg;
1953   plc->is_host = GNUNET_NO;
1954   plc->slicer = slicer;
1955   plc->pub_key = gconn->plc_msg.place_pub_key;
1956   plc->ego_pub_key = gconn->plc_msg.ego_pub_key;
1957
1958   plc->op = GNUNET_OP_create ();
1959
1960   gst->enter_cb = local_enter_cb;
1961   gst->cb_cls = cls;
1962
1963   guest_connect (gst);
1964   return gst;
1965 }
1966
1967
1968 /**
1969  * Talk to the host of the place.
1970  *
1971  * @param place
1972  *        Place where we want to talk to the host.
1973  * @param method_name
1974  *        Method to invoke on the host.
1975  * @param env
1976  *        Environment containing variables for the message, or NULL.
1977  * @param notify_data
1978  *        Function to use to get the payload for the method.
1979  * @param notify_data_cls
1980  *        Closure for @a notify_data.
1981  * @param flags
1982  *        Flags for the message being sent.
1983  *
1984  * @return NULL if we are already trying to talk to the host,
1985  *         otherwise handle to cancel the request.
1986  */
1987 struct GNUNET_SOCIAL_TalkRequest *
1988 GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *gst,
1989                           const char *method_name,
1990                           const struct GNUNET_PSYC_Environment *env,
1991                           GNUNET_PSYC_TransmitNotifyData notify_data,
1992                           void *notify_data_cls,
1993                           enum GNUNET_SOCIAL_TalkFlags flags)
1994 {
1995   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1996               "PSYC_transmit_message for guest\n");
1997
1998   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1999   GNUNET_assert (NULL != plc->tmit);
2000
2001   if (GNUNET_OK ==
2002       GNUNET_PSYC_transmit_message (plc->tmit, method_name, env,
2003                                     NULL, notify_data, notify_data_cls, flags))
2004     return (struct GNUNET_SOCIAL_TalkRequest *) plc->tmit;
2005   else
2006     return NULL;
2007 }
2008
2009
2010 /**
2011  * Resume talking to the host of the place.
2012  *
2013  * @param tr
2014  *        Talk request to resume.
2015  */
2016 void
2017 GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr)
2018 {
2019   GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tr);
2020 }
2021
2022
2023 /**
2024  * Cancel talking to the host of the place.
2025  *
2026  * @param tr
2027  *        Talk request to cancel.
2028  */
2029 void
2030 GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr)
2031 {
2032   GNUNET_PSYC_transmit_cancel ( (struct GNUNET_PSYC_TransmitHandle *) tr);
2033 }
2034
2035
2036 /**
2037  * Disconnect from a place.
2038  *
2039  * Invalidates guest handle.
2040  *
2041  * @param gst
2042  *        The guest to disconnect.
2043  */
2044 void
2045 GNUNET_SOCIAL_guest_disconnect (struct GNUNET_SOCIAL_Guest *gst,
2046                                 GNUNET_ContinuationCallback disconnect_cb,
2047                                 void *cls)
2048 {
2049   struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2050
2051   plc->disconnect_cb = disconnect_cb;
2052   plc->disconnect_cls = cls;
2053   place_disconnect (plc);
2054 }
2055
2056
2057 /**
2058  * Leave a place temporarily or permanently.
2059  *
2060  * Notifies the owner of the place about leaving, and destroys the place handle.
2061  *
2062  * @param place
2063  *        Place to leave.
2064  * @param keep_active
2065  *        Keep place active after last application disconnected.
2066  *        #GNUNET_YES or #GNUNET_NO
2067  * @param env
2068  *        Optional environment for the leave message if @a keep_active
2069  *        is #GNUNET_NO.  NULL if not needed.
2070  * @param leave_cb
2071  *        Called upon disconnecting from the social service.
2072  */
2073 void
2074 GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst,
2075                            struct GNUNET_PSYC_Environment *env,
2076                            GNUNET_ContinuationCallback disconnect_cb,
2077                            void *cls)
2078 {
2079   struct GNUNET_MQ_Envelope *envelope;
2080
2081   GNUNET_SOCIAL_guest_talk (gst, "_notice_place_leave", env, NULL, NULL,
2082                             GNUNET_SOCIAL_TALK_NONE);
2083   gst->plc.disconnect_cb = disconnect_cb;
2084   gst->plc.disconnect_cls = cls;
2085   envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE);
2086   GNUNET_MQ_send (gst->plc.mq,
2087                   envelope);
2088 }
2089
2090
2091 /**
2092  * Obtain handle for a place entered as guest.
2093  *
2094  * The returned handle can be used to access the place API.
2095  *
2096  * @param guest  Handle for the guest.
2097  *
2098  * @return Handle for the place, valid as long as @a guest is valid.
2099  */
2100 struct GNUNET_SOCIAL_Place *
2101 GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *gst)
2102 {
2103   return &gst->plc;
2104 }
2105
2106
2107 /**
2108  * Obtain the public key of a place.
2109  *
2110  * @param plc
2111  *        Place.
2112  *
2113  * @return Public key of the place.
2114  */
2115 const struct GNUNET_CRYPTO_EddsaPublicKey *
2116 GNUNET_SOCIAL_place_get_pub_key (const struct GNUNET_SOCIAL_Place *plc)
2117 {
2118   return &plc->pub_key;
2119 }
2120
2121
2122 /**
2123  * Set message processing @a flags for a @a method_prefix.
2124  *
2125  * @param plc
2126  *        Place.
2127  * @param method_prefix
2128  *        Method prefix @a flags apply to.
2129  * @param flags
2130  *        The flags that apply to a matching @a method_prefix.
2131  */
2132 void
2133 GNUNET_SOCIAL_place_msg_proc_set (struct GNUNET_SOCIAL_Place *plc,
2134                                   const char *method_prefix,
2135                                   enum GNUNET_SOCIAL_MsgProcFlags flags)
2136 {
2137   GNUNET_assert (NULL != method_prefix);
2138   struct MsgProcRequest *mpreq;
2139   uint16_t method_size = strnlen (method_prefix,
2140                                   GNUNET_MAX_MESSAGE_SIZE
2141                                   - sizeof (*mpreq)) + 1;
2142   GNUNET_assert ('\0' == method_prefix[method_size - 1]);
2143
2144   struct GNUNET_MQ_Envelope *
2145     env = GNUNET_MQ_msg_extra (mpreq, method_size,
2146                                GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET);
2147   mpreq->flags = htonl (flags);
2148   GNUNET_memcpy (&mpreq[1], method_prefix, method_size);
2149
2150   GNUNET_MQ_send (plc->mq, env);
2151 }
2152
2153
2154 /**
2155  * Clear all message processing flags previously set for this place.
2156  */
2157 void
2158 GNUNET_SOCIAL_place_msg_proc_clear (struct GNUNET_SOCIAL_Place *plc)
2159 {
2160   struct GNUNET_MessageHeader *req;
2161   struct GNUNET_MQ_Envelope *
2162     env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_CLEAR);
2163
2164   GNUNET_MQ_send (plc->mq, env);
2165 }
2166
2167
2168 static struct GNUNET_SOCIAL_HistoryRequest *
2169 place_history_replay (struct GNUNET_SOCIAL_Place *plc,
2170                       uint64_t start_message_id,
2171                       uint64_t end_message_id,
2172                       uint64_t message_limit,
2173                       const char *method_prefix,
2174                       uint32_t flags,
2175                       struct GNUNET_PSYC_Slicer *slicer,
2176                       GNUNET_ResultCallback result_cb,
2177                       void *cls)
2178 {
2179   struct GNUNET_PSYC_HistoryRequestMessage *req;
2180   struct GNUNET_SOCIAL_HistoryRequest *hist = GNUNET_malloc (sizeof (*hist));
2181   hist->plc = plc;
2182   hist->slicer = slicer;
2183   hist->result_cb = result_cb;
2184   hist->cls = cls;
2185   hist->op_id = GNUNET_OP_add (plc->op, op_recv_history_result, hist, NULL);
2186
2187   GNUNET_assert (NULL != method_prefix);
2188   uint16_t method_size = strnlen (method_prefix,
2189                                   GNUNET_MAX_MESSAGE_SIZE
2190                                   - sizeof (*req)) + 1;
2191   GNUNET_assert ('\0' == method_prefix[method_size - 1]);
2192
2193   struct GNUNET_MQ_Envelope *
2194     env = GNUNET_MQ_msg_extra (req, method_size,
2195                                GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY);
2196   req->start_message_id = GNUNET_htonll (start_message_id);
2197   req->end_message_id = GNUNET_htonll (end_message_id);
2198   req->message_limit = GNUNET_htonll (message_limit);
2199   req->flags = htonl (flags);
2200   req->op_id = GNUNET_htonll (hist->op_id);
2201   GNUNET_memcpy (&req[1], method_prefix, method_size);
2202
2203   GNUNET_MQ_send (plc->mq, env);
2204   return hist;
2205 }
2206
2207
2208 /**
2209  * Learn about the history of a place.
2210  *
2211  * Messages are returned through the @a slicer function
2212  * and have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
2213  *
2214  * @param place
2215  *        Place we want to learn more about.
2216  * @param start_message_id
2217  *        First historic message we are interested in.
2218  * @param end_message_id
2219  *        Last historic message we are interested in (inclusive).
2220  * @param method_prefix
2221  *        Only retrieve messages with this method prefix.
2222  * @param flags
2223  *        OR'ed GNUNET_PSYC_HistoryReplayFlags
2224  * @param slicer
2225  *        Slicer to use for retrieved messages.
2226  *        Can be the same as the slicer of the place.
2227  * @param result_cb
2228  *        Function called after all messages retrieved.
2229  *        NULL if not needed.
2230  * @param cls Closure for @a result_cb.
2231  */
2232 struct GNUNET_SOCIAL_HistoryRequest *
2233 GNUNET_SOCIAL_place_history_replay (struct GNUNET_SOCIAL_Place *plc,
2234                                     uint64_t start_message_id,
2235                                     uint64_t end_message_id,
2236                                     const char *method_prefix,
2237                                     uint32_t flags,
2238                                     struct GNUNET_PSYC_Slicer *slicer,
2239                                     GNUNET_ResultCallback result_cb,
2240                                     void *cls)
2241 {
2242   return place_history_replay (plc, start_message_id, end_message_id, 0,
2243                                method_prefix, flags, slicer, result_cb, cls);
2244 }
2245
2246
2247 /**
2248  * Learn about the history of a place.
2249  *
2250  * Sends messages through the slicer function of the place where
2251  * start_message_id <= message_id <= end_message_id.
2252  * The messages will have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
2253  *
2254  * To get the latest message, use 0 for both the start and end message ID.
2255  *
2256  * @param place
2257  *        Place we want to learn more about.
2258  * @param message_limit
2259  *        Maximum number of historic messages we are interested in.
2260  * @param method_prefix
2261  *        Only retrieve messages with this method prefix.
2262  * @param flags
2263  *        OR'ed GNUNET_PSYC_HistoryReplayFlags
2264  * @param result_cb
2265  *        Function called after all messages retrieved.
2266  *        NULL if not needed.
2267  * @param cls Closure for @a result_cb.
2268  */
2269 struct GNUNET_SOCIAL_HistoryRequest *
2270 GNUNET_SOCIAL_place_history_replay_latest (struct GNUNET_SOCIAL_Place *plc,
2271                                            uint64_t message_limit,
2272                                            const char *method_prefix,
2273                                            uint32_t flags,
2274                                            struct GNUNET_PSYC_Slicer *slicer,
2275                                            GNUNET_ResultCallback result_cb,
2276                                            void *cls)
2277 {
2278   return place_history_replay (plc, 0, 0, message_limit, method_prefix, flags,
2279                                slicer, result_cb, cls);
2280 }
2281
2282
2283 /**
2284  * Cancel learning about the history of a place.
2285  *
2286  * @param hist
2287  *        History lesson to cancel.
2288  */
2289 void
2290 GNUNET_SOCIAL_place_history_replay_cancel (struct GNUNET_SOCIAL_HistoryRequest *hist)
2291 {
2292   GNUNET_OP_remove (hist->plc->op, hist->op_id);
2293   GNUNET_free (hist);
2294 }
2295
2296
2297 /**
2298  * Request matching state variables.
2299  */
2300 static struct GNUNET_SOCIAL_LookHandle *
2301 place_state_get (struct GNUNET_SOCIAL_Place *plc,
2302                  uint16_t type, const char *name,
2303                  GNUNET_PSYC_StateVarCallback var_cb,
2304                  GNUNET_ResultCallback result_cb, void *cls)
2305 {
2306   struct GNUNET_PSYC_StateRequestMessage *req;
2307   struct GNUNET_SOCIAL_LookHandle *look = GNUNET_malloc (sizeof (*look));
2308   look->plc = plc;
2309   look->var_cb = var_cb;
2310   look->result_cb = result_cb;
2311   look->cls = cls;
2312   look->op_id = GNUNET_OP_add (plc->op, &op_recv_state_result, look, NULL);
2313
2314   GNUNET_assert (NULL != name);
2315   size_t name_size = strnlen (name, GNUNET_MAX_MESSAGE_SIZE
2316                               - sizeof (*req)) + 1;
2317   struct GNUNET_MQ_Envelope *
2318     env = GNUNET_MQ_msg_extra (req, name_size, type);
2319   req->op_id = GNUNET_htonll (look->op_id);
2320   GNUNET_memcpy (&req[1], name, name_size);
2321
2322   GNUNET_MQ_send (plc->mq, env);
2323   return look;
2324 }
2325
2326
2327 /**
2328  * Look at a particular object in the place.
2329  *
2330  * The best matching object is returned (its name might be less specific than
2331  * what was requested).
2332  *
2333  * @param place
2334  *        The place where to look.
2335  * @param full_name
2336  *        Full name of the object.
2337  * @param value_size
2338  *        Set to the size of the returned value.
2339  *
2340  * @return NULL if there is no such object at this place.
2341  */
2342 struct GNUNET_SOCIAL_LookHandle *
2343 GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *plc,
2344                              const char *full_name,
2345                              GNUNET_PSYC_StateVarCallback var_cb,
2346                              GNUNET_ResultCallback result_cb,
2347                              void *cls)
2348 {
2349   return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
2350                           full_name, var_cb, result_cb, cls);
2351 }
2352
2353
2354 /**
2355  * Look for objects in the place with a matching name prefix.
2356  *
2357  * @param place
2358  *        The place where to look.
2359  * @param name_prefix
2360  *        Look at objects with names beginning with this value.
2361  * @param var_cb
2362  *        Function to call for each object found.
2363  * @param cls
2364  *        Closure for callback function.
2365  *
2366  * @return Handle that can be used to stop looking at objects.
2367  */
2368 struct GNUNET_SOCIAL_LookHandle *
2369 GNUNET_SOCIAL_place_look_for (struct GNUNET_SOCIAL_Place *plc,
2370                               const char *name_prefix,
2371                               GNUNET_PSYC_StateVarCallback var_cb,
2372                               GNUNET_ResultCallback result_cb,
2373                               void *cls)
2374 {
2375   return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
2376                           name_prefix, var_cb, result_cb, cls);
2377 }
2378
2379
2380 /**
2381  * Cancel a state request operation.
2382  *
2383  * @param sr
2384  *        Handle for the operation to cancel.
2385  */
2386 void
2387 GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *look)
2388 {
2389   GNUNET_OP_remove (look->plc->op, look->op_id);
2390   GNUNET_free (look);
2391 }
2392
2393
2394 static void
2395 op_recv_zone_add_place_result (void *cls, int64_t result,
2396                                const void *err_msg, uint16_t err_msg_size)
2397 {
2398   LOG (GNUNET_ERROR_TYPE_DEBUG,
2399        "Received zone add place result: %" PRId64 ".\n", result);
2400
2401   struct ZoneAddPlaceHandle *add_plc = cls;
2402   if (NULL != add_plc->result_cb)
2403     add_plc->result_cb (add_plc->result_cls, result, err_msg, err_msg_size);
2404
2405   GNUNET_free (add_plc);
2406 }
2407
2408
2409 /**
2410  * Advertise @e place in the GNS zone of @e ego.
2411  *
2412  * @param app
2413  *        Application handle.
2414  * @param ego
2415  *        Ego.
2416  * @param place_pub_key
2417  *        Public key of place to add.
2418  * @param name
2419  *        The name for the PLACE record to put in the zone.
2420  * @param password
2421  *        Password used to encrypt the record or NULL to keep it cleartext.
2422  * @param relay_count
2423  *        Number of elements in the @a relays array.
2424  * @param relays
2425  *        List of relays to put in the PLACE record to advertise
2426  *        as entry points to the place in addition to the origin.
2427  * @param expiration_time
2428  *        Expiration time of the record, use 0 to remove the record.
2429  * @param result_cb
2430  *        Function called with the result of the operation.
2431  * @param result_cls
2432  *        Closure for @a result_cb
2433  *
2434  * @return #GNUNET_OK if the request was sent,
2435  *         #GNUNET_SYSERR on error, e.g. the name/password is too long.
2436  */
2437 int
2438 GNUNET_SOCIAL_zone_add_place (const struct GNUNET_SOCIAL_App *app,
2439                               const struct GNUNET_SOCIAL_Ego *ego,
2440                               const char *name,
2441                               const char *password,
2442                               const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
2443                               const struct GNUNET_PeerIdentity *origin,
2444                               uint32_t relay_count,
2445                               const struct GNUNET_PeerIdentity *relays,
2446                               struct GNUNET_TIME_Absolute expiration_time,
2447                               GNUNET_ResultCallback result_cb,
2448                               void *result_cls)
2449 {
2450   struct ZoneAddPlaceRequest *preq;
2451   size_t name_size = strlen (name) + 1;
2452   size_t password_size = strlen (password) + 1;
2453   size_t relay_size = relay_count * sizeof (*relays);
2454   size_t payload_size = name_size + password_size + relay_size;
2455
2456   if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*preq) + payload_size)
2457     return GNUNET_SYSERR;
2458
2459   struct GNUNET_MQ_Envelope *
2460     env = GNUNET_MQ_msg_extra (preq, payload_size,
2461                                GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_PLACE);
2462   preq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us);
2463   preq->ego_pub_key = ego->pub_key;
2464   preq->place_pub_key = *place_pub_key;
2465   preq->origin = *origin;
2466   preq->relay_count = htonl (relay_count);
2467
2468   char *p = (char *) &preq[1];
2469   GNUNET_memcpy (p, name, name_size);
2470   p += name_size;
2471   GNUNET_memcpy (p, password, password_size);
2472   p += password_size;
2473   GNUNET_memcpy (p, relays, relay_size);
2474
2475   struct ZoneAddPlaceHandle * add_plc = GNUNET_malloc (sizeof (*add_plc));
2476   add_plc->result_cb = result_cb;
2477   add_plc->result_cls = result_cls;
2478
2479   preq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op,
2480                                               op_recv_zone_add_place_result,
2481                                               add_plc, NULL));
2482
2483   GNUNET_MQ_send (app->mq, env);
2484   return GNUNET_OK;
2485 }
2486
2487
2488 static void
2489 op_recv_zone_add_nym_result (void *cls, int64_t result,
2490                              const void *err_msg, uint16_t err_msg_size)
2491 {
2492   LOG (GNUNET_ERROR_TYPE_DEBUG,
2493        "Received zone add nym result: %" PRId64 ".\n", result);
2494
2495   struct ZoneAddNymHandle *add_nym = cls;
2496   if (NULL != add_nym->result_cb)
2497     add_nym->result_cb (add_nym->result_cls, result, err_msg, err_msg_size);
2498
2499   GNUNET_free (add_nym);
2500 }
2501
2502
2503 /**
2504  * Add nym to the GNS zone of @e ego.
2505  *
2506  * @param cfg
2507  *        Configuration.
2508  * @param ego
2509  *        Ego.
2510  * @param name
2511  *        The name for the PKEY record to put in the zone.
2512  * @param nym_pub_key
2513  *        Public key of nym to add.
2514  * @param expiration_time
2515  *        Expiration time of the record, use 0 to remove the record.
2516  * @param result_cb
2517  *        Function called with the result of the operation.
2518  * @param result_cls
2519  *        Closure for @a result_cb
2520  *
2521  * @return #GNUNET_OK if the request was sent,
2522  *         #GNUNET_SYSERR on error, e.g. the name is too long.
2523  */
2524 int
2525 GNUNET_SOCIAL_zone_add_nym (const struct GNUNET_SOCIAL_App *app,
2526                             const struct GNUNET_SOCIAL_Ego *ego,
2527                             const char *name,
2528                             const struct GNUNET_CRYPTO_EcdsaPublicKey *nym_pub_key,
2529                             struct GNUNET_TIME_Absolute expiration_time,
2530                             GNUNET_ResultCallback result_cb,
2531                             void *result_cls)
2532 {
2533   struct ZoneAddNymRequest *nreq;
2534
2535   size_t name_size = strlen (name) + 1;
2536   if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*nreq) + name_size)
2537     return GNUNET_SYSERR;
2538
2539   struct GNUNET_MQ_Envelope *
2540     env = GNUNET_MQ_msg_extra (nreq, name_size,
2541                                GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_NYM);
2542   nreq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us);
2543   nreq->ego_pub_key = ego->pub_key;
2544   nreq->nym_pub_key = *nym_pub_key;
2545   GNUNET_memcpy (&nreq[1], name, name_size);
2546
2547   struct ZoneAddNymHandle *add_nym = GNUNET_malloc (sizeof (*add_nym));
2548   add_nym->result_cb = result_cb;
2549   add_nym->result_cls = result_cls;
2550
2551   nreq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op,
2552                                               op_recv_zone_add_nym_result,
2553                                               add_nym, NULL));
2554
2555   GNUNET_MQ_send (app->mq, env);
2556   return GNUNET_OK;
2557 }
2558
2559
2560 /*** APP ***/
2561
2562
2563 static void
2564 app_connect (struct GNUNET_SOCIAL_App *app);
2565
2566
2567 static void
2568 app_reconnect (void *cls)
2569 {
2570   app_connect (cls);
2571 }
2572
2573
2574 /**
2575  * App client disconnected from service.
2576  *
2577  * Reconnect after backoff period.
2578  */
2579 static void
2580 app_disconnected (void *cls, enum GNUNET_MQ_Error error)
2581 {
2582   struct GNUNET_SOCIAL_App *app = cls;
2583
2584   LOG (GNUNET_ERROR_TYPE_DEBUG,
2585        "App client disconnected (%d), re-connecting\n",
2586        (int) error);
2587   if (NULL != app->mq)
2588   {
2589     GNUNET_MQ_destroy (app->mq);
2590     app->mq = NULL;
2591   }
2592
2593   app->reconnect_task = GNUNET_SCHEDULER_add_delayed (app->reconnect_delay,
2594                                                       app_reconnect,
2595                                                       app);
2596   app->reconnect_delay = GNUNET_TIME_STD_BACKOFF (app->reconnect_delay);
2597 }
2598
2599
2600 static void
2601 app_connect (struct GNUNET_SOCIAL_App *app)
2602 {
2603   struct GNUNET_MQ_MessageHandler handlers[] = {
2604     GNUNET_MQ_hd_var_size (app_ego,
2605                            GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO,
2606                            struct AppEgoMessage,
2607                            app),
2608     GNUNET_MQ_hd_fixed_size (app_ego_end,
2609                              GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO_END,
2610                              struct GNUNET_MessageHeader,
2611                              app),
2612     GNUNET_MQ_hd_var_size (app_place,
2613                            GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE,
2614                            struct AppPlaceMessage,
2615                            app),
2616     GNUNET_MQ_hd_fixed_size (app_place_end,
2617                              GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE_END,
2618                              struct GNUNET_MessageHeader,
2619                              app),
2620     GNUNET_MQ_hd_var_size (app_result,
2621                            GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
2622                            struct GNUNET_OperationResultMessage,
2623                            app),
2624     GNUNET_MQ_handler_end ()
2625   };
2626
2627   app->mq = GNUNET_CLIENT_connect (app->cfg, "social",
2628                                    handlers, app_disconnected, app);
2629   GNUNET_assert (NULL != app->mq);
2630   GNUNET_MQ_send_copy (app->mq, app->connect_env);
2631 }
2632
2633
2634 /**
2635  * Connect application to the social service.
2636  *
2637  * The @host_place_cb and @guest_place_cb functions are
2638  * initially called for each entered places,
2639  * then later each time a new place is entered with the current application ID.
2640  *
2641  * @param cfg
2642  *        Configuration.
2643  * @param id
2644  *        Application ID.
2645  * @param ego_cb
2646  *        Function to notify about an available ego.
2647  * @param host_cb
2648  *        Function to notify about a place entered as host.
2649  * @param guest_cb
2650  *        Function to notify about a place entered as guest.
2651  * @param cls
2652  *        Closure for the callbacks.
2653  *
2654  * @return Handle that can be used to stop listening.
2655  */
2656 struct GNUNET_SOCIAL_App *
2657 GNUNET_SOCIAL_app_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
2658                            const char *id,
2659                            GNUNET_SOCIAL_AppEgoCallback ego_cb,
2660                            GNUNET_SOCIAL_AppHostPlaceCallback host_cb,
2661                            GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb,
2662                            GNUNET_SOCIAL_AppConnectedCallback connected_cb,
2663                            void *cls)
2664 {
2665   uint16_t app_id_size = strnlen (id, GNUNET_SOCIAL_APP_MAX_ID_SIZE);
2666   if (GNUNET_SOCIAL_APP_MAX_ID_SIZE == app_id_size)
2667     return NULL;
2668   app_id_size++;
2669
2670   struct GNUNET_SOCIAL_App *app = GNUNET_malloc (sizeof *app);
2671   app->cfg = cfg;
2672   app->ego_cb = ego_cb;
2673   app->host_cb = host_cb;
2674   app->guest_cb = guest_cb;
2675   app->connected_cb = connected_cb;
2676   app->cb_cls = cls;
2677   app->egos = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
2678   app->op = GNUNET_OP_create ();
2679   app->id = GNUNET_malloc (app_id_size);
2680   GNUNET_memcpy (app->id, id, app_id_size);
2681
2682   struct AppConnectRequest *creq;
2683   app->connect_env = GNUNET_MQ_msg_extra (creq, app_id_size,
2684                                           GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT);
2685   GNUNET_memcpy (&creq[1], app->id, app_id_size);
2686
2687   app_connect (app);
2688   return app;
2689 }
2690
2691
2692 static void
2693 app_cleanup (struct GNUNET_SOCIAL_App *app)
2694 {
2695   if (NULL != app->mq)
2696   {
2697     GNUNET_MQ_destroy (app->mq);
2698     app->mq = NULL;
2699   }
2700   if (NULL != app->disconnect_cb)
2701   {
2702     app->disconnect_cb (app->disconnect_cls);
2703     app->disconnect_cb = NULL;
2704   }
2705   GNUNET_free (app);
2706 }
2707
2708 /**
2709  * Disconnect application.
2710  *
2711  * @param app
2712  *        Application handle.
2713  * @param disconnect_cb
2714  *        Disconnect callback.
2715  * @param disconnect_cls
2716  *        Disconnect closure.
2717  */
2718 void
2719 GNUNET_SOCIAL_app_disconnect (struct GNUNET_SOCIAL_App *app,
2720                               GNUNET_ContinuationCallback disconnect_cb,
2721                               void *disconnect_cls)
2722 {
2723   app->disconnect_cb = disconnect_cb;
2724   app->disconnect_cls = disconnect_cls;
2725
2726   if (NULL != app->mq)
2727   {
2728     struct GNUNET_MQ_Envelope *env = GNUNET_MQ_get_last_envelope (app->mq);
2729     if (NULL != env)
2730     {
2731       GNUNET_MQ_notify_sent (env, (GNUNET_SCHEDULER_TaskCallback) app_cleanup, app);
2732     }
2733     else
2734     {
2735       app_cleanup (app);
2736     }
2737   }
2738   else
2739   {
2740     app_cleanup (app);
2741   }
2742 }
2743
2744
2745 /**
2746  * Detach application from a place.
2747  *
2748  * Removes the place from the entered places list for this application.
2749  * Note: this does not disconnect from the place.
2750  *
2751  * @see GNUNET_SOCIAL_host_disconnect() and GNUNET_SOCIAL_guest_disconnect()
2752  *
2753  * @param app
2754  *        Application.
2755  * @param plc
2756  *        Place.
2757  */
2758 void
2759 GNUNET_SOCIAL_app_detach (struct GNUNET_SOCIAL_App *app,
2760                           struct GNUNET_SOCIAL_Place *plc)
2761 {
2762   struct AppDetachRequest *dreq;
2763   struct GNUNET_MQ_Envelope *
2764     env = GNUNET_MQ_msg (dreq, GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH);
2765   dreq->place_pub_key = plc->pub_key;
2766   dreq->ego_pub_key = plc->ego_pub_key;
2767
2768   GNUNET_MQ_send (app->mq, env);
2769 }
2770
2771
2772 /* end of social_api.c */