NAMESTORE/JSON: fix parsing exp and flags
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
index 0f0660edcfed78906312a585e5daf98f7141d4cc..470716f43afc6e779081ca4a9d76f7ffbf21fe75 100644 (file)
@@ -14,6 +14,8 @@
     
      You should have received a copy of the GNU Affero General Public License
      along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
 */
 /**
  * @file gnunet-namestore.c
@@ -135,6 +137,11 @@ static int is_shadow;
  */
 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
 
+/**
+ * Queue entry for the 'set/replace' operation.
+ */
+static struct GNUNET_NAMESTORE_QueueEntry *set_qe;
+
 /**
  * Name of the records to add/list/remove.
  */
@@ -191,14 +198,9 @@ static void *data;
 static size_t data_size;
 
 /**
- * Expirationstring converted to relative time.
+ * Expiration string converted to numeric value.
  */
-static struct GNUNET_TIME_Relative etime_rel;
-
-/**
- * Expirationstring converted to absolute time.
- */
-static struct GNUNET_TIME_Absolute etime_abs;
+static uint64_t etime;
 
 /**
  * Is expiration time relative or absolute time?
@@ -255,6 +257,11 @@ do_shutdown (void *cls)
     GNUNET_NAMESTORE_cancel (add_qe);
     add_qe = NULL;
   }
+  if (NULL != set_qe)
+  {
+    GNUNET_NAMESTORE_cancel (set_qe);
+    set_qe = NULL;
+  }
   if (NULL != add_qe_uri)
   {
     GNUNET_NAMESTORE_cancel (add_qe_uri);
@@ -539,6 +546,8 @@ display_record_lookup (void *cls,
                        unsigned int rd_len,
                        const struct GNUNET_GNSRECORD_Data *rd)
 {
+  (void) cls;
+  (void) zone_key;
   get_qe = NULL;
   display_record (rname,
                   rd_len,
@@ -717,14 +726,10 @@ get_existing_record (void *cls,
     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
   if (1 != is_public)
     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
+  rde->expiration_time = etime;
   if (GNUNET_YES == etime_is_rel)
-  {
-    rde->expiration_time = etime_rel.rel_value_us;
     rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
-  }
-  else if (GNUNET_NO == etime_is_rel)
-    rde->expiration_time = etime_abs.abs_value_us;
-  else
+  else if (GNUNET_NO != etime_is_rel)
     rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
   GNUNET_assert (NULL != name);
   add_qe = GNUNET_NAMESTORE_records_store (ns,
@@ -936,6 +941,35 @@ parse_expiration (const char *expirationstring,
 }
 
 
+/**
+ * Function called when namestore is done with the replace
+ * operation.
+ *
+ * @param cls NULL
+ * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
+ *                #GNUNET_NO if content was already there or not found
+ *                #GNUNET_YES (or other positive value) on success
+ * @param emsg NULL on success, otherwise an error message
+ */
+static void
+replace_cont (void *cls,
+             int success,
+             const char *emsg)
+{
+  (void) cls;
+  
+  set_qe = NULL;
+  if (GNUNET_OK != success)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+               _("Failed to replace records: %s\n"),
+               emsg);
+    ret = 1; /* fail from 'main' */
+  }
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
 /**
  * Callback invoked from identity service with ego information.
  * An @a ego of NULL means the ego was not found.
@@ -950,7 +984,6 @@ identity_cb (void *cls,
   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
   struct GNUNET_GNSRECORD_Data rd;
-  uint64_t etime;
 
   el = NULL;
   if (NULL == ego)
@@ -969,7 +1002,7 @@ identity_cb (void *cls,
   GNUNET_free_non_null (ego_name);
   ego_name = NULL;
 
-  if (! (add|del|list|(NULL != nickstring)|(NULL != uri)|(NULL != reverse_pkey)) )
+  if (! (add|del|list|(NULL != nickstring)|(NULL != uri)|(NULL != reverse_pkey)|(NULL != recordset)) )
   {
     /* nothing more to be done */
     fprintf (stderr,
@@ -978,8 +1011,7 @@ identity_cb (void *cls,
     return;
   }
   GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey,
-                                    &pub);
-
+                                     &pub);
   ns = GNUNET_NAMESTORE_connect (cfg);
   if (NULL == ns)
   {
@@ -987,6 +1019,44 @@ identity_cb (void *cls,
                 _("Failed to connect to namestore\n"));
     return;
   }
+
+  if (NULL != recordset)
+  {
+    /* replace entire record set */
+    unsigned int rd_count;
+    struct GNUNET_GNSRECORD_Data *rd;
+
+    if (NULL == name)
+    {
+      fprintf (stderr,
+               _("Missing option `%s' for operation `%s'\n"),
+               "-R", _("replace"));
+      GNUNET_SCHEDULER_shutdown ();
+      ret = 1;
+      return;
+    }
+    rd_count = 0;
+    for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
+      rd_count++;
+    rd = GNUNET_new_array (rd_count,
+                          struct GNUNET_GNSRECORD_Data);
+    rd_count = 0;
+    for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
+    {
+      rd[rd_count] = e->record;
+      rd_count++;
+    }
+    set_qe = GNUNET_NAMESTORE_records_store (ns,
+                                            &zone_pkey,
+                                            name,
+                                            rd_count,
+                                            rd,
+                                            &replace_cont,
+                                            NULL);
+    GNUNET_free (rd);
+    return;
+  }
+  
   if (add)
   {
     if (NULL == name)
@@ -1266,6 +1336,10 @@ run (void *cls,
   (void) cls;
   (void) args;
   (void) cfgfile;
+  if (NULL != args[0])
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Superfluous command line arguments (starting with `%s') ignored\n"),
+               args[0]);
   if ( (NULL != args[0]) &&
        (NULL == uri) )
     uri = GNUNET_strdup (args[0]);
@@ -1302,6 +1376,7 @@ run (void *cls,
  * @param option name of the option (typically 'R')
  * @param value command line argument given; format is
  *        "TTL TYPE FLAGS VALUE" where TTL is an expiration time (rel or abs),
+ *        always given in seconds (without the unit),
  *         TYPE is a DNS/GNS record type, FLAGS is either "n" for no flags or
  *         a combination of 's' (shadow) and 'p' (public) and VALUE is the 
  *         value (in human-readable format)
@@ -1318,33 +1393,45 @@ multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
   struct GNUNET_GNSRECORD_Data record;
   char *cp;
   char *tok;
+  char *saveptr;
   int etime_is_rel;
   void *raw_data;
 
+  (void) ctx;
+  (void) option;
   cp = GNUNET_strdup (value);
-  tok = strtok (cp, " ");
+  tok = strtok_r (cp, " ", &saveptr);
   if (NULL == tok)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Empty record line argument is not allowed.\n"));
     GNUNET_free (cp);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
-      parse_expiration (tok,
-                       &etime_is_rel,
-                       &record.expiration_time))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
-               _("Invalid expiration time `%s'\n"),
-               tok);
-    GNUNET_free (cp);
-    return GNUNET_SYSERR;
+    char *etime_in_s;
+
+    GNUNET_asprintf (&etime_in_s,
+                    "%s s",
+                    tok);
+    if (GNUNET_OK !=
+       parse_expiration (etime_in_s,
+                         &etime_is_rel,
+                         &record.expiration_time))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Invalid expiration time `%s' (must be without unit)\n"),
+                 tok);
+      GNUNET_free (cp);
+      GNUNET_free (etime_in_s);
+      return GNUNET_SYSERR;
+    }
+    GNUNET_free (etime_in_s);
   }
-  tok = strtok (NULL, " ");
+  tok = strtok_r (NULL, " ", &saveptr);
   if (NULL == tok)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Missing entries in record line `%s'.\n"),
                value);
     GNUNET_free (cp);
@@ -1353,16 +1440,16 @@ multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
   record.record_type = GNUNET_GNSRECORD_typename_to_number (tok);
   if (UINT32_MAX == record.record_type)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Unknown record type `%s'\n"),
                tok);
     GNUNET_free (cp);
     return GNUNET_SYSERR;
   }
-  tok = strtok (NULL, " ");
+  tok = strtok_r (NULL, " ", &saveptr);
   if (NULL == tok)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Missing entries in record line `%s'.\n"),
                value);
     GNUNET_free (cp);
@@ -1379,7 +1466,7 @@ multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
   tok = strchr (&value[tok - cp], (unsigned char) ' ');
   if (NULL == tok)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Missing entries in record line `%s'.\n"),
                value);
     GNUNET_free (cp);
@@ -1393,7 +1480,7 @@ multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
                                        &raw_data,
                                        &record.data_size))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Invalid record data for type %s: `%s'.\n"),
                GNUNET_GNSRECORD_number_to_typename (record.record_type),               
                tok);
@@ -1493,9 +1580,9 @@ main (int argc,
                                  gettext_noop ("determine our name for the given PKEY"),
                                  &reverse_pkey),
     multirecord_option ('R',
-                       "record",
+                       "replace",
                        "RECORDLINE",
-                       gettext_noop ("complete record on one line to add/delete/display; can be specified multiple times"),
+                       gettext_noop ("set record set to values given by (possibly multiple) RECORDLINES; can be specified multiple times"),
                        &recordset),
     GNUNET_GETOPT_option_string ('t',
                                  "type",