-fix #3301
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
index 6a8df6945021842d80315c0d4f1a2656ccd17cdb..f5a13c96b9bda7af7bedf53912c8596c1c2ec7ca 100644 (file)
@@ -1,7 +1,6 @@
-
 /*
      This file is part of GNUnet.
-     (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+     (C) 2012, 2013, 2014 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -50,8 +49,14 @@ static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
  */
 static struct GNUNET_IDENTITY_EgoLookup *el;
 
+/**
+ * Identity service handle
+ */
 static struct GNUNET_IDENTITY_Handle *idh;
 
+/**
+ * Obtain default ego
+ */
 struct GNUNET_IDENTITY_Operation *get_default;
 
 /**
@@ -97,12 +102,17 @@ static int del;
 /**
  * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE)
  */
-static int public;
+static int is_public;
 
 /**
  * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW_RECORD)
  */
-static int shadow;
+static int is_shadow;
+
+/**
+ * Is record pending approval (#GNUNET_GNSRECORD_RF_PENDING)
+ */
+static int is_pending;
 
 /**
  * Queue entry for the 'del' operation.
@@ -300,6 +310,7 @@ add_continuation (void *cls,
     if (GNUNET_NO != success)
       ret = 1;
   }
+  ret = 0;
   test_finished ();
 }
 
@@ -378,6 +389,10 @@ display_record (void *cls,
           rname);
   for (i=0;i<rd_len;i++)
   {
+    if ( (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
+         (0 != strcmp (rname,
+                       "+")) )
+      continue;
     typestring = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
     s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
                                          rd[i].data,
@@ -444,31 +459,101 @@ get_existing_record (void *cls,
 {
   struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
   struct GNUNET_GNSRECORD_Data *rde;
+  unsigned int i;
 
   add_qe = NULL;
   if ( (NULL != zone_key) &&
        (0 != strcmp (rec_name, name)) )
   {
     GNUNET_break (0);
+    ret = 1;
+    test_finished ();
     return;
   }
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %u records for name `%s'\n",
-      rd_count, rec_name);
-
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received %u records for name `%s'\n",
+              rd_count, rec_name);
+  for (i=0;i<rd_count;i++)
+  {
+    switch (rd[i].record_type)
+    {
+    case GNUNET_DNSPARSER_TYPE_CNAME:
+      fprintf (stderr,
+               _("A %s record exists already under `%s', no other records can be added.\n"),
+               "CNAME",
+               rec_name);
+      ret = 1;
+      test_finished ();
+      return;
+    case GNUNET_GNSRECORD_TYPE_PKEY:
+      fprintf (stderr,
+               _("A %s record exists already under `%s', no other records can be added.\n"),
+               "PKEY",
+               rec_name);
+      ret = 1;
+      test_finished ();
+    case GNUNET_GNSRECORD_TYPE_GNS2DNS:
+      fprintf (stderr,
+               _("A %s record exists already under `%s', no other records can be added.\n"),
+               "GNS2DNS",
+               rec_name);
+      ret = 1;
+      test_finished ();
+      return;
+    }
+  }
+  switch (type)
+  {
+  case GNUNET_DNSPARSER_TYPE_CNAME:
+    if (0 != rd_count)
+    {
+      fprintf (stderr,
+               _("Records already exist under `%s', cannot add `%s' record.\n"),
+               rec_name,
+               "CNAME");
+      ret = 1;
+      test_finished ();
+      return;
+    }
+    break;
+  case GNUNET_GNSRECORD_TYPE_PKEY:
+    if (0 != rd_count)
+    {
+      fprintf (stderr,
+               _("Records already exist under `%s', cannot add `%s' record.\n"),
+               rec_name,
+               "PKEY");
+      ret = 1;
+      test_finished ();
+      return;
+    }
+    break;
+  case GNUNET_GNSRECORD_TYPE_GNS2DNS:
+    if (0 != rd_count)
+    {
+      fprintf (stderr,
+               _("Records already exist under `%s', cannot add `%s' record.\n"),
+               rec_name,
+               "GNS2DNS");
+      ret = 1;
+      test_finished ();
+      return;
+    }
+    break;
+  }
   memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
   memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
-  /* FIXME: should add some logic to overwrite records if there
-     can only be one record of a particular type, and to check
-     if the combination of records is valid to begin with... */
   rde = &rdn[0];
   rde->data = data;
   rde->data_size = data_size;
   rde->record_type = type;
-  if (1 == shadow)
+  if (1 == is_shadow)
     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
-  if (1 != public)
+  if (1 != is_public)
     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
+  if (1 == is_pending)
+    rde->flags |= GNUNET_GNSRECORD_RF_PENDING;
   if (GNUNET_YES == etime_is_rel)
   {
     rde->expiration_time = etime_rel.rel_value_us;
@@ -519,6 +604,88 @@ handle_reverse_lookup (void *cls,
 }
 
 
+/**
+ * We were asked to delete something; this function is called with
+ * the existing records. Now we should determine what should be
+ * deleted and then issue the deletion operation.
+ *
+ * @param cls NULL
+ * @param zone private key of the zone we are deleting from
+ * @param label name of the records we are editing
+ * @param rd_count size of the @a rd array
+ * @param rd existing records
+ */
+static void
+del_monitor (void *cls,
+             const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
+             const char *label,
+             unsigned int rd_count,
+             const struct GNUNET_GNSRECORD_Data *rd)
+{
+  struct GNUNET_GNSRECORD_Data rdx[rd_count];
+  unsigned int rd_left;
+  unsigned int i;
+  uint32_t type;
+  char *vs;
+
+  del_qe = NULL;
+  if (0 == rd_count)
+  {
+    FPRINTF (stderr,
+             _("There are no records under label `%s' that could be deleted.\n"),
+             label);
+    test_finished ();
+    return;
+  }
+  if ( (NULL == value) &&
+       (NULL == typestring) )
+  {
+    /* delete everything */
+    del_qe = GNUNET_NAMESTORE_records_store (ns,
+                                             &zone_pkey,
+                                             name,
+                                             0, NULL,
+                                             &del_continuation,
+                                             NULL);
+    return;
+  }
+  rd_left = 0;
+  if (NULL != typestring)
+    type = GNUNET_GNSRECORD_typename_to_number (typestring);
+  else
+    type = GNUNET_GNSRECORD_TYPE_ANY;
+  for (i=0;i<rd_count;i++)
+  {
+    vs = NULL;
+    if (! ( ( (GNUNET_GNSRECORD_TYPE_ANY == type) ||
+              (rd[i].record_type == type) ) &&
+            ( (NULL == value) ||
+              (NULL == (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
+                                                                rd[i].data,
+                                                                rd[i].data_size)))) ||
+              (0 == strcmp (vs, value)) ) ) )
+      rdx[rd_left++] = rd[i];
+    GNUNET_free_non_null (vs);
+  }
+  if (rd_count == rd_left)
+  {
+    /* nothing got deleted */
+    FPRINTF (stderr,
+             _("There are no records under label `%s' that match the request for deletion.\n"),
+             label);
+    test_finished ();
+    return;
+  }
+  /* delete everything but what we copied to 'rdx' */
+  del_qe = GNUNET_NAMESTORE_records_store (ns,
+                                           &zone_pkey,
+                                           name,
+                                           rd_left, rdx,
+                                           &del_continuation,
+                                           NULL);
+}
+
+
 /**
  * Function called with the result from the check if the namestore
  * service is actually running.  If it is, we start the actual
@@ -658,12 +825,11 @@ testservice_task (void *cls,
       ret = 1;
       return;
     }
-    del_qe = GNUNET_NAMESTORE_records_store (ns,
-                                            &zone_pkey,
-                                            name,
-                                            0, NULL,
-                                            &del_continuation,
-                                            NULL);
+    del_qe = GNUNET_NAMESTORE_records_lookup (ns,
+                                              &zone_pkey,
+                                              name,
+                                              &del_monitor,
+                                              NULL);
   }
   if (list)
   {
@@ -698,12 +864,8 @@ testservice_task (void *cls,
     char sname[64];
     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
 
-    if ( (2 != (sscanf (uri,
-                        "gnunet://gns/%104s/%63s",
-                        sh,
-                        sname)) ) ||
-         (GNUNET_OK !=
-          GNUNET_CRYPTO_ecdsa_public_key_from_string (sh, strlen (sh), &pkey)) )
+    if ( (2 != (sscanf (uri, "gnunet://gns/%52s/%63s", sh, sname)) ) ||
+         (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (sh, strlen (sh), &pkey)) )
     {
       fprintf (stderr,
                _("Invalid URI `%s'\n"),
@@ -726,7 +888,7 @@ testservice_task (void *cls,
     else
       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
 
-    if (1 != shadow)
+    if (1 == is_shadow)
       rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
                                                 &zone_pkey,
@@ -785,6 +947,7 @@ identity_cb (void *cls,
                ego_name);
     }
     GNUNET_SCHEDULER_shutdown ();
+    ret = -1;
     return;
   }
   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
@@ -799,9 +962,9 @@ identity_cb (void *cls,
 
 static void
 default_ego_cb (void *cls,
-    struct GNUNET_IDENTITY_Ego *ego,
-    void **ctx,
-    const char *name)
+                struct GNUNET_IDENTITY_Ego *ego,
+                void **ctx,
+                const char *name)
 {
   get_default = NULL;
   if (NULL == ego)
@@ -809,6 +972,7 @@ default_ego_cb (void *cls,
     fprintf (stderr,
              _("No default ego configured in identity service\n"));
     GNUNET_SCHEDULER_shutdown ();
+    ret = -1;
     return;
   }
   else
@@ -817,17 +981,20 @@ default_ego_cb (void *cls,
   }
 }
 
+
 static void
 id_connect_cb (void *cls,
-    struct GNUNET_IDENTITY_Ego *ego,
-    void **ctx,
-    const char *name)
+               struct GNUNET_IDENTITY_Ego *ego,
+               void **ctx,
+               const char *name)
 {
   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+
   if (NULL == ego)
   {
-    get_default = GNUNET_IDENTITY_get (idh, "namestore",
-        &default_ego_cb, (void *) cfg);
+    get_default = GNUNET_IDENTITY_get (idh,
+                                       "namestore",
+                                       &default_ego_cb, (void *) cfg);
   }
 }
 
@@ -841,6 +1008,7 @@ testservice_id_task (void *cls, int result)
     fprintf (stderr,
              _("Identity service is not running\n"));
     GNUNET_SCHEDULER_shutdown ();
+    ret = -1;
     return;
   }
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
@@ -851,6 +1019,7 @@ testservice_id_task (void *cls, int result)
     idh = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, (void *) cfg);
     if (NULL == idh)
       fprintf (stderr, _("Cannot connect to identity service\n"));
+    ret = -1;
     return;
   }
   el = GNUNET_IDENTITY_ego_lookup (cfg,
@@ -892,8 +1061,9 @@ run (void *cls, char *const *args, const char *cfgfile,
 int
 main (int argc, char *const *argv)
 {
-  public = -1;
-  shadow = -1;
+  is_public = -1;
+  is_pending = -1;
+  is_shadow = -1;
 
   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
     {'a', "add", NULL,
@@ -931,10 +1101,13 @@ main (int argc, char *const *argv)
      &GNUNET_GETOPT_set_string, &value},
     {'p', "public", NULL,
      gettext_noop ("create or list public record"), 0,
-     &GNUNET_GETOPT_set_one, &public},
+     &GNUNET_GETOPT_set_one, &is_public},
+    {'P', "pending", NULL,
+     gettext_noop ("create record that is pending approval (and thus for now inactive)"), 0,
+     &GNUNET_GETOPT_set_one, &is_pending},
     {'s', "shadow", NULL,
      gettext_noop ("create shadow record (only valid if all other records of the same type have expired"), 0,
-     &GNUNET_GETOPT_set_one, &shadow},
+     &GNUNET_GETOPT_set_one, &is_shadow},
     {'z', "zone", "EGO",
      gettext_noop ("name of the ego controlling the zone"), 1,
      &GNUNET_GETOPT_set_string, &ego_name},