gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r34021 - in gnunet: contrib src/include src/multicast src/p


From: gnunet
Subject: [GNUnet-SVN] r34021 - in gnunet: contrib src/include src/multicast src/psyc src/social src/util
Date: Wed, 23 Jul 2014 18:19:50 +0200

Author: tg
Date: 2014-07-23 18:19:49 +0200 (Wed, 23 Jul 2014)
New Revision: 34021

Modified:
   gnunet/contrib/gnunet-logread
   gnunet/src/include/gnunet_client_manager_lib.h
   gnunet/src/include/gnunet_common.h
   gnunet/src/include/gnunet_multicast_service.h
   gnunet/src/include/gnunet_protocols.h
   gnunet/src/include/gnunet_psyc_service.h
   gnunet/src/include/gnunet_psyc_util_lib.h
   gnunet/src/include/gnunet_social_service.h
   gnunet/src/multicast/gnunet-service-multicast.c
   gnunet/src/multicast/multicast_api.c
   gnunet/src/psyc/gnunet-service-psyc.c
   gnunet/src/psyc/psyc.h
   gnunet/src/psyc/psyc_api.c
   gnunet/src/psyc/psyc_util_lib.c
   gnunet/src/psyc/test_psyc.c
   gnunet/src/social/Makefile.am
   gnunet/src/social/gnunet-service-social.c
   gnunet/src/social/social.h
   gnunet/src/social/social_api.c
   gnunet/src/social/test_social.c
   gnunet/src/util/client_manager.c
Log:
social: implement enter/leave/messaging; psyc: improvements and fixes

- social: implement enter/leave, send/receive messages, slicer
- psyc, social: add struct GNUNET_PSYC_Message for single-fragment join messages
- psyc: add message callback in addition to message part callback
- client_manager, social, psyc, multicast: add disconnect callback

Modified: gnunet/contrib/gnunet-logread
===================================================================
--- gnunet/contrib/gnunet-logread       2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/contrib/gnunet-logread       2014-07-23 16:19:49 UTC (rev 34021)
@@ -47,7 +47,7 @@
     $ipc = $opts{i} || '/tmp/gnunet-logread-ipc.sock';
     $msg_level = exists $levels{$opts{L}} ? $levels{$opts{L}} : 0;
     $msg_regex = $opts{m};
-    print STDERR "RE: /$msg_regex/\n";
+    print STDERR "RE: /$msg_regex/\n" if defined $msg_regex;
     open IPC, '>', $ipc or die "$ipc: $!\n";
 }
 

Modified: gnunet/src/include/gnunet_client_manager_lib.h
===================================================================
--- gnunet/src/include/gnunet_client_manager_lib.h      2014-07-23 16:19:46 UTC 
(rev 34020)
+++ gnunet/src/include/gnunet_client_manager_lib.h      2014-07-23 16:19:49 UTC 
(rev 34021)
@@ -119,10 +119,14 @@
  *
  * @param mgr             Client manager connection.
  * @param transmit_queue  Transmit pending messages in queue before 
disconnecting.
+ * @param disconnect_cb   Function called after disconnected from the service.
+ * @param disconnect_cls  Closure for @a disconnect_cb.
  */
 void
 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
-                                  int transmit_queue);
+                                  int transmit_queue,
+                                  GNUNET_ContinuationCallback disconnect_cb,
+                                  void *disconnect_cls);
 
 
 /**

Modified: gnunet/src/include/gnunet_common.h
===================================================================
--- gnunet/src/include/gnunet_common.h  2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/include/gnunet_common.h  2014-07-23 16:19:49 UTC (rev 34021)
@@ -264,6 +264,15 @@
                             const char *filename);
 
 
+/**
+ * Generic continuation callback.
+ *
+ * @param cls  Closure.
+ */
+typedef void
+(*GNUNET_ContinuationCallback) (void *cls);
+
+
 /* ****************************** logging ***************************** */
 
 /**

Modified: gnunet/src/include/gnunet_multicast_service.h
===================================================================
--- gnunet/src/include/gnunet_multicast_service.h       2014-07-23 16:19:46 UTC 
(rev 34020)
+++ gnunet/src/include/gnunet_multicast_service.h       2014-07-23 16:19:49 UTC 
(rev 34021)
@@ -666,7 +666,9 @@
  * @param origin Multicast group to stop.
  */
 void
-GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin);
+GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin,
+                              GNUNET_ContinuationCallback stop_cb,
+                              void *stop_cls);
 
 
 /**
@@ -795,7 +797,9 @@
  * @param member Membership handle.
  */
 void
-GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *member);
+GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *member,
+                              GNUNET_ContinuationCallback part_cb,
+                              void *part_cls);
 
 
 /**

Modified: gnunet/src/include/gnunet_protocols.h
===================================================================
--- gnunet/src/include/gnunet_protocols.h       2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/include/gnunet_protocols.h       2014-07-23 16:19:49 UTC (rev 
34021)
@@ -2158,32 +2158,34 @@
 /** C->S: request to add channel slave to the membership database */
 #define GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_RM 690
 
-
-/** M<->S<->C: PSYC message which contains one or more message parts. */
+/** S<--C: PSYC message which contains one or more message parts. */
 #define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE 691
 
+/** M<->S<->C: PSYC message which contains a header and one or more message 
parts. */
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_HEADER 692 // FIXME: start using this 
where appropriate
+
 /** Message part: method */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD 692
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD 693
 
 /** Message part: modifier */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER 693
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER 694
 
 /** Message part: modifier continuation */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT 694
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT 695
 
 /** Message part: data */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA 695
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA 696
 
 /** Message part: end of message */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END 696
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END 697
 
 /** Message part: message cancelled */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL 697
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL 698
 
 /** S->C: message acknowledgement */
-#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK 698
+#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK 699
 
-/* 699-700 */
+/* 700 */
 
 /** C->S: client requests channel history from PSYCstore. */
 #define GNUNET_MESSAGE_TYPE_PSYC_STORY_REQUEST 701

Modified: gnunet/src/include/gnunet_psyc_service.h
===================================================================
--- gnunet/src/include/gnunet_psyc_service.h    2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/include/gnunet_psyc_service.h    2014-07-23 16:19:49 UTC (rev 
34021)
@@ -190,6 +190,24 @@
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**
+ * A PSYC message.
+ *
+ * Used for single-fragment messages e.g. in a join request or response.
+ */
+struct GNUNET_PSYC_Message
+{
+  /**
+   * Message header with size and type information.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /* Followed by concatenated PSYC message parts:
+   * messages with GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_* types
+   */
+};
+
+
+/**
  * Header of a PSYC message.
  *
  * Only present when receiving a message.
@@ -215,6 +233,12 @@
   uint64_t message_id GNUNET_PACKED;
 
   /**
+   * Byte offset of this @e fragment of the @e message.
+   * FIXME: use data_offset instead
+   */
+  uint64_t fragment_offset GNUNET_PACKED;
+
+  /**
    * Sending slave's public key.
    * Not set if the message is from the master.
    */
@@ -299,6 +323,9 @@
 };
 
 
+/**
+ * Join request sent to a PSYC master.
+ */
 struct GNUNET_PSYC_JoinRequestMessage
 {
   /**
@@ -314,6 +341,9 @@
 };
 
 
+/**
+ * Join decision sent in reply to a join request.
+ */
 struct GNUNET_PSYC_JoinDecisionMessage
 {
   /**
@@ -379,26 +409,45 @@
 
 
 /**
- * Method called from PSYC upon receiving part of a message.
+ * Method called from PSYC upon receiving a message.
  *
  * @param cls  Closure.
  * @param message_id  Sequence number of the message.
  * @param flags  OR'ed GNUNET_PSYC_MessageFlags
  * @param msg  Message part, one of the following types:
- * - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_HEADER
- * - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD
- * - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER
- * - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT
- * - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA
  */
 typedef void
 (*GNUNET_PSYC_MessageCallback) (void *cls,
                                 uint64_t message_id,
                                 uint32_t flags,
-                                const struct GNUNET_MessageHeader *msg);
+                                const struct GNUNET_PSYC_MessageHeader *msg);
 
 
 /**
+ * Method called from PSYC upon receiving part of a message.
+ *
+ * @param cls  Closure.
+ * @param message_id  Sequence number of the message.
+ * @param data_offset  Byte offset of data, only set if @a msg has a type
+ *                     #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA
+ * @param flags  OR'ed GNUNET_PSYC_MessageFlags
+ * @param msg  Message part, one of the following types:
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_HEADER
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA
+ * or NULL if an error occurred while receiving a message.
+ */
+typedef void
+(*GNUNET_PSYC_MessagePartCallback) (void *cls,
+                                    uint64_t message_id,
+                                    uint64_t data_offset,
+                                    uint32_t flags,
+                                    const struct GNUNET_MessageHeader *msg);
+
+
+/**
  * Method called from PSYC upon receiving a join request.
  *
  * @param cls  Closure.
@@ -408,10 +457,9 @@
  */
 typedef void
 (*GNUNET_PSYC_JoinRequestCallback) (void *cls,
-                                    const struct
-                                    GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
-                                    const struct
-                                    GNUNET_PSYC_MessageHeader *join_msg,
+                                    const struct 
GNUNET_PSYC_JoinRequestMessage *req,
+                                    const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                    const struct GNUNET_PSYC_Message *join_msg,
                                     struct GNUNET_PSYC_JoinHandle *jh);
 
 
@@ -445,7 +493,7 @@
                            int is_admitted,
                            uint32_t relay_count,
                            const struct GNUNET_PeerIdentity *relays,
-                           const struct GNUNET_PSYC_MessageHeader *join_resp);
+                           const struct GNUNET_PSYC_Message *join_resp);
 
 
 /**
@@ -501,6 +549,7 @@
                           GNUNET_PSYC_MasterStartCallback master_start_cb,
                           GNUNET_PSYC_JoinRequestCallback join_request_cb,
                           GNUNET_PSYC_MessageCallback message_cb,
+                          GNUNET_PSYC_MessagePartCallback message_part_cb,
                           void *cls);
 
 
@@ -645,10 +694,21 @@
 /**
  * Stop a PSYC master channel.
  *
- * @param master PSYC channel master to stop.
+ * @param master
+ *        PSYC channel master to stop.
+ * @param keep_active
+ *        Keep place active after last application disconnected.
+ * @param stop_cb
+ *        Function called after the master stopped
+ *        and disconnected from the psyc service.
+ * @param stop_cls
+ *        Closure for @a part_cb.
  */
 void
-GNUNET_PSYC_master_stop (struct GNUNET_PSYC_Master *master);
+GNUNET_PSYC_master_stop (struct GNUNET_PSYC_Master *master,
+                         int keep_active,
+                         GNUNET_ContinuationCallback stop_cb,
+                         void *stop_cls);
 
 
 /**
@@ -679,9 +739,9 @@
  */
 typedef void
 (*GNUNET_PSYC_JoinDecisionCallback) (void *cls,
+                                     const struct 
GNUNET_PSYC_JoinDecisionMessage *dcsn,
                                      int is_admitted,
-                                     const struct
-                                     GNUNET_PSYC_MessageHeader *join_msg);
+                                     const struct GNUNET_PSYC_Message 
*join_msg);
 
 
 /**
@@ -726,10 +786,11 @@
                         uint32_t relay_count,
                         const struct GNUNET_PeerIdentity *relays,
                         GNUNET_PSYC_MessageCallback message_cb,
+                        GNUNET_PSYC_MessagePartCallback message_part_cb,
                         GNUNET_PSYC_SlaveConnectCallback slave_connect_cb,
                         GNUNET_PSYC_JoinDecisionCallback join_decision_cb,
                         void *cls,
-                        const struct GNUNET_MessageHeader *join_msg);
+                        const struct GNUNET_PSYC_Message *join_msg);
 
 
 /**
@@ -738,10 +799,21 @@
  * Will terminate the connection to the PSYC service.  Polite clients should
  * first explicitly send a part request (via GNUNET_PSYC_slave_transmit()).
  *
- * @param slave Slave handle.
+ * @param slave
+ *        Slave handle.
+ * @param keep_active
+ *        Keep place active after last application disconnected.
+ * @param part_cb
+ *        Function called after the slave parted the channel
+ *        and disconnected from the psyc service.
+ * @param part_cls
+ *        Closure for @a part_cb.
  */
 void
-GNUNET_PSYC_slave_part (struct GNUNET_PSYC_Slave *slave);
+GNUNET_PSYC_slave_part (struct GNUNET_PSYC_Slave *slave,
+                        int keep_active,
+                        GNUNET_ContinuationCallback part_cb,
+                        void *part_cls);
 
 
 /**
@@ -937,6 +1009,7 @@
                                 uint64_t start_message_id,
                                 uint64_t end_message_id,
                                 GNUNET_PSYC_MessageCallback message_cb,
+                                GNUNET_PSYC_MessagePartCallback 
message_part_cb,
                                 GNUNET_PSYC_FinishCallback finish_cb,
                                 void *cls);
 

Modified: gnunet/src/include/gnunet_psyc_util_lib.h
===================================================================
--- gnunet/src/include/gnunet_psyc_util_lib.h   2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/include/gnunet_psyc_util_lib.h   2014-07-23 16:19:49 UTC (rev 
34021)
@@ -55,13 +55,37 @@
  * @return Message header with size information,
  *         followed by the message parts.
  */
-struct GNUNET_MessageHeader *
+struct GNUNET_PSYC_Message *
 GNUNET_PSYC_message_create (const char *method_name,
                             const struct GNUNET_ENV_Environment *env,
                             const void *data,
                             size_t data_size);
 
+/**
+ * Parse PSYC message.
+ *
+ * @param msg
+ *        The PSYC message to parse.
+ * @param env
+ *        The environment for the message with a list of modifiers.
+ * @param[out] method_name
+ *        Pointer to the method name inside @a pmsg.
+ * @param[out] data
+ *        Pointer to data inside @a pmsg.
+ * @param[out] data_size
+ *        Size of @data is written here.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on parse error.
+ */
+int
+GNUNET_PSYC_message_parse (const struct GNUNET_PSYC_Message *msg,
+                           const char **method_name,
+                           struct GNUNET_ENV_Environment *env,
+                           const void **data,
+                           uint16_t *data_size);
 
+
 void
 GNUNET_PSYC_log_message (enum GNUNET_ErrorType kind,
                          const struct GNUNET_MessageHeader *msg);
@@ -150,7 +174,7 @@
  */
 struct GNUNET_PSYC_ReceiveHandle *
 GNUNET_PSYC_receive_create (GNUNET_PSYC_MessageCallback message_cb,
-                            GNUNET_PSYC_MessageCallback hist_message_cb,
+                            GNUNET_PSYC_MessagePartCallback message_part_cb,
                             void *cb_cls);
 
 

Modified: gnunet/src/include/gnunet_social_service.h
===================================================================
--- gnunet/src/include/gnunet_social_service.h  2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/include/gnunet_social_service.h  2014-07-23 16:19:49 UTC (rev 
34021)
@@ -81,6 +81,8 @@
  *
  * @param cls
  *        Closure.
+ * @param msg
+ *        Message part, as it arrived from the network.
  * @param message_id
  *        Message counter, monotonically increasing from 1.
  * @param nym
@@ -92,12 +94,53 @@
  *        Original method name from PSYC.
  *        May be more specific than the registered method name due to
  *        try-and-slice matching.
- * @param env
- *        Environment with operations and variables for the message.
- *        Only set for the first call of this function for each @a message_id,
- *        NULL when notifying about further data fragments.
- *        It has to be freed using GNUNET_ENV_environment_destroy()
- *        when it is not needed anymore.
+ */
+typedef void
+(*GNUNET_SOCIAL_MethodCallback) (void *cls,
+                                 const struct GNUNET_PSYC_MessageMethod *msg,
+                                 uint64_t message_id,
+                                 uint32_t flags,
+                                 const struct GNUNET_SOCIAL_Nym *nym,
+                                 const char *method_name);
+
+
+/**
+ * Function called upon receiving a data fragment of a message.
+ *
+ * @param cls
+ *        Closure.
+ * @param message_id
+ *        Message ID this data fragment belongs to.
+ * @param msg
+ *        Message part, as it arrived from the network.
+ * @param oper
+ *        Operation to perform.
+ * @param name
+ *        Name of the modifier.
+ * @param value
+ *        Value of the modifier.
+ * @param value_size
+ *        Size of @value.
+ */
+typedef void
+(*GNUNET_SOCIAL_ModifierCallback) (void *cls,
+                                   const struct GNUNET_PSYC_MessageModifier 
*msg,
+                                   uint64_t message_id,
+                                   enum GNUNET_ENV_Operator oper,
+                                   const char *name,
+                                   const void *value,
+                                   uint16_t value_size);
+
+
+/**
+ * Function called upon receiving a data fragment of a message.
+ *
+ * @param cls
+ *        Closure.
+ * @param message_id
+ *        Message ID this data fragment belongs to.
+ * @param msg
+ *        Message part, as it arrived from the network.
  * @param data_offset
  *        Byte offset of @a data in the overall data of the method.
  * @param data_size
@@ -106,24 +149,40 @@
  *        Data stream given to the method.
  * @param end
  *        End of message?
- *   #GNUNET_NO     if there are further fragments,
- *   #GNUNET_YES    if this is the last fragment,
- *   #GNUNET_SYSERR indicates the message was cancelled by the sender.
+ *        #GNUNET_NO     if there are further fragments,
+ *        #GNUNET_YES    if this is the last fragment,
+ *        #GNUNET_SYSERR indicates the message was cancelled by the sender.
  */
 typedef void
-(*GNUNET_SOCIAL_MethodCallback) (void *cls,
-                                 uint64_t message_id,
-                                 uint32_t flags,
-                                 const struct GNUNET_SOCIAL_Nym *nym,
-                                 const char *method_name,
-                                 struct GNUNET_ENV_Environment *env,
-                                 uint64_t data_offset,
-                                 size_t data_size,
-                                 const void *data,
-                                 int end);
+(*GNUNET_SOCIAL_DataCallback) (void *cls,
+                               const struct GNUNET_MessageHeader *msg,
+                               uint64_t message_id,
+                               uint64_t data_offset,
+                               const void *data,
+                               uint16_t data_size);
 
 
 /**
+ * End of message.
+ *
+ * @param cls
+ *        Closure.
+ * @param msg
+ *        Message part, as it arrived from the network.
+ * @param message_id
+ *        Message ID this data fragment belongs to.
+ * @param cancelled.
+ *        #GNUNET_YES if the message was cancelled,
+ *        #GNUNET_NO  if the message is complete.
+ */
+typedef void
+(*GNUNET_SOCIAL_EndOfMessageCallback) (void *cls,
+                                       const struct GNUNET_MessageHeader *msg,
+                                       uint64_t message_id,
+                                       uint8_t cancelled);
+
+
+/**
  * Create a try-and-slice instance.
  *
  * @return A new try-and-slice construct.
@@ -148,21 +207,28 @@
 GNUNET_SOCIAL_slicer_add (struct GNUNET_SOCIAL_Slicer *slicer,
                           const char *method_name,
                           GNUNET_SOCIAL_MethodCallback method_cb,
+                          GNUNET_SOCIAL_ModifierCallback modifier_cb,
+                          GNUNET_SOCIAL_DataCallback data_cb,
+                          GNUNET_SOCIAL_EndOfMessageCallback eom_cb,
                           void *cls);
 
 
 /**
- * Remove a registered method from the try-and-slice instance.
+ * Remove a registered method handler from the try-and-slice instance.
  *
  * @param slicer The try-and-slice instance.
  * @param method_name Name of the method to remove.
  * @param method Method handler.
  */
-void
+int
 GNUNET_SOCIAL_slicer_remove (struct GNUNET_SOCIAL_Slicer *slicer,
                              const char *method_name,
-                             GNUNET_SOCIAL_MethodCallback method_cb);
+                             GNUNET_SOCIAL_MethodCallback method_cb,
+                             GNUNET_SOCIAL_ModifierCallback modifier_cb,
+                             GNUNET_SOCIAL_DataCallback data_cb,
+                             GNUNET_SOCIAL_EndOfMessageCallback eom_cb);
 
+
 /**
  * Destroy a given try-and-slice instance.
  *
@@ -257,7 +323,7 @@
  */
 struct GNUNET_SOCIAL_Host *
 GNUNET_SOCIAL_host_enter (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                          struct GNUNET_IDENTITY_Ego *ego,
+                          const struct GNUNET_IDENTITY_Ego *ego,
                           const struct GNUNET_CRYPTO_EddsaPrivateKey 
*place_key,
                           enum GNUNET_PSYC_Policy policy,
                           struct GNUNET_SOCIAL_Slicer *slicer,
@@ -268,17 +334,32 @@
 
 
 /**
- * Admit @a nym to the place.
+ * Decision whether to admit @a nym into the place or refuse entry.
  *
- * The @a nym reference will remain valid until either the @a host or @a nym
- * leaves the place.
- *
- * @param host  Host of the place.
- * @param nym  Handle for the entity that wants to enter.
+ * @param hst
+ *        Host of the place.
+ * @param nym
+ *        Handle for the entity that wanted to enter.
+ * @param is_admitted
+ *        #GNUNET_YES    if @a nym is admitted,
+ *        #GNUNET_NO     if @a nym is refused entry,
+ *        #GNUNET_SYSERR if we cannot answer the request.
+ * @param method_name
+ *        Method name for the rejection message.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param data
+ *        Data for the rejection message to send back.
+ * @param data_size
+ *        Number of bytes in @a data for method.
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if the message is too large.
  */
-void
-GNUNET_SOCIAL_host_admit (struct GNUNET_SOCIAL_Host *host,
-                          struct GNUNET_SOCIAL_Nym *nym);
+int
+GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
+                                   struct GNUNET_SOCIAL_Nym *nym,
+                                   int is_admitted,
+                                   const struct GNUNET_PSYC_Message 
*entry_resp);
 
 
 /**
@@ -297,35 +378,17 @@
 
 
 /**
- * Refuse @a nym entry into the place.
- *
- * @param host  Host of the place.
- * @param nym Handle for the entity that wanted to enter.
- * @param method_name Method name for the rejection message.
- * @param env Environment containing variables for the message, or NULL.
- * @param data Data for the rejection message to send back.
- * @param data_size Number of bytes in @a data for method.
- */
-void
-GNUNET_SOCIAL_host_refuse_entry (struct GNUNET_SOCIAL_Host *host,
-                                 struct GNUNET_SOCIAL_Nym *nym,
-                                 const char *method_name,
-                                 const struct GNUNET_ENV_Environment *env,
-                                 const void *data,
-                                 size_t data_size);
-
-
-/**
  * Get the public key of a @a nym.
  *
  * Suitable, for example, to be used with GNUNET_NAMESTORE_zone_to_name().
  *
- * @param nym Pseudonym to map to a cryptographic identifier.
- * @param[out] nym_key Set to the public key of the nym.
+ * @param nym
+ *        Pseudonym to map to a cryptographic identifier.
+ *
+ * @return Public key of nym;
  */
-void
-GNUNET_SOCIAL_nym_get_key (struct GNUNET_SOCIAL_Nym *nym,
-                           struct GNUNET_CRYPTO_EddsaPublicKey *nym_key);
+struct GNUNET_CRYPTO_EcdsaPublicKey *
+GNUNET_SOCIAL_nym_get_key (struct GNUNET_SOCIAL_Nym *nym);
 
 
 /**
@@ -418,9 +481,20 @@
 
 
 /**
+ * Resume transmitting announcement.
+ *
+ * @param a
+ *        The announcement to resume.
+ */
+void
+GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a);
+
+
+/**
  * Cancel announcement.
  *
- * @param a The announcement to cancel.
+ * @param a
+ *        The announcement to cancel.
  */
 void
 GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a);
@@ -431,7 +505,8 @@
  *
  * The returned handle can be used to access the place API.
  *
- * @param host  Handle for the host.
+ * @param host
+ *        Handle for the host.
  *
  * @return Handle for the hosted place, valid as long as @a host is valid.
  */
@@ -444,11 +519,21 @@
  *
  * Invalidates host handle.
  *
- * @param host  Host leaving the place.
- * @param keep_active  Keep the place active after last host disconnected.
+ * @param host
+ *        Host leaving the place.
+ * @param keep_active
+ *        Keep the place active after last host disconnected.
+ * @param leave_cb
+ *        Function called after the host left the place
+ *        and disconnected from the social service.
+ * @param leave_cls
+ *        Closure for @a leave_cb.
  */
 void
-GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *host, int keep_active);
+GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *host,
+                          int keep_active,
+                          GNUNET_ContinuationCallback leave_cb,
+                          void *leave_cls);
 
 
 /**
@@ -493,13 +578,10 @@
  * @param data
  *   Payload of the message.
  */
-typedef int
+typedef void
 (*GNUNET_SOCIAL_EntryDecisionCallback) (void *cls,
                                         int is_admitted,
-                                        const char *method_name,
-                                        struct GNUNET_ENV_Environment *env,
-                                        size_t data_size,
-                                        const void *data);
+                                        const struct GNUNET_PSYC_Message 
*entry_resp);
 
 
 /**
@@ -521,15 +603,12 @@
  */
 struct GNUNET_SOCIAL_Guest *
 GNUNET_SOCIAL_guest_enter (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                           struct GNUNET_IDENTITY_Ego *ego,
-                           struct GNUNET_CRYPTO_EddsaPublicKey *place_key,
-                           struct GNUNET_PeerIdentity *origin,
+                           const struct GNUNET_IDENTITY_Ego *ego,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_key,
+                           const struct GNUNET_PeerIdentity *origin,
                            uint32_t relay_count,
-                           struct GNUNET_PeerIdentity *relays,
-                           const char *method_name,
-                           const struct GNUNET_ENV_Environment *env,
-                           const void *data,
-                           size_t data_size,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_PSYC_Message *entry_msg,
                            struct GNUNET_SOCIAL_Slicer *slicer,
                            GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
                            GNUNET_SOCIAL_EntryDecisionCallback 
entry_decision_cb,
@@ -558,10 +637,7 @@
 GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_CONFIGURATION_Handle 
*cfg,
                                    struct GNUNET_IDENTITY_Ego *ego,
                                    char *gns_name,
-                                   const char *method_name,
-                                   const struct GNUNET_ENV_Environment *env,
-                                   const void *data,
-                                   size_t data_size,
+                                   const struct GNUNET_PSYC_Message *join_msg,
                                    struct GNUNET_SOCIAL_Slicer *slicer,
                                    GNUNET_SOCIAL_GuestEnterCallback 
local_enter_cb,
                                    GNUNET_SOCIAL_EntryDecisionCallback 
entry_decision_cb,
@@ -612,9 +688,20 @@
 
 
 /**
+ * Resume talking to the host of the place.
+ *
+ * @param tr
+ *        Talk request to resume.
+ */
+void
+GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr);
+
+
+/**
  * Cancel talking to the host of the place.
  *
- * @param tr Talk request to cancel.
+ * @param tr
+ *        Talk request to cancel.
  */
 void
 GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr);
@@ -625,11 +712,21 @@
  *
  * Notifies the owner of the place about leaving, and destroys the place 
handle.
  *
- * @param place Place to leave permanently.
- * @param keep_active Keep place active after last application disconnected.
+ * @param place
+ *        Place to leave permanently.
+ * @param keep_active
+ *        Keep place active after last application disconnected.
+ * @param leave_cb
+ *        Function called after the guest left the place
+ *        and disconnected from the social service.
+ * @param leave_cls
+ *        Closure for @a leave_cb.
  */
 void
-GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *guest, int keep_active);
+GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *guest,
+                           int keep_active,
+                           GNUNET_ContinuationCallback leave_cb,
+                           void *leave_cls);
 
 
 /**

Modified: gnunet/src/multicast/gnunet-service-multicast.c
===================================================================
--- gnunet/src/multicast/gnunet-service-multicast.c     2014-07-23 16:19:46 UTC 
(rev 34020)
+++ gnunet/src/multicast/gnunet-service-multicast.c     2014-07-23 16:19:49 UTC 
(rev 34021)
@@ -423,6 +423,7 @@
   {
     orig = GNUNET_new (struct Origin);
     orig->priv_key = msg->group_key;
+    orig->max_fragment_id = GNUNET_ntohll (msg->max_fragment_id);
     grp = &orig->grp;
     grp->is_origin = GNUNET_YES;
     grp->pub_key = pub_key;
@@ -482,6 +483,7 @@
     mem->priv_key = msg->member_key;
     mem->pub_key = mem_pub_key;
     mem->pub_key_hash = mem_pub_key_hash;
+    mem->max_fragment_id = 0; // FIXME
 
     grp = &mem->grp;
     grp->is_origin = GNUNET_NO;
@@ -663,7 +665,7 @@
   struct GNUNET_MULTICAST_MessageHeader *
     msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
 
-  msg->fragment_id = GNUNET_htonll (orig->max_fragment_id++);
+  msg->fragment_id = GNUNET_htonll (++orig->max_fragment_id);
   msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
                              - sizeof (msg->header)
                              - sizeof (msg->hop_counter)
@@ -699,8 +701,7 @@
   struct GNUNET_MULTICAST_RequestHeader *
     req = (struct GNUNET_MULTICAST_RequestHeader *) m;
 
-  req->fragment_id = GNUNET_ntohll (mem->max_fragment_id++);
-
+  req->fragment_id = GNUNET_ntohll (++mem->max_fragment_id);
   req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
                              - sizeof (req->header)
                              - sizeof (req->member_key)

Modified: gnunet/src/multicast/multicast_api.c
===================================================================
--- gnunet/src/multicast/multicast_api.c        2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/multicast/multicast_api.c        2014-07-23 16:19:49 UTC (rev 
34021)
@@ -88,6 +88,16 @@
   void *cb_cls;
 
   /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
    * Are we currently transmitting a message?
    */
   uint8_t in_transmit;
@@ -96,6 +106,12 @@
    * Is this the origin or a member?
    */
   uint8_t is_origin;
+
+  /**
+   * Is this channel in the process of disconnecting from the service?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
 };
 
 
@@ -320,8 +336,9 @@
     mem->join_dcsn_cb (grp->cb_cls, is_admitted, &hdcsn->peer,
                        relay_count, relays, join_resp);
 
-  if (GNUNET_YES != is_admitted)
-    GNUNET_MULTICAST_member_part (mem);
+  // FIXME:
+  //if (GNUNET_YES != is_admitted)
+  //  GNUNET_MULTICAST_member_part (mem);
 }
 
 
@@ -371,6 +388,33 @@
 };
 
 
+static void
+group_cleanup (struct GNUNET_MULTICAST_Group *grp)
+{
+  GNUNET_free (grp->connect_msg);
+  if (NULL != grp->disconnect_cb)
+    grp->disconnect_cb (grp->disconnect_cls);
+}
+
+
+static void
+origin_cleanup (void *cls)
+{
+  struct GNUNET_MULTICAST_Origin *orig = cls;
+  group_cleanup (&orig->grp);
+  GNUNET_free (orig);
+}
+
+
+static void
+member_cleanup (void *cls)
+{
+  struct GNUNET_MULTICAST_Member *mem = cls;
+  group_cleanup (&mem->grp);
+  GNUNET_free (mem);
+}
+
+
 /**
  * Function to call with the decision made for a join request.
  *
@@ -565,10 +609,18 @@
  * @param origin Multicast group to stop.
  */
 void
-GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig)
+GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig,
+                              GNUNET_ContinuationCallback stop_cb,
+                              void *stop_cls)
 {
-  GNUNET_CLIENT_MANAGER_disconnect (orig->grp.client, GNUNET_YES);
-  GNUNET_free (orig);
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+
+  grp->is_disconnecting = GNUNET_YES;
+  grp->disconnect_cb = stop_cb;
+  grp->disconnect_cls = stop_cls;
+
+  GNUNET_CLIENT_MANAGER_disconnect (orig->grp.client, GNUNET_YES,
+                                    &origin_cleanup, orig);
 }
 
 
@@ -774,10 +826,18 @@
  * @param member Membership handle.
  */
 void
-GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem)
+GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem,
+                              GNUNET_ContinuationCallback part_cb,
+                              void *part_cls)
 {
-  GNUNET_CLIENT_MANAGER_disconnect (mem->grp.client, GNUNET_YES);
-  GNUNET_free (mem);
+  struct GNUNET_MULTICAST_Group *grp = &mem->grp;
+
+  grp->is_disconnecting = GNUNET_YES;
+  grp->disconnect_cb = part_cb;
+  grp->disconnect_cls = part_cls;
+
+  GNUNET_CLIENT_MANAGER_disconnect (mem->grp.client, GNUNET_YES,
+                                    &member_cleanup, mem);
 }
 
 

Modified: gnunet/src/psyc/gnunet-service-psyc.c
===================================================================
--- gnunet/src/psyc/gnunet-service-psyc.c       2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/psyc/gnunet-service-psyc.c       2014-07-23 16:19:49 UTC (rev 
34021)
@@ -168,7 +168,7 @@
    * Is the message queued for delivery to the client?
    * i.e. added to the recv_msgs queue
    */
-  uint8_t queued;
+  uint8_t is_queued;
 };
 
 
@@ -382,7 +382,7 @@
   /**
    * Join request to be transmitted to the master on join.
    */
-  struct GNUNET_MessageHeader *join_req;
+  struct GNUNET_PSYC_Message *join_msg;
 
   /**
    * Join decision received from multicast.
@@ -435,7 +435,7 @@
   struct Channel *chn = &mst->chn;
 
   if (NULL != mst->origin)
-    GNUNET_MULTICAST_origin_stop (mst->origin);
+    GNUNET_MULTICAST_origin_stop (mst->origin, NULL, NULL); // FIXME
   GNUNET_CONTAINER_multihashmap_destroy (mst->join_reqs);
   GNUNET_CONTAINER_multihashmap_remove (masters, &chn->pub_key_hash, chn);
 }
@@ -462,12 +462,21 @@
   }
   GNUNET_CONTAINER_multihashmap_remove (slaves, &chn->pub_key_hash, slv);
 
-  if (NULL != slv->join_req)
-    GNUNET_free (slv->join_req);
+  if (NULL != slv->join_msg)
+  {
+    GNUNET_free (slv->join_msg);
+    slv->join_msg = NULL;
+  }
   if (NULL != slv->relays)
+  {
     GNUNET_free (slv->relays);
+    slv->relays = NULL;
+  }
   if (NULL != slv->member)
-    GNUNET_MULTICAST_member_part (slv->member);
+  {
+    GNUNET_MULTICAST_member_part (slv->member, NULL, NULL); // FIXME
+    slv->member = NULL;
+  }
   GNUNET_CONTAINER_multihashmap_remove (slaves, &chn->pub_key_hash, chn);
 }
 
@@ -482,7 +491,10 @@
   GNUNET_CONTAINER_multihashmap_remove_all (recv_cache, &chn->pub_key_hash);
 
   if (NULL != chn->store_op)
+  {
     GNUNET_PSYCSTORE_operation_cancel (chn->store_op);
+    chn->store_op = NULL;
+  }
 
   (GNUNET_YES == chn->is_master)
     ? cleanup_master ((struct Master *) chn)
@@ -574,7 +586,7 @@
   struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
   struct Channel *chn;
   struct GNUNET_MULTICAST_JoinHandle *jh;
-  struct MasterJoinRequest *master_join_req;
+  struct GNUNET_PSYC_JoinRequestMessage *join_msg;
 };
 
 
@@ -594,14 +606,14 @@
                         &slave_key_hash);
     GNUNET_CONTAINER_multihashmap_put (mst->join_reqs, &slave_key_hash, 
jcls->jh,
                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
-    client_send_msg (jcls->chn, &jcls->master_join_req->header);
+    client_send_msg (jcls->chn, &jcls->join_msg->header);
   }
   else
   {
     // FIXME: add relays
     GNUNET_MULTICAST_join_decision (jcls->jh, result, 0, NULL, NULL);
   }
-  GNUNET_free (jcls->master_join_req);
+  GNUNET_free (jcls->join_msg);
   GNUNET_free (jcls);
 }
 
@@ -633,7 +645,8 @@
     }
   }
 
-  struct MasterJoinRequest *req = GNUNET_malloc (sizeof (*req) + 
join_msg_size);
+  struct GNUNET_PSYC_JoinRequestMessage *
+    req = GNUNET_malloc (sizeof (*req) + join_msg_size);
   req->header.size = htons (sizeof (*req) + join_msg_size);
   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST);
   req->slave_key = *slave_key;
@@ -644,7 +657,7 @@
   jcls->slave_key = *slave_key;
   jcls->chn = chn;
   jcls->jh = jh;
-  jcls->master_join_req = req;
+  jcls->join_msg = req;
 
   GNUNET_PSYCSTORE_membership_test (store, &chn->pub_key, slave_key,
                                     chn->max_message_id, 0,
@@ -780,6 +793,7 @@
   pmsg->header.size = htons (psize);
   pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
   pmsg->message_id = mmsg->message_id;
+  pmsg->fragment_offset = mmsg->fragment_offset;
 
   memcpy (&pmsg[1], &mmsg[1], size - sizeof (*mmsg));
   client_send_msg (chn, &pmsg->header);
@@ -810,6 +824,7 @@
   pmsg->header.size = htons (psize);
   pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
   pmsg->message_id = req->request_id;
+  pmsg->fragment_offset = req->fragment_offset;
   pmsg->flags = htonl (GNUNET_PSYC_MESSAGE_REQUEST);
 
   memcpy (&pmsg[1], &req[1], size - sizeof (*req));
@@ -870,11 +885,12 @@
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "%p Adding message fragment to cache. "
-                "message_id: %" PRIu64 ", fragment_id: %" PRIu64 ", "
-                "header_size: %" PRIu64 " + %u).\n",
+                "message_id: %" PRIu64 ", fragment_id: %" PRIu64 "\n",
                 chn, GNUNET_ntohll (mmsg->message_id),
-                GNUNET_ntohll (mmsg->fragment_id),
-                fragq->header_size, size);
+                GNUNET_ntohll (mmsg->fragment_id));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p header_size: %" PRIu64 " + %u\n",
+                chn, fragq->header_size, size);
     cache_entry = GNUNET_new (struct RecvCacheEntry);
     cache_entry->ref_count = 1;
     cache_entry->mmsg = GNUNET_malloc (size);
@@ -955,11 +971,11 @@
   case MSG_FRAG_STATE_DATA:
   case MSG_FRAG_STATE_END:
   case MSG_FRAG_STATE_CANCEL:
-    if (GNUNET_NO == fragq->queued)
+    if (GNUNET_NO == fragq->is_queued)
     {
       GNUNET_CONTAINER_heap_insert (chn->recv_msgs, NULL,
                                     GNUNET_ntohll (mmsg->message_id));
-      fragq->queued = GNUNET_YES;
+      fragq->is_queued = GNUNET_YES;
     }
   }
 
@@ -1034,7 +1050,7 @@
   if (MSG_FRAG_STATE_END <= fragq->state)
   {
     struct GNUNET_HashCode msg_id_hash;
-    hash_key_from_nll (&msg_id_hash, msg_id);
+    hash_key_from_hll (&msg_id_hash, msg_id);
 
     GNUNET_CONTAINER_multihashmap_remove (chn->recv_frags, &msg_id_hash, 
fragq);
     GNUNET_CONTAINER_heap_destroy (fragq->fragments);
@@ -1042,7 +1058,7 @@
   }
   else
   {
-    fragq->queued = GNUNET_NO;
+    fragq->is_queued = GNUNET_NO;
   }
 }
 
@@ -1331,13 +1347,18 @@
       = GNUNET_MULTICAST_member_join (cfg, &chn->pub_key, &slv->priv_key,
                                       &slv->origin,
                                       slv->relay_count, slv->relays,
-                                      slv->join_req,
+                                      &slv->join_msg->header,
                                       &mcast_recv_join_request,
                                       &mcast_recv_join_decision,
                                       &mcast_recv_membership_test,
                                       &mcast_recv_replay_fragment,
                                       &mcast_recv_replay_message,
                                       &mcast_recv_message, chn);
+    if (NULL != slv->join_msg)
+    {
+      GNUNET_free (slv->join_msg);
+      slv->join_msg = NULL;
+    }
   }
   else
   {
@@ -1435,6 +1456,7 @@
 {
   const struct SlaveJoinRequest *req
     = (const struct SlaveJoinRequest *) msg;
+  uint16_t req_size = ntohs (req->header.size);
 
   struct GNUNET_CRYPTO_EcdsaPublicKey slv_pub_key;
   struct GNUNET_HashCode pub_key_hash, slv_pub_key_hash;
@@ -1460,15 +1482,32 @@
     slv->pub_key_hash = slv_pub_key_hash;
     slv->origin = req->origin;
     slv->relay_count = ntohl (req->relay_count);
+
+    const struct GNUNET_PeerIdentity *
+      relays = (const struct GNUNET_PeerIdentity *) &req[1];
+    uint16_t relay_size = slv->relay_count * sizeof (*relays);
+    uint16_t join_msg_size = 0;
+
+    if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
+        <= req_size)
+    {
+      join_msg_size = ntohs (slv->join_msg->header.size);
+      slv->join_msg = GNUNET_malloc (join_msg_size);
+      memcpy (slv->join_msg, ((char *) &req[1]) + relay_size, join_msg_size);
+    }
+    if (sizeof (*req) + relay_size + join_msg_size != req_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%u + %u + %u != %u\n",
+                  sizeof (*req), relay_size, join_msg_size, req_size);
+      GNUNET_break (0);
+      GNUNET_SERVER_client_disconnect (client);
+      return;
+    }
     if (0 < slv->relay_count)
     {
-      const struct GNUNET_PeerIdentity *relays
-        = (const struct GNUNET_PeerIdentity *) &req[1];
-      slv->relays
-        = GNUNET_malloc (slv->relay_count * sizeof (struct 
GNUNET_PeerIdentity));
-      uint32_t i;
-      for (i = 0; i < slv->relay_count; i++)
-        memcpy (&slv->relays[i], &relays[i], sizeof (*relays));
+      slv->relays = GNUNET_malloc (relay_size);
+      memcpy (slv->relays, &req[1], relay_size);
     }
 
     chn = &slv->chn;
@@ -1510,14 +1549,18 @@
         = GNUNET_MULTICAST_member_join (cfg, &chn->pub_key, &slv->priv_key,
                                         &slv->origin,
                                         slv->relay_count, slv->relays,
-                                        slv->join_req,
+                                        &slv->join_msg->header,
                                         &mcast_recv_join_request,
                                         &mcast_recv_join_decision,
                                         &mcast_recv_membership_test,
                                         &mcast_recv_replay_fragment,
                                         &mcast_recv_replay_message,
                                         &mcast_recv_message, chn);
-
+      if (NULL != slv->join_msg)
+      {
+        GNUNET_free (slv->join_msg);
+        slv->join_msg = NULL;
+      }
     }
     else if (NULL != slv->join_dcsn)
     {
@@ -1549,13 +1592,14 @@
 
 
 /**
- * Iterator callback for responding to join requests of a slave.
+ * Iterator callback for sending join decisions to multicast.
  */
 static int
 mcast_send_join_decision (void *cls, const struct GNUNET_HashCode 
*pub_key_hash,
-                          void *jh)
+                          void *value)
 {
   struct JoinDecisionClosure *jcls = cls;
+  struct GNUNET_MULTICAST_JoinHandle *jh = value;
   // FIXME: add relays
   GNUNET_MULTICAST_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
   return GNUNET_YES;
@@ -1579,8 +1623,7 @@
   struct JoinDecisionClosure jcls;
   jcls.is_admitted = ntohl (dcsn->is_admitted);
   jcls.msg
-    = (sizeof (*dcsn) + sizeof (struct GNUNET_PSYC_MessageHeader)
-       <= ntohs (msg->size))
+    = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
     ? (struct GNUNET_MessageHeader *) &dcsn[1]
     : NULL;
 
@@ -1901,6 +1944,9 @@
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message with first part type %u and last part type 
%u.\n",
+              chn, first_ptype, last_ptype);
 
   queue_message (chn, client, size - sizeof (*msg), &msg[1],
                  first_ptype, last_ptype);

Modified: gnunet/src/psyc/psyc.h
===================================================================
--- gnunet/src/psyc/psyc.h      2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/psyc/psyc.h      2014-07-23 16:19:49 UTC (rev 34021)
@@ -229,22 +229,6 @@
    */
 };
 
-
-struct MasterJoinRequest
-{
-  /**
-   * Type: GNUNET_MESSAGE_TYPE_PSYC_MASTER_JOIN_REQUEST
-   */
-  struct GNUNET_MessageHeader header;
-  /**
-   * Public key of the joining slave.
-   */
-  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
-
-  /* Followed by struct GNUNET_MessageHeader join_request */
-};
-
-
 GNUNET_NETWORK_STRUCT_END
 
 #endif

Modified: gnunet/src/psyc/psyc_api.c
===================================================================
--- gnunet/src/psyc/psyc_api.c  2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/psyc/psyc_api.c  2014-07-23 16:19:49 UTC (rev 34021)
@@ -74,6 +74,16 @@
   struct GNUNET_MessageHeader *connect_msg;
 
   /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
    * Are we polling for incoming messages right now?
    */
   uint8_t in_receive;
@@ -82,6 +92,12 @@
    * Is this a master or slave channel?
    */
   uint8_t is_master;
+
+  /**
+   * Is this channel in the process of disconnecting from the service?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
 };
 
 
@@ -232,19 +248,26 @@
   struct GNUNET_PSYC_Master *
     mst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
                                                    sizeof (struct 
GNUNET_PSYC_Channel));
+  if (NULL == mst->join_req_cb)
+    return;
 
-  const struct MasterJoinRequest *req = (const struct MasterJoinRequest *) msg;
+  const struct GNUNET_PSYC_JoinRequestMessage *
+    req = (const struct GNUNET_PSYC_JoinRequestMessage *) msg;
+  const struct GNUNET_PSYC_Message *join_msg = NULL;
+  if (sizeof (*req) + sizeof (*join_msg) <= ntohs (req->header.size))
+  {
+    join_msg = (struct GNUNET_PSYC_Message *) &req[1];
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Received join_msg of type %u and size %u.\n",
+         ntohs (join_msg->header.type), ntohs (join_msg->header.size));
+  }
 
-  struct GNUNET_PSYC_MessageHeader *pmsg = NULL;
-  if (ntohs (req->header.size) <= sizeof (*req) + sizeof (*pmsg))
-    pmsg = (struct GNUNET_PSYC_MessageHeader *) &req[1];
-
   struct GNUNET_PSYC_JoinHandle *jh = GNUNET_malloc (sizeof (*jh));
   jh->mst = mst;
   jh->slave_key = req->slave_key;
 
   if (NULL != mst->join_req_cb)
-    mst->join_req_cb (mst->cb_cls, &req->slave_key, pmsg, jh);
+    mst->join_req_cb (mst->cb_cls, req, &req->slave_key, join_msg, jh);
 }
 
 
@@ -273,13 +296,12 @@
   const struct GNUNET_PSYC_JoinDecisionMessage *
     dcsn = (const struct GNUNET_PSYC_JoinDecisionMessage *) msg;
 
-  struct GNUNET_PSYC_MessageHeader *pmsg = NULL;
+  struct GNUNET_PSYC_Message *pmsg = NULL;
   if (ntohs (dcsn->header.size) <= sizeof (*dcsn) + sizeof (*pmsg))
-    pmsg = (struct GNUNET_PSYC_MessageHeader *) &dcsn[1];
+    pmsg = (struct GNUNET_PSYC_Message *) &dcsn[1];
 
-  struct GNUNET_PSYC_JoinHandle *jh = GNUNET_malloc (sizeof (*jh));
   if (NULL != slv->join_dcsn_cb)
-    slv->join_dcsn_cb (slv->cb_cls, ntohl (dcsn->is_admitted), pmsg);
+    slv->join_dcsn_cb (slv->cb_cls, dcsn, ntohl (dcsn->is_admitted), pmsg);
 }
 
 
@@ -299,7 +321,7 @@
 
   { &master_recv_join_request, NULL,
     GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
-    sizeof (struct MasterJoinRequest), GNUNET_YES },
+    sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES },
 
   { &channel_recv_disconnect, NULL, 0, 0, GNUNET_NO },
 
@@ -331,6 +353,35 @@
 };
 
 
+static void
+channel_cleanup (struct GNUNET_PSYC_Channel *chn)
+{
+  GNUNET_PSYC_transmit_destroy (chn->tmit);
+  GNUNET_PSYC_receive_destroy (chn->recv);
+  GNUNET_free (chn->connect_msg);
+  if (NULL != chn->disconnect_cb)
+    chn->disconnect_cb (chn->disconnect_cls);
+}
+
+
+static void
+master_cleanup (void *cls)
+{
+  struct GNUNET_PSYC_Master *mst = cls;
+  channel_cleanup (&mst->chn);
+  GNUNET_free (mst);
+}
+
+
+static void
+slave_cleanup (void *cls)
+{
+  struct GNUNET_PSYC_Slave *slv = cls;
+  channel_cleanup (&slv->chn);
+  GNUNET_free (slv);
+}
+
+
 /**
  * Start a PSYC master channel.
  *
@@ -367,6 +418,7 @@
                           GNUNET_PSYC_MasterStartCallback start_cb,
                           GNUNET_PSYC_JoinRequestCallback join_request_cb,
                           GNUNET_PSYC_MessageCallback message_cb,
+                          GNUNET_PSYC_MessagePartCallback message_part_cb,
                           void *cls)
 {
   struct GNUNET_PSYC_Master *mst = GNUNET_malloc (sizeof (*mst));
@@ -390,7 +442,7 @@
   GNUNET_CLIENT_MANAGER_set_user_context_ (chn->client, mst, sizeof (*chn));
 
   chn->tmit = GNUNET_PSYC_transmit_create (chn->client);
-  chn->recv = GNUNET_PSYC_receive_create (message_cb, message_cb, cls);
+  chn->recv = GNUNET_PSYC_receive_create (message_cb, message_part_cb, cls);
 
   channel_send_connect_msg (chn);
   return mst;
@@ -404,10 +456,21 @@
  * @param keep_active  FIXME
  */
 void
-GNUNET_PSYC_master_stop (struct GNUNET_PSYC_Master *mst)
+GNUNET_PSYC_master_stop (struct GNUNET_PSYC_Master *mst,
+                         int keep_active,
+                         GNUNET_ContinuationCallback stop_cb,
+                         void *stop_cls)
 {
-  GNUNET_CLIENT_MANAGER_disconnect (mst->chn.client, GNUNET_YES);
-  GNUNET_free (mst);
+  struct GNUNET_PSYC_Channel *chn = &mst->chn;
+
+  /* FIXME: send msg to service */
+
+  chn->is_disconnecting = GNUNET_YES;
+  chn->disconnect_cb = stop_cb;
+  chn->disconnect_cls = stop_cls;
+
+  GNUNET_CLIENT_MANAGER_disconnect (mst->chn.client, GNUNET_YES,
+                                    &master_cleanup, mst);
 }
 
 
@@ -439,7 +502,7 @@
                            int is_admitted,
                            uint32_t relay_count,
                            const struct GNUNET_PeerIdentity *relays,
-                           const struct GNUNET_PSYC_MessageHeader *join_resp)
+                           const struct GNUNET_PSYC_Message *join_resp)
 {
   struct GNUNET_PSYC_Channel *chn = &jh->mst->chn;
   struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
@@ -461,6 +524,7 @@
     memcpy (&dcsn[1], join_resp, join_resp_size);
 
   GNUNET_CLIENT_MANAGER_transmit (chn->client, &dcsn->header);
+  GNUNET_free (jh);
   return GNUNET_OK;
 }
 
@@ -576,15 +640,19 @@
                         uint32_t relay_count,
                         const struct GNUNET_PeerIdentity *relays,
                         GNUNET_PSYC_MessageCallback message_cb,
+                        GNUNET_PSYC_MessagePartCallback message_part_cb,
                         GNUNET_PSYC_SlaveConnectCallback connect_cb,
                         GNUNET_PSYC_JoinDecisionCallback join_decision_cb,
                         void *cls,
-                        const struct GNUNET_MessageHeader *join_msg)
+                        const struct GNUNET_PSYC_Message *join_msg)
 {
   struct GNUNET_PSYC_Slave *slv = GNUNET_malloc (sizeof (*slv));
   struct GNUNET_PSYC_Channel *chn = &slv->chn;
+
+  uint16_t relay_size = relay_count * sizeof (*relays);
+  uint16_t join_msg_size = ntohs (join_msg->header.size);
   struct SlaveJoinRequest *req
-    = GNUNET_malloc (sizeof (*req) + relay_count * sizeof (*relays));
+    = GNUNET_malloc (sizeof (*req) + relay_size + join_msg_size);
   req->header.size = htons (sizeof (*req)
                             + relay_count * sizeof (*relays));
   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN);
@@ -592,8 +660,13 @@
   req->slave_key = *slave_key;
   req->origin = *origin;
   req->relay_count = htonl (relay_count);
-  memcpy (&req[1], relays, relay_count * sizeof (*relays));
 
+  if (0 < relay_size)
+    memcpy (&req[1], relays, relay_size);
+
+  if (0 < join_msg_size)
+    memcpy ((char *) &req[1] + relay_size, join_msg, join_msg_size);
+
   chn->connect_msg = (struct GNUNET_MessageHeader *) req;
   chn->cfg = cfg;
   chn->is_master = GNUNET_NO;
@@ -605,7 +678,7 @@
   chn->client = GNUNET_CLIENT_MANAGER_connect (cfg, "psyc", slave_handlers);
   GNUNET_CLIENT_MANAGER_set_user_context_ (chn->client, slv, sizeof (*chn));
 
-  chn->recv = GNUNET_PSYC_receive_create (message_cb, message_cb, cls);
+  chn->recv = GNUNET_PSYC_receive_create (message_cb, message_part_cb, cls);
   chn->tmit = GNUNET_PSYC_transmit_create (chn->client);
 
   channel_send_connect_msg (chn);
@@ -622,10 +695,21 @@
  * @param slave Slave handle.
  */
 void
-GNUNET_PSYC_slave_part (struct GNUNET_PSYC_Slave *slv)
+GNUNET_PSYC_slave_part (struct GNUNET_PSYC_Slave *slv,
+                        int keep_active,
+                        GNUNET_ContinuationCallback part_cb,
+                        void *part_cls)
 {
-  GNUNET_CLIENT_MANAGER_disconnect (slv->chn.client, GNUNET_YES);
-  GNUNET_free (slv);
+  struct GNUNET_PSYC_Channel *chn = &slv->chn;
+
+  /* FIXME: send msg to service */
+
+  chn->is_disconnecting = GNUNET_YES;
+  chn->disconnect_cb = part_cb;
+  chn->disconnect_cls = part_cls;
+
+  GNUNET_CLIENT_MANAGER_disconnect (slv->chn.client, GNUNET_YES,
+                                    &slave_cleanup, slv);
 }
 
 
@@ -796,6 +880,7 @@
                                 uint64_t start_message_id,
                                 uint64_t end_message_id,
                                 GNUNET_PSYC_MessageCallback message_cb,
+                                GNUNET_PSYC_MessagePartCallback 
message_part_cb,
                                 GNUNET_PSYC_FinishCallback finish_cb,
                                 void *cls)
 {

Modified: gnunet/src/psyc/psyc_util_lib.c
===================================================================
--- gnunet/src/psyc/psyc_util_lib.c     2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/psyc/psyc_util_lib.c     2014-07-23 16:19:49 UTC (rev 34021)
@@ -76,9 +76,13 @@
    *
    */
   const char *mod_value;
-  size_t mod_value_size;
 
   /**
+   * Number of bytes remaining to be transmitted from the current modifier 
value.
+   */
+  uint32_t mod_value_remaining;
+
+  /**
    * State of the current message being received from client.
    */
   enum GNUNET_PSYC_MessageState state;
@@ -104,14 +108,14 @@
 struct GNUNET_PSYC_ReceiveHandle
 {
   /**
-   * Message part callback.
+   * Message callback.
    */
   GNUNET_PSYC_MessageCallback message_cb;
 
   /**
-   * Message part callback for historic message.
+   * Message part callback.
    */
-  GNUNET_PSYC_MessageCallback hist_message_cb;
+  GNUNET_PSYC_MessagePartCallback message_part_cb;
 
   /**
    * Closure for the callbacks.
@@ -149,6 +153,7 @@
   uint32_t mod_value_size;
 };
 
+
 /**** Messages ****/
 
 
@@ -167,7 +172,7 @@
  * @return Message header with size information,
  *         followed by the message parts.
  */
-struct GNUNET_MessageHeader *
+struct GNUNET_PSYC_Message *
 GNUNET_PSYC_message_create (const char *method_name,
                             const struct GNUNET_ENV_Environment *env,
                             const void *data,
@@ -188,7 +193,7 @@
     }
   }
 
-  struct GNUNET_MessageHeader *msg;
+  struct GNUNET_PSYC_Message *msg;
   uint16_t method_name_size = strlen (method_name) + 1;
   if (method_name_size == 1)
     return NULL;
@@ -199,12 +204,13 @@
     + ((0 < data_size) ? sizeof (*pmsg) + data_size : 0)/* data */
     + sizeof (*pmsg);                                  /* end of message */
   msg = GNUNET_malloc (msg_size);
-  msg->size = htons (msg_size);
-  msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+  msg->header.size = htons (msg_size);
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE); /* FIXME */
 
   pmeth = (struct GNUNET_PSYC_MessageMethod *) &msg[1];
+  pmeth->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD);
   pmeth->header.size = htons (sizeof (*pmeth) + method_name_size);
-  memcpy (pmeth, method_name, method_name_size);
+  memcpy (&pmeth[1], method_name, method_name_size);
 
   uint16_t p = sizeof (*msg) + sizeof (*pmeth) + method_name_size;
   if (NULL != env)
@@ -215,7 +221,7 @@
       uint16_t mod_name_size = strlen (mod->name) + 1;
       pmod = (struct GNUNET_PSYC_MessageModifier *) ((char *) msg + p);
       pmod->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER);
-      pmod->header.size = sizeof (*pmod) + mod_name_size + 1 + mod->value_size;
+      pmod->header.size = sizeof (*pmod) + mod_name_size + mod->value_size;
       p += pmod->header.size;
       pmod->header.size = htons (pmod->header.size);
 
@@ -241,6 +247,7 @@
   pmsg->size = htons (sizeof (*pmsg));
   pmsg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END);
 
+  GNUNET_assert (p + sizeof (*pmsg) == msg_size);
   return msg;
 }
 
@@ -276,8 +283,8 @@
     uint16_t name_size = ntohs (mod->name_size);
     char oper = ' ' < mod->oper ? mod->oper : ' ';
     GNUNET_log (kind, "\t%c%.*s\t%.*s\n", oper, name_size, &mod[1],
-                size - sizeof (*mod) - name_size - 1,
-                ((char *) &mod[1]) + name_size + 1);
+                size - sizeof (*mod) - name_size,
+                ((char *) &mod[1]) + name_size);
     break;
   }
   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
@@ -331,7 +338,7 @@
   uint16_t size = (NULL != msg) ? ntohs (msg->size) : 0;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Queueing message of type %u and size %u (end: %u)).\n",
+       "Queueing message part of type %u and size %u (end: %u)).\n",
        ntohs (msg->type), size, end);
 
   if (NULL != tmit->msg)
@@ -396,6 +403,9 @@
   msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA);
 
   int notify_ret = tmit->notify_data (tmit->notify_data_cls, &data_size, 
&msg[1]);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "transmit_data (ret: %d, size: %u): %.*s\n",
+       notify_ret, data_size, data_size, &msg[1]);
   switch (notify_ret)
   {
   case GNUNET_NO:
@@ -463,9 +473,15 @@
     msg->size = sizeof (struct GNUNET_PSYC_MessageModifier);
     notify_ret = tmit->notify_mod (tmit->notify_mod_cls, &data_size, &mod[1],
                                    &mod->oper, &mod->value_size);
-    mod->name_size = strnlen ((char *) &mod[1], data_size);
+
+    mod->name_size = strnlen ((char *) &mod[1], data_size) + 1;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "transmit_mod (ret: %d, size: %u + %u): %.*s\n",
+         notify_ret, mod->name_size, mod->value_size, data_size, &mod[1]);
     if (mod->name_size < data_size)
     {
+      tmit->mod_value_remaining
+        = mod->value_size - (data_size - mod->name_size);
       mod->value_size = htonl (mod->value_size);
       mod->name_size = htons (mod->name_size);
     }
@@ -483,6 +499,10 @@
     msg->size = sizeof (struct GNUNET_MessageHeader);
     notify_ret = tmit->notify_mod (tmit->notify_mod_cls,
                                    &data_size, &msg[1], NULL, NULL);
+    tmit->mod_value_remaining -= data_size;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "transmit_mod (ret: %d, size: %u): %.*s\n",
+         notify_ret, data_size, data_size, &msg[1]);
     break;
   }
   default:
@@ -497,26 +517,19 @@
       tmit->paused = GNUNET_YES;
       return;
     }
-    tmit->state = GNUNET_PSYC_MESSAGE_STATE_MOD_CONT;
+    tmit->state
+      = (0 == tmit->mod_value_remaining)
+      ? GNUNET_PSYC_MESSAGE_STATE_MODIFIER
+      : GNUNET_PSYC_MESSAGE_STATE_MOD_CONT;
     break;
 
-  case GNUNET_YES:
-    if (0 == data_size)
-    {
-      /* End of modifiers. */
-      tmit->state = GNUNET_PSYC_MESSAGE_STATE_DATA;
-      if (0 == tmit->acks_pending)
-        transmit_data (tmit);
-
-      return;
-    }
-    tmit->state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
+  case GNUNET_YES: /* End of modifiers. */
+    GNUNET_assert (0 == tmit->mod_value_remaining);
     break;
 
   default:
     LOG (GNUNET_ERROR_TYPE_ERROR,
-         "TransmitNotifyModifier callback returned error "
-         "when requesting a modifier.\n");
+         "TransmitNotifyModifier callback returned with error.\n");
 
     tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
     msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
@@ -533,7 +546,16 @@
     transmit_queue_insert (tmit, msg, GNUNET_NO);
   }
 
-  transmit_mod (tmit);
+  if (GNUNET_YES == notify_ret)
+  {
+    tmit->state = GNUNET_PSYC_MESSAGE_STATE_DATA;
+    if (0 == tmit->acks_pending)
+      transmit_data (tmit);
+  }
+  else
+  {
+    transmit_mod (tmit);
+  }
 }
 
 
@@ -547,9 +569,10 @@
   size_t value_size = 0;
   const char *value = NULL;
 
-  if (NULL != oper && NULL != tmit->mod)
+  if (NULL != oper)
   { /* New modifier */
-    tmit->mod = tmit->mod->next;
+    if (NULL != tmit->mod)
+      tmit->mod = tmit->mod->next;
     if (NULL == tmit->mod)
     { /* No more modifiers, continue with data */
       *data_size = 0;
@@ -559,30 +582,29 @@
     GNUNET_assert (tmit->mod->value_size < UINT32_MAX);
     *full_value_size = tmit->mod->value_size;
     *oper = tmit->mod->oper;
-    name_size = strlen (tmit->mod->name);
+    name_size = strlen (tmit->mod->name) + 1;
 
-    if (name_size + 1 + tmit->mod->value_size <= *data_size)
+    if (name_size + tmit->mod->value_size <= *data_size)
     {
-      *data_size = name_size + 1 + tmit->mod->value_size;
+      *data_size = name_size + tmit->mod->value_size;
     }
     else
     {
-      tmit->mod_value_size = tmit->mod->value_size;
-      value_size = *data_size - name_size - 1;
-      tmit->mod_value_size -= value_size;
+      value_size = *data_size - name_size;
       tmit->mod_value = tmit->mod->value + value_size;
     }
 
     memcpy (data, tmit->mod->name, name_size);
-    ((char *)data)[name_size] = '\0';
-    memcpy ((char *)data + name_size + 1, tmit->mod->value, value_size);
+    memcpy ((char *)data + name_size, tmit->mod->value, value_size);
+    return GNUNET_NO;
   }
-  else if (NULL != tmit->mod_value && 0 < tmit->mod_value_size)
+  else
   { /* Modifier continuation */
+    GNUNET_assert (NULL != tmit->mod_value && 0 < tmit->mod_value_remaining);
     value = tmit->mod_value;
-    if (tmit->mod_value_size <= *data_size)
+    if (tmit->mod_value_remaining <= *data_size)
     {
-      value_size = tmit->mod_value_size;
+      value_size = tmit->mod_value_remaining;
       tmit->mod_value = NULL;
     }
     else
@@ -590,7 +612,6 @@
       value_size = *data_size;
       tmit->mod_value += value_size;
     }
-    tmit->mod_value_size -= value_size;
 
     if (*data_size < value_size)
     {
@@ -603,9 +624,8 @@
 
     *data_size = value_size;
     memcpy (data, value, value_size);
+    return (tmit->mod_value = NULL) ? GNUNET_YES : GNUNET_NO;
   }
-
-  return 0 == tmit->mod_value_size ? GNUNET_YES : GNUNET_NO;
 }
 
 
@@ -663,10 +683,16 @@
   {
     tmit->notify_mod = &transmit_notify_env;
     tmit->notify_mod_cls = tmit;
-    tmit->mod
-      = (NULL != env)
-      ? GNUNET_ENV_environment_head (env)
-      : NULL;
+    if (NULL != env)
+    {
+      struct GNUNET_ENV_Modifier mod = {};
+      mod.next = GNUNET_ENV_environment_head (env);
+      tmit->mod = &mod;
+    }
+    else
+    {
+      tmit->mod = NULL;
+    }
   }
 
   transmit_mod (tmit);
@@ -762,12 +788,12 @@
  */
 struct GNUNET_PSYC_ReceiveHandle *
 GNUNET_PSYC_receive_create (GNUNET_PSYC_MessageCallback message_cb,
-                            GNUNET_PSYC_MessageCallback hist_message_cb,
+                            GNUNET_PSYC_MessagePartCallback message_part_cb,
                             void *cb_cls)
 {
   struct GNUNET_PSYC_ReceiveHandle *recv = GNUNET_malloc (sizeof (*recv));
   recv->message_cb = message_cb;
-  recv->hist_message_cb = hist_message_cb;
+  recv->message_part_cb = message_part_cb;
   recv->cb_cls = cb_cls;
   return recv;
 }
@@ -800,13 +826,11 @@
 static void
 recv_error (struct GNUNET_PSYC_ReceiveHandle *recv)
 {
-  GNUNET_PSYC_MessageCallback message_cb
-    = recv->flags & GNUNET_PSYC_MESSAGE_HISTORIC
-    ? recv->hist_message_cb
-    : recv->message_cb;
+  if (NULL != recv->message_part_cb)
+    recv->message_part_cb (recv->cb_cls, recv->message_id, 0, recv->flags, 
NULL);
 
-  if (NULL != message_cb)
-    message_cb (recv->cb_cls, recv->message_id, recv->flags, NULL);
+  if (NULL != recv->message_cb)
+    recv->message_cb (recv->cb_cls, recv->message_id, recv->flags, NULL);
 
   GNUNET_PSYC_receive_reset (recv);
 }
@@ -827,6 +851,7 @@
 {
   uint16_t size = ntohs (msg->header.size);
   uint32_t flags = ntohl (msg->flags);
+  uint64_t message_id;
 
   GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG,
                            (struct GNUNET_MessageHeader *) msg);
@@ -858,6 +883,7 @@
     recv_error (recv);
     return GNUNET_SYSERR;
   }
+  message_id = recv->message_id;
 
   uint16_t pos = 0, psize = 0, ptype, size_eq, size_min;
 
@@ -964,10 +990,10 @@
 
       uint16_t name_size = ntohs (mod->name_size);
       recv->mod_value_size_expected = ntohl (mod->value_size);
-      recv->mod_value_size = psize - sizeof (*mod) - name_size - 1;
+      recv->mod_value_size = psize - sizeof (*mod) - name_size;
 
-      if (psize < sizeof (*mod) + name_size + 1
-          || '\0' != *((char *) &mod[1] + name_size)
+      if (psize < sizeof (*mod) + name_size
+          || '\0' != *((char *) &mod[1] + name_size - 1)
           || recv->mod_value_size_expected < recv->mod_value_size)
       {
         LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping malformed modifier.\n");
@@ -1018,14 +1044,10 @@
     }
     }
 
-    GNUNET_PSYC_MessageCallback message_cb
-      = recv->flags & GNUNET_PSYC_MESSAGE_HISTORIC
-      ? recv->hist_message_cb
-      : recv->message_cb;
+    if (NULL != recv->message_part_cb)
+      recv->message_part_cb (recv->cb_cls, recv->message_id, 0, // FIXME: 
data_offset
+                             recv->flags, pmsg);
 
-    if (NULL != message_cb)
-      message_cb (recv->cb_cls, recv->message_id, recv->flags, pmsg);
-
     switch (ptype)
     {
     case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
@@ -1034,6 +1056,9 @@
       break;
     }
   }
+
+  if (NULL != recv->message_cb)
+    recv->message_cb (recv->cb_cls, message_id, flags, msg);
   return GNUNET_OK;
 }
 
@@ -1063,6 +1088,7 @@
   for (pos = 0; pos < data_size; pos += psize, parts++)
   {
     pmsg = (const struct GNUNET_MessageHeader *) (data + pos);
+    GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
     psize = ntohs (pmsg->size);
     ptype = ntohs (pmsg->type);
     if (0 == parts && NULL != first_ptype)
@@ -1084,3 +1110,111 @@
   }
   return parts;
 }
+
+
+struct ParseMessageClosure
+{
+  struct GNUNET_ENV_Environment *env;
+  const char **method_name;
+  const void **data;
+  uint16_t *data_size;
+  enum GNUNET_PSYC_MessageState msg_state;
+};
+
+
+static void
+parse_message_part_cb (void *cls, uint64_t message_id, uint64_t data_offset,
+                       uint32_t flags, const struct GNUNET_MessageHeader *msg)
+{
+  struct ParseMessageClosure *pmc = cls;
+  if (NULL == msg)
+  {
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+    return;
+  }
+
+  switch (ntohs (msg->type))
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+  {
+    struct GNUNET_PSYC_MessageMethod *
+      pmeth = (struct GNUNET_PSYC_MessageMethod *) msg;
+    *pmc->method_name = (const char *) &pmeth[1];
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    struct GNUNET_PSYC_MessageModifier *
+      pmod = (struct GNUNET_PSYC_MessageModifier *) msg;
+
+    const char *name = (const char *) &pmod[1];
+    const void *value = name + pmod->name_size;
+    GNUNET_ENV_environment_add (pmc->env, pmod->oper, name, value,
+                                pmod->value_size);
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    *pmc->data = &msg[1];
+    *pmc->data_size = ntohs (msg->size) - sizeof (*msg);
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_DATA;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_END;
+    break;
+
+  default:
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+  }
+}
+
+
+/**
+ * Parse PSYC message.
+ *
+ * @param msg
+ *        The PSYC message to parse.
+ * @param[out] method_name
+ *        Pointer to the method name inside @a pmsg.
+ * @param env
+ *        The environment for the message with a list of modifiers.
+ * @param[out] data
+ *        Pointer to data inside @a pmsg.
+ * @param[out] data_size
+ *        Size of @data is written here.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on parse error.
+ */
+int
+GNUNET_PSYC_message_parse (const struct GNUNET_PSYC_Message *msg,
+                           const char **method_name,
+                           struct GNUNET_ENV_Environment *env,
+                           const void **data,
+                           uint16_t *data_size)
+{
+  struct ParseMessageClosure cls;
+  cls.env = env;
+  cls.method_name = method_name;
+  cls.data = data;
+  cls.data_size = data_size;
+
+  uint16_t msg_size = ntohs (msg->header.size);
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = GNUNET_malloc (sizeof (*pmsg) + msg_size - sizeof (*msg));
+  memcpy (&pmsg[1], &msg[1], msg_size - sizeof (*msg));
+
+  struct GNUNET_PSYC_ReceiveHandle *
+    recv = GNUNET_PSYC_receive_create (NULL, &parse_message_part_cb, &cls);
+  GNUNET_PSYC_receive_message (recv, pmsg);
+  GNUNET_PSYC_receive_destroy (recv);
+  GNUNET_free (pmsg);
+
+  return (GNUNET_PSYC_MESSAGE_STATE_END == cls.msg_state)
+    ? GNUNET_OK
+    : GNUNET_SYSERR;
+}

Modified: gnunet/src/psyc/test_psyc.c
===================================================================
--- gnunet/src/psyc/test_psyc.c 2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/psyc/test_psyc.c 2014-07-23 16:19:49 UTC (rev 34021)
@@ -62,8 +62,6 @@
 static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
 static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
 
-struct GNUNET_PSYC_MasterTransmitHandle *mth;
-
 struct TransmitClosure
 {
   struct GNUNET_PSYC_MasterTransmitHandle *mst_tmit;
@@ -95,6 +93,28 @@
 master_transmit ();
 
 
+void master_stopped (void *cls)
+{
+  if (NULL != tmit)
+  {
+    GNUNET_ENV_environment_destroy (tmit->env);
+    GNUNET_free (tmit);
+    tmit = NULL;
+  }
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+void slave_parted (void *cls)
+{
+  if (NULL != mst)
+  {
+    GNUNET_PSYC_master_stop (mst, GNUNET_NO, &master_stopped, NULL);
+    mst = NULL;
+  }
+  else
+    master_stopped (NULL);
+}
+
 /**
  * Clean up all resources used.
  */
@@ -103,21 +123,11 @@
 {
   if (NULL != slv)
   {
-    GNUNET_PSYC_slave_part (slv);
+    GNUNET_PSYC_slave_part (slv, GNUNET_NO, &slave_parted, NULL);
     slv = NULL;
   }
-  if (NULL != mst)
-  {
-    GNUNET_PSYC_master_stop (mst);
-    mst = NULL;
-  }
-  if (NULL != tmit)
-  {
-    GNUNET_ENV_environment_destroy (tmit->env);
-    GNUNET_free (tmit);
-    tmit = NULL;
-  }
-  GNUNET_SCHEDULER_shutdown ();
+  else
+    slave_parted (NULL);
 }
 
 
@@ -171,8 +181,21 @@
 
 static void
 master_message_cb (void *cls, uint64_t message_id, uint32_t flags,
-                   const struct GNUNET_MessageHeader *msg)
+                   const struct GNUNET_PSYC_MessageHeader *msg)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Master got PSYC message fragment of size %u "
+              "belonging to message ID %llu with flags %x\n",
+              ntohs (msg->header.size), message_id, flags);
+  // FIXME
+}
+
+
+static void
+master_message_part_cb (void *cls, uint64_t message_id,
+                        uint64_t data_offset, uint32_t flags,
+                        const struct GNUNET_MessageHeader *msg)
+{
   if (NULL == msg)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -215,8 +238,21 @@
 
 static void
 slave_message_cb (void *cls, uint64_t message_id, uint32_t flags,
-                  const struct GNUNET_MessageHeader *msg)
+                  const struct GNUNET_PSYC_MessageHeader *msg)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Slave got PSYC message fragment of size %u "
+              "belonging to message ID %llu with flags %x\n",
+              ntohs (msg->header.size), message_id, flags);
+  // FIXME
+}
+
+
+static void
+slave_message_part_cb (void *cls, uint64_t message_id,
+                       uint64_t data_offset, uint32_t flags,
+                       const struct GNUNET_MessageHeader *msg)
+{
   if (NULL == msg)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -371,7 +407,7 @@
     memcpy (data, value, value_size);
   }
 
-  return 0 == tmit->mod_value_size ? GNUNET_YES : GNUNET_NO;
+  return GNUNET_NO;
 }
 
 
@@ -380,8 +416,10 @@
 
 
 static void
-join_decision_cb (void *cls, int is_admitted,
-                  const struct GNUNET_PSYC_MessageHeader *join_msg)
+join_decision_cb (void *cls,
+                  const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
+                  int is_admitted,
+                  const struct GNUNET_PSYC_Message *join_msg)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
               "Slave got join decision: %d\n", is_admitted);
@@ -415,8 +453,10 @@
 
 
 static void
-join_request_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
-                 const struct GNUNET_PSYC_MessageHeader *msg,
+join_request_cb (void *cls,
+                 const struct GNUNET_PSYC_JoinRequestMessage *req,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                 const struct GNUNET_PSYC_Message *join_msg,
                  struct GNUNET_PSYC_JoinHandle *jh)
 {
   struct GNUNET_HashCode slave_key_hash;
@@ -450,11 +490,11 @@
                               "_foo", "bar baz", 7);
   GNUNET_ENV_environment_add (env, GNUNET_ENV_OP_ASSIGN,
                               "_foo_bar", "foo bar baz", 11);
-  struct GNUNET_MessageHeader *
+  struct GNUNET_PSYC_Message *
     join_msg = GNUNET_PSYC_message_create ("_request_join", env, "some data", 
9);
 
-  slv = GNUNET_PSYC_slave_join (cfg, &channel_pub_key, slave_key, &origin,
-                                0, NULL, &slave_message_cb,
+  slv = GNUNET_PSYC_slave_join (cfg, &channel_pub_key, slave_key, &origin, 0, 
NULL,
+                                &slave_message_cb, &slave_message_part_cb,
                                 &slave_connect_cb, &join_decision_cb, NULL,
                                 join_msg);
   GNUNET_ENV_environment_destroy (env);
@@ -551,7 +591,8 @@
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting master.\n");
   mst = GNUNET_PSYC_master_start (cfg, channel_key, 
GNUNET_PSYC_CHANNEL_PRIVATE,
                                   &master_start_cb, &join_request_cb,
-                                  &master_message_cb, NULL);
+                                  &master_message_cb, &master_message_part_cb,
+                                  NULL);
 }
 
 

Modified: gnunet/src/social/Makefile.am
===================================================================
--- gnunet/src/social/Makefile.am       2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/social/Makefile.am       2014-07-23 16:19:49 UTC (rev 34021)
@@ -24,6 +24,11 @@
 libgnunetsocial_la_LIBADD = \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(top_builddir)/src/env/libgnunetenv.la \
+  $(top_builddir)/src/psyc/libgnunetpsycutil.la \
+  $(top_builddir)/src/core/libgnunetcore.la \
+  $(top_builddir)/src/identity/libgnunetidentity.la \
+  $(top_builddir)/src/gns/libgnunetgns.la \
+  $(top_builddir)/src/namestore/libgnunetnamestore.la \
   $(GN_LIBINTL) $(XLIB)
 libgnunetsocial_la_LDFLAGS = \
   $(GN_LIB_LDFLAGS)  $(WINFLAGS) \

Modified: gnunet/src/social/gnunet-service-social.c
===================================================================
--- gnunet/src/social/gnunet-service-social.c   2014-07-23 16:19:46 UTC (rev 
34020)
+++ gnunet/src/social/gnunet-service-social.c   2014-07-23 16:19:49 UTC (rev 
34021)
@@ -32,6 +32,7 @@
 #include "gnunet_protocols.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_psyc_service.h"
+#include "gnunet_psyc_util_lib.h"
 #include "gnunet_social_service.h"
 #include "social.h"
 
@@ -71,41 +72,56 @@
 
 
 /**
- * Message in the transmission queue.
+ * Message fragment transmission queue.
  */
-struct TransmitMessage
+struct FragmentTransmitQueue
 {
-  struct TransmitMessage *prev;
-  struct TransmitMessage *next;
+  struct FragmentTransmitQueue *prev;
+  struct FragmentTransmitQueue *next;
 
   struct GNUNET_SERVER_Client *client;
 
   /**
-   * ID assigned to the message.
+   * Pointer to the next message part inside the data after this struct.
    */
-  uint64_t id;
+  struct GNUNET_MessageHeader *next_part;
 
   /**
-   * Size of @a buf
+   * Size of message.
    */
   uint16_t size;
 
   /**
-   * @see enum MessageState
+   * @see enum GNUNET_PSYC_MessageState
    */
   uint8_t state;
 
-  /* Followed by message */
+  /* Followed by one or more message parts. */
 };
 
 
 /**
+ * Message transmission queue.
+ */
+struct MessageTransmitQueue
+{
+  struct MessageTransmitQueue *prev;
+  struct MessageTransmitQueue *next;
+
+  struct FragmentTransmitQueue *frags_head;
+  struct FragmentTransmitQueue *frags_tail;
+
+  struct GNUNET_SERVER_Client *client;
+};
+
+/**
  * List of connected clients.
  */
-struct ClientList
+struct ClientListItem
 {
-  struct ClientList *prev;
-  struct ClientList *next;
+  struct ClientListItem *prev;
+  struct ClientListItem *next;
+
   struct GNUNET_SERVER_Client *client;
 };
 
@@ -115,11 +131,11 @@
  */
 struct Place
 {
-  struct ClientList *clients_head;
-  struct ClientList *clients_tail;
+  struct ClientListItem *clients_head;
+  struct ClientListItem *clients_tail;
 
-  struct TransmitMessage *tmit_head;
-  struct TransmitMessage *tmit_tail;
+  struct MessageTransmitQueue *tmit_msgs_head;
+  struct MessageTransmitQueue *tmit_msgs_tail;
 
   /**
    * Public key of the channel.
@@ -132,9 +148,27 @@
   struct GNUNET_HashCode pub_key_hash;
 
   /**
+   * Last message ID received for the place.
+   * 0 if there is no such message.
+   */
+  uint64_t max_message_id;
+
+  /**
    * Is this a host (#GNUNET_YES), or guest (#GNUNET_NO)?
    */
   uint8_t is_host;
+
+  /**
+   * Is this place ready to receive messages from client?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_ready;
+
+  /**
+   * Is the client disconnected?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnected;
 };
 
 
@@ -146,7 +180,7 @@
   /**
    * Place struct common for Host and Guest
    */
-  struct Place pl;
+  struct Place plc;
 
   /**
    * Private key of the channel.
@@ -184,17 +218,17 @@
   /**
    * Place struct common for Host and Guest.
    */
-  struct Place pl;
+  struct Place plc;
 
   /**
    * Private key of the slave.
    */
-  struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
+  struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
 
   /**
    * Public key of the slave.
    */
-  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
 
   /**
    * Hash of @a pub_key.
@@ -230,13 +264,34 @@
    * Join request to be transmitted to the master on join.
    */
   struct GNUNET_MessageHeader *join_req;
+
+  /**
+   * Join decision received from PSYC.
+   */
+  struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
+
 };
 
 
-static inline void
-transmit_message (struct Place *pl);
+struct Client
+{
+  /**
+   * Place where the client entered.
+   */
+  struct Place *plc;
 
+  /**
+   * Message queue for the message currently being transmitted
+   * by this client.
+   */
+  struct MessageTransmitQueue *tmit_msg;
+};
 
+
+static int
+psyc_transmit_message (struct Place *plc);
+
+
 /**
  * Task run during shutdown.
  *
@@ -265,12 +320,12 @@
 static void
 cleanup_host (struct Host *hst)
 {
-  struct Place *pl = &hst->pl;
+  struct Place *plc = &hst->plc;
 
   if (NULL != hst->master)
-    GNUNET_PSYC_master_stop (hst->master);
+    GNUNET_PSYC_master_stop (hst->master, GNUNET_NO, NULL, NULL); // FIXME
   GNUNET_CONTAINER_multihashmap_destroy (hst->join_reqs);
-  GNUNET_CONTAINER_multihashmap_remove (hosts, &pl->pub_key_hash, pl);
+  GNUNET_CONTAINER_multihashmap_remove (hosts, &plc->pub_key_hash, plc);
 }
 
 
@@ -280,28 +335,28 @@
 static void
 cleanup_guest (struct Guest *gst)
 {
-  struct Place *pl = &gst->pl;
+  struct Place *plc = &gst->plc;
   struct GNUNET_CONTAINER_MultiHashMap *
-    pl_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
-                                                &pl->pub_key_hash);
-  GNUNET_assert (NULL != pl_gst);
-  GNUNET_CONTAINER_multihashmap_remove (pl_gst, &gst->pub_key_hash, gst);
+    plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
+                                                &plc->pub_key_hash);
+  GNUNET_assert (NULL != plc_gst);
+  GNUNET_CONTAINER_multihashmap_remove (plc_gst, &gst->pub_key_hash, gst);
 
-  if (0 == GNUNET_CONTAINER_multihashmap_size (pl_gst))
+  if (0 == GNUNET_CONTAINER_multihashmap_size (plc_gst))
   {
-    GNUNET_CONTAINER_multihashmap_remove (place_guests, &pl->pub_key_hash,
-                                          pl_gst);
-    GNUNET_CONTAINER_multihashmap_destroy (pl_gst);
+    GNUNET_CONTAINER_multihashmap_remove (place_guests, &plc->pub_key_hash,
+                                          plc_gst);
+    GNUNET_CONTAINER_multihashmap_destroy (plc_gst);
   }
-  GNUNET_CONTAINER_multihashmap_remove (guests, &pl->pub_key_hash, gst);
+  GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, gst);
 
   if (NULL != gst->join_req)
     GNUNET_free (gst->join_req);
   if (NULL != gst->relays)
     GNUNET_free (gst->relays);
   if (NULL != gst->slave)
-    GNUNET_PSYC_slave_part (gst->slave);
-  GNUNET_CONTAINER_multihashmap_remove (guests, &pl->pub_key_hash, pl);
+    GNUNET_PSYC_slave_part (gst->slave, GNUNET_NO, NULL, NULL); // FIXME
+  GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, plc);
 }
 
 
@@ -309,15 +364,26 @@
  * Clean up place data structures after a client disconnected.
  */
 static void
-cleanup_place (struct Place *pl)
+cleanup_place (struct Place *plc)
 {
-  (GNUNET_YES == pl->is_host)
-    ? cleanup_host ((struct Host *) pl)
-    : cleanup_guest ((struct Guest *) pl);
-  GNUNET_free (pl);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Cleaning up place %s\n",
+              plc, GNUNET_h2s (&plc->pub_key_hash));
+
+  (GNUNET_YES == plc->is_host)
+    ? cleanup_host ((struct Host *) plc)
+    : cleanup_guest ((struct Guest *) plc);
+  GNUNET_free (plc);
 }
 
 
+static void
+schedule_cleanup_place (void *cls, const struct GNUNET_SCHEDULER_TaskContext 
*tc)
+{
+  cleanup_place (cls);
+}
+
+
 /**
  * Called whenever a client is disconnected.
  * Frees our resources associated with that client.
@@ -331,76 +397,1073 @@
   if (NULL == client)
     return;
 
-  struct Place *
-    pl = GNUNET_SERVER_client_get_user_context (client, struct Place);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "%p Client (%s) disconnected from place %s\n",
-              pl, (GNUNET_YES == pl->is_host) ? "host" : "guest",
-              GNUNET_h2s (&pl->pub_key_hash));
-
-  if (NULL == pl)
+  struct Client *
+    ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
+  if (NULL == ctx)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "%p User context is NULL in client_disconnect()\n", pl);
+                "%p User context is NULL in client_disconnect()\n", ctx);
     GNUNET_break (0);
     return;
   }
 
-  struct ClientList *cl = pl->clients_head;
-  while (NULL != cl)
+  struct Place *plc = ctx->plc;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client (%s) disconnected from place %s\n",
+              plc, (GNUNET_YES == plc->is_host) ? "host" : "guest",
+              GNUNET_h2s (&plc->pub_key_hash));
+
+  struct ClientListItem *cli = plc->clients_head;
+  while (NULL != cli)
   {
-    if (cl->client == client)
+    if (cli->client == client)
     {
-      GNUNET_CONTAINER_DLL_remove (pl->clients_head, pl->clients_tail, cl);
-      GNUNET_free (cl);
+      GNUNET_CONTAINER_DLL_remove (plc->clients_head, plc->clients_tail, cli);
+      GNUNET_free (cli);
       break;
     }
-    cl = cl->next;
+    cli = cli->next;
   }
 
-  if (NULL == pl->clients_head)
+  if (NULL == plc->clients_head)
   { /* Last client disconnected. */
-    if (NULL != pl->tmit_head)
-    { /* Send pending messages to PSYC before cleanup. */
-      //FIXME: transmit_message (pl);
+    if (GNUNET_YES != plc->is_disconnected)
+    {
+      plc->is_disconnected = GNUNET_YES;
+      if (NULL != plc->tmit_msgs_head)
+      { /* Send pending messages to PSYC before cleanup. */
+        psyc_transmit_message (plc);
+      }
+      else
+      {
+        cleanup_place (plc);
+      }
     }
-    else
+  }
+}
+
+
+/**
+ * Send message to all clients connected to the channel.
+ */
+static void
+client_send_msg (const struct Place *plc,
+                 const struct GNUNET_MessageHeader *msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "%p Sending message to clients.\n", plc);
+
+  struct ClientListItem *cli = plc->clients_head;
+  while (NULL != cli)
+  {
+    GNUNET_SERVER_notification_context_add (nc, cli->client);
+    GNUNET_SERVER_notification_context_unicast (nc, cli->client, msg, 
GNUNET_NO);
+    cli = cli->next;
+  }
+}
+
+
+/**
+ * Called after a PSYC master is started.
+ */
+static void
+psyc_master_started (void *cls, uint64_t max_message_id)
+{
+  struct Host *hst = cls;
+  struct Place *plc = &hst->plc;
+  plc->max_message_id = max_message_id;
+  plc->is_ready = GNUNET_YES;
+
+  struct CountersResult res;
+  res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
+  res.header.size = htons (sizeof (res));
+  res.result_code = htonl (GNUNET_OK);
+  res.max_message_id = GNUNET_htonll (plc->max_message_id);
+
+  client_send_msg (plc, &res.header);
+}
+
+
+/**
+ * Called when a PSYC master receives a join request.
+ */
+static void
+psyc_recv_join_request (void *cls,
+                        const struct GNUNET_PSYC_JoinRequestMessage *req,
+                        const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                        const struct GNUNET_PSYC_Message *join_msg,
+                        struct GNUNET_PSYC_JoinHandle *jh)
+{
+  struct Host *hst = cls;
+  struct GNUNET_HashCode slave_key_hash;
+  GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
+  GNUNET_CONTAINER_multihashmap_put (hst->join_reqs, &slave_key_hash, jh,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  client_send_msg (&hst->plc, &req->header);
+}
+
+
+/**
+ * Called after a PSYC slave is connected.
+ */
+static void
+psyc_slave_connected (void *cls, uint64_t max_message_id)
+{
+  struct Guest *gst = cls;
+  struct Place *plc = &gst->plc;
+  plc->max_message_id = max_message_id;
+  plc->is_ready = GNUNET_YES;
+
+  struct CountersResult res;
+  res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
+  res.header.size = htons (sizeof (res));
+  res.result_code = htonl (GNUNET_OK);
+  res.max_message_id = GNUNET_htonll (plc->max_message_id);
+
+  client_send_msg (plc, &res.header);
+}
+
+
+/**
+ * Called when a PSYC slave receives a join decision.
+ */
+static void
+psyc_recv_join_dcsn (void *cls,
+                     const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
+                     int is_admitted,
+                     const struct GNUNET_PSYC_Message *join_msg)
+{
+  struct Guest *gst = cls;
+  client_send_msg (&gst->plc, &dcsn->header);
+}
+
+
+/**
+ * Called when a PSYC master or slave receives a message.
+ */
+static void
+psyc_recv_message (void *cls,
+                   uint64_t message_id,
+                   uint32_t flags,
+                   const struct GNUNET_PSYC_MessageHeader *msg)
+{
+  struct Place *plc = cls;
+  client_send_msg (plc, &msg->header);
+
+  /* FIXME: further processing */
+}
+
+
+/**
+ * Initialize place data structure.
+ */
+static void
+place_init (struct Place *plc)
+{
+
+}
+
+
+/**
+ * Handle a connecting client entering a place as host.
+ */
+static void
+client_recv_host_enter (void *cls, struct GNUNET_SERVER_Client *client,
+                        const struct GNUNET_MessageHeader *msg)
+{
+  const struct HostEnterRequest *req
+    = (const struct HostEnterRequest *) msg;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&req->place_key, &pub_key);
+  GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
+
+  struct Host *
+    hst = GNUNET_CONTAINER_multihashmap_get (hosts, &pub_key_hash);
+  struct Place *plc;
+
+  if (NULL == hst)
+  {
+    hst = GNUNET_new (struct Host);
+    hst->policy = ntohl (req->policy);
+    hst->priv_key = req->place_key;
+    hst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+
+    plc = &hst->plc;
+    plc->is_host = GNUNET_YES;
+    plc->pub_key = pub_key;
+    plc->pub_key_hash = pub_key_hash;
+    place_init (plc);
+
+    GNUNET_CONTAINER_multihashmap_put (hosts, &plc->pub_key_hash, plc,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    hst->master = GNUNET_PSYC_master_start (cfg, &hst->priv_key, hst->policy,
+                                            &psyc_master_started,
+                                            &psyc_recv_join_request,
+                                            &psyc_recv_message, NULL, hst);
+  }
+  else
+  {
+    plc = &hst->plc;
+
+    struct CountersResult res;
+    res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
+    res.header.size = htons (sizeof (res));
+    res.result_code = htonl (GNUNET_OK);
+    res.max_message_id = GNUNET_htonll (plc->max_message_id);
+
+    GNUNET_SERVER_notification_context_add (nc, client);
+    GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
+                                                GNUNET_NO);
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "%p Client connected as host to place %s.\n",
+              hst, GNUNET_h2s (&plc->pub_key_hash));
+
+  struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
+  cli->client = client;
+  GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
+
+  struct Client *ctx = GNUNET_new (struct Client);
+  ctx->plc = plc;
+  GNUNET_SERVER_client_set_user_context (client, ctx);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Handle a connecting client entering a place as guest.
+ */
+static void
+client_recv_guest_enter (void *cls, struct GNUNET_SERVER_Client *client,
+                         const struct GNUNET_MessageHeader *msg)
+{
+  const struct GuestEnterRequest *req
+    = (const struct GuestEnterRequest *) msg;
+  uint16_t req_size = ntohs (req->header.size);
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey gst_pub_key;
+  struct GNUNET_HashCode pub_key_hash, gst_pub_key_hash;
+
+  GNUNET_CRYPTO_ecdsa_key_get_public (&req->guest_key, &gst_pub_key);
+  GNUNET_CRYPTO_hash (&gst_pub_key, sizeof (gst_pub_key), &gst_pub_key_hash);
+  GNUNET_CRYPTO_hash (&req->place_key, sizeof (req->place_key), &pub_key_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests, &pub_key_hash);
+  struct Guest *gst = NULL;
+  struct Place *plc;
+
+  if (NULL != plc_gst)
+  {
+    gst = GNUNET_CONTAINER_multihashmap_get (plc_gst, &gst_pub_key_hash);
+  }
+  if (NULL == gst || NULL == gst->slave)
+  {
+    gst = GNUNET_new (struct Guest);
+    gst->priv_key = req->guest_key;
+    gst->pub_key = gst_pub_key;
+    gst->pub_key_hash = gst_pub_key_hash;
+    gst->origin = req->origin;
+    gst->relay_count = ntohl (req->relay_count);
+
+    const struct GNUNET_PeerIdentity *
+      relays = (const struct GNUNET_PeerIdentity *) &req[1];
+    uint16_t relay_size = gst->relay_count * sizeof (*relays);
+    struct GNUNET_PSYC_Message *join_msg = NULL;
+    uint16_t join_msg_size = 0;
+
+    if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
+        <= req_size)
     {
-      cleanup_place (pl);
+      join_msg = (struct GNUNET_PSYC_Message *)
+        (((char *) &req[1]) + relay_size);
+      join_msg_size = ntohs (join_msg->header.size);
     }
+    if (sizeof (*req) + relay_size + join_msg_size != req_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%u + %u + %u != %u\n",
+                  sizeof (*req), relay_size, join_msg_size, req_size);
+      GNUNET_break (0);
+      GNUNET_SERVER_client_disconnect (client);
+      return;
+    }
+    if (0 < gst->relay_count)
+    {
+      gst->relays = GNUNET_malloc (relay_size);
+      memcpy (gst->relays, &req[1], relay_size);
+    }
+
+    plc = &gst->plc;
+    plc->is_host = GNUNET_NO;
+    plc->pub_key = req->place_key;
+    plc->pub_key_hash = pub_key_hash;
+    place_init (plc);
+
+    if (NULL == plc_gst)
+    {
+      plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+      GNUNET_CONTAINER_multihashmap_put (place_guests, &plc->pub_key_hash, 
plc_gst,
+                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    }
+    GNUNET_CONTAINER_multihashmap_put (plc_gst, &gst->pub_key_hash, plc,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, plc,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    gst->slave
+      = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &gst->priv_key,
+                                &gst->origin, gst->relay_count, gst->relays,
+                                &psyc_recv_message, NULL, 
&psyc_slave_connected,
+                                &psyc_recv_join_dcsn, gst, join_msg);
   }
+  else
+  {
+    plc = &gst->plc;
+
+    struct CountersResult res;
+    res.header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
+    res.header.size = htons (sizeof (res));
+    res.result_code = htonl (GNUNET_OK);
+    res.max_message_id = GNUNET_htonll (plc->max_message_id);
+
+    GNUNET_SERVER_notification_context_add (nc, client);
+    GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
+                                                GNUNET_NO);
+    if (NULL != gst->join_dcsn)
+    {
+      GNUNET_SERVER_notification_context_add (nc, client);
+      GNUNET_SERVER_notification_context_unicast (nc, client,
+                                                  &gst->join_dcsn->header,
+                                                  GNUNET_NO);
+    }
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client connected as guest to place %s.\n",
+              gst, GNUNET_h2s (&plc->pub_key_hash));
+
+  struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
+  cli->client = client;
+  GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
+
+  struct Client *ctx = GNUNET_new (struct Client);
+  ctx->plc = plc;
+  GNUNET_SERVER_client_set_user_context (client, ctx);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
+struct JoinDecisionClosure
+{
+  int32_t is_admitted;
+  struct GNUNET_PSYC_Message *msg;
+};
+
+
+/**
+ * Iterator callback for responding to join requests.
+ */
+static int
+psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+                         void *value)
+{
+  struct JoinDecisionClosure *jcls = cls;
+  struct GNUNET_PSYC_JoinHandle *jh = value;
+  // FIXME: add relays
+  GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Handle an entry decision from a host client.
+ */
 static void
-client_home_enter (void *cls, struct GNUNET_SERVER_Client *client,
-                   const struct GNUNET_MessageHeader *msg)
+client_recv_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
+                           const struct GNUNET_MessageHeader *msg)
 {
+  struct Client *
+    ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
+  GNUNET_assert (NULL != ctx);
+  struct Place *plc = ctx->plc;
+  GNUNET_assert (GNUNET_YES == plc->is_host);
+  struct Host *hst = (struct Host *) plc;
 
+  struct GNUNET_PSYC_JoinDecisionMessage *
+    dcsn = (struct GNUNET_PSYC_JoinDecisionMessage *) msg;
+  struct JoinDecisionClosure jcls;
+  jcls.is_admitted = ntohl (dcsn->is_admitted);
+  jcls.msg
+    = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (msg->size))
+    ? (struct GNUNET_PSYC_Message *) &dcsn[1]
+    : NULL;
+
+  struct GNUNET_HashCode slave_key_hash;
+  GNUNET_CRYPTO_hash (&dcsn->slave_key, sizeof (dcsn->slave_key),
+                      &slave_key_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Got join decision (%d) from client for place %s..\n",
+              hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p ..and slave %s.\n",
+              hst, GNUNET_h2s (&slave_key_hash));
+
+  GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_key_hash,
+                                              &psyc_send_join_decision, &jcls);
+  GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_key_hash);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
+/**
+ * Send acknowledgement to a client.
+ *
+ * Sent after a message fragment has been passed on to multicast.
+ *
+ * @param plc The place struct for the client.
+ */
 static void
-client_place_enter (void *cls, struct GNUNET_SERVER_Client *client,
-                    const struct GNUNET_MessageHeader *msg)
+send_message_ack (struct Place *plc, struct GNUNET_SERVER_Client *client)
 {
+  struct GNUNET_MessageHeader res;
+  res.size = htons (sizeof (res));
+  res.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
 
+  GNUNET_SERVER_notification_context_add (nc, client);
+  GNUNET_SERVER_notification_context_unicast (nc, client, &res, GNUNET_NO);
 }
 
 
+/**
+ * Proceed to the next message part in the transmission queue.
+ *
+ * @param plc
+ *        Place where the transmission is going on.
+ * @param tmit_msg
+ *        Currently transmitted message.
+ * @param tmit_frag
+ *        Currently transmitted message fragment.
+ *
+ * @return @a tmit_frag, or NULL if reached the end of fragment.
+ */
+static struct FragmentTransmitQueue *
+psyc_transmit_queue_next_part (struct Place *plc,
+                               struct MessageTransmitQueue *tmit_msg,
+                               struct FragmentTransmitQueue *tmit_frag)
+{
+  uint16_t psize = ntohs (tmit_frag->next_part->size);
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "%p psyc_transmit_queue_next_part: %x + %u - %x = %u < %u\n",
+              plc, tmit_frag->next_part, psize, &tmit_frag[1],
+              (char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1]),
+              tmit_frag->size);
+  if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
+      < tmit_frag->size)
+  {
+    tmit_frag->next_part
+      = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + 
psize);
+  }
+  else /* Reached end of current fragment. */
+  {
+    if (NULL != tmit_frag->client)
+      send_message_ack (plc, tmit_frag->client);
+    GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, 
tmit_frag);
+    GNUNET_free (tmit_frag);
+    tmit_frag = NULL;
+  }
+  return tmit_frag;
+}
+
+
+/**
+ * Proceed to next message in transmission queue.
+ *
+ * @param plc
+ *        Place where the transmission is going on.
+ * @param tmit_msg
+ *        Currently transmitted message.
+ *
+ * @return The next message in queue, or NULL if queue is empty.
+ */
+static struct MessageTransmitQueue *
+psyc_transmit_queue_next_msg (struct Place *plc,
+                              struct MessageTransmitQueue *tmit_msg)
+{
+  GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, 
tmit_msg);
+  GNUNET_free (tmit_msg);
+  return plc->tmit_msgs_head;
+}
+
+
+/**
+ * Callback for data transmission to PSYC.
+ */
+static int
+psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  struct Place *plc = cls;
+  struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
+  GNUNET_assert (NULL != tmit_msg);
+  struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
+  if (NULL == tmit_frag)
+  { /* Rest of the message have not arrived yet, pause transmission */
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+  if (NULL == pmsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_data: nothing to send.\n", plc);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p psyc_transmit_notify_data()\n", plc);
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
+
+  uint16_t ptype = ntohs (pmsg->type);
+  uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
+  int ret;
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    if (*data_size < pdata_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_data: buffer size too small for 
data.\n", plc);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_data: sending %u bytes.\n",
+                plc, pdata_size);
+
+    *data_size = pdata_size;
+    memcpy (data, &pmsg[1], *data_size);
+    ret = GNUNET_NO;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    *data_size = 0;
+    ret = GNUNET_YES;
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_notify_data: unexpected message part of type 
%u.\n",
+                plc, ptype);
+    ret = GNUNET_SYSERR;
+  }
+
+  if (GNUNET_SYSERR == ret)
+  {
+    *data_size = 0;
+    tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    plc->is_disconnected = GNUNET_YES;
+    GNUNET_SERVER_client_disconnect (tmit_frag->client);
+    GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
+    return ret;
+  }
+  else
+  {
+    psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+
+    if (NULL == tmit_msg->frags_head
+        && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
+    { /* Reached end of current message. */
+      tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    }
+  }
+
+  if (ret != GNUNET_NO)
+  {
+    if (NULL != tmit_msg)
+    {
+      psyc_transmit_message (plc);
+    }
+    else if (GNUNET_YES == plc->is_disconnected)
+    {
+      /* FIXME: handle partial message (when still in_transmit) */
+      cleanup_place (plc);
+    }
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for modifier transmission to PSYC.
+ */
+static int
+psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
+                          uint8_t *oper, uint32_t *full_value_size)
+{
+  struct Place *plc = cls;
+  struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
+  GNUNET_assert (NULL != tmit_msg);
+  struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
+  if (NULL == tmit_frag)
+  { /* Rest of the message have not arrived yet, pause transmission */
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+  if (NULL == pmsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p psyc_transmit_notify_mod()\n", plc);
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
+
+  uint16_t ptype = ntohs (pmsg->type);
+  int ret;
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    if (NULL == oper)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
+      ret = GNUNET_SYSERR;
+      break;
+    }
+    struct GNUNET_PSYC_MessageModifier *
+      pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
+    uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
+
+    if (*data_size < mod_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: buffer size too small for 
data.\n", plc);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+
+    *full_value_size = ntohl (pmod->value_size);
+    *oper = pmod->oper;
+    *data_size = mod_size;
+    memcpy (data, &pmod[1], mod_size);
+    ret = GNUNET_NO;
+#if REMOVE // FIXME
+    ret = (mod_size - strnlen ((char *) &pmod[1], mod_size) - 1
+           == *full_value_size)
+      ? GNUNET_YES
+      : GNUNET_NO;
+#endif
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+  {
+    if (NULL != oper)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
+      ret = GNUNET_SYSERR;
+      break;
+    }
+    uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
+    if (*data_size < mod_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: buffer size too small for 
data.\n", plc);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, 
mod_size);
+
+    *data_size = mod_size;
+    memcpy (data, &pmsg[1], *data_size);
+    ret = GNUNET_NO;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    *data_size = 0;
+    ret = GNUNET_YES;
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_notify_mod: unexpected message part of type 
%u.\n",
+                plc, ptype);
+    ret = GNUNET_SYSERR;
+  }
+
+  if (GNUNET_SYSERR == ret)
+  {
+    *data_size = 0;
+    ret = GNUNET_SYSERR;
+    tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    plc->is_disconnected = GNUNET_YES;
+    GNUNET_SERVER_client_disconnect (tmit_frag->client);
+    GNUNET_SCHEDULER_add_now (&schedule_cleanup_place, plc);
+  }
+  else
+  {
+    psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+
+    if (NULL == tmit_msg->frags_head
+        && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
+    { /* Reached end of current message. */
+      tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    }
+  }
+  return ret;
+}
+
+/**
+ * Callback for data transmission from a host to PSYC.
+ */
+static int
+host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  int ret = psyc_transmit_notify_data (cls, data_size, data);
+
+  if (GNUNET_NO != ret)
+  {
+    struct Host *hst = cls;
+    hst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for the transmit functions of multicast.
+ */
+static int
+guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  int ret = psyc_transmit_notify_data (cls, data_size, data);
+
+  if (GNUNET_NO != ret)
+  {
+    struct Guest *gst = cls;
+    gst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for modifier transmission from a host to PSYC.
+ */
+static int
+host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
+                          uint8_t *oper, uint32_t *full_value_size)
+{
+  int ret = psyc_transmit_notify_mod (cls, data_size, data,
+                                      oper, full_value_size);
+  if (GNUNET_SYSERR == ret)
+  {
+    struct Host *hst = cls;
+    hst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for modifier transmission from a guest to PSYC.
+ */
+static int
+guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
+                           uint8_t *oper, uint32_t *full_value_size)
+{
+  int ret = psyc_transmit_notify_mod (cls, data_size, data,
+                                      oper, full_value_size);
+  if (GNUNET_SYSERR == ret)
+  {
+    struct Guest *gst = cls;
+    gst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Get method part of next message from transmission queue.
+ *
+ * @param tmit_msg
+ *        Next item in message transmission queue.
+ * @param[out] pmeth
+ *        The message method is returned here.
+ *
+ * @return #GNUNET_OK on success
+ *         #GNUNET_NO if there are no more messages in queue.
+ *         #GNUNET_SYSERR if the next message is malformed.
+ */
+static int
+psyc_transmit_queue_next_method (struct Place *plc,
+                                 struct GNUNET_PSYC_MessageMethod **pmeth)
+{
+  struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
+  if (NULL == tmit_msg)
+    return GNUNET_NO;
+
+  struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
+  if (NULL == tmit_frag)
+  {
+    GNUNET_break (0);
+    return GNUNET_NO;
+  }
+
+  struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+  if (NULL == pmsg
+      || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_queue_next_method: unexpected message part 
of type %u.\n",
+                plc, ntohs (pmsg->type));
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  uint16_t psize = ntohs (pmsg->size);
+  *pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
+
+  if (psize < sizeof (**pmeth) + 1 || '\0' != *((char *) *pmeth + psize - 1))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_queue_next_method: invalid method name.\n",
+                plc, ntohs (pmsg->type));
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%u <= %u || NUL != %u\n",
+                sizeof (**pmeth), psize, *((char *) *pmeth + psize - 1));
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Transmit the next message in queue from the host to the PSYC channel.
+ */
+static int
+psyc_master_transmit_message (struct Host *hst)
+{
+
+  if (NULL == hst->tmit_handle)
+  {
+    struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
+    int ret = psyc_transmit_queue_next_method (&hst->plc, &pmeth);
+    if (GNUNET_OK != ret)
+      return ret;
+
+    hst->tmit_handle
+      = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
+                                     &host_transmit_notify_mod,
+                                     &host_transmit_notify_data, hst,
+                                     pmeth->flags);
+  }
+  else
+  {
+    GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Transmit the next message in queue from a guest to the PSYC channel.
+ */
+static int
+psyc_slave_transmit_message (struct Guest *gst)
+{
+  if (NULL == gst->tmit_handle)
+  {
+    struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
+    int ret = psyc_transmit_queue_next_method (&gst->plc, &pmeth);
+    if (GNUNET_OK != ret)
+      return ret;
+
+    gst->tmit_handle
+      = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
+                                    &guest_transmit_notify_mod,
+                                    &guest_transmit_notify_data, gst,
+                                    pmeth->flags);
+  }
+  else
+  {
+    GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Transmit a message to PSYC.
+ */
+static int
+psyc_transmit_message (struct Place *plc)
+{
+  return
+    (plc->is_host)
+    ? psyc_master_transmit_message ((struct Host *) plc)
+    : psyc_slave_transmit_message ((struct Guest *) plc);
+}
+
+
+/**
+ * Queue message parts for sending to PSYC.
+ *
+ * @param plc          Place to send to.
+ * @param client       Client the message originates from.
+ * @param data_size    Size of @a data.
+ * @param data         Concatenated message parts.
+ * @param first_ptype  First message part type in @a data.
+ * @param last_ptype   Last message part type in @a data.
+ */
+static struct MessageTransmitQueue *
+psyc_transmit_queue_message (struct Place *plc,
+                             struct GNUNET_SERVER_Client *client,
+                             size_t data_size,
+                             const void *data,
+                             uint16_t first_ptype, uint16_t last_ptype,
+                             struct MessageTransmitQueue *tmit_msg)
+{
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
+  {
+    tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
+    GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, 
plc->tmit_msgs_tail, tmit_msg);
+  }
+  else if (NULL == tmit_msg)
+  {
+    return NULL;
+  }
+
+  struct FragmentTransmitQueue *
+    tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
+  memcpy (&tmit_frag[1], data, data_size);
+  tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
+  tmit_frag->client = client;
+  tmit_frag->size = data_size;
+
+  GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, 
tmit_msg->frags_tail, tmit_frag);
+  tmit_msg->client = client;
+  return tmit_msg;
+}
+
+
+/**
+ * Cancel transmission of current message to PSYC.
+ *
+ * @param plc    Place to send to.
+ * @param client  Client the message originates from.
+ */
 static void
-client_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
-                      const struct GNUNET_MessageHeader *msg)
+psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVER_Client *client)
 {
+  uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
 
+  struct GNUNET_MessageHeader msg;
+  msg.size = htons (sizeof (msg));
+  msg.type = htons (type);
+
+  psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, 
NULL);
+  psyc_transmit_message (plc);
+
+  /* FIXME: cleanup */
 }
 
 
+/**
+ * Handle an incoming message from a client, to be transmitted to the place.
+ */
 static void
-client_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
-                     const struct GNUNET_MessageHeader *msg)
+client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
+                          const struct GNUNET_MessageHeader *msg)
 {
+  struct Client *
+    ctx = GNUNET_SERVER_client_get_user_context (client, struct Client);
+  GNUNET_assert (NULL != ctx);
+  struct Place *plc = ctx->plc;
+  int ret = GNUNET_SYSERR;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message from client.\n", plc);
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
+
+  if (GNUNET_YES != plc->is_ready)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p Place is not ready yet, disconnecting client.\n", plc);
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+
+  uint16_t size = ntohs (msg->size);
+  uint16_t psize = size - sizeof (*msg);
+  if (psize < sizeof (struct GNUNET_MessageHeader)
+      || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received message with invalid payload size (%u) from 
client.\n",
+                plc, psize);
+    GNUNET_break (0);
+    psyc_transmit_cancel (plc, client);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+
+  uint16_t first_ptype = 0, last_ptype = 0;
+  if (GNUNET_SYSERR
+      == GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
+                                          &first_ptype, &last_ptype))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received invalid message part from client.\n", plc);
+    GNUNET_break (0);
+    psyc_transmit_cancel (plc, client);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message with first part type %u and last part type 
%u.\n",
+              plc, first_ptype, last_ptype);
+
+  ctx->tmit_msg
+    = psyc_transmit_queue_message (plc, client, psize, &msg[1],
+                                   first_ptype, last_ptype, ctx->tmit_msg);
+  if (NULL != ctx->tmit_msg)
+  {
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
+      ctx->tmit_msg = NULL;
+    ret = psyc_transmit_message (plc);
+  }
+
+  if (GNUNET_OK != ret)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received invalid message part from client.\n", plc);
+    GNUNET_break (0);
+    psyc_transmit_cancel (plc, client);
+    ret = GNUNET_SYSERR;
+  }
+  GNUNET_SERVER_receive_done (client, ret);
 }
 
 
@@ -416,16 +1479,16 @@
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
-    { &client_home_enter, NULL,
-      GNUNET_MESSAGE_TYPE_SOCIAL_HOME_ENTER, 0 },
+    { &client_recv_host_enter, NULL,
+      GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER, 0 },
 
-    { &client_place_enter, NULL,
-      GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_ENTER, 0 },
+    { &client_recv_guest_enter, NULL,
+      GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER, 0 },
 
-    { &client_join_decision, NULL,
-      GNUNET_MESSAGE_TYPE_SOCIAL_JOIN_DECISION, 0 },
+    { &client_recv_join_decision, NULL,
+      GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, 0 },
 
-    { &client_psyc_message, NULL,
+    { &client_recv_psyc_message, NULL,
       GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 }
   };
 

Modified: gnunet/src/social/social.h
===================================================================
--- gnunet/src/social/social.h  2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/social/social.h  2014-07-23 16:19:49 UTC (rev 34021)
@@ -19,7 +19,7 @@
  */
 
 /**
- * @file psyc/psyc.h
+ * @file social/social.h
  * @brief Common type definitions for the Social service and API.
  * @author Gabor X Toth
  */
@@ -30,17 +30,100 @@
 #include "platform.h"
 #include "gnunet_social_service.h"
 
+enum MessageState
+{
+  MSG_STATE_START    = 0,
+  MSG_STATE_HEADER   = 1,
+  MSG_STATE_METHOD   = 2,
+  MSG_STATE_MODIFIER = 3,
+  MSG_STATE_MOD_CONT = 4,
+  MSG_STATE_DATA     = 5,
+  MSG_STATE_END      = 6,
+  MSG_STATE_CANCEL   = 7,
+  MSG_STATE_ERROR    = 8,
+};
 
+
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**** library -> service ****/
 
 
+struct HostEnterRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER
+   */
+  struct GNUNET_MessageHeader header;
 
+  uint32_t policy GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EcdsaPrivateKey host_key;
+
+  struct GNUNET_CRYPTO_EddsaPrivateKey place_key;
+};
+
+
+struct GuestEnterRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ADDR
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t relay_count GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EcdsaPrivateKey guest_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey place_key;
+
+  struct GNUNET_PeerIdentity origin;
+
+  /* Followed by struct GNUNET_PeerIdentity relays[relay_count] */
+
+  /* Followed by struct GNUNET_MessageHeader join_msg */
+};
+
+
 /**** service -> library ****/
 
 
+struct CountersResult
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_RESULT_COUNTERS
+   */
+  struct GNUNET_MessageHeader header;
 
+  /**
+   * Status code for the operation.
+   */
+  int32_t result_code GNUNET_PACKED;
+
+  /**
+   * Last message ID sent to the channel.
+   */
+  uint64_t max_message_id;
+};
+
+
+#if REMOVE
+struct NymEnterRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_NYM_ENTER
+   */
+  struct GNUNET_MessageHeader header;
+  /**
+   * Public key of the joining slave.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey nym_key;
+
+  /* Followed by struct GNUNET_MessageHeader join_request */
+};
+#endif
+
+
 GNUNET_NETWORK_STRUCT_END
 
 #endif

Modified: gnunet/src/social/social_api.c
===================================================================
--- gnunet/src/social/social_api.c      2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/social/social_api.c      2014-07-23 16:19:49 UTC (rev 34021)
@@ -1,4 +1,4 @@
-/*
+ /*
  * This file is part of GNUnet
  * (C) 2013 Christian Grothoff (and other contributing authors)
  *
@@ -25,30 +25,93 @@
  */
 
 #include <inttypes.h>
+#include <string.h>
 
 #include "platform.h"
 #include "gnunet_util_lib.h"
 #include "gnunet_env_lib.h"
+#include "gnunet_core_service.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_namestore_service.h"
+#include "gnunet_gns_service.h"
 #include "gnunet_psyc_service.h"
+#include "gnunet_psyc_util_lib.h"
 #include "gnunet_social_service.h"
 #include "social.h"
 
+#define LOG(kind,...) GNUNET_log_from (kind, "social-api",__VA_ARGS__)
 
-/**
- * Handle for a pseudonym of another user in the network.
- */
-struct GNUNET_SOCIAL_Nym
-{
 
-};
+static struct GNUNET_CORE_Handle *core;
+static struct GNUNET_GNS_Handle *gns;
+static struct GNUNET_NAMESTORE_Handle *namestore;
+static struct GNUNET_PeerIdentity this_peer;
 
-
 /**
  * Handle for a place where social interactions happen.
  */
 struct GNUNET_SOCIAL_Place
 {
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
 
+  /**
+   * Client connection to the service.
+   */
+  struct GNUNET_CLIENT_MANAGER_Connection *client;
+
+  /**
+   * Transmission handle;
+   */
+  struct GNUNET_PSYC_TransmitHandle *tmit;
+
+  /**
+   * Receipt handle;
+   */
+  struct GNUNET_PSYC_ReceiveHandle *recv;
+
+  /**
+   * Message to send on reconnect.
+   */
+  struct GNUNET_MessageHeader *connect_msg;
+
+  /**
+   * Slicer for processing incoming methods.
+   */
+  struct GNUNET_SOCIAL_Slicer *slicer;
+
+  /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
+   * Public key of the place.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+
+  /**
+   * Private key of the ego.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey ego_key;
+
+  /**
+   * Does this place belong to a host (#GNUNET_YES) or guest (#GNUNET_NO)?
+   */
+  uint8_t is_host;
+
+  /**
+   * Is this place in the process of disconnecting from the service?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
 };
 
 
@@ -57,7 +120,20 @@
  */
 struct GNUNET_SOCIAL_Host
 {
+  struct GNUNET_SOCIAL_Place plc;
 
+  struct GNUNET_CRYPTO_EddsaPrivateKey place_key;
+
+  GNUNET_SOCIAL_HostEnterCallback enter_cb;
+
+  GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb;
+
+  GNUNET_SOCIAL_FarewellCallback farewell_cb;
+
+  /**
+   * Closure for callbacks.
+   */
+  void *cb_cls;
 };
 
 
@@ -66,20 +142,95 @@
  */
 struct GNUNET_SOCIAL_Guest
 {
+  struct GNUNET_SOCIAL_Place plc;
 
+  GNUNET_SOCIAL_GuestEnterCallback enter_cb;
+
+  GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb;
+
+  /**
+   * Closure for callbacks.
+   */
+  void *cb_cls;
 };
 
 
 /**
- * Handle to an implementation of try-and-slice.
+ * Handle for a pseudonym of another user in the network.
  */
+struct GNUNET_SOCIAL_Nym
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+};
+
+
+/**
+ * Hash map of all nyms.
+ * pub_key_hash -> struct GNUNET_SOCIAL_Nym *
+ */
+struct GNUNET_CONTAINER_MultiHashMap *nyms;
+
+
+/**
+ * Handle for a try-and-slice instance.
+ */
 struct GNUNET_SOCIAL_Slicer
 {
+  /**
+   * Message handlers: method_name -> SlicerCallbacks
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *handlers;
 
+
+  /**
+   * Currently being processed message part.
+   */
+  const struct GNUNET_MessageHeader *msg;
+
+  /**
+   * ID of currently being received message.
+   */
+  uint64_t message_id;
+
+  /**
+   * Method name of currently being received message.
+   */
+  char *method_name;
+
+  /**
+   * Public key of the nym the current message originates from.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey nym_key;
+
+  /**
+   * Size of @a method_name (including terminating \0).
+   */
+  uint16_t method_name_size;
 };
 
 
 /**
+ * Callbacks for a slicer method handler.
+ */
+struct SlicerCallbacks
+{
+  GNUNET_SOCIAL_MethodCallback method_cb;
+  GNUNET_SOCIAL_ModifierCallback modifier_cb;
+  GNUNET_SOCIAL_DataCallback data_cb;
+  GNUNET_SOCIAL_EndOfMessageCallback eom_cb;
+  void *cls;
+};
+
+
+struct SlicerRemoveClosure
+{
+  struct GNUNET_SOCIAL_Slicer *slicer;
+  struct SlicerCallbacks rm_cbs;
+};
+
+
+/**
  * Handle for an announcement request.
  */
 struct GNUNET_SOCIAL_Announcement
@@ -118,9 +269,184 @@
 };
 
 
+static struct GNUNET_SOCIAL_Nym *
+nym_get_or_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key)
+{
+  struct GNUNET_SOCIAL_Nym *nym = NULL;
+  struct GNUNET_HashCode pub_key_hash;
 
+  if (NULL == pub_key)
+    return NULL;
 
+  GNUNET_CRYPTO_hash (pub_key, sizeof (*pub_key), &pub_key_hash);
+
+  if (NULL == nyms)
+    nyms = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  else
+    nym = GNUNET_CONTAINER_multihashmap_get (nyms, &pub_key_hash);
+
+  if (NULL == nym)
+  {
+    nym = GNUNET_new (struct GNUNET_SOCIAL_Nym);
+    nym->pub_key = *pub_key;
+    nym->pub_key_hash = pub_key_hash;
+    GNUNET_CONTAINER_multihashmap_put (nyms, &nym->pub_key_hash, nym,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  return nym;
+}
+
+
+static void
+nym_destroy (struct GNUNET_SOCIAL_Nym *nym)
+{
+  GNUNET_CONTAINER_multihashmap_remove (nyms, &nym->pub_key_hash, nym);
+  GNUNET_free (nym);
+}
+
+
 /**
+ * Call a handler for an incoming message part.
+ *
+ * @param cls
+ * @param key
+ * @param value
+ *
+ * @return
+ */
+int
+slicer_handler_notify (void *cls, const struct GNUNET_HashCode *key,
+                       void *value)
+{
+  struct GNUNET_SOCIAL_Slicer *slicer = cls;
+  const struct GNUNET_MessageHeader *msg = slicer->msg;
+  struct SlicerCallbacks *cbs = value;
+  uint16_t ptype = ntohs (msg->type);
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+  {
+    if (NULL == cbs->method_cb)
+      break;
+    struct GNUNET_PSYC_MessageMethod *
+      meth = (struct GNUNET_PSYC_MessageMethod *) msg;
+    cbs->method_cb (cbs->cls, meth, slicer->message_id,
+                    ntohl (meth->flags),
+                    nym_get_or_create (&slicer->nym_key),
+                    slicer->method_name);
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    if (NULL == cbs->modifier_cb)
+      break;
+    struct GNUNET_PSYC_MessageModifier *
+      mod = (struct GNUNET_PSYC_MessageModifier *) msg;
+    cbs->modifier_cb (cbs->cls, mod, slicer->message_id,
+                      mod->oper, (const char *) &mod[1],
+                      (const void *) &mod[1] + ntohs (mod->name_size),
+                      ntohs (mod->value_size));
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+  {
+    if (NULL == cbs->modifier_cb)
+      break;
+    /* FIXME: concatenate until done */
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+  {
+    if (NULL == cbs->data_cb)
+      break;
+    uint64_t data_offset = 0; // FIXME
+    cbs->data_cb (cbs->cls, msg, slicer->message_id,
+                  data_offset, &msg[1], ntohs (msg->size) - sizeof (*msg));
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    if (NULL == cbs->eom_cb)
+      break;
+    cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_NO);
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    if (NULL == cbs->eom_cb)
+      break;
+    cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_YES);
+    break;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Process an incoming message part and call matching handlers.
+ *
+ * @param cls
+ *        Closure.
+ * @param message_id
+ *        ID of the message.
+ * @param flags
+ *        Flags for the message.
+ *        @see enum GNUNET_PSYC_MessageFlags
+ * @param msg
+ *        The message part. as it arrived from the network.
+ */
+static void
+slicer_message (void *cls, uint64_t message_id, uint64_t fragment_offset,
+                uint32_t flags, const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Slicer *slicer = cls;
+  uint16_t ptype = ntohs (msg->type);
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
+  {
+    struct GNUNET_PSYC_MessageMethod *
+      meth = (struct GNUNET_PSYC_MessageMethod *) msg;
+    slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth);
+    slicer->method_name = GNUNET_malloc (slicer->method_name_size);
+    memcpy (slicer->method_name, &meth[1], slicer->method_name_size);
+    slicer->message_id = message_id;
+  }
+  else
+  {
+    GNUNET_assert (message_id == slicer->message_id);
+  }
+
+  LOG (GNUNET_ERROR_TYPE_WARNING,
+       "Slicer received message of type %u and size %u, "
+       "with ID %" PRIu64 " and method %s\n",
+       ptype, ntohs (msg->size), message_id, slicer->method_name);
+
+  slicer->msg = msg;
+  char *name = GNUNET_malloc (slicer->method_name_size);
+  memcpy (name, slicer->method_name, slicer->method_name_size);
+  do
+  {
+    struct GNUNET_HashCode key;
+    uint16_t name_len = strlen (name);
+    GNUNET_CRYPTO_hash (name, name_len, &key);
+    GNUNET_CONTAINER_multihashmap_get_multiple (slicer->handlers, &key,
+                                                &slicer_handler_notify, 
slicer);
+    char *p = strrchr (name, '_');
+    if (NULL == p)
+      break;
+    *p = '\0';
+  } while (1);
+  GNUNET_free (name);
+  slicer->msg = NULL;
+
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
+    GNUNET_free (slicer->method_name);
+}
+
+
+/**
  * Create a try-and-slice instance.
  *
  * @return A new try-and-slice construct.
@@ -128,7 +454,9 @@
 struct GNUNET_SOCIAL_Slicer *
 GNUNET_SOCIAL_slicer_create (void)
 {
-  return NULL;
+  struct GNUNET_SOCIAL_Slicer *slicer = GNUNET_malloc (sizeof (*slicer));
+  slicer->handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  return slicer;
 }
 
 
@@ -148,89 +476,524 @@
 GNUNET_SOCIAL_slicer_add (struct GNUNET_SOCIAL_Slicer *slicer,
                           const char *method_name,
                           GNUNET_SOCIAL_MethodCallback method_cb,
+                          GNUNET_SOCIAL_ModifierCallback modifier_cb,
+                          GNUNET_SOCIAL_DataCallback data_cb,
+                          GNUNET_SOCIAL_EndOfMessageCallback eom_cb,
                           void *cls)
 {
+  struct GNUNET_HashCode key;
+  GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
 
+  struct SlicerCallbacks *cbs = GNUNET_malloc (sizeof (*cbs));
+  cbs->method_cb = method_cb;
+  cbs->modifier_cb = modifier_cb;
+  cbs->data_cb = data_cb;
+  cbs->eom_cb = eom_cb;
+  cbs->cls = cls;
+
+  GNUNET_CONTAINER_multihashmap_put (slicer->handlers, &key, cbs,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
 }
 
 
+int
+slicer_remove_handler (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct SlicerRemoveClosure *rm_cls = cls;
+  struct GNUNET_SOCIAL_Slicer *slicer = rm_cls->slicer;
+  struct SlicerCallbacks *rm_cbs = &rm_cls->rm_cbs;
+  struct SlicerCallbacks *cbs = value;
+
+  if (cbs->method_cb == rm_cbs->method_cb
+      && cbs->modifier_cb == rm_cbs->modifier_cb
+      && cbs->data_cb == rm_cbs->data_cb
+      && cbs->eom_cb == rm_cbs->eom_cb)
+  {
+    GNUNET_CONTAINER_multihashmap_remove (slicer->handlers, key, cbs);
+    GNUNET_free (cbs);
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
+
+
 /**
  * Remove a registered method from the try-and-slice instance.
  *
+ * Removes the first matching handler registered with @a method and the given 
callbacks.
+ *
  * @param slicer The try-and-slice instance.
  * @param method_name Name of the method to remove.
  * @param method Method handler.
+ *
+ * @return #GNUNET_OK if a method handler was removed,
+ *         #GNUNET_NO if no handler matched the given method name and 
callbacks.
  */
-void
+int
 GNUNET_SOCIAL_slicer_remove (struct GNUNET_SOCIAL_Slicer *slicer,
                              const char *method_name,
-                             GNUNET_SOCIAL_MethodCallback method_cb)
+                             GNUNET_SOCIAL_MethodCallback method_cb,
+                             GNUNET_SOCIAL_ModifierCallback modifier_cb,
+                             GNUNET_SOCIAL_DataCallback data_cb,
+                             GNUNET_SOCIAL_EndOfMessageCallback eom_cb)
 {
+  struct GNUNET_HashCode key;
+  GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
 
+  struct SlicerRemoveClosure rm_cls;
+  rm_cls.slicer = slicer;
+  struct SlicerCallbacks *rm_cbs = &rm_cls.rm_cbs;
+  rm_cbs->method_cb = method_cb;
+  rm_cbs->modifier_cb = modifier_cb;
+  rm_cbs->data_cb = data_cb;
+  rm_cbs->eom_cb = eom_cb;
+
+  return
+    (GNUNET_SYSERR
+     == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->handlers, &key,
+                                                    &slicer_remove_handler,
+                                                    &rm_cls))
+    ? GNUNET_NO
+    : GNUNET_OK;
 }
 
+
+int
+slicer_free_handler (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+  struct SlicerCallbacks *cbs = value;
+  GNUNET_free (cbs);
+  return GNUNET_YES;
+}
+
+
 /**
  * Destroy a given try-and-slice instance.
  *
- * @param slicer slicer to destroy
+ * @param slicer
+ *        Slicer to destroy
  */
 void
 GNUNET_SOCIAL_slicer_destroy (struct GNUNET_SOCIAL_Slicer *slicer)
 {
+  GNUNET_CONTAINER_multihashmap_iterate (slicer->handlers, 
&slicer_free_handler,
+                                         NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (slicer->handlers);
+  GNUNET_free (slicer);
+}
 
+
+static void
+place_send_connect_msg (struct GNUNET_SOCIAL_Place *plc)
+{
+  uint16_t cmsg_size = ntohs (plc->connect_msg->size);
+  struct GNUNET_MessageHeader * cmsg = GNUNET_malloc (cmsg_size);
+  memcpy (cmsg, plc->connect_msg, cmsg_size);
+  GNUNET_CLIENT_MANAGER_transmit_now (plc->client, cmsg);
 }
 
 
+static void
+place_recv_message_ack (void *cls,
+                        struct GNUNET_CLIENT_MANAGER_Connection *client,
+                        const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Place *
+    plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
+  GNUNET_PSYC_transmit_got_ack (plc->tmit);
+}
+
+
+static void
+place_recv_message (void *cls,
+                    struct GNUNET_CLIENT_MANAGER_Connection *client,
+                    const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Place *
+    plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
+  GNUNET_PSYC_receive_message (plc->recv,
+                               (const struct GNUNET_PSYC_MessageHeader *) msg);
+}
+
+
+static void
+place_recv_disconnect (void *cls,
+                       struct GNUNET_CLIENT_MANAGER_Connection *client,
+                       const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Place *
+    plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc));
+
+  GNUNET_CLIENT_MANAGER_reconnect (client);
+  place_send_connect_msg (plc);
+}
+
+
+static void
+host_recv_enter_ack (void *cls,
+                     struct GNUNET_CLIENT_MANAGER_Connection *client,
+                     const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Host *
+    hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
+                                                   sizeof (struct 
GNUNET_SOCIAL_Place));
+
+  struct GNUNET_PSYC_CountersResultMessage *
+    cres = (struct GNUNET_PSYC_CountersResultMessage *) msg;
+  if (NULL != hst->enter_cb)
+    hst->enter_cb (hst->cb_cls, GNUNET_ntohll (cres->max_message_id));
+}
+
+
+static void
+host_recv_enter_request (void *cls,
+                         struct GNUNET_CLIENT_MANAGER_Connection *client,
+                         const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Host *
+    hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
+                                                   sizeof (struct 
GNUNET_SOCIAL_Place));
+  if (NULL == hst->answer_door_cb)
+     return;
+
+  const char *method_name = NULL;
+  struct GNUNET_ENV_Environment *env = NULL;
+  const void *data = NULL;
+  uint16_t data_size = 0;
+
+  const struct GNUNET_PSYC_JoinRequestMessage *
+    req = (const struct GNUNET_PSYC_JoinRequestMessage *) msg;
+  const struct GNUNET_PSYC_Message *entry_msg = NULL;
+  if (sizeof (*req) + sizeof (*entry_msg) <= ntohs (req->header.size))
+  {
+    entry_msg = (struct GNUNET_PSYC_Message *) &req[1];
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received entry_msg of type %u and size %u.\n",
+         ntohs (entry_msg->header.type), ntohs (entry_msg->header.size));
+
+    env = GNUNET_ENV_environment_create ();
+    if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_msg, &method_name, env,
+                                                &data, &data_size))
+    {
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+           "Ignoring invalid entry request from nym %s.\n",
+           GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_key));
+      GNUNET_break_op (0);
+      GNUNET_ENV_environment_destroy (env);
+      return;
+    }
+  }
+
+  struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_key);
+  hst->answer_door_cb (hst->cb_cls, nym, method_name, env,
+                       data_size, data);
+
+  if (NULL != env)
+    GNUNET_ENV_environment_destroy (env);
+}
+
+
+static void
+guest_recv_enter_ack (void *cls,
+                     struct GNUNET_CLIENT_MANAGER_Connection *client,
+                     const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Guest *
+    gst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
+                                                   sizeof (struct 
GNUNET_SOCIAL_Place));
+
+  struct GNUNET_PSYC_CountersResultMessage *
+    cres = (struct GNUNET_PSYC_CountersResultMessage *) msg;
+  if (NULL != gst->enter_cb)
+    gst->enter_cb (gst->cb_cls, ntohl (cres->result_code),
+                   GNUNET_ntohll (cres->max_message_id));
+}
+
+
+static void
+guest_recv_join_decision (void *cls,
+                          struct GNUNET_CLIENT_MANAGER_Connection *client,
+                          const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Guest *
+    gst = GNUNET_CLIENT_MANAGER_get_user_context_ (client,
+                                                   sizeof (struct 
GNUNET_SOCIAL_Place));
+  const struct GNUNET_PSYC_JoinDecisionMessage *
+    dcsn = (const struct GNUNET_PSYC_JoinDecisionMessage *) msg;
+
+  struct GNUNET_PSYC_Message *pmsg = NULL;
+  if (ntohs (dcsn->header.size) <= sizeof (*dcsn) + sizeof (*pmsg))
+    pmsg = (struct GNUNET_PSYC_Message *) &dcsn[1];
+
+  if (NULL != gst->entry_dcsn_cb)
+    gst->entry_dcsn_cb (gst->cb_cls, ntohl (dcsn->is_admitted), pmsg);
+}
+
+
+static struct GNUNET_CLIENT_MANAGER_MessageHandler host_handlers[] =
+{
+  { &host_recv_enter_ack, NULL,
+    GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK,
+    sizeof (struct CountersResult), GNUNET_NO },
+
+  { &host_recv_enter_request, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
+    sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES },
+
+  { &place_recv_message, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+    sizeof (struct GNUNET_PSYC_MessageHeader), GNUNET_YES },
+
+  { &place_recv_message_ack, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
+    sizeof (struct GNUNET_MessageHeader), GNUNET_NO },
+
+  { &place_recv_disconnect, NULL, 0, 0, GNUNET_NO },
+
+  { NULL, NULL, 0, 0, GNUNET_NO }
+};
+
+
+static struct GNUNET_CLIENT_MANAGER_MessageHandler guest_handlers[] =
+{
+  { &guest_recv_enter_ack, NULL,
+    GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK,
+    sizeof (struct CountersResult), GNUNET_NO },
+
+  { &host_recv_enter_request, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
+    sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES },
+
+  { &place_recv_message, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+    sizeof (struct GNUNET_PSYC_MessageHeader), GNUNET_YES },
+
+  { &place_recv_message_ack, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
+    sizeof (struct GNUNET_MessageHeader), GNUNET_NO },
+
+  { &guest_recv_join_decision, NULL,
+    GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
+    sizeof (struct GNUNET_PSYC_JoinDecisionMessage), GNUNET_YES },
+
+  { &place_recv_disconnect, NULL, 0, 0, GNUNET_NO },
+
+  { NULL, NULL, 0, 0, GNUNET_NO }
+};
+
+
+static void
+place_cleanup (struct GNUNET_SOCIAL_Place *plc)
+{
+  GNUNET_PSYC_transmit_destroy (plc->tmit);
+  GNUNET_PSYC_receive_destroy (plc->recv);
+  GNUNET_free (plc->connect_msg);
+  if (NULL != plc->disconnect_cb)
+    plc->disconnect_cb (plc->disconnect_cls);
+}
+
+
+static void
+host_cleanup (void *cls)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+  place_cleanup (&hst->plc);
+  GNUNET_free (hst);
+}
+
+
+static void
+guest_cleanup (void *cls)
+{
+  struct GNUNET_SOCIAL_Guest *gst = cls;
+  place_cleanup (&gst->plc);
+  GNUNET_free (gst);
+}
+
+
 /**
  * Enter a place as host.
  *
  * A place is created upon first entering, and it is active until permanently
  * left using GNUNET_SOCIAL_host_leave().
  *
- * @param cfg  Configuration to contact the social service.
- * @param place_keyfile  File with the private-public key pair of the place,
- *        created if the file does not exist; pass NULL for ephemeral places.
- * @param policy  Policy specifying entry and history restrictions of the 
place.
- * @param ego  Identity of the host.
- * @param slicer  Slicer to handle incoming messages.
- * @param listener_cb  Function to handle new nyms that want to enter.
- * @param farewell_cb  Function to handle departing nyms.
- * @param cls  Closure for @a listener_cb and @a farewell_cb.
+ * @param cfg
+ *        Configuration to contact the social service.
+ * @param ego
+ *        Identity of the host.
+ * @param place_key
+ *        Private-public key pair of the place.
+ *        NULL for ephemeral places.
+ * @param policy
+ *        Policy specifying entry and history restrictions for the place.
+ * @param slicer
+ *        Slicer to handle incoming messages.
+ * @param answer_door_cb
+ *        Function to handle new nyms that want to enter.
+ * @param farewell_cb
+ *        Function to handle departing nyms.
+ * @param cls
+ *        Closure for the callbacks.
  *
  * @return Handle for the host.
  */
 struct GNUNET_SOCIAL_Host *
 GNUNET_SOCIAL_host_enter (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                          const char *place_keyfile,
+                          const struct GNUNET_IDENTITY_Ego *ego,
+                          const struct GNUNET_CRYPTO_EddsaPrivateKey 
*place_key,
                           enum GNUNET_PSYC_Policy policy,
-                          struct GNUNET_IDENTITY_Ego *ego,
                           struct GNUNET_SOCIAL_Slicer *slicer,
-                          GNUNET_SOCIAL_AnswerDoorCallback listener_cb,
+                          GNUNET_SOCIAL_HostEnterCallback enter_cb,
+                          GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
                           GNUNET_SOCIAL_FarewellCallback farewell_cb,
                           void *cls)
 {
-  return NULL;
+  struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+  struct HostEnterRequest *req = GNUNET_malloc (sizeof (*req));
+
+  if (NULL != place_key)
+  {
+    hst->place_key = *place_key;
+  }
+  else
+  {
+    struct GNUNET_CRYPTO_EddsaPrivateKey *
+      ephemeral_key = GNUNET_CRYPTO_eddsa_key_create ();
+    hst->place_key = *ephemeral_key;
+    GNUNET_CRYPTO_eddsa_key_get_public (&hst->place_key, &plc->pub_key);
+    GNUNET_CRYPTO_eddsa_key_clear (ephemeral_key);
+    GNUNET_free (ephemeral_key);
+  }
+  plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
+
+  req->header.size = htons (sizeof (*req));
+  req->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
+  req->policy = policy;
+  req->place_key = hst->place_key;
+  req->host_key = plc->ego_key;
+
+  plc->connect_msg = (struct GNUNET_MessageHeader *) req;
+  plc->cfg = cfg;
+  plc->is_host = GNUNET_YES;
+  plc->slicer = slicer;
+
+  hst->plc.ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
+  hst->enter_cb = enter_cb;
+  hst->answer_door_cb = answer_door_cb;
+  hst->cb_cls = cls;
+
+  plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", host_handlers);
+  GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, hst, sizeof (*plc));
+
+  plc->tmit = GNUNET_PSYC_transmit_create (plc->client);
+  plc->recv = GNUNET_PSYC_receive_create (NULL, &slicer_message, plc->slicer);
+
+  place_send_connect_msg (plc);
+  return hst;
 }
 
 
 /**
- * Admit @a nym to the place.
+ * Enter a place as host.
  *
- * The @a nym reference will remain valid until either the @a host or @a nym
- * leaves the place.
+ * A place is created upon first entering, and it is active until permanently
+ * left using GNUNET_SOCIAL_host_leave().
  *
- * @param host  Host of the place.
- * @param nym  Handle for the entity that wants to enter.
+ * @param cfg
+ *        Configuration to contact the social service.
+ * @param ego
+ *        Identity of the host.
+ * @param gns_name
+ *        GNS name in the zone of the @a ego that contains the
+ *        public key of the place in a PLACE record.
+ * @param policy
+ *        Policy specifying entry and history restrictions for the place.
+ * @param slicer
+ *        Slicer to handle incoming messages.
+ * @param answer_door_cb
+ *        Function to handle new nyms that want to enter.
+ * @param farewell_cb
+ *        Function to handle departing nyms.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle for the host.
  */
-void
-GNUNET_SOCIAL_host_admit (struct GNUNET_SOCIAL_Host *host,
-                          struct GNUNET_SOCIAL_Nym *nym)
+struct GNUNET_SOCIAL_Host *
+GNUNET_SOCIAL_host_enter_by_name (const struct GNUNET_CONFIGURATION_Handle 
*cfg,
+                                  struct GNUNET_IDENTITY_Ego *ego,
+                                  const char *gns_name,
+                                  enum GNUNET_PSYC_Policy policy,
+                                  struct GNUNET_SOCIAL_Slicer *slicer,
+                                  GNUNET_SOCIAL_HostEnterCallback enter_cb,
+                                  GNUNET_SOCIAL_AnswerDoorCallback 
answer_door_cb,
+                                  GNUNET_SOCIAL_FarewellCallback farewell_cb,
+                                  void *cls)
 {
+  struct GNUNET_CRYPTO_EddsaPrivateKey place_key = {};
 
+  /* FIXME:
+   * 1. get public key by looking up PLACE entry under gns_name
+   *    in the zone of the ego.
+   * 2. get private key from $GNUNET_DATA_HOME/social/places/PUB_KEY_HASH
+   */
+
+  return GNUNET_SOCIAL_host_enter (cfg, ego, &place_key, policy, slicer,
+                                   enter_cb, answer_door_cb, farewell_cb, cls);
 }
 
 
 /**
+ * Decision whether to admit @a nym into the place or refuse entry.
+ *
+ * @param hst
+ *        Host of the place.
+ * @param nym
+ *        Handle for the entity that wanted to enter.
+ * @param is_admitted
+ *        #GNUNET_YES    if @a nym is admitted,
+ *        #GNUNET_NO     if @a nym is refused entry,
+ *        #GNUNET_SYSERR if we cannot answer the request.
+ * @param method_name
+ *        Method name for the rejection message.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param data
+ *        Data for the rejection message to send back.
+ * @param data_size
+ *        Number of bytes in @a data for method.
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if the message is too large.
+ */
+int
+GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
+                                   struct GNUNET_SOCIAL_Nym *nym,
+                                   int is_admitted,
+                                   const struct GNUNET_PSYC_Message 
*entry_resp)
+{
+  struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
+  uint16_t entry_resp_size
+    = (NULL != entry_resp) ? ntohs (entry_resp->header.size) : 0;
+
+  if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < sizeof (*dcsn) + entry_resp_size)
+    return GNUNET_SYSERR;
+
+  dcsn = GNUNET_malloc (sizeof (*dcsn) + entry_resp_size);
+  dcsn->header.size = htons (sizeof (*dcsn) + entry_resp_size);
+  dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
+  dcsn->is_admitted = htonl (is_admitted);
+  dcsn->slave_key = nym->pub_key;
+
+  if (0 < entry_resp_size)
+    memcpy (&dcsn[1], entry_resp, entry_resp_size);
+
+  GNUNET_CLIENT_MANAGER_transmit (hst->plc.client, &dcsn->header);
+  return GNUNET_OK;
+}
+
+
+/**
  * Throw @a nym out of the place.
  *
  * The @a nym reference will remain valid until the
@@ -249,63 +1012,60 @@
 
 
 /**
- * Refuse @a nym entry into the place.
+ * Get the public key of a @a nym.
  *
- * @param host  Host of the place.
- * @param nym Handle for the entity that wanted to enter.
- * @param method_name Method name for the rejection message.
- * @param env Environment containing variables for the message, or NULL.
- * @param data Data for the rejection message to send back.
- * @param data_size Number of bytes in @a data for method.
+ * Suitable, for example, to be used with GNUNET_NAMESTORE_zone_to_name().
+ *
+ * @param nym Pseudonym to map to a cryptographic identifier.
+ * @param[out] nym_key Set to the public key of the nym.
  */
-void
-GNUNET_SOCIAL_host_refuse_entry (struct GNUNET_SOCIAL_Host *host,
-                                 struct GNUNET_SOCIAL_Nym *nym,
-                                 const char *method_name,
-                                 const struct GNUNET_ENV_Environment *env,
-                                 const void *data,
-                                 size_t data_size)
+struct GNUNET_CRYPTO_EcdsaPublicKey *
+GNUNET_SOCIAL_nym_get_key (struct GNUNET_SOCIAL_Nym *nym)
 {
-
+  return &nym->pub_key;
 }
 
 
 /**
- * Get the public key of a @a nym.
+ * Obtain the private-public key pair of the hosted place.
  *
- * Suitable, for example, to be used with GNUNET_NAMESTORE_zone_to_name().
+ * The public part is suitable for storing in GNS within a PLACE record,
+ * along with peer IDs to join at.
  *
- * @param nym Pseudonym to map to a cryptographic identifier.
- * @param[out] nym_key Set to the public key of the nym.
+ * @param host
+ *        Host of the place.
+ *
+ * @return Private-public key pair of the hosted place.
  */
-void
-GNUNET_SOCIAL_nym_get_key (struct GNUNET_SOCIAL_Nym *nym,
-                           struct GNUNET_CRYPTO_EddsaPublicKey *nym_key)
+const struct GNUNET_CRYPTO_EddsaPrivateKey *
+GNUNET_SOCIAL_host_get_place_key (struct GNUNET_SOCIAL_Host *hst)
 {
+  return &hst->place_key;
+}
 
+
+static void
+namestore_result_host_advertise (void *cls, int32_t success, const char *emsg)
+{
+
 }
 
 
 /**
- * Obtain the private-public key pair of the host.
- *
- * @param host  Host to get the key of.
- * @param[out] host_key  Set to the private-public key pair of the host.  The
- *                 public part is suitable for storing in GNS within a "PLACE"
- *                 record, along with peer IDs to join at.
+ * Connected to core service.
  */
-void
-GNUNET_SOCIAL_host_get_key (struct GNUNET_SOCIAL_Host *host,
-                            struct GNUNET_CRYPTO_EddsaPrivateKey *host_key)
+static void
+core_connected_cb  (void *cls, const struct GNUNET_PeerIdentity *my_identity)
 {
-
+  this_peer = *my_identity;
+  // FIXME
 }
 
 
 /**
  * Advertise the place in the GNS zone of the @e ego of the @a host.
  *
- * @param host  Host of the place.
+ * @param hst  Host of the place.
  * @param name The name for the PLACE record to put in the zone.
  * @param peer_count Number of elements in the @a peers array.
  * @param peers List of peers in the PLACE record that can be used to send join
@@ -314,14 +1074,37 @@
  * @param password Password used to encrypt the record.
  */
 void
-GNUNET_SOCIAL_host_advertise (struct GNUNET_SOCIAL_Host *host,
+GNUNET_SOCIAL_host_advertise (struct GNUNET_SOCIAL_Host *hst,
                               const char *name,
                               size_t peer_count,
                               const struct GNUNET_PeerIdentity *peers,
                               struct GNUNET_TIME_Relative expiration_time,
                               const char *password)
 {
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+  if (NULL == namestore)
+    namestore = GNUNET_NAMESTORE_connect (plc->cfg);
+  if (NULL == core)
+    core = GNUNET_CORE_connect (plc->cfg, NULL, core_connected_cb, NULL, NULL,
+                                NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
 
+  struct GNUNET_GNSRECORD_Data rd = { 0 };
+  rd.record_type = GNUNET_GNSRECORD_TYPE_PLACE;
+  rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
+  rd.expiration_time
+    = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, 1).rel_value_us;
+
+  struct GNUNET_GNSRECORD_PlaceData *rec = GNUNET_malloc (sizeof (*rec));
+  rec->place_key = plc->pub_key;
+  rec->origin = this_peer;
+  rec->relay_count = htons (0); // FIXME
+
+  rd.data_size = sizeof (*rec);
+  rd.data = rec;
+
+  GNUNET_NAMESTORE_records_store (namestore, &hst->plc.ego_key,
+                                  name, 1, &rd, 
namestore_result_host_advertise,
+                                  hst);
 }
 
 
@@ -342,26 +1125,43 @@
  * @return NULL on error (announcement already in progress?).
  */
 struct GNUNET_SOCIAL_Announcement *
-GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *host,
+GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *hst,
                              const char *method_name,
                              const struct GNUNET_ENV_Environment *env,
-                             GNUNET_CONNECTION_TransmitReadyNotify notify,
-                             void *notify_cls,
+                             GNUNET_PSYC_TransmitNotifyData notify_data,
+                             void *notify_data_cls,
                              enum GNUNET_SOCIAL_AnnounceFlags flags)
 {
-  return NULL;
+  if (GNUNET_OK ==
+      GNUNET_PSYC_transmit_message (hst->plc.tmit, method_name, env,
+                                    NULL, notify_data, notify_data_cls, 
flags));
+  return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit;
 }
 
 
 /**
+ * Resume transmitting announcement.
+ *
+ * @param a
+ *        The announcement to resume.
+ */
+void
+GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a)
+{
+  GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) a);
+}
+
+
+/**
  * Cancel announcement.
  *
- * @param a The announcement to cancel.
+ * @param a
+ *        The announcement to cancel.
  */
 void
 GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a)
 {
-
+  GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) a);
 }
 
 
@@ -375,9 +1175,9 @@
  * @return Handle for the hosted place, valid as long as @a host is valid.
  */
 struct GNUNET_SOCIAL_Place *
-GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *host)
+GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *hst)
 {
-  return NULL;
+  return &hst->plc;
 }
 
 
@@ -390,22 +1190,65 @@
  * @param keep_active  Keep the place active after last host disconnected.
  */
 void
-GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *host, int keep_active)
+GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst,
+                          int keep_active,
+                          GNUNET_ContinuationCallback leave_cb,
+                          void *leave_cls)
 {
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
 
+ /* FIXME: send msg to service */
+
+  plc->is_disconnecting = GNUNET_YES;
+  plc->disconnect_cb = leave_cb;
+  plc->disconnect_cls = leave_cls;
+
+  GNUNET_CLIENT_MANAGER_disconnect (plc->client, GNUNET_YES,
+                                    &host_cleanup, hst);
 }
 
 
+static struct GuestEnterRequest *
+guest_enter_request_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey 
*guest_key,
+                            const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_key,
+                            const struct GNUNET_PeerIdentity *origin,
+                            size_t relay_count,
+                            const struct GNUNET_PeerIdentity *relays,
+                            const struct GNUNET_PSYC_Message *join_msg)
+{
+  uint16_t join_msg_size = ntohs (join_msg->header.size);
+  uint16_t relay_size = relay_count * sizeof (*relays);
+
+  struct GuestEnterRequest *
+    req = GNUNET_malloc (sizeof (*req) + relay_size + join_msg_size);
+
+  req->header.size = htons (sizeof (*req) + relay_size + join_msg_size);
+  req->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
+  req->place_key = *place_key;
+  req->guest_key = *guest_key;
+  req->origin = *origin;
+  req->relay_count = relay_count;
+
+  uint16_t p = sizeof (*req);
+  if (0 < relay_size)
+  {
+    memcpy ((char *) req + p, relays, relay_size);
+    p += relay_size;
+  }
+
+  memcpy ((char *) req + p, join_msg, join_msg_size);
+  return req;
+}
+
 /**
  * Request entry to a place as a guest.
  *
- * @param cfg  Configuration to contact the social service.
+ * @param cfg Configuration to contact the social service.
  * @param ego  Identity of the guest.
- * @param address GNS name of the place to enter.  Either in the form of
- *        'room.friend.gnu', or 'NYMPUBKEY.zkey'.  This latter case refers to
- *        the 'PLACE' record of the empty label ("+") in the GNS zone with the
- *        nym's public key 'NYMPUBKEY', and can be used to request entry to a
- *        pseudonym's place directly.
+ * @param crypto_address Public key of the place to enter.
+ * @param origin Peer identity of the origin of the underlying multicast group.
+ * @param relay_count Number of elements in the @a relays array.
+ * @param relays Relays for the underlying multicast group.
  * @param method_name Method name for the message.
  * @param env Environment containing variables for the message, or NULL.
  * @param data Payload for the message to give to the enter callback.
@@ -416,26 +1259,132 @@
  */
 struct GNUNET_SOCIAL_Guest *
 GNUNET_SOCIAL_guest_enter (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                           struct GNUNET_IDENTITY_Ego *ego,
-                           char *address,
-                           const char *method_name,
-                           const struct GNUNET_ENV_Environment *env,
-                           const void *data,
-                           size_t data_size,
-                           struct GNUNET_SOCIAL_Slicer *slicer)
+                           const struct GNUNET_IDENTITY_Ego *ego,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_key,
+                           const struct GNUNET_PeerIdentity *origin,
+                           uint32_t relay_count,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_PSYC_Message *entry_msg,
+                           struct GNUNET_SOCIAL_Slicer *slicer,
+                           GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
+                           GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb,
+                           void *cls)
 {
-  return NULL;
+  struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  struct GuestEnterRequest *
+    req = guest_enter_request_create (&plc->ego_key, place_key, origin,
+                                      relay_count, relays, entry_msg);
+  plc->connect_msg = &req->header;
+  plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
+  plc->pub_key = *place_key;
+  plc->cfg = cfg;
+  plc->is_host = GNUNET_YES;
+  plc->slicer = slicer;
+
+  gst->enter_cb = local_enter_cb;
+  gst->entry_dcsn_cb = entry_dcsn_cb;
+  gst->cb_cls = cls;
+
+  plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", guest_handlers);
+  GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, gst, sizeof (*plc));
+
+  plc->tmit = GNUNET_PSYC_transmit_create (plc->client);
+  plc->recv = GNUNET_PSYC_receive_create (NULL, &slicer_message, plc->slicer);
+
+  place_send_connect_msg (plc);
+  return gst;
 }
 
+
 /**
+ * Result of a GNS name lookup for entering a place.
+ *
+ * @see GNUNET_SOCIAL_guest_enter_by_name
+ */
+static void
+gns_result_guest_enter (void *cls, uint32_t rd_count,
+                        const struct GNUNET_GNSRECORD_Data *rd)
+{
+  struct GNUNET_SOCIAL_Guest *gst = cls;
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  const struct GNUNET_GNSRECORD_PlaceData *
+    rec = (const struct GNUNET_GNSRECORD_PlaceData *) rd->data;
+
+  if (0 == rd_count)
+  {
+    if (NULL != gst->enter_cb)
+      gst->enter_cb (gst->cb_cls, GNUNET_SYSERR, 0);
+    return;
+  }
+
+
+  if (rd->data_size < sizeof (*rec))
+  {
+    GNUNET_break_op (0);
+    if (NULL != gst->enter_cb)
+      gst->enter_cb (gst->cb_cls, GNUNET_SYSERR, 0);
+    return;
+  }
+
+  struct GuestEnterRequest *
+    req = (struct GuestEnterRequest *) plc->connect_msg;
+  uint16_t req_size = ntohs (req->header.size);
+
+  struct GNUNET_PeerIdentity *relays = NULL;
+  uint16_t relay_count = ntohs (rec->relay_count);
+
+  if (0 < relay_count)
+  {
+    uint16_t relay_size = relay_count * sizeof (struct GNUNET_PeerIdentity);
+    struct GuestEnterRequest *
+      req2 = GNUNET_malloc (req_size + relay_size);
+
+    req2->header.size = htons (req_size + relay_size);
+    req2->header.type = req->header.type;
+    req2->guest_key = req->guest_key;
+
+    uint16_t p = sizeof (*req);
+    if (0 < relay_size)
+    {
+      memcpy ((char *) req2 + p, relays, relay_size);
+      p += relay_size;
+    }
+
+    memcpy ((char *) req + p, &req[1], req_size - sizeof (*req));
+
+    plc->connect_msg = &req2->header;
+    GNUNET_free (req);
+    req = req2;
+  }
+
+  req->place_key = rec->place_key;
+  req->origin = rec->origin;
+  req->relay_count = rec->relay_count;
+  memcpy (&req[1], &rec[1],
+          ntohl (rec->relay_count) * sizeof (struct GNUNET_PeerIdentity));
+
+  plc->connect_msg = &req->header;
+  plc->pub_key = req->place_key;
+
+  plc->tmit = GNUNET_PSYC_transmit_create (plc->client);
+  plc->recv = GNUNET_PSYC_receive_create (NULL, &slicer_message, plc);
+
+  place_send_connect_msg (plc);
+}
+
+/**
  * Request entry to a place as a guest.
  *
- * @param cfg Configuration to contact the social service.
+ * @param cfg  Configuration to contact the social service.
  * @param ego  Identity of the guest.
- * @param crypto_address Public key of the place to enter.
- * @param origin Peer identity of the origin of the underlying multicast group.
- * @param relay_count Number of elements in the @a relays array.
- * @param relays Relays for the underlying multicast group.
+ * @param address GNS name of the place to enter.  Either in the form of
+ *        'room.friend.gnu', or 'NYMPUBKEY.zkey'.  This latter case refers to
+ *        the 'PLACE' record of the empty label ("+") in the GNS zone with the
+ *        nym's public key 'NYMPUBKEY', and can be used to request entry to a
+ *        pseudonym's place directly.
  * @param method_name Method name for the message.
  * @param env Environment containing variables for the message, or NULL.
  * @param data Payload for the message to give to the enter callback.
@@ -445,41 +1394,75 @@
  * @return NULL on errors, otherwise handle for the guest.
  */
 struct GNUNET_SOCIAL_Guest *
-GNUNET_SOCIAL_guest_enter2 (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                            struct GNUNET_IDENTITY_Ego *ego,
-                            struct GNUNET_CRYPTO_EddsaPublicKey 
*crypto_address,
-                            struct GNUNET_PeerIdentity *origin,
-                            size_t relay_count,
-                            struct GNUNET_PeerIdentity *relays,
-                            const char *method_name,
-                            const struct GNUNET_ENV_Environment *env,
-                            const void *data,
-                            size_t data_size,
-                            struct GNUNET_SOCIAL_Slicer *slicer)
+GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_CONFIGURATION_Handle 
*cfg,
+                                   struct GNUNET_IDENTITY_Ego *ego,
+                                   char *gns_name,
+                                   const struct GNUNET_PSYC_Message *join_msg,
+                                   struct GNUNET_SOCIAL_Slicer *slicer,
+                                   GNUNET_SOCIAL_GuestEnterCallback 
local_enter_cb,
+                                   GNUNET_SOCIAL_EntryDecisionCallback 
entry_decision_cb,
+                                   void *cls)
 {
-  return NULL;
+  struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  gst->enter_cb = local_enter_cb;
+  gst->cb_cls = cls;
+
+  plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego);
+  plc->cfg = cfg;
+  plc->is_host = GNUNET_NO;
+  plc->slicer = slicer;
+
+  struct GuestEnterRequest *
+    req = guest_enter_request_create (&plc->ego_key, NULL, NULL, 0, NULL,
+                                      join_msg);
+  plc->connect_msg = &req->header;
+
+  /* FIXME: get the public key of the origin and relays
+   *        by looking up the PLACE record of gns_name.
+   */
+  if (NULL == gns)
+    gns = GNUNET_GNS_connect (cfg);
+
+  plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", guest_handlers);
+  GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, gst, sizeof (*plc));
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+  GNUNET_IDENTITY_ego_get_public_key (ego, &ego_pub_key);
+  GNUNET_GNS_lookup (gns, gns_name, &ego_pub_key,
+                     GNUNET_GNSRECORD_TYPE_PLACE, GNUNET_GNS_LO_DEFAULT,
+                     NULL, gns_result_guest_enter, gst);
+
+  return gst;
 }
 
 
 /**
  * Talk to the host of the place.
  *
- * @param place Place where we want to talk to the host.
- * @param method_name Method to invoke on the host.
- * @param env Environment containing variables for the message, or NULL.
- * @param notify Function to use to get the payload for the method.
- * @param notify_cls Closure for @a notify.
- * @param flags Flags for the message being sent.
+ * @param place
+ *        Place where we want to talk to the host.
+ * @param method_name
+ *        Method to invoke on the host.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param notify_data
+ *        Function to use to get the payload for the method.
+ * @param notify_data_cls
+ *        Closure for @a notify_data.
+ * @param flags
+ *        Flags for the message being sent.
  *
  * @return NULL if we are already trying to talk to the host,
  *         otherwise handle to cancel the request.
  */
 struct GNUNET_SOCIAL_TalkRequest *
-GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Place *place,
+GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *guest,
                           const char *method_name,
                           const struct GNUNET_ENV_Environment *env,
-                          GNUNET_CONNECTION_TransmitReadyNotify notify,
-                          void *notify_cls,
+                          GNUNET_PSYC_TransmitNotifyData notify_data,
+                          void *notify_data_cls,
                           enum GNUNET_SOCIAL_TalkFlags flags)
 {
   return NULL;
@@ -487,14 +1470,28 @@
 
 
 /**
+ * Resume talking to the host of the place.
+ *
+ * @param tr
+ *        Talk request to resume.
+ */
+void
+GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr)
+{
+  GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tr);
+}
+
+
+/**
  * Cancel talking to the host of the place.
  *
- * @param tr Talk request to cancel.
+ * @param tr
+ *        Talk request to cancel.
  */
 void
 GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr)
 {
-
+  GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) tr);
 }
 
 
@@ -507,9 +1504,21 @@
  * @param keep_active Keep place active after last application disconnected.
  */
 void
-GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Place *place, int keep_active)
+GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst,
+                           int keep_active,
+                           GNUNET_ContinuationCallback leave_cb,
+                           void *leave_cls)
 {
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
 
+  /* FIXME: send msg to service */
+
+  plc->is_disconnecting = GNUNET_YES;
+  plc->disconnect_cb = leave_cb;
+  plc->disconnect_cls = leave_cls;
+
+  GNUNET_CLIENT_MANAGER_disconnect (plc->client, GNUNET_YES,
+                                    &guest_cleanup, gst);
 }
 
 
@@ -523,9 +1532,9 @@
  * @return Handle for the place, valid as long as @a guest is valid.
  */
 struct GNUNET_SOCIAL_Place *
-GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Host *guest)
+GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *gst)
 {
-  return NULL;
+  return &gst->plc;
 }
 
 
@@ -671,6 +1680,4 @@
 }
 
 
-
-
 /* end of social_api.c */

Modified: gnunet/src/social/test_social.c
===================================================================
--- gnunet/src/social/test_social.c     2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/social/test_social.c     2014-07-23 16:19:49 UTC (rev 34021)
@@ -17,7 +17,6 @@
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
-
 /**
  * @file social/test_social.c
  * @brief Tests for the Social API.
@@ -32,32 +31,132 @@
 #include "gnunet_util_lib.h"
 #include "gnunet_testing_lib.h"
 #include "gnunet_env_lib.h"
+#include "gnunet_psyc_util_lib.h"
 #include "gnunet_social_service.h"
+#include "gnunet_core_service.h"
+#include "gnunet_identity_service.h"
 
 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
 
 #define DEBUG_SERVICE 0
+#define DATA2ARG(data) data, sizeof (data)
 
 /**
  * Return value from 'main'.
  */
-static int res;
+int res;
 
-static const struct GNUNET_CONFIGURATION_Handle *cfg;
-
 /**
  * Handle for task for timeout termination.
  */
-static GNUNET_SCHEDULER_TaskIdentifier end_badly_task;
+GNUNET_SCHEDULER_TaskIdentifier end_badly_task;
 
+const struct GNUNET_CONFIGURATION_Handle *cfg;
 
+struct GNUNET_CORE_Handle *core;
+struct GNUNET_PeerIdentity this_peer;
+
+struct GNUNET_IDENTITY_Handle *id;
+
+const struct GNUNET_IDENTITY_Ego *host_ego;
+const struct GNUNET_IDENTITY_Ego *guest_ego;
+
+const char *host_name = "Host One";
+const char *guest_name = "Guest One";
+
+struct GNUNET_CRYPTO_EddsaPrivateKey *place_key;
+struct GNUNET_CRYPTO_EcdsaPrivateKey *guest_key;
+
+struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+struct GNUNET_CRYPTO_EcdsaPublicKey guest_pub_key;
+
+struct GNUNET_SOCIAL_Slicer *host_slicer;
+struct GNUNET_SOCIAL_Slicer *guest_slicer;
+
+struct GNUNET_SOCIAL_Host *hst;
+struct GNUNET_SOCIAL_Guest *gst;
+
+struct GuestEnterMessage
+{
+  struct GNUNET_PSYC_Message *msg;
+  const char *method_name;
+  struct GNUNET_ENV_Environment *env;
+  void *data;
+  uint16_t data_size;
+} guest_enter_msg;
+
+struct TransmitClosure
+{
+  struct GNUNET_SOCIAL_Announcement *host_ann;
+  struct GNUNET_SOCIAL_TalkRequest *guest_talk;
+  struct GNUNET_ENV_Environment *env;
+  char *data[16];
+  uint8_t data_delay[16];
+  uint8_t data_count;
+  uint8_t paused;
+  uint8_t n;
+} tmit;
+
+uint8_t join_req_count;
+struct GNUNET_PSYC_Message *join_resp;
+
+enum
+{
+  TEST_NONE = 0,
+  TEST_HOST_ANSWER_DOOR_REFUSE      = 1,
+  TEST_GUEST_RECV_ENTRY_DCSN_REFUSE = 2,
+  TEST_HOST_ANSWER_DOOR_ADMIT       = 3,
+  TEST_GUEST_RECV_ENTRY_DCSN_ADMIT  = 4,
+  TEST_HOST_ANNOUNCE     = 5,
+  TEST_HOST_ANNOUNCE_END = 6,
+  TEST_GUEST_TALK        = 7,
+  TEST_GUEST_TALK_END    = 8,
+  TEST_GUEST_LEAVE       = 9,
+  TEST_HOST_LEAVE       = 10,
+} test;
+
+
+void
+guest_enter ();
+
+
+void
+guest_talk ();
+
+
+void
+host_announce2 ();
+
+
 /**
  * Clean up all resources used.
  */
-static void
+void
 cleanup ()
 {
+  if (NULL != core)
+  {
+    GNUNET_CORE_disconnect (core);
+    core = NULL;
+  }
 
+  if (NULL != id)
+  {
+    GNUNET_IDENTITY_disconnect (id);
+    id = NULL;
+  }
+
+  if (NULL != gst)
+  {
+    GNUNET_SOCIAL_guest_leave (gst, GNUNET_NO, NULL, NULL);
+    gst = NULL;
+  }
+  if (NULL != hst)
+  {
+    GNUNET_SOCIAL_host_leave (hst, GNUNET_NO, NULL, NULL);
+    hst = NULL;
+  }
+  GNUNET_SCHEDULER_shutdown ();
 }
 
 
@@ -67,7 +166,7 @@
  * @param cls NULL
  * @param tc scheduler context
  */
-static void
+void
 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   res = 1;
@@ -82,7 +181,7 @@
  * @param cls NULL
  * @param tc scheduler context
  */
-static void
+void
 end_normally (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   res = 0;
@@ -94,7 +193,7 @@
 /**
  * Finish the test case (successfully).
  */
-static void
+void
 end ()
 {
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending tests.\n");
@@ -109,6 +208,564 @@
 }
 
 
+static void
+transmit_resume (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission resumed.\n");
+  struct TransmitClosure *tmit = cls;
+  if (NULL != tmit->host_ann)
+    GNUNET_SOCIAL_host_announce_resume (tmit->host_ann);
+  else
+    GNUNET_SOCIAL_guest_talk_resume (tmit->guest_talk);
+}
+
+
+int
+notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  struct TransmitClosure *tmit = cls;
+  if (NULL != tmit->env)
+  {
+    GNUNET_ENV_environment_destroy (tmit->env);
+    tmit->env = NULL;
+  }
+  if (0 == tmit->data_count)
+  {
+    *data_size = 0;
+    return GNUNET_YES;
+  }
+
+  uint16_t size = strlen (tmit->data[tmit->n]) + 1;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Transmit notify data: %u bytes available, "
+              "processing fragment %u/%u (size %u).\n",
+              *data_size, tmit->n + 1, tmit->data_count, size);
+  if (*data_size < size)
+  {
+    *data_size = 0;
+    GNUNET_assert (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission paused.\n");
+    tmit->paused = GNUNET_YES;
+    GNUNET_SCHEDULER_add_delayed (
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                     tmit->data_delay[tmit->n]),
+      &transmit_resume, tmit);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  tmit->paused = GNUNET_NO;
+
+  *data_size = size;
+  memcpy (data, tmit->data[tmit->n], size);
+
+  return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
+}
+
+
+void host_left ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "The host has left the place.\n");
+  GNUNET_SOCIAL_slicer_destroy (host_slicer);
+  host_slicer = NULL;
+  hst = NULL;
+
+  end ();
+}
+
+
+void
+schedule_host_leave (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  test = TEST_HOST_LEAVE;
+  GNUNET_SOCIAL_host_leave (hst, GNUNET_NO, &host_left, NULL);
+}
+
+
+void
+host_farewell (void *cls,
+               struct GNUNET_SOCIAL_Nym *nym,
+               struct GNUNET_ENV_Environment *env,
+               size_t variable_count,
+               struct GNUNET_ENV_Modifier *variables)
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey *
+    nym_key = GNUNET_SOCIAL_nym_get_key (nym);
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Nym %s has left the place.\n",
+              GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key));
+  GNUNET_assert (0 == memcmp (&guest_pub_key, nym_key, sizeof (*nym_key)));
+
+  GNUNET_SCHEDULER_add_now (&schedule_host_leave, NULL);
+}
+
+
+void
+guest_left (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "The guest has left the place.\n");
+  GNUNET_SOCIAL_slicer_destroy (guest_slicer);
+  guest_slicer = NULL;
+  gst = NULL;
+
+  GNUNET_SCHEDULER_add_now (&schedule_host_leave, NULL);
+}
+
+
+void
+schedule_guest_leave (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  test = TEST_GUEST_LEAVE;
+  /* FIXME test keep_active */
+  GNUNET_SOCIAL_guest_leave (gst, GNUNET_NO, &guest_left, NULL);
+}
+
+
+
+void
+guest_recv_method (void *cls,
+                  const struct GNUNET_PSYC_MessageMethod *meth,
+                  uint64_t message_id,
+                  uint32_t flags,
+                  const struct GNUNET_SOCIAL_Nym *nym,
+                  const char *method_name)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Guest received method for message ID %" PRIu64 ":\n"
+              "%s\n",
+              test, message_id, method_name);
+  /* FIXME: check message */
+}
+
+
+void
+guest_recv_modifier (void *cls,
+                    const struct GNUNET_PSYC_MessageModifier *mod,
+                    uint64_t message_id,
+                    enum GNUNET_ENV_Operator oper,
+                    const char *name,
+                    const void *value,
+                    uint16_t value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Guest received modifier for message ID %" PRIu64 ":\n"
+              "%c%s: %.*s\n",
+              test, message_id, oper, name, value_size, value);
+}
+
+
+void
+guest_recv_data (void *cls,
+                const struct GNUNET_MessageHeader *msg,
+                uint64_t message_id,
+                uint64_t data_offset,
+                const void *data,
+                uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Guest received data for message ID %" PRIu64 ":\n"
+              "%.*s\n",
+              test, message_id, data_size, data);
+}
+
+
+void
+guest_recv_eom (void *cls,
+               const struct GNUNET_MessageHeader *msg,
+               uint64_t message_id,
+               uint8_t cancelled)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Guest received end of message ID %" PRIu64
+              ", cancelled: %u\n",
+              test, message_id, cancelled);
+
+  switch (test)
+  {
+  case TEST_HOST_ANNOUNCE:
+    test = TEST_HOST_ANNOUNCE_END;
+    break;
+
+  case TEST_HOST_ANNOUNCE_END:
+    guest_talk ();
+    break;
+
+  case TEST_GUEST_TALK:
+    test = TEST_GUEST_TALK_END;
+    break;
+
+  case TEST_GUEST_TALK_END:
+    GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
+    break;
+
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+void
+host_recv_method (void *cls,
+                  const struct GNUNET_PSYC_MessageMethod *meth,
+                  uint64_t message_id,
+                  uint32_t flags,
+                  const struct GNUNET_SOCIAL_Nym *nym,
+                  const char *method_name)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Host received method for message ID %" PRIu64 ":\n"
+              "%s\n",
+              test, message_id, method_name);
+  /* FIXME: check message */
+}
+
+
+void
+host_recv_modifier (void *cls,
+                    const struct GNUNET_PSYC_MessageModifier *mod,
+                    uint64_t message_id,
+                    enum GNUNET_ENV_Operator oper,
+                    const char *name,
+                    const void *value,
+                    uint16_t value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Host received modifier for message ID %" PRIu64 ":\n"
+              "%c%s: %.*s\n",
+              test, message_id, oper, name, value_size, value);
+}
+
+
+void
+host_recv_data (void *cls,
+                const struct GNUNET_MessageHeader *msg,
+                uint64_t message_id,
+                uint64_t data_offset,
+                const void *data,
+                uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Host received data for message ID %" PRIu64 ":\n"
+              "%.*s\n",
+              test, message_id, data_size, data);
+}
+
+
+void
+host_recv_eom (void *cls,
+               const struct GNUNET_MessageHeader *msg,
+               uint64_t message_id,
+               uint8_t cancelled)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Test #%u: Host received end of message ID %" PRIu64
+              ", cancelled: %u\n",
+              test, message_id, cancelled);
+
+  switch (test)
+  {
+  case TEST_HOST_ANNOUNCE:
+    test = TEST_HOST_ANNOUNCE_END;
+    //host_announce2 ();
+    break;
+
+  case TEST_HOST_ANNOUNCE_END:
+    guest_talk ();
+    break;
+
+  case TEST_GUEST_TALK:
+    test = TEST_GUEST_TALK_END;
+    break;
+
+  case TEST_GUEST_TALK_END:
+    GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
+    break;
+
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+void
+guest_talk ()
+{
+  test = TEST_GUEST_TALK;
+
+  tmit = (struct TransmitClosure) {};
+  tmit.env = GNUNET_ENV_environment_create ();
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_bar_foo", DATA2ARG ("one two three"));
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_bar_baz", DATA2ARG ("four five"));
+  tmit.data[0] = "zzz xxx yyy";
+  tmit.data[1] = "zyx wvu tsr qpo";
+  tmit.data[2] = "testing ten nine eight";
+  tmit.data_count = 3;
+
+  tmit.host_ann
+    = GNUNET_SOCIAL_host_announce (hst, "_message_guest", tmit.env,
+                                   &notify_data, &tmit,
+                                   GNUNET_SOCIAL_TALK_NONE);
+}
+
+void
+host_announce ()
+{
+  test = TEST_HOST_ANNOUNCE;
+
+  tmit = (struct TransmitClosure) {};
+  tmit.env = GNUNET_ENV_environment_create ();
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_foo", DATA2ARG ("bar baz"));
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_foo_bar", DATA2ARG ("foo bar"));
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_foo_bar_baz", DATA2ARG ("foo bar baz"));
+  tmit.data[0] = "aaa bbb ccc";
+  tmit.data[1] = "abc def ghi jkl";
+  tmit.data[2] = "testing one two three";
+  tmit.data[3] = "four five";
+  tmit.data_count = 4;
+
+  tmit.host_ann
+    = GNUNET_SOCIAL_host_announce (hst, "_message_host", tmit.env,
+                                   &notify_data, &tmit,
+                                   GNUNET_SOCIAL_ANNOUNCE_NONE);
+}
+
+void
+host_announce2 ()
+{
+  test = TEST_HOST_ANNOUNCE;
+
+  tmit = (struct TransmitClosure) {};
+  tmit.env = GNUNET_ENV_environment_create ();
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_foo2", DATA2ARG ("BAR BAZ"));
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_foo2_bar", DATA2ARG ("FOO BAR"));
+  GNUNET_ENV_environment_add (tmit.env, GNUNET_ENV_OP_ASSIGN,
+                              "_foo2_bar", DATA2ARG ("FOO BAR BAZ"));
+  tmit.data[0] = "AAA BBB CCC";
+  tmit.data[1] = "ABC DEF GHI JKL";
+  tmit.data[2] = "TESTING ONE TWO THREE";
+  tmit.data_count = 3;
+
+  tmit.host_ann
+    = GNUNET_SOCIAL_host_announce (hst, "_message_host_two", tmit.env,
+                                   &notify_data, &tmit,
+                                   GNUNET_SOCIAL_ANNOUNCE_NONE);
+}
+
+
+void
+guest_recv_entry_decision (void *cls,
+                           int is_admitted,
+                           const struct GNUNET_PSYC_Message *entry_resp)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Guest received entry decision (try %u): %d.\n",
+              join_req_count, is_admitted);
+
+  if (NULL != entry_resp)
+  {
+    struct GNUNET_ENV_Environment *env = GNUNET_ENV_environment_create ();
+    const char *method_name = NULL;
+    const void *data = NULL;
+    uint16_t data_size = 0;
+    GNUNET_PSYC_message_parse (entry_resp, &method_name, env, &data, 
&data_size);
+
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%s\n%.*s\n",
+                method_name, data_size, data);
+    /* FIXME: check response message */
+  }
+
+  switch (test)
+  {
+  case TEST_GUEST_RECV_ENTRY_DCSN_REFUSE:
+    GNUNET_assert (GNUNET_NO == is_admitted);
+    guest_enter ();
+    break;
+
+  case TEST_GUEST_RECV_ENTRY_DCSN_ADMIT:
+    GNUNET_assert (GNUNET_YES == is_admitted);
+    host_announce ();
+    break;
+
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+void
+host_answer_door (void *cls,
+                  struct GNUNET_SOCIAL_Nym *nym,
+                  const char *method_name,
+                  struct GNUNET_ENV_Environment *env,
+                  size_t data_size,
+                  const void *data)
+{
+  join_req_count++;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Host received entry request from guest (try %u).\n",
+                join_req_count);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%s\n%.*s\n",
+              method_name, data_size, data);
+
+  switch (test)
+  {
+  case TEST_HOST_ANSWER_DOOR_REFUSE:
+    test = TEST_GUEST_RECV_ENTRY_DCSN_REFUSE;
+    join_resp = GNUNET_PSYC_message_create ("_refuse_nym", env,
+                                            DATA2ARG ("Go away!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, join_resp);
+    break;
+
+  case TEST_HOST_ANSWER_DOOR_ADMIT:
+    test = TEST_GUEST_RECV_ENTRY_DCSN_ADMIT;
+    join_resp = GNUNET_PSYC_message_create ("_admit_nym", env,
+                                            DATA2ARG ("Welcome, nym!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, join_resp);
+    break;
+
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+void
+guest_recv_local_enter (void *cls, int result, uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Guest entered to local place.\n");
+
+}
+
+
+void
+guest_enter ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Entering to place as guest.\n");
+
+  struct GuestEnterMessage *emsg = &guest_enter_msg;
+
+  emsg->method_name = "_request_enter";
+  emsg->env = GNUNET_ENV_environment_create ();
+  GNUNET_ENV_environment_add (emsg->env, GNUNET_ENV_OP_ASSIGN,
+                              "_abc", "abc def", 7);
+  GNUNET_ENV_environment_add (emsg->env, GNUNET_ENV_OP_ASSIGN,
+                              "_abc_def", "abc def ghi", 11);
+  emsg->data = "let me in";
+  emsg->data_size = strlen (emsg->data) + 1;
+  emsg->msg = GNUNET_PSYC_message_create (emsg->method_name, emsg->env,
+                                          emsg->data, emsg->data_size);
+
+  gst = GNUNET_SOCIAL_guest_enter (cfg, guest_ego, &place_pub_key,
+                                   &this_peer, 0, NULL, emsg->msg,
+                                   guest_slicer, &guest_recv_local_enter,
+                                   &guest_recv_entry_decision, NULL);
+}
+
+
+void id_guest_ego_cb (void *cls, const struct GNUNET_IDENTITY_Ego *ego)
+{
+  GNUNET_assert (NULL != ego);
+  guest_ego = ego;
+
+  guest_slicer = GNUNET_SOCIAL_slicer_create ();
+  GNUNET_SOCIAL_slicer_add (guest_slicer, "",
+                            &guest_recv_method, &guest_recv_modifier,
+                            &guest_recv_data, &guest_recv_eom, NULL);
+  test = TEST_HOST_ANSWER_DOOR_ADMIT;
+  //host_announce ();
+  guest_enter ();
+}
+
+
+void id_guest_created (void *cls, const char *emsg)
+{
+  if (NULL != emsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not create guest identity: %s\n", emsg);
+#if 0 == DEBUG_SERVICE
+    GNUNET_assert (0);
+#endif
+  }
+
+ GNUNET_IDENTITY_ego_lookup (cfg, guest_name, &id_guest_ego_cb, NULL);
+}
+
+
+void host_entered (void *cls, uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Host entered to place.\n");
+
+  GNUNET_IDENTITY_create (id, guest_name, &id_guest_created, NULL);
+}
+
+
+void id_host_ego_cb (void *cls, const struct GNUNET_IDENTITY_Ego *ego)
+{
+  GNUNET_assert (NULL != ego);
+  host_ego = ego;
+
+  host_slicer = GNUNET_SOCIAL_slicer_create ();
+  GNUNET_SOCIAL_slicer_add (host_slicer, "",
+                            &host_recv_method, &host_recv_modifier,
+                            &host_recv_data, &host_recv_eom, NULL);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Entering to place as host.\n");
+  hst = GNUNET_SOCIAL_host_enter (cfg, host_ego, place_key,
+                                  GNUNET_PSYC_CHANNEL_PRIVATE, host_slicer,
+                                  &host_entered, &host_answer_door,
+                                  &host_farewell, NULL);
+}
+
+
+void id_host_created (void *cls, const char *emsg)
+{
+  if (NULL != emsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not create host identity: %s\n", emsg);
+#if 0 == DEBUG_SERVICE
+    GNUNET_assert (0);
+#endif
+  }
+
+  GNUNET_IDENTITY_ego_lookup (cfg, host_name, &id_host_ego_cb, NULL);
+}
+
+
+void identity_ego_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego,
+                      void **ctx, const char *name)
+{
+
+}
+
+
+void
+core_connected (void *cls, const struct GNUNET_PeerIdentity *my_identity)
+{
+  this_peer = *my_identity;
+
+  id = GNUNET_IDENTITY_connect (cfg, &identity_ego_cb, NULL);
+  GNUNET_IDENTITY_create (id, host_name, &id_host_created, NULL);
+}
+
+
 /**
  * Main function of the test, run from scheduler.
  *
@@ -116,7 +773,7 @@
  * @param cfg configuration we use (also to connect to Social service)
  * @param peer handle to access more of the peer (not used)
  */
-static void
+void
 #if DEBUG_SERVICE
 run (void *cls, char *const *args, const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
@@ -129,9 +786,14 @@
   cfg = c;
   end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
 
-  /* FIXME: add tests */
+  place_key = GNUNET_CRYPTO_eddsa_key_create ();
+  guest_key = GNUNET_CRYPTO_ecdsa_key_create ();
 
-  end ();
+  GNUNET_CRYPTO_eddsa_key_get_public (place_key, &place_pub_key);
+  GNUNET_CRYPTO_ecdsa_key_get_public (guest_key, &guest_pub_key);
+
+  core = GNUNET_CORE_connect (cfg, NULL, &core_connected, NULL, NULL,
+                              NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
 }
 
 

Modified: gnunet/src/util/client_manager.c
===================================================================
--- gnunet/src/util/client_manager.c    2014-07-23 16:19:46 UTC (rev 34020)
+++ gnunet/src/util/client_manager.c    2014-07-23 16:19:49 UTC (rev 34021)
@@ -30,7 +30,7 @@
 #include "platform.h"
 #include "gnunet_util_lib.h"
 
-#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
+#define LOG(kind,...) GNUNET_log_from (kind, "util-client-mgr", __VA_ARGS__)
 
 
 /**
@@ -94,6 +94,16 @@
   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
 
   /**
+   * Disconnect callback.
+   */
+  void (*disconnect_cb)(void *);
+
+  /**
+   * Disconnect closure.
+   */
+  void *disconnect_cls;
+
+  /**
    * User context value.
    * @see GNUNET_CLIENT_MANAGER_set_user_context()
    * @see GNUNET_CLIENT_MANAGER_get_user_context()
@@ -125,7 +135,7 @@
    * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
    * and we're transmitting the last messages from the queue.
    */
-  uint8_t disconnecting;
+  uint8_t is_disconnecting;
 };
 
 
@@ -185,6 +195,15 @@
 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
 
 
+static void
+schedule_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
+  GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
+                                    mgr->disconnect_cb, mgr->disconnect_cls);
+}
+
+
 /**
  * Transmit next message to service.
  *
@@ -221,7 +240,14 @@
   GNUNET_free (mqi);
 
   if (NULL != mgr->tmit_head)
+  {
     transmit_next (mgr);
+  }
+  else if (GNUNET_YES == mgr->is_disconnecting)
+  {
+    GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
+    return size;
+  }
 
   if (GNUNET_NO == mgr->in_receive)
   {
@@ -247,8 +273,9 @@
 
   if (NULL == mgr->tmit_head)
   {
-    if (GNUNET_YES == mgr->disconnecting)
-      GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO);
+    if (GNUNET_YES == mgr->is_disconnecting)
+      GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
+                                        mgr->disconnect_cb, 
mgr->disconnect_cls);
     return;
   }
 
@@ -269,7 +296,7 @@
  * @param tc Scheduler context.
  */
 static void
-reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+schedule_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
   mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
@@ -305,7 +332,7 @@
   mgr->service_name = service_name;
   mgr->handlers = handlers;
   mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
-  mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, mgr);
+  mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
   return mgr;
 }
 
@@ -315,17 +342,25 @@
  *
  * @param mgr             Client manager connection.
  * @param transmit_queue  Transmit pending messages in queue before 
disconnecting.
+ * @param disconnect_cb   Function called after disconnected from the service.
+ * @param disconnect_cls  Closure for @a disconnect_cb.
  */
 void
 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
-                                  int transmit_queue)
+                                  int transmit_queue,
+                                  GNUNET_ContinuationCallback disconnect_cb,
+                                  void *disconnect_cls)
 {
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
+  mgr->disconnect_cb = disconnect_cb;
+  mgr->disconnect_cls = disconnect_cls;
   if (NULL != mgr->tmit_head)
   {
     if (GNUNET_YES == transmit_queue)
     {
-      mgr->disconnecting = GNUNET_YES;
+      mgr->is_disconnecting = GNUNET_YES;
       transmit_next (mgr);
+      return;
     }
     else
     {
@@ -350,7 +385,10 @@
     GNUNET_CLIENT_disconnect (mgr->client);
     mgr->client = NULL;
   }
+  if (NULL != mgr->disconnect_cb)
+    mgr->disconnect_cb (mgr->disconnect_cls);
   GNUNET_free (mgr);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
 }
 
 
@@ -380,7 +418,7 @@
        "Scheduling task to reconnect to service in %s.\n",
        GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, 
GNUNET_YES));
   mgr->reconnect_task =
-      GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &reconnect, mgr);
+    GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, 
mgr);
   mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
 }
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]