social: put the sock in the right cupboard
[oweals/gnunet.git] / src / social / gnunet-social.c
index 0d236b2ff4a983a76ffd4ec31de1d7f477ad150e..2d6990869382c128fd1a25da01a21b78825def21 100644 (file)
@@ -52,6 +52,9 @@ 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;
 
@@ -106,9 +109,9 @@ static int opt_deny;
 /** --method */
 static char *opt_method;
 
-/** --body */
+/** --data */
 // FIXME: should come from STDIN
-static char *opt_body;
+static char *opt_data;
 
 /** --name */
 static char *opt_name;
@@ -154,27 +157,52 @@ struct GNUNET_SOCIAL_Place *plc;
 /* DISCONNECT */
 
 
+/**
+ * Callback called after the host or guest place disconnected.
+ */
 static void
-disconnect ()
+disconnected (void *cls)
+{
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Callback called after the application disconnected.
+ */
+static void
+app_disconnected (void *cls)
 {
-  if (hst)
+  if (hst || gst)
   {
-    GNUNET_SOCIAL_host_disconnect (hst, NULL, NULL);
+    if (hst)
+    {
+      GNUNET_SOCIAL_host_disconnect (hst, disconnected, NULL);
+    }
+    if (gst)
+    {
+      GNUNET_SOCIAL_guest_disconnect (gst, disconnected, NULL);
+    }
   }
-  if (gst)
+  else
   {
-    GNUNET_SOCIAL_guest_disconnect (gst, NULL, NULL);
+    GNUNET_SCHEDULER_shutdown ();
   }
+}
 
-  GNUNET_SOCIAL_app_disconnect (app);
+
+/**
+ * Disconnect from connected GNUnet services.
+ */
+static void
+disconnect ()
+{
   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)
@@ -198,6 +226,9 @@ schedule_fail (void *cls)
 }
 
 
+/**
+ * Schedule exit with success result.
+ */
 static void
 exit_success ()
 {
@@ -206,10 +237,13 @@ exit_success ()
     GNUNET_SCHEDULER_cancel (timeout_task);
     timeout_task = NULL;
   }
-  GNUNET_SCHEDULER_add_now (&schedule_success, NULL);
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_success, NULL);
 }
 
 
+/**
+ * Schedule exit with failure result.
+ */
 static void
 exit_fail ()
 {
@@ -218,13 +252,18 @@ exit_fail ()
     GNUNET_SCHEDULER_cancel (timeout_task);
     timeout_task = NULL;
   }
-  GNUNET_SCHEDULER_add_now (&schedule_fail, 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 ()
 {
@@ -234,15 +273,23 @@ host_left ()
 }
 
 
+/**
+ * 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)
 {
@@ -251,21 +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 ();
-    // method in the middle of vars? FIXME
+  // 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
@@ -275,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)
 {
@@ -292,19 +346,22 @@ notify_data (void *cls, uint16_t *data_size, void *data)
 
   if (0 == tmit->size)
   {
-    if (op_host_announce || op_guest_talk)
+    if (op_host_announce || op_host_assign || op_guest_talk)
     {
       exit_success ();
     }
-    return GNUNET_NO;
+    return GNUNET_YES;
   }
   else
   {
-    return GNUNET_YES;
+    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)
 {
@@ -317,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)
@@ -335,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);
 }
 
@@ -343,6 +420,9 @@ 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)
@@ -359,24 +439,31 @@ recv_history_replay_result (void *cls, int64_t result,
 }
 
 
+/**
+ * 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);
 }
 
@@ -384,6 +471,9 @@ 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)
@@ -398,6 +488,9 @@ look_result (void *cls, int64_t result_code,
 }
 
 
+/**
+ * Callback notifying about a state var result.
+ */
 static void
 look_var (void *cls,
           const struct GNUNET_MessageHeader *mod,
@@ -407,28 +500,37 @@ look_var (void *cls,
           uint32_t full_value_size)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "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,
@@ -443,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,
@@ -461,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,
@@ -476,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,
@@ -490,6 +601,9 @@ slicer_recv_eom (void *cls,
 }
 
 
+/**
+ * Create a slicer for receiving message parts.
+ */
 static struct GNUNET_PSYC_Slicer *
 slicer_create ()
 {
@@ -506,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,
@@ -538,6 +658,9 @@ guest_recv_entry_decision (void *cls,
 }
 
 
+/**
+ * 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 *pub_key,
@@ -556,6 +679,9 @@ guest_recv_local_enter (void *cls, int result,
 }
 
 
+/**
+ * Create entry requset message.
+ */
 static struct GNUNET_PSYC_Message *
 guest_enter_msg_create ()
 {
@@ -570,6 +696,9 @@ guest_enter_msg_create ()
 }
 
 
+/**
+ * Enter a place as guest, using its public key and peer ID.
+ */
 static void
 guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
              const struct GNUNET_PeerIdentity *peer)
@@ -594,6 +723,9 @@ guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
 }
 
 
+/**
+ * Enter a place as guest using its GNS address.
+ */
 static void
 guest_enter_by_name (const char *gns_name)
 {
@@ -611,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,
@@ -649,6 +786,9 @@ host_answer_door (void *cls,
 }
 
 
+/**
+ * Callback called when a @a nym has left the place.
+ */
 static void
 host_farewell (void *cls,
                const struct GNUNET_SOCIAL_Nym *nym,
@@ -665,6 +805,9 @@ host_farewell (void *cls,
 }
 
 
+/**
+ * Callback called after the host entered the place.
+ */
 static void
 host_entered (void *cls, int result,
               const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
@@ -683,6 +826,9 @@ host_entered (void *cls, int result,
 }
 
 
+/**
+ * Enter and start hosting a place.
+ */
 static void
 host_enter ()
 {
@@ -706,9 +852,17 @@ host_enter ()
 /* PLACE RECONNECT */
 
 
+/**
+ * Perform operations common to both host & guest places.
+ */
 static void
 place_reconnected ()
 {
+  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);
   }
@@ -724,6 +878,9 @@ 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,
@@ -736,7 +893,10 @@ host_reconnected (void *cls, int result,
     host_leave ();
   }
   else if (op_host_announce) {
-    host_announce (opt_method, opt_body, strlen (opt_body));
+    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 ();
@@ -744,6 +904,9 @@ host_reconnected (void *cls, int result,
 }
 
 
+/**
+ * Callback called after reconnecting to a guest place.
+ */
 static void
 guest_reconnected (void *cls, int result,
                    const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
@@ -756,7 +919,7 @@ guest_reconnected (void *cls, int result,
     guest_leave ();
   }
   else if (op_guest_talk) {
-    guest_talk (opt_method, opt_body, strlen (opt_body));
+    guest_talk (opt_method, opt_data, strlen (opt_data));
   }
   else {
     place_reconnected ();
@@ -767,6 +930,9 @@ guest_reconnected (void *cls, int result,
 /* APP */
 
 
+/**
+ * Callback called after the ego and place callbacks.
+ */
 static void
 app_connected (void *cls)
 {
@@ -811,6 +977,9 @@ app_connected (void *cls)
 }
 
 
+/**
+ * Callback notifying about a host place available for reconnection.
+ */
 static void
 app_recv_host (void *cls,
                struct GNUNET_SOCIAL_HostConnection *hconn,
@@ -823,7 +992,7 @@ app_recv_host (void *cls,
               "Host:  %s\n", host_pub_str);
   GNUNET_free (host_pub_str);
 
-  if ((op_host_reconnect || op_host_leave || op_host_announce
+  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)))
@@ -835,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,
@@ -859,6 +1031,9 @@ app_recv_guest (void *cls,
 }
 
 
+/**
+ * Callback notifying about an available ego.
+ */
 static void
 app_recv_ego (void *cls,
               struct GNUNET_SOCIAL_Ego *e,
@@ -878,6 +1053,10 @@ app_recv_ego (void *cls,
 }
 
 
+
+/**
+ * Establish application connection to receive available egos and places.
+ */
 static void
 app_connect ()
 {
@@ -916,18 +1095,24 @@ 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 (!opt_method)
     opt_method = "message";
-  if (!opt_body)
-    opt_body = "";
+  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_guest_enter || op_guest_reconnect || op_guest_leave || op_guest_talk
+         || 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))
   {
@@ -936,10 +1121,10 @@ 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_host_reconnect || op_host_leave || op_host_announce
+  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
@@ -991,6 +1176,10 @@ main (int argc, char *const *argv)
 
     /* operations */
 
+    { '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 },
@@ -1050,9 +1239,9 @@ main (int argc, char *const *argv)
       gettext_noop ("application ID to use when connecting"),
       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_app },
 
-    { 'b', "body", "MESSAGE_BODY",
-      gettext_noop ("message body to transmit"),
-      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_body },
+    { 'd', "data", "DATA",
+      gettext_noop ("message body or state value"),
+      GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_data },
 
     { 'e', "ego", "NAME|PUBKEY",
       gettext_noop ("name or public key of ego"),
@@ -1071,7 +1260,7 @@ main (int argc, char *const *argv)
       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_peer },
 
     { 'k', "name", "VAR_NAME",
-      gettext_noop ("state variable name (key) to query"),
+      gettext_noop ("name (key) to query from state"),
       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_name },
 
     { 'm', "method", "METHOD_NAME",
@@ -1116,13 +1305,18 @@ main (int argc, char *const *argv)
     "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> --body <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> --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_NAME> --body <MESSAGE_BODY>\n"
+    "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
     "\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"