-add adv port
[oweals/gnunet.git] / src / util / pseudonym.c
index 85353c6da7fe801e3f4aa899767ee6c940c1420f..48852ee378f9eb1c192679a8c1768c3d1e4d697d 100644 (file)
 #include "gnunet_pseudonym_lib.h"
 #include "gnunet_bio_lib.h"
 
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
 /**
  * Name of the directory which stores meta data for pseudonym
  */
 #define PS_NAMES_DIR    DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms" DIR_SEPARATOR_STR "names"    DIR_SEPARATOR_STR
 
 
+/**
+ * Configuration section we use.
+ */
+#define GNUNET_CLIENT_SERVICE_NAME "client"
+
+
+
 /**
  * Registered callbacks for discovery of pseudonyms.
  */
@@ -77,7 +88,7 @@ static struct DiscoveryCallback *head;
  * @param rating rating of pseudonym
  */
 static void
-internal_notify (const GNUNET_HashCode * id,
+internal_notify (const struct GNUNET_HashCode * id,
                  const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
   struct DiscoveryCallback *pos;
@@ -85,7 +96,7 @@ internal_notify (const GNUNET_HashCode * id,
   pos = head;
   while (pos != NULL)
   {
-    pos->callback (pos->closure, id, md, rating);
+    pos->callback (pos->closure, id, NULL, NULL, md, rating);
     pos = pos->next;
   }
 }
@@ -93,6 +104,9 @@ internal_notify (const GNUNET_HashCode * id,
 /**
  * Register callback to be invoked whenever we discover
  * a new pseudonym.
+ * Will immediately call provided iterator callback for all
+ * already discovered pseudonyms.
+ *
  * @param cfg configuration to use
  * @param iterator iterator over pseudonym
  * @param closure point to a closure
@@ -155,7 +169,7 @@ GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
  */
 static char *
 get_data_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                   const char *prefix, const GNUNET_HashCode * psid)
+                   const char *prefix, const struct GNUNET_HashCode * psid)
 {
   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
 
@@ -174,11 +188,11 @@ get_data_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
  * @param nsid hash code of a pseudonym
  * @param meta meta data to be written into a file
  * @param ranking ranking of a pseudonym
- * @param ns_name name of a pseudonym
+ * @param ns_name non-unique name of a pseudonym
  */
 static void
 write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                      const GNUNET_HashCode * nsid,
+                      const struct GNUNET_HashCode * nsid,
                       const struct GNUNET_CONTAINER_MetaData *meta,
                       int32_t ranking, const char *ns_name)
 {
@@ -208,9 +222,9 @@ write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
   }
   GNUNET_free (fn);
   /* create entry for pseudonym name in names */
-  /* FIXME: 90% of what this call does is not needed
-   * here => refactor code to only create the entry! */
-  GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
+  if (ns_name != NULL)
+    GNUNET_free_non_null (GNUNET_PSEUDONYM_name_uniquify (cfg, nsid, ns_name,
+        NULL));
 }
 
 
@@ -224,7 +238,7 @@ write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
  */
 static int
 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
-           const GNUNET_HashCode * nsid,
+           const struct GNUNET_HashCode * nsid,
            struct GNUNET_CONTAINER_MetaData **meta, int32_t * ranking,
            char **ns_name)
 {
@@ -258,10 +272,9 @@ read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
   }
   if (GNUNET_OK != GNUNET_BIO_read_close (fileR, &emsg))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                _
-                ("Failed to parse metadata about pseudonym from file `%s': %s\n"),
-                fn, emsg);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Failed to parse metadata about pseudonym from file `%s': %s\n"), fn,
+         emsg);
     GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
     GNUNET_CONTAINER_meta_data_destroy (*meta);
     *meta = NULL;
@@ -276,62 +289,38 @@ read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
 }
 
 
-
 /**
- * Return the unique, human readable name for the given namespace.
+ * Return unique variant of the namespace name.
+ * Use it after GNUNET_PSEUDONYM_get_info() to make sure
+ * that name is unique.
  *
  * @param cfg configuration
  * @param nsid cryptographic ID of the namespace
- * @return NULL on failure (should never happen)
+ * @param name name to uniquify
+ * @param suffix if not NULL, filled with the suffix value
+ * @return NULL on failure (should never happen), name on success.
+ *         Free the name with GNUNET_free().
  */
 char *
-GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                             const GNUNET_HashCode * nsid)
+GNUNET_PSEUDONYM_name_uniquify (const struct GNUNET_CONFIGURATION_Handle *cfg,
+    const struct GNUNET_HashCode * nsid, const char *name, unsigned int *suffix)
 {
-  struct GNUNET_CONTAINER_MetaData *meta;
-  char *name;
-  GNUNET_HashCode nh;
-  char *fn;
+  struct GNUNET_HashCode nh;
   uint64_t len;
+  char *fn;
   struct GNUNET_DISK_FileHandle *fh;
   unsigned int i;
   unsigned int idx;
   char *ret;
   struct stat sbuf;
-  int32_t temp = 0;
-  int32_t *rank = &temp;
 
-  meta = NULL;
-  name = NULL;
-  if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
-  {
-    if ((meta != NULL) && (name == NULL))
-      name =
-          GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
-                                                         EXTRACTOR_METATYPE_TITLE,
-                                                         EXTRACTOR_METATYPE_FILENAME,
-                                                         EXTRACTOR_METATYPE_DESCRIPTION,
-                                                         EXTRACTOR_METATYPE_SUBJECT,
-                                                         EXTRACTOR_METATYPE_PUBLISHER,
-                                                         EXTRACTOR_METATYPE_AUTHOR_NAME,
-                                                         EXTRACTOR_METATYPE_COMMENT,
-                                                         EXTRACTOR_METATYPE_SUMMARY,
-                                                         -1);
-    if (meta != NULL)
-    {
-      GNUNET_CONTAINER_meta_data_destroy (meta);
-      meta = NULL;
-    }
-  }
-  if (name == NULL)
-    name = GNUNET_strdup (_("no-name"));
   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
   GNUNET_assert (fn != NULL);
 
   len = 0;
   if (0 == STAT (fn, &sbuf))
-    GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
+    GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (fn, &len, GNUNET_YES, GNUNET_YES));
   fh = GNUNET_DISK_file_open (fn,
                               GNUNET_DISK_OPEN_CREATE |
                               GNUNET_DISK_OPEN_READWRITE,
@@ -339,69 +328,155 @@ GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
                               GNUNET_DISK_PERM_USER_WRITE);
   i = 0;
   idx = -1;
-  while ((len >= sizeof (GNUNET_HashCode)) &&
-         (sizeof (GNUNET_HashCode) ==
-          GNUNET_DISK_file_read (fh, &nh, sizeof (GNUNET_HashCode))))
+  while ((len >= sizeof (struct GNUNET_HashCode)) &&
+         (sizeof (struct GNUNET_HashCode) ==
+          GNUNET_DISK_file_read (fh, &nh, sizeof (struct GNUNET_HashCode))))
   {
-    if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
+    if (0 == memcmp (&nh, nsid, sizeof (struct GNUNET_HashCode)))
     {
       idx = i;
       break;
     }
     i++;
-    len -= sizeof (GNUNET_HashCode);
+    len -= sizeof (struct GNUNET_HashCode);
   }
   if (idx == -1)
   {
     idx = i;
-    if (sizeof (GNUNET_HashCode) !=
-        GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
+    if (sizeof (struct GNUNET_HashCode) !=
+        GNUNET_DISK_file_write (fh, nsid, sizeof (struct GNUNET_HashCode)))
+      LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "write", fn);
   }
   GNUNET_DISK_file_close (fh);
   ret = GNUNET_malloc (strlen (name) + 32);
   GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
-  GNUNET_free (name);
+  if (suffix != NULL)
+    *suffix = idx;
   GNUNET_free (fn);
   return ret;
 }
 
+/**
+ * Get namespace name, metadata and rank
+ * This is a wrapper around internal read_info() call, and ensures that
+ * returned data is not invalid (not NULL).
+ *
+ * @param cfg configuration
+ * @param nsid cryptographic ID of the namespace
+ * @param ret_meta a location to store metadata pointer. NULL, if metadata
+ *        is not needed. Destroy with GNUNET_CONTAINER_meta_data_destroy().
+ * @param ret_rank a location to store rank. NULL, if rank not needed.
+ * @param ret_name a location to store human-readable name. Name is not unique.
+ *        NULL, if name is not needed. Free with GNUNET_free().
+ * @param name_is_a_dup is set to GNUNET_YES, if ret_name was filled with
+ *        a duplicate of a "no-name" placeholder
+ * @return GNUNET_OK on success. GNUENT_SYSERR if the data was
+ *         unobtainable (in that case ret_* are filled with placeholders - 
+ *         empty metadata container, rank -1 and a "no-name" name).
+ */
+int
+GNUNET_PSEUDONYM_get_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
+    const struct GNUNET_HashCode * nsid, struct GNUNET_CONTAINER_MetaData **ret_meta,
+    int32_t *ret_rank, char **ret_name, int *name_is_a_dup)
+{
+  struct GNUNET_CONTAINER_MetaData *meta;
+  char *name;
+  int32_t rank = -1;
+
+  meta = NULL;
+  name = NULL;
+  if (GNUNET_OK == read_info (cfg, nsid, &meta, &rank, &name))
+  {
+    if ((meta != NULL) && (name == NULL))
+      name =
+          GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+                                                         EXTRACTOR_METATYPE_TITLE,
+                                                         EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
+                                                         EXTRACTOR_METATYPE_FILENAME,
+                                                         EXTRACTOR_METATYPE_DESCRIPTION,
+                                                         EXTRACTOR_METATYPE_SUBJECT,
+                                                         EXTRACTOR_METATYPE_PUBLISHER,
+                                                         EXTRACTOR_METATYPE_AUTHOR_NAME,
+                                                         EXTRACTOR_METATYPE_COMMENT,
+                                                         EXTRACTOR_METATYPE_SUMMARY,
+                                                         -1);
+    if (ret_name != NULL)
+    {
+      if (name == NULL)
+      {
+        name = GNUNET_strdup (_("no-name"));
+        if (name_is_a_dup != NULL)
+          *name_is_a_dup = GNUNET_YES;
+      }
+      else if (name_is_a_dup != NULL)
+        *name_is_a_dup = GNUNET_NO;
+      *ret_name = name;
+    }
+    else if (name != NULL)
+      GNUNET_free (name);
+
+    if (ret_meta != NULL)
+    {
+      if (meta == NULL)
+        meta = GNUNET_CONTAINER_meta_data_create ();
+      *ret_meta = meta;
+    }
+    else if (meta != NULL)
+      GNUNET_CONTAINER_meta_data_destroy (meta);
+
+    if (ret_rank != NULL)
+      *ret_rank = rank;
+
+    return GNUNET_OK;
+  }
+  if (ret_name != NULL)
+    *ret_name = GNUNET_strdup (_("no-name"));
+  if (ret_meta != NULL)
+    *ret_meta = GNUNET_CONTAINER_meta_data_create ();
+  if (ret_rank != NULL)
+    *ret_rank = -1;
+  if (name_is_a_dup != NULL)
+    *name_is_a_dup = GNUNET_YES;
+  return GNUNET_SYSERR;
+}
+
 /**
  * Get the namespace ID belonging to the given namespace name.
  *
  * @param cfg configuration to use
- * @param ns_uname human-readable name for the namespace
+ * @param ns_uname unique (!) human-readable name for the namespace
  * @param nsid set to namespace ID based on 'ns_uname'
- * @return GNUNET_OK on success
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  */
 int
 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                             const char *ns_uname, GNUNET_HashCode * nsid)
+    const char *ns_uname, struct GNUNET_HashCode * nsid)
 {
   size_t slen;
   uint64_t len;
   unsigned int idx;
   char *name;
-  GNUNET_HashCode nh;
+  struct GNUNET_HashCode nh;
   char *fn;
   struct GNUNET_DISK_FileHandle *fh;
 
   idx = -1;
   slen = strlen (ns_uname);
-  while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
+  while ((slen > 0) && (1 != SSCANF (&ns_uname[slen - 1], "-%u", &idx)))
     slen--;
   if (slen == 0)
     return GNUNET_SYSERR;
   name = GNUNET_strdup (ns_uname);
   name[slen - 1] = '\0';
+
   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
   GNUNET_free (name);
   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
   GNUNET_assert (fn != NULL);
 
   if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
-       (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
-      ((idx + 1) * sizeof (GNUNET_HashCode) > len))
+       (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES, GNUNET_YES))) ||
+      ((idx + 1) * sizeof (struct GNUNET_HashCode) > len))
   {
     GNUNET_free (fn);
     return GNUNET_SYSERR;
@@ -412,10 +487,15 @@ GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
                               GNUNET_DISK_PERM_USER_READ |
                               GNUNET_DISK_PERM_USER_WRITE);
   GNUNET_free (fn);
-  GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
-                         GNUNET_DISK_SEEK_SET);
-  if (sizeof (GNUNET_HashCode) !=
-      GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
+  if (GNUNET_SYSERR ==
+      GNUNET_DISK_file_seek (fh, idx * sizeof (struct GNUNET_HashCode),
+                            GNUNET_DISK_SEEK_SET))
+  {
+    GNUNET_DISK_file_close (fh);
+    return GNUNET_SYSERR;
+  }
+  if (sizeof (struct GNUNET_HashCode) !=
+      GNUNET_DISK_file_read (fh, nsid, sizeof (struct GNUNET_HashCode)))
   {
     GNUNET_DISK_file_close (fh);
     return GNUNET_SYSERR;
@@ -460,11 +540,12 @@ list_pseudonym_helper (void *cls, const char *fullname)
 {
   struct ListPseudonymClosure *c = cls;
   int ret;
-  GNUNET_HashCode id;
-  int rating;
+  struct GNUNET_HashCode id;
+  int32_t rating;
   struct GNUNET_CONTAINER_MetaData *meta;
   const char *fn;
   char *str;
+  char *name_unique;
 
   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
     return GNUNET_OK;
@@ -476,11 +557,22 @@ list_pseudonym_helper (void *cls, const char *fullname)
   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
     return GNUNET_OK;           /* invalid name */
   str = NULL;
-  if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
-    return GNUNET_OK;           /* ignore entry */
-  GNUNET_free_non_null (str);
+  if (GNUNET_OK != GNUNET_PSEUDONYM_get_info (c->cfg, &id, &meta, &rating,
+      &str, NULL))
+  {
+    /* ignore entry. FIXME: Why? Lack of data about a pseudonym is not a reason
+     * to ignore it... So yeah, it will have placeholders instead of name,
+     * empty metadata container and a default rank == -1, so what? We know
+     * its nsid - that's all we really need. Right? */
+    GNUNET_free (str);
+    GNUNET_CONTAINER_meta_data_destroy (meta);
+    return GNUNET_OK;
+  }
+  name_unique = GNUNET_PSEUDONYM_name_uniquify (c->cfg, &id, str, NULL);
   if (c->iterator != NULL)
-    ret = c->iterator (c->closure, &id, meta, rating);
+    ret = c->iterator (c->closure, &id, str, name_unique, meta, rating);
+  GNUNET_free_non_null (str);
+  GNUNET_free_non_null (name_unique);
   GNUNET_CONTAINER_meta_data_destroy (meta);
   return ret;
 }
@@ -525,7 +617,7 @@ GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
  */
 int
 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                       const GNUNET_HashCode * nsid, int delta)
+                       const struct GNUNET_HashCode * nsid, int delta)
 {
   struct GNUNET_CONTAINER_MetaData *meta;
   int ret;
@@ -547,6 +639,31 @@ GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
 }
 
 
+/**
+ * Set the pseudonym metadata, rank and name.
+ *
+ * @param cfg overall configuration
+ * @param nsid id of the pseudonym
+ * @param name name to set. Must be the non-unique version of it.
+ *        May be NULL, in which case it erases pseudonym's name!
+ * @param md metadata to set
+ *        May be NULL, in which case it erases pseudonym's metadata!
+ * @param rank rank to assign
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_PSEUDONYM_set_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
+    const struct GNUNET_HashCode * nsid, const char *name,
+    const struct GNUNET_CONTAINER_MetaData *md, int rank)
+{
+  GNUNET_assert (cfg != NULL);
+  GNUNET_assert (nsid != NULL);
+
+  write_pseudonym_info (cfg, nsid, md, rank, name);
+  return GNUNET_OK;
+}
+
+
 /**
  * Add a pseudonym to the set of known pseudonyms.
  * For all pseudonym advertisements that we discover
@@ -558,7 +675,7 @@ GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
  */
 void
 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                      const GNUNET_HashCode * id,
+                      const struct GNUNET_HashCode * id,
                       const struct GNUNET_CONTAINER_MetaData *meta)
 {
   char *name;