social: put the sock in the right cupboard
[oweals/gnunet.git] / src / social / gnunet-social.c
index b9b26496e4dcc35e4e0c8724a9dcf73f3bb377df..2d6990869382c128fd1a25da01a21b78825def21 100644 (file)
@@ -43,26 +43,35 @@ static int op_status;
 /** --host-enter */
 static int op_host_enter;
 
+/** --host-reconnect */
+static int op_host_reconnect;
+
 /** --host-leave */
 static int op_host_leave;
 
 /** --host-announce */
 static int op_host_announce;
 
+/** --host-assign */
+static int op_host_assign;
+
 /** --guest-enter */
 static int op_guest_enter;
 
+/** --guest-reconnect */
+static int op_guest_reconnect;
+
 /** --guest-leave */
 static int op_guest_leave;
 
 /** --guest-talk */
 static int op_guest_talk;
 
-/** --history-replay */
-static char *op_history_replay;
+/** --replay */
+static char *op_replay;
 
-/** --history-replay-latest */
-static char *op_history_replay_latest;
+/** --replay-latest */
+static char *op_replay_latest;
 
 /** --look-at */
 static int op_look_at;
@@ -82,17 +91,26 @@ static char *opt_place;
 /** --ego */
 static char *opt_ego;
 
+/** --gns */
+static char *opt_gns;
+
 /** --peer */
 static char *opt_peer;
 
 /** --follow */
 static int opt_follow;
 
+/** --welcome */
+static int opt_welcome;
+
+/** --deny */
+static int opt_deny;
+
 /** --method */
 static char *opt_method;
 
 /** --data */
-// FIXME: could also come from STDIN
+// FIXME: should come from STDIN
 static char *opt_data;
 
 /** --name */
@@ -101,8 +119,8 @@ static char *opt_name;
 /** --start */
 static uint64_t opt_start;
 
-/** --end */
-static uint64_t opt_end;
+/** --until */
+static uint64_t opt_until;
 
 /** --limit */
 static int opt_limit;
@@ -119,16 +137,13 @@ struct GNUNET_SCHEDULER_Task *timeout_task;
 const struct GNUNET_CONFIGURATION_Handle *cfg;
 
 struct GNUNET_CORE_Handle *core;
-struct GNUNET_PeerIdentity peer;
+struct GNUNET_PeerIdentity peer, this_peer;
 
 struct GNUNET_SOCIAL_App *app;
 
 /** public key of connected place */
 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
 
-/** hash of @a place_pub_key */
-struct GNUNET_HashCode place_pub_hash;
-
 struct GNUNET_PSYC_Slicer *slicer;
 
 struct GNUNET_SOCIAL_Ego *ego;
@@ -142,18 +157,52 @@ struct GNUNET_SOCIAL_Place *plc;
 /* DISCONNECT */
 
 
+/**
+ * Callback called after the host or guest place disconnected.
+ */
+static void
+disconnected (void *cls)
+{
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Callback called after the application disconnected.
+ */
+static void
+app_disconnected (void *cls)
+{
+  if (hst || gst)
+  {
+    if (hst)
+    {
+      GNUNET_SOCIAL_host_disconnect (hst, disconnected, NULL);
+    }
+    if (gst)
+    {
+      GNUNET_SOCIAL_guest_disconnect (gst, disconnected, NULL);
+    }
+  }
+  else
+  {
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
+
+
+/**
+ * Disconnect from connected GNUnet services.
+ */
 static void
 disconnect ()
 {
-  GNUNET_SOCIAL_app_disconnect (app);
   GNUNET_CORE_disconnect (core);
-  GNUNET_SCHEDULER_shutdown ();
+  GNUNET_SOCIAL_app_disconnect (app, app_disconnected, NULL);
 }
 
 /**
- * Terminate the test case (failure).
- *
- * @param cls NULL
+ * Callback called when the program failed to finish the requested operation in time.
  */
 static void
 timeout (void *cls)
@@ -163,7 +212,7 @@ timeout (void *cls)
 }
 
 static void
-schedule_end (void *cls)
+schedule_success (void *cls)
 {
   ret = 0;
   disconnect ();
@@ -171,40 +220,76 @@ schedule_end (void *cls)
 
 
 static void
-end ()
+schedule_fail (void *cls)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "The end.\n");
+  disconnect ();
+}
+
 
+/**
+ * Schedule exit with success result.
+ */
+static void
+exit_success ()
+{
   if (timeout_task != NULL)
   {
     GNUNET_SCHEDULER_cancel (timeout_task);
     timeout_task = NULL;
   }
-  GNUNET_SCHEDULER_add_now (&schedule_end, NULL);
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_success, NULL);
+}
+
+
+/**
+ * Schedule exit with failure result.
+ */
+static void
+exit_fail ()
+{
+  if (timeout_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (timeout_task);
+    timeout_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_fail, NULL);
 }
 
 
 /* LEAVE */
 
 
+/**
+ * Callback notifying about the host has left and stopped hosting the place.
+ *
+ * This also indicates the end of the connection to the service.
+ */
 static void
 host_left ()
 {
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
               "The host has left the place.\n");
-  end ();
+  exit_success ();
 }
 
 
+/**
+ * Leave a place permanently and stop hosting a place.
+ */
 static void
 host_leave ()
 {
-  GNUNET_SOCIAL_host_leave (hst, NULL, &host_left, NULL);
+  GNUNET_SOCIAL_host_leave (hst, NULL, host_left, NULL);
   hst = NULL;
   plc = NULL;
 }
 
 
+/**
+ * Callback notifying about the guest has left the place.
+ *
+ * This also indicates the end of the connection to the service.
+ */
 static void
 guest_left (void *cls)
 {
@@ -213,20 +298,24 @@ guest_left (void *cls)
 }
 
 
+/**
+ * Leave a place permanently as guest.
+ */
 static void
 guest_leave ()
 {
   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  // FIXME: wrong use of vars
   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
-                       "_notice_place_leave", DATA2ARG ("Leaving."));
-  GNUNET_SOCIAL_guest_leave (gst, env, &guest_left, NULL);
+                       "_message", DATA2ARG ("Leaving."));
+  GNUNET_SOCIAL_guest_leave (gst, env, guest_left, NULL);
   GNUNET_PSYC_env_destroy (env);
   gst = NULL;
   plc = NULL;
 }
 
 
-/* ANNOUNCE / TALK */
+/* ANNOUNCE / ASSIGN / TALK */
 
 
 struct TransmitClosure
@@ -236,6 +325,10 @@ struct TransmitClosure
 } tmit;
 
 
+/**
+ * Callback notifying about available buffer space to write message data
+ * when transmitting messages using host_announce() or guest_talk()
+ */
 static int
 notify_data (void *cls, uint16_t *data_size, void *data)
 {
@@ -251,10 +344,24 @@ notify_data (void *cls, uint16_t *data_size, void *data)
   tmit->size -= size;
   tmit->data += size;
 
-  return 0 == tmit->size ? GNUNET_NO : GNUNET_YES;
+  if (0 == tmit->size)
+  {
+    if (op_host_announce || op_host_assign || op_guest_talk)
+    {
+      exit_success ();
+    }
+    return GNUNET_YES;
+  }
+  else
+  {
+    return GNUNET_NO;
+  }
 }
 
 
+/**
+ * Host announcement - send a message to the place.
+ */
 static void
 host_announce (const char *method, const char *data, size_t data_size)
 {
@@ -267,11 +374,31 @@ host_announce (const char *method, const char *data, size_t data_size)
   tmit.size = data_size;
 
   GNUNET_SOCIAL_host_announce (hst, method, env,
-                               &notify_data, &tmit,
+                               notify_data, &tmit,
                                GNUNET_SOCIAL_ANNOUNCE_NONE);
 }
 
 
+/**
+ * Assign a state var of @a name to the value of @a data.
+ */
+static void
+host_assign (const char *name, const char *data, size_t data_size)
+{
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN,
+                       name, data, data_size);
+
+  tmit = (struct TransmitClosure) {};
+  GNUNET_SOCIAL_host_announce (hst, "_assign", env,
+                               notify_data, &tmit,
+                               GNUNET_SOCIAL_ANNOUNCE_NONE);
+}
+
+
+/**
+ * Guest talk request to host.
+ */
 static void
 guest_talk (const char *method,
             const char *data, size_t data_size)
@@ -285,7 +412,7 @@ guest_talk (const char *method,
   tmit.size = data_size;
 
   GNUNET_SOCIAL_guest_talk (gst, method, env,
-                            &notify_data, &tmit,
+                            notify_data, &tmit,
                             GNUNET_SOCIAL_TALK_NONE);
 }
 
@@ -293,35 +420,50 @@ guest_talk (const char *method,
 /* HISTORY REPLAY */
 
 
+/**
+ * Callback notifying about the end of history replay results.
+ */
 static void
 recv_history_replay_result (void *cls, int64_t result,
                             const void *data, uint16_t data_size)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "Guest received history replay result: %" PRId64 "\n"
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received history replay result: %" PRId64 "\n"
               "%.*s\n",
               result, data_size, (const char *) data);
+
+  if (op_replay || op_replay_latest)
+  {
+    exit_success ();
+  }
 }
 
 
+/**
+ * Replay history between a given @a start and @a end message IDs,
+ * optionally filtered by a method @a prefix.
+ */
 static void
 history_replay (uint64_t start, uint64_t end, const char *prefix)
 {
   GNUNET_SOCIAL_place_history_replay (plc, start, end, prefix,
                                       GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
                                       slicer,
-                                      &recv_history_replay_result,
+                                      recv_history_replay_result,
                                       NULL);
 }
 
 
+/**
+ * Replay latest @a limit messages.
+ */
 static void
 history_replay_latest (uint64_t limit, const char *prefix)
 {
   GNUNET_SOCIAL_place_history_replay_latest (plc, limit, prefix,
                                              GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
                                              slicer,
-                                             &recv_history_replay_result,
+                                             recv_history_replay_result,
                                              NULL);
 }
 
@@ -329,16 +471,26 @@ history_replay_latest (uint64_t limit, const char *prefix)
 /* LOOK AT/FOR */
 
 
+/**
+ * Callback notifying about the end of state var results.
+ */
 static void
 look_result (void *cls, int64_t result_code,
              const void *data, uint16_t data_size)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "look_result: %" PRId64 "\n", result_code);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received look result: %" PRId64 "\n", result_code);
 
+  if (op_look_at || op_look_for)
+  {
+    exit_success ();
+  }
 }
 
 
+/**
+ * Callback notifying about a state var result.
+ */
 static void
 look_var (void *cls,
           const struct GNUNET_MessageHeader *mod,
@@ -348,28 +500,37 @@ look_var (void *cls,
           uint32_t full_value_size)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "guest_look_at_var: %s\n%.*s\n",
+              "Received var: %s\n%.*s\n",
               name, value_size, (const char *) value);
 }
 
 
+/**
+ * Look for a state var using exact match of the name.
+ */
 static void
-look_at (const char *name)
+look_at (const char *full_name)
 {
-  GNUNET_SOCIAL_place_look_at (plc, name, look_var, look_result, NULL);
+  GNUNET_SOCIAL_place_look_at (plc, full_name, look_var, look_result, NULL);
 }
 
 
+/**
+ * Look for state vars by name prefix.
+ */
 static void
-look_for (const char *name)
+look_for (const char *name_prefix)
 {
-  GNUNET_SOCIAL_place_look_for (plc, name, look_var, look_result, NULL);
+  GNUNET_SOCIAL_place_look_for (plc, name_prefix, look_var, look_result, NULL);
 }
 
 
 /* SLICER */
 
 
+/**
+ * Callback notifying about the start of a new incoming message.
+ */
 static void
 slicer_recv_method (void *cls,
                     const struct GNUNET_PSYC_MessageHeader *msg,
@@ -384,6 +545,9 @@ slicer_recv_method (void *cls,
 }
 
 
+/**
+ * Callback notifying about an incoming modifier.
+ */
 static void
 slicer_recv_modifier (void *cls,
                       const struct GNUNET_PSYC_MessageHeader *msg,
@@ -402,6 +566,9 @@ slicer_recv_modifier (void *cls,
 }
 
 
+/**
+ * Callback notifying about an incoming data fragment.
+ */
 static void
 slicer_recv_data (void *cls,
                   const struct GNUNET_PSYC_MessageHeader *msg,
@@ -417,6 +584,9 @@ slicer_recv_data (void *cls,
 }
 
 
+/**
+ * Callback notifying about the end of a message.
+ */
 static void
 slicer_recv_eom (void *cls,
                 const struct GNUNET_PSYC_MessageHeader *msg,
@@ -431,6 +601,9 @@ slicer_recv_eom (void *cls,
 }
 
 
+/**
+ * Create a slicer for receiving message parts.
+ */
 static struct GNUNET_PSYC_Slicer *
 slicer_create ()
 {
@@ -447,6 +620,12 @@ slicer_create ()
 /* GUEST ENTER */
 
 
+/**
+ * Callback called when the guest receives an entry decision from the host.
+ *
+ * It is called once after using guest_enter() or guest_enter_by_name(),
+ * in case of a reconnection only the local enter callback is called.
+ */
 static void
 guest_recv_entry_decision (void *cls,
                            int is_admitted,
@@ -467,25 +646,42 @@ guest_recv_entry_decision (void *cls,
     GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
     GNUNET_free (pmsg);
 
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "%s\n%.*s\n",
                 method_name, data_size, (const char *) data);
   }
+
+  if (op_guest_enter && !opt_follow)
+  {
+    exit_success ();
+  }
 }
 
 
+/**
+ * Callback called after a guest connection is established to the local service.
+ */
 static void
 guest_recv_local_enter (void *cls, int result,
-                        const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
                         uint64_t max_message_id)
 {
+  char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "Guest entered to local place: %d, max_message_id: %" PRIu64 "\n",
-              result, max_message_id);
+              "Guest entered to local place: %s, max_message_id: %" PRIu64 "\n",
+              pub_str, max_message_id);
   GNUNET_assert (0 <= result);
+
+  if (op_guest_enter && !opt_follow)
+  {
+    exit_success ();
+  }
 }
 
 
+/**
+ * Create entry requset message.
+ */
 static struct GNUNET_PSYC_Message *
 guest_enter_msg_create ()
 {
@@ -500,13 +696,24 @@ guest_enter_msg_create ()
 }
 
 
+/**
+ * Enter a place as guest, using its public key and peer ID.
+ */
 static void
-guest_enter (struct GNUNET_PeerIdentity *peer)
+guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
+             const struct GNUNET_PeerIdentity *peer)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Entering to place as guest.\n");
 
-  gst = GNUNET_SOCIAL_guest_enter (app, ego, &place_pub_key,
+  if (NULL == ego)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
+    exit_fail ();
+    return;
+  }
+
+  gst = GNUNET_SOCIAL_guest_enter (app, ego, pub_key,
                                    GNUNET_PSYC_SLAVE_JOIN_NONE,
                                    peer, 0, NULL, guest_enter_msg_create (),
                                    slicer_create (),
@@ -516,10 +723,13 @@ guest_enter (struct GNUNET_PeerIdentity *peer)
 }
 
 
+/**
+ * Enter a place as guest using its GNS address.
+ */
 static void
 guest_enter_by_name (const char *gns_name)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Entering to place by name as guest.\n");
 
   gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
@@ -533,6 +743,11 @@ guest_enter_by_name (const char *gns_name)
 /* HOST ENTER */
 
 
+/**
+ * Callback called when a @a nym wants to enter the place.
+ *
+ * The request needs to be replied with an entry decision.
+ */
 static void
 host_answer_door (void *cls,
                   struct GNUNET_SOCIAL_Nym *nym,
@@ -546,12 +761,34 @@ host_answer_door (void *cls,
   char *
     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
               "Entry request: %s\n", nym_str);
   GNUNET_free (nym_str);
+
+  if (opt_welcome)
+  {
+    struct GNUNET_PSYC_Message *
+      resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
+                                         DATA2ARG ("Welcome, nym!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, resp);
+    GNUNET_free (resp);
+  }
+  else if (opt_deny)
+  {
+    struct GNUNET_PSYC_Message *
+      resp = GNUNET_PSYC_message_create ("_notice_place_refuse", NULL,
+                                         DATA2ARG ("Go away!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, resp);
+    GNUNET_free (resp);
+  }
+
+
 }
 
 
+/**
+ * Callback called when a @a nym has left the place.
+ */
 static void
 host_farewell (void *cls,
                const struct GNUNET_SOCIAL_Nym *nym,
@@ -562,30 +799,48 @@ host_farewell (void *cls,
   char *
     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
               "Farewell: %s\n", nym_str);
   GNUNET_free (nym_str);
 }
 
 
+/**
+ * Callback called after the host entered the place.
+ */
 static void
 host_entered (void *cls, int result,
               const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
               uint64_t max_message_id)
 {
   place_pub_key = *pub_key;
-  GNUNET_CRYPTO_hash (&place_pub_key, sizeof (place_pub_key), &place_pub_hash);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Entered: %s, max_message_id: %" PRIu64 "\n",
-              GNUNET_h2s_full (&place_pub_hash), max_message_id);
+  char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Host entered: %s, max_message_id: %" PRIu64 "\n",
+              pub_str, max_message_id);
+
+  if (op_host_enter && !opt_follow)
+  {
+    exit_success ();
+  }
 }
 
 
+/**
+ * Enter and start hosting a place.
+ */
 static void
 host_enter ()
 {
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
 
+  if (NULL == ego)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
+    exit_fail ();
+    return;
+  }
+
   hst = GNUNET_SOCIAL_host_enter (app, ego,
                                   GNUNET_PSYC_CHANNEL_PRIVATE,
                                   slicer_create (), host_entered,
@@ -597,13 +852,21 @@ host_enter ()
 /* PLACE RECONNECT */
 
 
+/**
+ * Perform operations common to both host & guest places.
+ */
 static void
 place_reconnected ()
 {
-  if (op_history_replay) {
-    history_replay (opt_start, opt_end, opt_method);
+  static int first_run = GNUNET_YES;
+  if (GNUNET_NO == first_run)
+    return;
+  first_run = GNUNET_NO;
+
+  if (op_replay) {
+    history_replay (opt_start, opt_until, opt_method);
   }
-  else if (op_history_replay_latest) {
+  else if (op_replay_latest) {
     history_replay_latest (opt_limit, opt_method);
   }
   else if (op_look_at) {
@@ -615,13 +878,16 @@ place_reconnected ()
 }
 
 
+/**
+ * Callback called after reconnecting to a host place.
+ */
 static void
 host_reconnected (void *cls, int result,
                  const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
                  uint64_t max_message_id)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "Host reconnected\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Host reconnected.\n");
 
   if (op_host_leave) {
     host_leave ();
@@ -629,19 +895,25 @@ host_reconnected (void *cls, int result,
   else if (op_host_announce) {
     host_announce (opt_method, opt_data, strlen (opt_data));
   }
+  else if (op_host_assign) {
+    host_assign (opt_name, opt_data, strlen (opt_data) + 1);
+  }
   else {
     place_reconnected ();
   }
 }
 
 
+/**
+ * Callback called after reconnecting to a guest place.
+ */
 static void
 guest_reconnected (void *cls, int result,
                    const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
                    uint64_t max_message_id)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "Guest reconnected\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Guest reconnected.\n");
 
   if (op_guest_leave) {
     guest_leave ();
@@ -658,26 +930,56 @@ guest_reconnected (void *cls, int result,
 /* APP */
 
 
+/**
+ * Callback called after the ego and place callbacks.
+ */
 static void
 app_connected (void *cls)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "App connected: %p\n", cls);
 
   if (op_status)
   {
-    GNUNET_SCHEDULER_add_now (&schedule_end, NULL);
+    exit_success ();
   }
-  else if (op_host_enter) {
+  else if (op_host_enter)
+  {
     host_enter ();
   }
-  else if (op_guest_enter) {
-    guest_enter (&peer);
-    // FIXME: guest_enter_by_name
+  else if (op_guest_enter)
+  {
+    if (opt_gns)
+    {
+      guest_enter_by_name (opt_gns);
+    }
+    else
+    {
+      if (opt_peer)
+      {
+        if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_peer,
+                                                                     strlen (opt_peer),
+                                                                     &peer.public_key))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "--peer invalid");
+          exit_fail ();
+          return;
+        }
+      }
+      else
+      {
+        peer = this_peer;
+      }
+      guest_enter (&place_pub_key, &peer);
+    }
   }
 }
 
 
+/**
+ * Callback notifying about a host place available for reconnection.
+ */
 static void
 app_recv_host (void *cls,
                struct GNUNET_SOCIAL_HostConnection *hconn,
@@ -685,17 +987,15 @@ app_recv_host (void *cls,
                const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
                enum GNUNET_SOCIAL_AppPlaceState place_state)
 {
-  struct GNUNET_HashCode host_pub_hash;
-  GNUNET_CRYPTO_hash (host_pub_key, sizeof (*host_pub_key), &host_pub_hash);
-  char *
-    host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Host: %s (%s)\n",
-              GNUNET_h2s_full (&host_pub_hash), host_pub_str);
+  char *host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Host:  %s\n", host_pub_str);
   GNUNET_free (host_pub_str);
 
-  if (0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
+  if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
+       || op_replay || op_replay_latest
+       || op_look_at || op_look_for)
+      && 0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
   {
     hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), host_reconnected,
                                               host_answer_door, host_farewell, NULL);
@@ -704,6 +1004,9 @@ app_recv_host (void *cls,
 }
 
 
+/**
+ * Callback notifying about a guest place available for reconnection.
+ */
 static void
 app_recv_guest (void *cls,
                 struct GNUNET_SOCIAL_GuestConnection *gconn,
@@ -711,17 +1014,15 @@ app_recv_guest (void *cls,
                 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
                 enum GNUNET_SOCIAL_AppPlaceState place_state)
 {
-  struct GNUNET_HashCode guest_pub_hash;
-  GNUNET_CRYPTO_hash (guest_pub_key, sizeof (*guest_pub_key), &guest_pub_hash);
-  char *
-    guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (guest_pub_key);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Guest: %s (%s)\n",
-              GNUNET_h2s_full (&guest_pub_hash), guest_pub_str);
+  char *guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (guest_pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Guest: %s\n", guest_pub_str);
   GNUNET_free (guest_pub_str);
 
-  if (0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
+  if ((op_guest_reconnect || op_guest_leave || op_guest_talk
+       || op_replay || op_replay_latest
+       || op_look_at || op_look_for)
+      && 0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
   {
     gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, GNUNET_PSYC_SLAVE_JOIN_NONE,
                                                slicer_create (), guest_reconnected, NULL);
@@ -730,18 +1031,21 @@ app_recv_guest (void *cls,
 }
 
 
+/**
+ * Callback notifying about an available ego.
+ */
 static void
 app_recv_ego (void *cls,
               struct GNUNET_SOCIAL_Ego *e,
               const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
               const char *name)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Ego: %s\t%s\n",
-              name, GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key));
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Ego:   %s\t%s\n",
+              GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key), name);
 
   if (0 == memcmp (&ego_pub_key, pub_key, sizeof (*pub_key))
-      || 0 == strcmp (opt_ego, name))
+      || (NULL != opt_ego && 0 == strcmp (opt_ego, name)))
   {
     ego = e;
   }
@@ -749,6 +1053,10 @@ app_recv_ego (void *cls,
 }
 
 
+
+/**
+ * Establish application connection to receive available egos and places.
+ */
 static void
 app_connect ()
 {
@@ -767,7 +1075,7 @@ app_connect ()
 static void
 core_connected (void *cls, const struct GNUNET_PeerIdentity *my_identity)
 {
-  peer = *my_identity;
+  this_peer = *my_identity;
   app_connect ();
 }
 
@@ -787,11 +1095,25 @@ static void
 run (void *cls, char *const *args, const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
+  GNUNET_SIGNAL_handler_install (SIGINT, disconnect);
+  GNUNET_SIGNAL_handler_install (SIGTERM, disconnect);
+  GNUNET_SIGNAL_handler_install (SIGKILL, disconnect);
+
   cfg = c;
 
-  if (! (op_status || op_host_enter || op_host_leave || op_host_announce
-         || op_guest_enter || op_guest_leave || op_guest_talk
-         || op_history_replay || op_history_replay_latest
+  if (!opt_method)
+    opt_method = "message";
+  if (!opt_data)
+    opt_data = "";
+  if (!opt_name)
+    opt_name = "";
+
+  if (! (op_status
+         || op_host_enter || op_host_reconnect || op_host_leave
+         || op_host_announce || op_host_assign
+         || op_guest_enter || op_guest_reconnect
+         || op_guest_leave || op_guest_talk
+         || op_replay || op_replay_latest
          || op_look_at || op_look_for))
   {
     op_status = 1;
@@ -799,10 +1121,14 @@ run (void *cls, char *const *args, const char *cfgfile,
 
   if (!opt_follow)
   {
-    timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout, NULL);
+    timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, timeout, NULL);
   }
 
-  if (!op_status && !op_host_enter
+  if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
+       || op_guest_reconnect || (op_guest_enter && !opt_gns)
+       || op_guest_leave || op_guest_talk
+       || op_replay || op_replay_latest
+       || op_look_at || op_look_for)
       && (!opt_place
           || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_place,
                                                                       strlen (opt_place),
@@ -810,6 +1136,7 @@ run (void *cls, char *const *args, const char *cfgfile,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 _("--place missing or invalid.\n"));
+    exit_fail ();
     return;
   }
 
@@ -820,11 +1147,6 @@ run (void *cls, char *const *args, const char *cfgfile,
                                                 &ego_pub_key);
   }
 
-  if (opt_peer)
-  {
-    // FIXME: peer ID from string
-  }
-
   core = GNUNET_CORE_connect (cfg, NULL, &core_connected, NULL, NULL,
                               NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
 }
@@ -854,87 +1176,120 @@ main (int argc, char *const *argv)
 
     /* operations */
 
-    { 's', "status", NULL,
-      gettext_noop ("list of egos and subscribed places"),
-      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_status },
+    { 'A', "host-assign", NULL,
+      gettext_noop ("assign --name in state to --data"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_assign },
+
+    { 'B', "guest-leave", NULL,
+      gettext_noop ("say good-bye and leave somebody else's place"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_leave },
 
-    { 'E', "host-enter", NULL,
-      gettext_noop ("create a place for nyms to join"),
+    { 'C', "host-enter", NULL,
+      gettext_noop ("create a place"),
       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_enter },
 
     { 'D', "host-leave", NULL,
       gettext_noop ("destroy a place we were hosting"),
       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_leave },
 
-    { 'T', "host-announce", NULL,
+    { 'E', "guest-enter", NULL,
+      gettext_noop ("enter somebody else's place"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_enter },
+
+    { 'F', "look-for", NULL,
+      gettext_noop ("find state matching name prefix"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_for },
+
+    { 'H', "replay-latest", NULL,
+      gettext_noop ("replay history of messages up to the given --limit"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay_latest },
+
+    { 'N', "host-reconnect", NULL,
+      gettext_noop ("reconnect to a previously created place"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_reconnect },
+
+    { 'P', "host-announce", NULL,
       gettext_noop ("publish something to a place we are hosting"),
       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_announce },
 
-    { 'e', "guest-enter", NULL,
-      gettext_noop ("join somebody else's place"),
-      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_enter },
+    { 'R', "guest-reconnect", NULL,
+      gettext_noop ("reconnect to a previously entered place"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_reconnect },
 
-    { 'd', "guest-leave", NULL,
-      gettext_noop ("leave somebody else's place"),
-      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_leave },
+    { 'S', "look-at", NULL,
+      gettext_noop ("search for state matching exact name"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_at },
 
-    { 't', "guest-talk", NULL,
+    { 'T', "guest-talk", NULL,
       gettext_noop ("submit something to somebody's place"),
       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_talk },
 
-    { 'R', "history-replay", NULL,
-      gettext_noop ("replay history of messages between message IDs --start and --end"),
-      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_history_replay },
+    { 'U', "status", NULL,
+      gettext_noop ("list of egos and subscribed places"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_status },
+
+    { 'X', "replay", NULL,
+      gettext_noop ("extract and replay history between message IDs --start and --until"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay },
 
-    { 'r', "history-replay-latest", NULL,
-      gettext_noop ("replay history of latest messages up to the given --limit"),
-      GNUNET_NO, &GNUNET_GETOPT_set_one, &op_history_replay_latest },
 
     /* options */
 
-    { 'A', "app", "APPLICATION_ID",
+    { 'a', "app", "APPLICATION_ID",
       gettext_noop ("application ID to use when connecting"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_app },
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_app },
 
-    { 'p', "place", "PUBKEY",
-      gettext_noop ("public key of place"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_place },
+    { 'd', "data", "DATA",
+      gettext_noop ("message body or state value"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_data },
 
-    { 'P', "peer", "PEER_ID",
-      gettext_noop ("peer ID for --guest-enter"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_peer },
-
-    { 'g', "ego", "NAME|PUBKEY",
-      gettext_noop ("public key of ego"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_place },
+    { 'e', "ego", "NAME|PUBKEY",
+      gettext_noop ("name or public key of ego"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_ego },
 
     { 'f', "follow", NULL,
       gettext_noop ("wait for incoming messages"),
       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_follow },
 
+    { 'g', "gns", "GNS_NAME",
+      gettext_noop ("GNS name"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_gns },
+
+    { 'i', "peer", "PEER_ID",
+      gettext_noop ("peer ID for --guest-enter"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_peer },
+
+    { 'k', "name", "VAR_NAME",
+      gettext_noop ("name (key) to query from state"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_name },
+
     { 'm', "method", "METHOD_NAME",
       gettext_noop ("method name"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_method },
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_method },
 
-    { 'b', "body", "DATA",
-      gettext_noop ("message body to transmit"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_data },
+    { 'n', "limit", NULL,
+      gettext_noop ("number of messages to replay from history"),
+      GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_limit },
 
-    { 'k', "name", "VAR_NAME",
-      gettext_noop ("state var name to query"),
-      GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_name },
+    { 'p', "place", "PUBKEY",
+      gettext_noop ("key address of place"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_place },
 
-    { 'a', "start", NULL,
+    { 's', "start", NULL,
       gettext_noop ("start message ID for history replay"),
-      GNUNET_NO, &GNUNET_GETOPT_set_ulong, &opt_start },
+      GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_start },
 
-    { 'z', "end", NULL,
+    { 'w', "welcome", NULL,
+      gettext_noop ("respond to entry requests by admitting all guests"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_welcome },
+
+    { 'u', "until", NULL,
       gettext_noop ("end message ID for history replay"),
-      GNUNET_NO, &GNUNET_GETOPT_set_ulong, &opt_end },
+      GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_until },
 
-    { 'n', "limit", NULL,
-      gettext_noop ("number of messages to replay from history"),
-      GNUNET_NO, &GNUNET_GETOPT_set_ulong, &opt_limit },
+    { 'y', "deny", NULL,
+      gettext_noop ("respond to entry requests by refusing all guests"),
+      GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_deny },
 
     GNUNET_GETOPT_OPTION_END
   };
@@ -943,19 +1298,27 @@ main (int argc, char *const *argv)
     return 2;
 
   const char *help =
-    _ ("Interact with the social service: enter/leave, send/receive messages, access history and state.\n");
+    _ ("gnunet-social - Interact with the social service: enter/leave, send/receive messages, access history and state.\n");
   const char *usage =
     "gnunet-social [--status]\n"
     "\n"
-    "gnunet-social --host-enter --ego <NAME or PUBKEY> [--listen]\n"
+    "gnunet-social --host-enter --ego <NAME or PUBKEY> [--follow] [--welcome | --deny]\n"
+    "gnunet-social --host-reconnect --place <PUBKEY> [--follow] [--welcome | --deny]\n"
     "gnunet-social --host-leave --place <PUBKEY>\n"
-    "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE BODY>\n"
+    "gnunet-social --host-assign --place <PUBKEY> --name <NAME> --data <VALUE>\n"
+// FIXME: some state ops not implemented yet (no hurry)
+//  "gnunet-social --host-augment --place <PUBKEY> --name <NAME> --data <VALUE>\n"
+//  "gnunet-social --host-diminish --place <PUBKEY> --name <NAME> --data <VALUE>\n"
+//  "gnunet-social --host-set --place <PUBKEY> --name <NAME> --data <VALUE>\n"
+    "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
     "\n"
-    "gnunet-social --guest-enter --place <PUBKEY> --ego <NAME or PUBKEY> [--listen]\n"
+    "gnunet-social --guest-enter --place <PUBKEY> --peer <PEERID> --ego <NAME or PUBKEY> [--follow]\n"
+    "gnunet-social --guest-enter --gns <GNS_NAME> --ego <NAME or PUBKEY> [--follow]\n"
+    "gnunet-social --guest-reconnect --place <PUBKEY> [--follow]\n"
     "gnunet-social --guest-leave --place <PUBKEY>\n"
-    "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NMAE> --data <DATA>\n"
+    "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
     "\n"
-    "gnunet-social --history-replay --place <PUBKEY> --start <MSGID> --end <MSGID>  [--method <METHOD_PREFIX>]\n"
+    "gnunet-social --history-replay --place <PUBKEY> --start <MSGID> --until <MSGID>  [--method <METHOD_PREFIX>]\n"
     "gnunet-social --history-replay-latest --place <PUBKEY> --limit <MSG_LIMIT> [--method <METHOD_PREFIX>]\n"
     "\n"
     "gnunet-social --look-at --place <PUBKEY> --name <FULL_NAME>\n"