View Issue Details

IDProjectCategoryView StatusLast Update
0001952gnunet-gtkgnunet-fs-gtkpublic2013-12-24 20:54
ReporterChristian Grothoff Assigned ToChristian Grothoff  
PriorityhighSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product VersionGit master 
Target Version0.10.0Fixed in Version0.10.0 
Summary0001952: namespace list in search dialog should have context menu
DescriptionRight-clicking on NS list in search dialog should open menu that allows
* viewing full meta data about the namespace
* deletion of namespace information (especially needed if it is our own namespace, then with warning that the private key will be unrecoverable)
TagsNo tags attached.
Attached Files
0001-Change-pseudonym-management.patch (30,413 bytes)   
From c856f29bee0aee8004f93606c40be4b8e25ed91c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1=D1?=
 =?UTF-8?q?=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= <lrn1986@gmail.com>
Date: Wed, 29 Feb 2012 11:18:17 +0400
Subject: [PATCH] Change pseudonym management

---
 src/chat/gnunet-chat.c             |   96 +++++++++++++++---
 src/fs/fs_uri.c                    |   12 ++-
 src/fs/gnunet-pseudonym.c          |   23 +++--
 src/include/gnunet_pseudonym_lib.h |   72 ++++++++++++--
 src/util/pseudonym.c               |  201 +++++++++++++++++++++++++++---------
 src/util/test_pseudonym.c          |   30 ++++--
 6 files changed, 344 insertions(+), 90 deletions(-)

diff --git a/src/chat/gnunet-chat.c b/src/chat/gnunet-chat.c
index 4abc58c..fb958d9 100644
--- a/src/chat/gnunet-chat.c
+++ b/src/chat/gnunet-chat.c
@@ -114,14 +114,27 @@ receive_cb (void *cls, struct GNUNET_CHAT_Room *room,
             const char *message, struct GNUNET_TIME_Absolute timestamp,
             enum GNUNET_CHAT_MsgOptions options)
 {
+  char *non_unique_nick;
   char *nick;
+  int nick_is_a_dup;
   char *time;
   const char *fmt;
 
-  if (NULL != sender)
-    nick = GNUNET_PSEUDONYM_id_to_name (cfg, sender);
-  else
+  if (NULL == sender)
     nick = GNUNET_strdup (_("anonymous"));
+  else
+  {
+    if (GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+        sender, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
+        || (nick_is_a_dup == GNUNET_YES))
+    {
+      GNUNET_free (non_unique_nick);
+      non_unique_nick = GNUNET_strdup (_("anonymous"));
+    }
+    nick = GNUNET_PSEUDONYM_name_uniquify (cfg, sender, non_unique_nick, NULL);
+    GNUNET_free (non_unique_nick);
+  }
+
   fmt = NULL;
   switch ((int) options)
   {
@@ -188,9 +201,20 @@ confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room,
                  const GNUNET_HashCode * receiver)
 {
   char *nick;
+  char *unique_nick;
+  int nick_is_a_dup;
 
-  nick = GNUNET_PSEUDONYM_id_to_name (cfg, receiver);
-  FPRINTF (stdout, _("'%s' acknowledged message #%d\n"), nick, orig_seq_number);
+  if (GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+      receiver, NULL, NULL, &nick, &nick_is_a_dup)
+      || (nick_is_a_dup == GNUNET_YES))
+  {
+    GNUNET_free (nick);
+    nick = GNUNET_strdup (_("anonymous"));
+  }
+  unique_nick = GNUNET_PSEUDONYM_name_uniquify (cfg, receiver, nick, NULL);
+  GNUNET_free (nick);
+  FPRINTF (stdout, _("'%s' acknowledged message #%d\n"), unique_nick, orig_seq_number);
+  GNUNET_free (unique_nick);
   return GNUNET_OK;
 }
 
@@ -210,7 +234,8 @@ member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info,
                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
                 enum GNUNET_CHAT_MsgOptions options)
 {
-  char *nick;
+  char *nick, *non_unique_nick;
+  int nick_is_a_dup;
   GNUNET_HashCode id;
   struct UserList *pos;
   struct UserList *prev;
@@ -218,7 +243,16 @@ member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info,
   GNUNET_CRYPTO_hash (member_id,
                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                       &id);
-  nick = GNUNET_PSEUDONYM_id_to_name (cfg, &id);
+  if (GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+      &id, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
+      || (nick_is_a_dup == GNUNET_YES))
+  {
+    GNUNET_free (non_unique_nick);
+    non_unique_nick = GNUNET_strdup (_("anonymous"));
+  }
+  nick = GNUNET_PSEUDONYM_name_uniquify (cfg, &id, non_unique_nick, NULL);
+  GNUNET_free (non_unique_nick);
+
   FPRINTF (stdout,
            member_info !=
            NULL ? _("`%s' entered the room\n") : _("`%s' left the room\n"),
@@ -267,6 +301,7 @@ static int
 do_join (const char *arg, const void *xtra)
 {
   char *my_name;
+  int my_name_is_a_dup;
   GNUNET_HashCode me;
 
   if (arg[0] == '#')
@@ -284,7 +319,16 @@ do_join (const char *arg, const void *xtra)
     FPRINTF (stdout, "%s",  _("Could not change username\n"));
     return GNUNET_SYSERR;
   }
-  my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
+  if ((GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+      &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
+      (my_name_is_a_dup == GNUNET_YES))
+  {
+    GNUNET_free (my_name);
+    my_name = GNUNET_strdup (_("anonymous"));
+  }
+  /* Don't uniquify our own name - other people will have a different
+   * suffix for our own name anyway.
+   */
   FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name,
            my_name);
   GNUNET_free (my_name);
@@ -296,6 +340,7 @@ static int
 do_nick (const char *msg, const void *xtra)
 {
   char *my_name;
+  int my_name_is_a_dup;
   GNUNET_HashCode me;
 
   GNUNET_CHAT_leave_room (room);
@@ -316,7 +361,13 @@ do_nick (const char *msg, const void *xtra)
     FPRINTF (stdout, "%s",  _("Could not change username\n"));
     return GNUNET_SYSERR;
   }
-  my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
+  if ((GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+      &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
+      (my_name_is_a_dup == GNUNET_YES))
+  {
+    GNUNET_free (my_name);
+    my_name = GNUNET_strdup (_("anonymous"));
+  }
   FPRINTF (stdout, _("Changed username to `%s'\n"), my_name);
   GNUNET_free (my_name);
   return GNUNET_OK;
@@ -327,6 +378,8 @@ static int
 do_names (const char *msg, const void *xtra)
 {
   char *name;
+  char *unique_name;
+  int name_is_a_dup;
   struct UserList *pos;
   GNUNET_HashCode pid;
 
@@ -337,9 +390,17 @@ do_names (const char *msg, const void *xtra)
     GNUNET_CRYPTO_hash (&pos->pkey,
                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                         &pid);
-    name = GNUNET_PSEUDONYM_id_to_name (cfg, &pid);
-    FPRINTF (stdout, "`%s' ", name);
+    if (GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+        &pid, NULL, NULL, &name, &name_is_a_dup)
+        || (name_is_a_dup == GNUNET_YES))
+    {
+      GNUNET_free (name);
+      name = GNUNET_strdup (_("anonymous"));
+    }
+    unique_name = GNUNET_PSEUDONYM_name_uniquify (cfg, &pid, name, NULL);
     GNUNET_free (name);
+    FPRINTF (stdout, "`%s' ", unique_name);
+    GNUNET_free (unique_name);
     pos = pos->next;
   }
   FPRINTF (stdout, "%s",  "\n");
@@ -376,7 +437,9 @@ do_send_pm (const char *msg, const void *xtra)
   msg += strlen (user) + 1;
   if (GNUNET_OK != GNUNET_PSEUDONYM_name_to_id (cfg, user, &uid))
   {
-    FPRINTF (stderr, _("Unknown user `%s'\n"), user);
+    FPRINTF (stderr,
+        _("Unknown user `%s'. Make sure you specify its numeric suffix, if any.\n"),
+        user);
     GNUNET_free (user);
     return GNUNET_OK;
   }
@@ -598,6 +661,7 @@ run (void *cls, char *const *args, const char *cfgfile,
 {
   GNUNET_HashCode me;
   char *my_name;
+  int my_name_is_a_dup;
 
   cfg = c;
   /* check arguments */
@@ -626,7 +690,13 @@ run (void *cls, char *const *args, const char *cfgfile,
     ret = -1;
     return;
   }
-  my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
+  if ((GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
+      &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
+      (my_name_is_a_dup == GNUNET_YES))
+  {
+    GNUNET_free (my_name);
+    my_name = GNUNET_strdup (_("anonymous"));
+  }
   FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name,
            my_name);
   GNUNET_free (my_name);
diff --git a/src/fs/fs_uri.c b/src/fs/fs_uri.c
index 5dfdcb5..43cf2c5 100644
--- a/src/fs/fs_uri.c
+++ b/src/fs/fs_uri.c
@@ -1377,15 +1377,17 @@ GNUNET_FS_uri_sks_to_string_fancy (struct GNUNET_CONFIGURATION_Handle *cfg,
                                    const struct GNUNET_FS_Uri *uri)
 {
   char *ret;
-  char *name;
+  char *name, *unique_name;
+  int getinfo_result;
 
   if (uri->type != sks)
     return NULL;
-  name = GNUNET_PSEUDONYM_id_to_name (cfg, &uri->data.sks.namespace);
-  if (name == NULL)
-    return GNUNET_FS_uri_to_string (uri);
-  GNUNET_asprintf (&ret, "%s: %s", name, uri->data.sks.identifier);
+  getinfo_result = GNUNET_PSEUDONYM_get_info (cfg, &uri->data.sks.namespace,
+      NULL, NULL, &name, NULL);
+  unique_name = GNUNET_PSEUDONYM_name_uniquify (cfg, &uri->data.sks.namespace, name, NULL);
   GNUNET_free (name);
+  GNUNET_asprintf (&ret, "%s: %s", unique_name, uri->data.sks.identifier);
+  GNUNET_free (unique_name);
   return ret;
 }
 
diff --git a/src/fs/gnunet-pseudonym.c b/src/fs/gnunet-pseudonym.c
index 412ddd2..247a5a0 100644
--- a/src/fs/gnunet-pseudonym.c
+++ b/src/fs/gnunet-pseudonym.c
@@ -106,20 +106,28 @@ ns_printer (void *cls, const char *name, const GNUNET_HashCode * id)
 
 static int
 pseudo_printer (void *cls, const GNUNET_HashCode * pseudonym,
+                const char *name, const char *unique_name,
                 const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
-  char *id;
-
-  id = GNUNET_PSEUDONYM_id_to_name (cfg, pseudonym);
-  if (id == NULL)
+  char *id, *unique_id;
+  int getinfo_result;
+
+  /* While we get a name from the caller, it might be NULL.
+   * GNUNET_PSEUDONYM_get_info () never returns NULL.
+   */
+  getinfo_result = GNUNET_PSEUDONYM_get_info (cfg, pseudonym,
+      NULL, NULL, &id, NULL);
+  if (getinfo_result != GNUNET_OK)
   {
     GNUNET_break (0);
     return GNUNET_OK;
   }
-  FPRINTF (stdout, "%s (%d):\n", id, rating);
+  unique_id = GNUNET_PSEUDONYM_name_uniquify (cfg, pseudonym, id, NULL);
+  GNUNET_free (id);
+  FPRINTF (stdout, "%s (%d):\n", unique_id, rating);
   GNUNET_CONTAINER_meta_data_iterate (md, &EXTRACTOR_meta_data_print, stdout);
   FPRINTF (stdout, "%s",  "\n");
-  GNUNET_free (id);
+  GNUNET_free (unique_id);
   return GNUNET_OK;
 }
 
@@ -162,7 +170,8 @@ post_advertising (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
       }
       else
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Namespace `%s' unknown.\n"),
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
+                    ("Namespace `%s' unknown. Make sure you specify its numeric suffix, if any.\n"),
                     rating_change);
       }
     }
diff --git a/src/include/gnunet_pseudonym_lib.h b/src/include/gnunet_pseudonym_lib.h
index bde98ef..8fd938d 100644
--- a/src/include/gnunet_pseudonym_lib.h
+++ b/src/include/gnunet_pseudonym_lib.h
@@ -44,12 +44,16 @@ extern "C"
  *
  * @param cls closure
  * @param pseudonym hash code of public key of pseudonym
+ * @param name name of the pseudonym (might be NULL)
+ * @param unique_name unique name of the pseudonym (might be NULL)
  * @param md meta data known about the pseudonym
  * @param rating the local rating of the pseudonym
  * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
  */
 typedef int (*GNUNET_PSEUDONYM_Iterator) (void *cls,
                                           const GNUNET_HashCode * pseudonym,
+                                          const char *name,
+                                          const char *unique_name,
                                           const struct GNUNET_CONTAINER_MetaData
                                           * md, int rating);
 
@@ -110,22 +114,76 @@ GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
                                                 iterator, void *closure);
 
 /**
- * Return the unique, human readable name for the given pseudonym.
+ * Return unique variant of the namespace name.
+ * Use after GNUNET_PSEUDONYM_id_to_name() to make sure
+ * that name is unique.
  *
- * @return NULL on failure (should never happen)
+ * @param cfg configuration
+ * @param nsid cryptographic ID of the namespace
+ * @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 * pseudo);
+GNUNET_PSEUDONYM_name_uniquify (const struct GNUNET_CONFIGURATION_Handle *cfg,
+    const GNUNET_HashCode * nsid, const char *name, unsigned int *suffix);
 
 /**
- * Get the pseudonym ID belonging to the given human readable name.
+ * 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).
+ * Writing back information returned by this function will give
+ * a name "no-name" to pseudonyms that have no name. This side-effect is
+ * unavoidable, but hardly harmful.
  *
- * @return GNUNET_OK on success
+ * @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 GNUNET_HashCode * nsid, struct GNUNET_CONTAINER_MetaData **ret_meta,
+    int32_t *ret_rank, char **ret_name, int *name_is_a_dup);
+
+
+/**
+ * Get the namespace ID belonging to the given namespace name.
+ *
+ * @param cfg configuration to use
+ * @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, GNUNET_SYSERR on failure
  */
 int
 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                             const char *hname, GNUNET_HashCode * psid);
+    const char *ns_uname, GNUNET_HashCode * nsid);
+
+/**
+ * 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 GNUNET_HashCode * nsid, const char *name,
+    const struct GNUNET_CONTAINER_MetaData *md, int rank);
 
 
 #if 0                           /* keep Emacsens' auto-indent happy */
diff --git a/src/util/pseudonym.c b/src/util/pseudonym.c
index 782a405..dd8ad08 100644
--- a/src/util/pseudonym.c
+++ b/src/util/pseudonym.c
@@ -96,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;
   }
 }
@@ -104,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
@@ -185,7 +188,7 @@ 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,
@@ -219,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));
 }
 
 
@@ -285,57 +288,31 @@ read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
   return GNUNET_OK;
 }
 
-
-
 /**
- * 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 GNUNET_HashCode * nsid, const char *name, unsigned int *suffix)
 {
-  struct GNUNET_CONTAINER_MetaData *meta;
-  char *name;
   GNUNET_HashCode nh;
-  char *fn;
   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_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 (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);
@@ -372,22 +349,107 @@ GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
   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 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, GNUNET_HashCode * nsid)
 {
   size_t slen;
   uint64_t len;
@@ -405,6 +467,7 @@ GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
     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);
@@ -472,10 +535,10 @@ list_pseudonym_helper (void *cls, const char *fullname)
   struct ListPseudonymClosure *c = cls;
   int ret;
   GNUNET_HashCode id;
-  int rating;
+  int32_t rating;
   struct GNUNET_CONTAINER_MetaData *meta;
   const char *fn;
-  char *str;
+  char *str, *name_unique;
 
   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
     return GNUNET_OK;
@@ -487,11 +550,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;
 }
@@ -559,6 +633,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 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
  * FS should automatically call this function.
diff --git a/src/util/test_pseudonym.c b/src/util/test_pseudonym.c
index 20a3d3d..4ce8b38 100644
--- a/src/util/test_pseudonym.c
+++ b/src/util/test_pseudonym.c
@@ -39,6 +39,7 @@ static GNUNET_HashCode id1;
 
 static int
 iter (void *cls, const GNUNET_HashCode * pseudonym,
+      const char *name, const char *unique_name,
       const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
   int *ok = cls;
@@ -54,6 +55,7 @@ iter (void *cls, const GNUNET_HashCode * pseudonym,
 
 static int
 noti_callback (void *cls, const GNUNET_HashCode * pseudonym,
+               const char *name, const char *unique_name,
                const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
   int *ret = cls;
@@ -64,6 +66,7 @@ noti_callback (void *cls, const GNUNET_HashCode * pseudonym,
 
 static int
 fake_noti_callback (void *cls, const GNUNET_HashCode * pseudonym,
+                    const char *name, const char *unique_name,
                     const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
   int *ret = cls;
@@ -74,6 +77,7 @@ fake_noti_callback (void *cls, const GNUNET_HashCode * pseudonym,
 
 static int
 false_callback (void *cls, const GNUNET_HashCode * pseudonym,
+                const char *name, const char *unique_name,
                 const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
   return GNUNET_OK;
@@ -95,7 +99,10 @@ main (int argc, char *argv[])
   char *name1;
   char *name2;
   char *name3;
+  char *name1_unique;
+  char *name2_unique;
   char *noname;
+  int noname_is_a_dup;
   int notiCount, fakenotiCount;
   int count;
   static char m[1024 * 1024 * 10];
@@ -152,15 +159,21 @@ main (int argc, char *argv[])
                                                     strlen (m) + 1));
   GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &id3);
   GNUNET_PSEUDONYM_add (cfg, &id3, meta);
-  name3 = GNUNET_PSEUDONYM_id_to_name (cfg, &id3);
-  name2 = GNUNET_PSEUDONYM_id_to_name (cfg, &id2);
+  GNUNET_PSEUDONYM_get_info (cfg, &id3, NULL, NULL, &name3, NULL);
+  CHECK (name3 != NULL);
+  GNUNET_PSEUDONYM_get_info (cfg, &id2, NULL, NULL, &name2, NULL);
   CHECK (name2 != NULL);
-  name1 = GNUNET_PSEUDONYM_id_to_name (cfg, &id1);
+  GNUNET_PSEUDONYM_get_info (cfg, &id1, NULL, NULL, &name1, NULL);
   CHECK (name1 != NULL);
-  CHECK (0 != strcmp (name1, name2));
+  CHECK (0 == strcmp (name1, name2));
+  name1_unique = GNUNET_PSEUDONYM_name_uniquify (cfg, &id1, name1, NULL);
+  name2_unique = GNUNET_PSEUDONYM_name_uniquify (cfg, &id2, name2, NULL);
+  CHECK (0 != strcmp (name1_unique, name2_unique));
   CHECK (GNUNET_SYSERR == GNUNET_PSEUDONYM_name_to_id (cfg, "fake", &rid2));
-  CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name2, &rid2));
-  CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name1, &rid1));
+  CHECK (GNUNET_SYSERR == GNUNET_PSEUDONYM_name_to_id (cfg, name2, &rid2));
+  CHECK (GNUNET_SYSERR == GNUNET_PSEUDONYM_name_to_id (cfg, name1, &rid1));
+  CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name2_unique, &rid2));
+  CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name1_unique, &rid1));
   CHECK (0 == memcmp (&id1, &rid1, sizeof (GNUNET_HashCode)));
   CHECK (0 == memcmp (&id2, &rid2, sizeof (GNUNET_HashCode)));
 
@@ -168,14 +181,17 @@ main (int argc, char *argv[])
   GNUNET_log_skip (1, GNUNET_NO);
   CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &fid, 0));
   GNUNET_log_skip (0, GNUNET_YES);
-  noname = GNUNET_PSEUDONYM_id_to_name (cfg, &fid);
+  CHECK (GNUNET_OK == GNUNET_PSEUDONYM_get_info (cfg, &fid, NULL, NULL, &noname, &noname_is_a_dup));
   CHECK (noname != NULL);
+  CHECK (noname_is_a_dup == GNUNET_YES);
   CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 0));
   CHECK (5 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5));
   CHECK (-5 == GNUNET_PSEUDONYM_rank (cfg, &id1, -10));
   CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5));
   GNUNET_free (name1);
   GNUNET_free (name2);
+  GNUNET_free (name1_unique);
+  GNUNET_free (name2_unique);
   GNUNET_free (name3);
   GNUNET_free (noname);
   /* END OF TEST CODE */
-- 
1.7.4

0001-Namespace-manager.patch (85,041 bytes)   
From 9b1f87619d59ff48fb141483d435c9de9a95c7eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1=D1?=
 =?UTF-8?q?=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= <lrn1986@gmail.com>
Date: Sun, 26 Feb 2012 08:06:45 +0400
Subject: [PATCH] Namespace manager

---
 contrib/Makefile.am                                |    1 +
 contrib/gnunet_fs_gtk_main_window.glade            |    9 +
 contrib/gnunet_fs_gtk_namespace_manager.glade      |  628 +++++++++++++
 src/fs/Makefile.am                                 |    3 +-
 src/fs/gnunet-fs-gtk_common.c                      |  112 +++
 src/fs/gnunet-fs-gtk_common.h                      |   43 +
 src/fs/gnunet-fs-gtk_event-handler.c               |    8 +
 .../gnunet-fs-gtk_main-window-namespace-dropdown.c |   24 +-
 src/fs/gnunet-fs-gtk_namespace_manager.c           |  970 ++++++++++++++++++++
 src/fs/gnunet-fs-gtk_namespace_manager.h           |   35 +
 10 files changed, 1823 insertions(+), 10 deletions(-)
 create mode 100644 contrib/gnunet_fs_gtk_namespace_manager.glade
 create mode 100644 src/fs/gnunet-fs-gtk_namespace_manager.c
 create mode 100644 src/fs/gnunet-fs-gtk_namespace_manager.h

diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 2ac5e30..b796dde 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -17,6 +17,7 @@ pkgdata_DATA = \
   gnunet_fs_gtk_create_namespace_dialog.glade \
   gnunet_fs_gtk_download_as_dialog.glade \
   gnunet_fs_gtk_main_window.glade \
+  gnunet_fs_gtk_namespace_manager.glade \
   gnunet_fs_gtk_edit_publication.glade \
   gnunet_fs_gtk_open_directory_dialog.glade \
   gnunet_fs_gtk_open_url_dialog.glade \
diff --git a/contrib/gnunet_fs_gtk_main_window.glade b/contrib/gnunet_fs_gtk_main_window.glade
index bf520f0..710c769 100644
--- a/contrib/gnunet_fs_gtk_main_window.glade
+++ b/contrib/gnunet_fs_gtk_main_window.glade
@@ -250,6 +250,15 @@
                       </object>
                     </child>
                     <child>
+                      <object class="GtkMenuItem" id="GNUNET_GTK_main_menu_file_manage_pseudonyms">
+                        <property name="visible">True</property>
+                        <property name="tooltip_text" translatable="yes">Opens namespace manager dialog to adjust the list of namespaces you want to use, as well as get detailed information about all discovered namespaces.</property>
+                        <property name="label" translatable="yes">_Manage namespaces</property>
+                        <property name="use_underline">True</property>
+                        <signal name="activate" handler="GNUNET_GTK_main_menu_file_manage_pseudonyms_activate_cb"/>
+                      </object>
+                    </child>
+                    <child>
                       <object class="GtkSeparatorMenuItem" id="GNUNET_GTK_main_menu_file_sep3">
                         <property name="visible">True</property>
                       </object>
diff --git a/contrib/gnunet_fs_gtk_namespace_manager.glade b/contrib/gnunet_fs_gtk_namespace_manager.glade
new file mode 100644
index 0000000..211d473
--- /dev/null
+++ b/contrib/gnunet_fs_gtk_namespace_manager.glade
@@ -0,0 +1,628 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.24"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="GNUNET_GTK_namespace_manager_known_liststore">
+    <columns>
+      <!-- column-name IsMine -->
+      <column type="gboolean"/>
+      <!-- column-name Rank -->
+      <column type="gint"/>
+      <!-- column-name NamespaceName -->
+      <column type="gchararray"/>
+      <!-- column-name NamespaceKey -->
+      <column type="gchararray"/>
+      <!-- column-name NamespaceKeyBinary -->
+      <column type="gpointer"/>
+      <!-- column-name OrderListRowReference -->
+      <column type="gpointer"/>
+      <!-- column-name MetaData -->
+      <column type="gpointer"/>
+      <!-- column-name NonUniqueName -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkAdjustment" id="GNUNET_GTK_namespace_manager_known_namespaces_hadj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="GNUNET_GTK_namespace_manager_known_namespaces_vadj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="GNUNET_GTK_namespace_manager_namespace_details_hadj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="GNUNET_GTK_namespace_manager_dialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">True</property>
+    <signal name="response" handler="GNUNET_GTK_namespace_manager_dialog_response_cb" swapped="no"/>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="GNUNET_GTK_namespace_manager_vbox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="GNUNET_GTK_namespace_manager_action_area">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="GNUNET_GTK_namespace_manager_apply_button">
+                <property name="label">gtk-apply</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="tooltip_text" translatable="yes">Saves all changes made in this dialog to disk without closing the dialog.</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="GNUNET_GTK_namespace_manager_ok_button">
+                <property name="label">gtk-ok</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="tooltip_text" translatable="yes">Saves all changes made in this dialog to disk and closes the dialog.</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="GNUNET_GTK_namespace_manager_cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="tooltip_text" translatable="yes">Closes the dialog. Changes made in this dialog will not be preserved.</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVPaned" id="GNUNET_GTK_namespace_manager_main_vpaned">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <object class="GtkHPaned" id="GNUNET_GTK_namespace_manager_top_hpaned">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <child>
+                  <object class="GtkVBox" id="GNUNET_GTK_namespace_manager_known_namespaces_vbox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkLabel" id="GNUNET_GTK_namespace_manager_known_namespaces_label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Known namespaces:
+Select a namespace to see its complete metadata. Click "Delete" to delete (forget) namespace.
+Namespace names are editable.</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="GNUNET_GTK_namespace_manager_known_namespaces_scrolledwindow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hadjustment">GNUNET_GTK_namespace_manager_known_namespaces_hadj</property>
+                        <property name="vadjustment">GNUNET_GTK_namespace_manager_known_namespaces_vadj</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="GNUNET_GTK_namespace_manager_known_treeview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="model">GNUNET_GTK_namespace_manager_known_liststore</property>
+                            <property name="hadjustment">GNUNET_GTK_namespace_manager_known_namespaces_hadj</property>
+                            <property name="vadjustment">GNUNET_GTK_namespace_manager_known_namespaces_vadj</property>
+                            <property name="rules_hint">True</property>
+                            <property name="search_column">2</property>
+                            <property name="show_expanders">False</property>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_is_mine_column">
+                                <property name="title" translatable="yes">Is mine</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_indicator">True</property>
+                                <property name="sort_column_id">0</property>
+                                <child>
+                                  <object class="GtkCellRendererToggle" id="GNUNET_GTK_namespace_manager_is_mine_toggle">
+                                    <property name="activatable">False</property>
+                                  </object>
+                                  <attributes>
+                                    <attribute name="active">0</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_rank_column">
+                                <property name="title" translatable="yes">Rank</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_indicator">True</property>
+                                <property name="sort_column_id">1</property>
+                                <child>
+                                  <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_rank_text"/>
+                                  <attributes>
+                                    <attribute name="text">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_namespace_name_column">
+                                <property name="title" translatable="yes">Name</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_indicator">True</property>
+                                <property name="sort_column_id">2</property>
+                                <child>
+                                  <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_name_text">
+                                    <property name="editable">True</property>
+                                    <signal name="editing-started" handler="GNUNET_GTK_namespace_manager_name_text_editing_started_cb" swapped="no"/>
+                                    <signal name="edited" handler="GNUNET_GTK_namespace_manager_name_text_edited_cb" swapped="no"/>
+                                  </object>
+                                  <attributes>
+                                    <attribute name="text">2</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_namespace_id_column">
+                                <property name="title" translatable="yes">Identifier</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_indicator">True</property>
+                                <property name="sort_column_id">3</property>
+                                <child>
+                                  <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_namespace_id_text"/>
+                                  <attributes>
+                                    <attribute name="text">3</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHButtonBox" id="GNUNET_GTK_namespace_manager_known_namespaces_buttonbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="extension_events">cursor</property>
+                        <property name="border_width">5</property>
+                        <property name="spacing">5</property>
+                        <property name="layout_style">end</property>
+                        <child>
+                          <object class="GtkButton" id="GNUNET_GTK_namespace_manager_known_namespaces_delete_button">
+                            <property name="label">gtk-delete</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="tooltip_text" translatable="yes">Makes GNUnet "forget" about selected known namespace.
+However, GNUnet will be able to learn about this namespace, if it ever discovers it again.</property>
+                            <property name="use_stock">True</property>
+                            <signal name="clicked" handler="GNUNET_GTK_namespace_manager_known_namespaces_delete_button_clicked_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="GNUNET_GTK_namespace_manager_known_namespaces_swap_rank_button">
+                            <property name="label" translatable="yes">Swap _Rank</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="tooltip_text" translatable="yes">Changes the rank value of the selected namespace in "Known namespaces" list.
+If it had rank 0, it's given rank -1, otherwise the sign of its rank is flipped.
+Only namespaces with positive ranks are displayed in "Namespace order" list.</property>
+                            <property name="use_underline">True</property>
+                            <signal name="clicked" handler="GNUNET_GTK_namespace_manager_known_namespaces_swap_rank_button_clicked_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="resize">True</property>
+                    <property name="shrink">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="GNUNET_GTK_namespace_manager_namespace_order_vbox_vbox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkLabel" id="GNUNET_GTK_namespace_manager_namespace_order_label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Namespace order:
+Drag rows to change namespace order, click "Apply" to save it.</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="GNUNET_GTK_namespace_manager_namespace_order_scrolledwindow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hadjustment">GNUNET_GTK_namespace_manager_namespace_order_hadj</property>
+                        <property name="vadjustment">GNUNET_GTK_namespace_manager_namespace_order_vadj</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="GNUNET_GTK_namespace_manager_namespace_order_treeview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="model">GNUNET_GTK_namespace_manager_namespace_order_liststore</property>
+                            <property name="hadjustment">GNUNET_GTK_namespace_manager_namespace_order_hadj</property>
+                            <property name="vadjustment">GNUNET_GTK_namespace_manager_namespace_order_vadj</property>
+                            <property name="headers_clickable">False</property>
+                            <property name="reorderable">True</property>
+                            <property name="rules_hint">True</property>
+                            <property name="search_column">0</property>
+                            <property name="show_expanders">False</property>
+                            <property name="rubber_banding">True</property>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_namespace_order_rank_column">
+                                <property name="title" translatable="yes">Rank</property>
+                                <property name="clickable">True</property>
+                                <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_order_column_clicked_cb" swapped="no"/>
+                                <child>
+                                  <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_namespace_order_rank_text"/>
+                                  <attributes>
+                                    <attribute name="text">0</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_namespace_order_name_column">
+                                <property name="title" translatable="yes">Name</property>
+                                <property name="clickable">True</property>
+                                <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_order_column_clicked_cb" swapped="no"/>
+                                <child>
+                                  <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_namespace_order_name_text"/>
+                                  <attributes>
+                                    <attribute name="text">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_namespace_order_identifier_column">
+                                <property name="title" translatable="yes">Identifier</property>
+                                <property name="clickable">True</property>
+                                <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_order_column_clicked_cb" swapped="no"/>
+                                <child>
+                                  <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_namespace_order_identifier_text"/>
+                                  <attributes>
+                                    <attribute name="text">2</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHButtonBox" id="GNUNET_GTK_namespace_manager_namespace_order_buttonbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="border_width">5</property>
+                        <property name="layout_style">end</property>
+                        <child>
+                          <object class="GtkButton" id="GNUNET_GTK_namespace_manager_namespace_order_apply">
+                            <property name="label">gtk-apply</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="tooltip_text" translatable="yes">Convert the order in which namespaces are arranged in "Namespace order" list to ranks.
+Top namespace is assigned rank 0, namespace below it - rank 1, and so on.</property>
+                            <property name="use_stock">True</property>
+                            <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_order_apply_clicked_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="resize">True</property>
+                    <property name="shrink">False</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="GNUNET_GTK_namespace_manager_namespace_details_vbox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="GNUNET_GTK_namespace_manager_namespace_details_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Selected namespace details:
+Metadata can be &lt;s&gt;added,&lt;/s&gt; deleted and edited. Click "Apply" to save metadata changes.</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="GNUNET_GTK_namespace_manager_namespace_details_scrolledwindow">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hadjustment">GNUNET_GTK_namespace_manager_namespace_details_hadj</property>
+                    <property name="vadjustment">GNUNET_GTK_namespace_manager_namespace_details_vadj</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTreeView" id="GNUNET_GTK_namespace_manager_namespace_details_treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="model">GNUNET_GTK_namespace_manager_namespace_details_liststore</property>
+                        <property name="hadjustment">GNUNET_GTK_namespace_manager_namespace_details_hadj</property>
+                        <property name="vadjustment">GNUNET_GTK_namespace_manager_namespace_details_vadj</property>
+                        <property name="headers_clickable">False</property>
+                        <property name="rules_hint">True</property>
+                        <property name="search_column">0</property>
+                        <property name="show_expanders">False</property>
+                        <property name="rubber_banding">True</property>
+                        <property name="enable_grid_lines">both</property>
+                        <child>
+                          <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_details_type_column">
+                            <property name="title" translatable="yes">Type</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_namespace_details_type_text">
+                                <signal name="edited" handler="GNUNET_GTK_namespace_manager_namespace_details_type_text_edited_cb" swapped="no"/>
+                              </object>
+                              <attributes>
+                                <attribute name="text">2</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn" id="GNUNET_GTK_namespace_manager_details_value_column">
+                            <property name="title" translatable="yes">Value</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="GNUNET_GTK_namespace_manager_namespace_details_value_text">
+                                <property name="editable">True</property>
+                                <signal name="edited" handler="GNUNET_GTK_namespace_manager_namespace_details_value_text_edited_cb" swapped="no"/>
+                              </object>
+                              <attributes>
+                                <attribute name="text">5</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHButtonBox" id="GNUNET_GTK_namespace_manager_namespace_details_buttonbox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="border_width">5</property>
+                    <property name="spacing">5</property>
+                    <property name="layout_style">end</property>
+                    <child>
+                      <object class="GtkButton" id="GNUNET_GTK_namespace_manager_namespace_details_add_button">
+                        <property name="label">gtk-add</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="tooltip_text" translatable="yes">Adds a new metadata Type=Value pair to the list.
+This functionality is not available at the moment.</property>
+                        <property name="use_stock">True</property>
+                        <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_details_add_button_clicked_cb" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="GNUNET_GTK_namespace_manager_namespace_details_delete_button">
+                        <property name="label">gtk-delete</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="tooltip_text" translatable="yes">Removes selected Type=Value pair from metadata list.
+The change will not last unless you click on "Apply" button before closing the window or selecing some other known namespace.</property>
+                        <property name="use_stock">True</property>
+                        <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_details_delete_button_clicked_cb" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="GNUNET_GTK_namespace_manager_namespace_details_apply_button">
+                        <property name="label">gtk-apply</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="tooltip_text" translatable="yes">Applies changes made in metadata list to currently selected known namespace.</property>
+                        <property name="use_stock">True</property>
+                        <signal name="clicked" handler="GNUNET_GTK_namespace_manager_namespace_details_apply_button_clicked_cb" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">False</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-10">GNUNET_GTK_namespace_manager_apply_button</action-widget>
+      <action-widget response="-5">GNUNET_GTK_namespace_manager_ok_button</action-widget>
+      <action-widget response="-6">GNUNET_GTK_namespace_manager_cancel_button</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkListStore" id="GNUNET_GTK_namespace_manager_namespace_details_liststore">
+    <columns>
+      <!-- column-name PluginName -->
+      <column type="gchararray"/>
+      <!-- column-name TypeBin -->
+      <column type="gint"/>
+      <!-- column-name Type -->
+      <column type="gchararray"/>
+      <!-- column-name Format -->
+      <column type="gint"/>
+      <!-- column-name DataMime -->
+      <column type="gchararray"/>
+      <!-- column-name Value -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkAdjustment" id="GNUNET_GTK_namespace_manager_namespace_details_vadj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="GNUNET_GTK_namespace_manager_namespace_order_hadj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkListStore" id="GNUNET_GTK_namespace_manager_namespace_order_liststore">
+    <columns>
+      <!-- column-name Rank -->
+      <column type="gint"/>
+      <!-- column-name Name -->
+      <column type="gchararray"/>
+      <!-- column-name Identifier -->
+      <column type="gchararray"/>
+      <!-- column-name IdentifierBin -->
+      <column type="gpointer"/>
+      <!-- column-name KnownListRowReference -->
+      <column type="gpointer"/>
+    </columns>
+  </object>
+  <object class="GtkAdjustment" id="GNUNET_GTK_namespace_manager_namespace_order_vadj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+</interface>
diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am
index 0793513..20d8a7d 100644
--- a/src/fs/Makefile.am
+++ b/src/fs/Makefile.am
@@ -25,7 +25,8 @@ gnunet_fs_gtk_SOURCES = \
   gnunet-fs-gtk_open-directory.c \
   gnunet-fs-gtk_open-uri.c \
   gnunet-fs-gtk_publish-dialog.c \
-  gnunet-fs-gtk_publish-edit-dialog.c gnunet-fs-gtk_publish-edit-dialog.h 
+  gnunet-fs-gtk_publish-edit-dialog.c gnunet-fs-gtk_publish-edit-dialog.h \
+  gnunet-fs-gtk_namespace_manager.c gnunet-fs-gtk_namespace_manager.h
 gnunet_fs_gtk_LDADD = \
   $(top_builddir)/src/lib/libgnunetgtk.la \
   @GTK_LIBS@ \
diff --git a/src/fs/gnunet-fs-gtk_common.c b/src/fs/gnunet-fs-gtk_common.c
index 8ca006e..82d93e8 100644
--- a/src/fs/gnunet-fs-gtk_common.c
+++ b/src/fs/gnunet-fs-gtk_common.c
@@ -416,5 +416,117 @@ GNUNET_FS_GTK_handle_uri (const struct GNUNET_FS_Uri *uri)
   GNUNET_break (0);
 }
 
+/* Largest rating value among all namespaces. INT_MIN means "undefined" */
+static int largest_namespace_rating = INT_MIN;
+
+
+/**
+ * Helper function for finding the largest namespace rating.
+ *
+ * @param cls closure
+ * @param pseudonym pseudonym hash
+ * @param md metadata container
+ * @param rating rating
+ * @return GNUNET_OK to keep iterating
+ */
+static int
+find_largest_namespace_rating_iterator (void *cls,
+    const GNUNET_HashCode *pseudonym, const char *name,
+    const char *unique_name,
+    const struct GNUNET_CONTAINER_MetaData *md, int rating)
+{
+  int *largest = cls;
+  if (*largest < rating)
+    *largest = rating;
+  return GNUNET_OK;
+}
+
+/**
+ * Finds largest namespace rating.
+ * Used to calculate a rating for newly discovered namespaces.
+ * Returns from cache, if possible.
+ *
+ * @return largest namespace rating. Might be negative and even. INT_MIN means
+ *         that no namespaces are known.
+ */
+int
+GNUNET_GTK_find_largest_namespace_rating ()
+{
+  if (largest_namespace_rating != INT_MIN)
+    return largest_namespace_rating;
+  (void) GNUNET_PSEUDONYM_list_all (GNUNET_FS_GTK_get_configuration (),
+      find_largest_namespace_rating_iterator, &largest_namespace_rating);
+  return largest_namespace_rating;
+}
+
+/**
+ * Sets largest namespace rating.
+ * Used to change cached largest namespace rating, when namespace list
+ * was changed in a way that is easy to track.
+ * If namespace list was changed in a way that makes it difficult to
+ * decide upon the new value, set new value to INT_MIN.
+ *
+ * @param new_value new value for the rating.
+ */
+void
+GNUNET_GTK_set_largest_namespace_rating (int new_value)
+{
+  largest_namespace_rating = new_value;
+}
+
+/**
+ * Converts a GtkTreeRowReference to a GtkTreeIter.
+ *
+ * @param rr row reference
+ * @param iter pointer to an iter structure to fill
+ * @return GNUNET_OK if iter was filled, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_GTK_get_iter_from_reference (GtkTreeRowReference *rr, GtkTreeIter *iter)
+{
+  int result = GNUNET_SYSERR;
+  if (rr != NULL)
+  {
+    if (gtk_tree_row_reference_valid (rr))
+    {
+      GtkTreePath *path;
+      GtkTreeModel *model;
+      path = gtk_tree_row_reference_get_path (rr);
+      model = gtk_tree_row_reference_get_model (rr);
+      if (path != NULL && model != NULL)
+      {
+        if (gtk_tree_model_get_iter (model,
+            iter, path))
+          result = GNUNET_OK;
+        gtk_tree_path_free (path);
+      }
+    }
+  }
+  return result;
+}
+
+/**
+ * Creates a GtkTreeRowReference from a GtkTreeIter.
+ *
+ * @param model a model to reference
+ * @param iter an iter that points to a row in the model
+ * @return newly created reference or NULL in case of error
+ */
+GtkTreeRowReference *
+GNUNET_GTK_get_reference_from_iter (GtkTreeModel *model, GtkTreeIter *iter)
+{
+  GtkTreeRowReference *result = NULL;
+  if (iter != NULL && model != NULL)
+  {
+    GtkTreePath *path = gtk_tree_model_get_path (model, iter);
+    if (path != NULL)
+    {
+      result = gtk_tree_row_reference_new (model, path);
+      gtk_tree_path_free (path);
+    }
+  }
+  return result;
+}
+
 
 /* end of gnunet-fs-gtk-common.c */
diff --git a/src/fs/gnunet-fs-gtk_common.h b/src/fs/gnunet-fs-gtk_common.h
index b5048d7..3076695 100644
--- a/src/fs/gnunet-fs-gtk_common.h
+++ b/src/fs/gnunet-fs-gtk_common.h
@@ -147,5 +147,48 @@ GNUNET_FS_GTK_get_description_from_metadata (
 void
 GNUNET_FS_GTK_handle_uri (const struct GNUNET_FS_Uri *uri);
 
+/**
+ * Finds largest namespace rating.
+ * Used to calculate a rating for newly discovered namespaces.
+ * Returns from cache, if possible.
+ *
+ * @return largest namespace rating. Might be negative and even. INT_MIN means
+ *         that no namespaces are known.
+ */
+int
+GNUNET_GTK_find_largest_namespace_rating ();
+
+/**
+ * Sets largest namespace rating.
+ * Used to change cached largest namespace rating, when namespace list
+ * was changed in a way that is easy to track.
+ * If namespace list was changed in a way that makes it difficult to
+ * decide upon the new value, set new value to INT_MIN.
+ *
+ * @param new_value new value for the rating.
+ */
+void
+GNUNET_GTK_set_largest_namespace_rating (int new_value);
+
+/**
+ * Converts a GtkTreeRowReference to a GtkTreeIter.
+ *
+ * @param rr row reference
+ * @param iter pointer to an iter structure to fill
+ * @return GNUNET_OK if iter was filled, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_GTK_get_iter_from_reference (GtkTreeRowReference *rr, GtkTreeIter *iter);
+
+/**
+ * Creates a GtkTreeRowReference from a GtkTreeIter.
+ *
+ * @param model a model to reference
+ * @param iter an iter that points to a row in the model
+ * @return newly created reference or NULL in case of error
+ */
+GtkTreeRowReference *
+GNUNET_GTK_get_reference_from_iter (GtkTreeModel *model, GtkTreeIter *iter);
+
 #endif
 /* end of gnunet-fs-gtk-common.h */
diff --git a/src/fs/gnunet-fs-gtk_event-handler.c b/src/fs/gnunet-fs-gtk_event-handler.c
index 4017e10..0c7606c 100644
--- a/src/fs/gnunet-fs-gtk_event-handler.c
+++ b/src/fs/gnunet-fs-gtk_event-handler.c
@@ -27,6 +27,7 @@
 #include "gnunet-fs-gtk_common.h"
 #include "gnunet-fs-gtk_download-save-as.h"
 #include "gnunet-fs-gtk_event-handler.h"
+#include "gnunet-fs-gtk_namespace_manager.h"
 
 
 /**
@@ -3030,5 +3031,12 @@ GNUNET_GTK_fs_event_handler (void *cls,
   return NULL;
 }
 
+void
+GNUNET_GTK_main_menu_file_manage_pseudonyms_activate_cb (GtkMenuItem *menuitem,
+    gpointer user_data)
+{
+  GNUNET_GTK_namespace_manager_open ();
+}
+
 
 /* end of gnunet-fs-gtk-event_handler.c */
diff --git a/src/fs/gnunet-fs-gtk_main-window-namespace-dropdown.c b/src/fs/gnunet-fs-gtk_main-window-namespace-dropdown.c
index cb5dc46..818caa6 100644
--- a/src/fs/gnunet-fs-gtk_main-window-namespace-dropdown.c
+++ b/src/fs/gnunet-fs-gtk_main-window-namespace-dropdown.c
@@ -322,11 +322,12 @@ GNUNET_FS_GTK_search_namespace_dropdown_button_toggled_cb (GtkToggleButton *
  */
 static int
 add_namespace_to_ts (void *cls, const GNUNET_HashCode * pseudonym,
+                     const char *name, const char *unique_name,
                      const struct GNUNET_CONTAINER_MetaData *md, int rating)
 {
   GtkTreeStore *ts = cls;
   char *root;
-  char *ns_name;
+  char *ns_name, *unique_ns_name;
   GNUNET_HashCode *nsid;
   char *description;
   int desc_is_a_dup;
@@ -335,9 +336,14 @@ add_namespace_to_ts (void *cls, const GNUNET_HashCode * pseudonym,
   struct GNUNET_FS_Uri *uri;
   GtkTreeIter iter;
 
-  ns_name =
-      GNUNET_PSEUDONYM_id_to_name (GNUNET_FS_GTK_get_configuration (),
-                                   pseudonym);
+  if (rating < 0)
+    return GNUNET_OK;
+
+  GNUNET_PSEUDONYM_get_info (GNUNET_FS_GTK_get_configuration (),
+                             pseudonym, NULL, NULL, &ns_name, NULL);
+  unique_ns_name = GNUNET_PSEUDONYM_name_uniquify (
+      GNUNET_FS_GTK_get_configuration (), pseudonym, ns_name, NULL);
+  GNUNET_free (ns_name);
   nsid = GNUNET_malloc (sizeof (GNUNET_HashCode));
   *nsid = *pseudonym;
   root = NULL;
@@ -352,13 +358,13 @@ add_namespace_to_ts (void *cls, const GNUNET_HashCode * pseudonym,
     GNUNET_FS_uri_destroy (uri);
   }
   description = GNUNET_FS_GTK_get_description_from_metadata (md, &desc_is_a_dup);
-  gtk_tree_store_insert_with_values (ts, &iter, NULL, G_MAXINT, 
-				     0, ns_name, 
-				     1, nsid, 
+  gtk_tree_store_insert_with_values (ts, &iter, NULL, G_MAXINT,
+				     0, unique_ns_name,
+				     1, nsid,
 				     2, root,
-				     3, description, 
+				     3, description,
 				     -1);
-  GNUNET_free (ns_name);
+  GNUNET_free (unique_ns_name);
   GNUNET_free_non_null (root);
   GNUNET_free (description);
   return GNUNET_OK;
diff --git a/src/fs/gnunet-fs-gtk_namespace_manager.c b/src/fs/gnunet-fs-gtk_namespace_manager.c
new file mode 100644
index 0000000..a7d7937
--- /dev/null
+++ b/src/fs/gnunet-fs-gtk_namespace_manager.c
@@ -0,0 +1,970 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010, 2012 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
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs/gnunet-fs-gtk_namespace_manager.c
+ * @author LRN
+ */
+#include "gnunet-fs-gtk_common.h"
+#include "gnunet-fs-gtk.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_fs_service.h>
+
+struct GNUNET_GTK_NamespaceManagerContext
+{
+  GtkBuilder *builder;
+  GtkWidget *known_ns;
+  GtkWidget *ns_order;
+  GtkWidget *ns_details;
+  GtkTreeSelection *known_ns_sel;
+  GtkTreeSelection *ns_order_sel;
+  GtkTreeSelection *ns_details_sel;
+  GtkListStore *ns_order_store;
+  GtkListStore *known_ns_store;
+  GtkListStore *ns_details_store;
+  GtkWindow *namespace_manager;
+  GtkTreeViewColumn *order_rank;
+  GtkTreeViewColumn *order_name;
+  GtkTreeViewColumn *order_id;
+  GtkWidget *details_apply_button;
+  GtkWidget *details_delete_button;
+  int sort_direction;
+  struct GNUNET_CONTAINER_MetaData *uneditable_md;
+};
+
+/**
+ * THIS IS EVIL! Used to avoid opening more than one ns manager
+ * at once. Could be fixed by saving a pointer to the ns manager window
+ * somewhere in the main gnunet-fs-gtk window, but main window does
+ * not have a context structure for such things at the moment, hence
+ * this VERY EVIL GLOBAL VARIABLE!
+ */
+static struct GNUNET_GTK_NamespaceManagerContext *ns_manager = NULL;
+
+#define GNUNET_GTK_KNOWN_NAMESPACES_IS_MINE_COLUMN 0
+#define GNUNET_GTK_KNOWN_NAMESPACES_RANK_COLUMN 1
+#define GNUNET_GTK_KNOWN_NAMESPACES_NAME_COLUMN 2
+#define GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_COLUMN 3
+#define GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN 4
+#define GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN 5
+#define GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN 6
+#define GNUNET_GTK_KNOWN_NAMESPACES_NON_UNIQUE_NAME_COLUMN 7
+
+#define GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN 0
+#define GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN 1
+#define GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_COLUMN 2
+#define GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_BIN_COLUMN 3
+#define GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN 4
+
+#define GNUNET_GTK_NAMESPACE_DETAILS_PLUGIN_NAME_COLUMN 0
+#define GNUNET_GTK_NAMESPACE_DETAILS_TYPE_BIN_COLUMN 1
+#define GNUNET_GTK_NAMESPACE_DETAILS_TYPE_COLUMN 2
+#define GNUNET_GTK_NAMESPACE_DETAILS_FORMAT_COLUMN 3
+#define GNUNET_GTK_NAMESPACE_DETAILS_DATA_MIME_COLUMN 4
+#define GNUNET_GTK_NAMESPACE_DETAILS_VALUE_COLUMN 5
+
+static void
+sort_order_list (struct GNUNET_GTK_NamespaceManagerContext *ctx,
+    int sort_column)
+{
+  int sorted = GNUNET_NO;
+  GtkTreeIter iter;
+  gint i;
+  gint row_count;
+  gint *row_indices, *row_ints;
+  gchar **row_strings;
+
+  ctx->sort_direction = ctx->sort_direction <= 0 ? 1 : 0;
+
+  row_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (
+      ctx->ns_order_store), NULL);
+  if (TRUE != gtk_tree_model_get_iter_first (GTK_TREE_MODEL (
+      ctx->ns_order_store), &iter))
+    return;
+
+  row_indices = g_new0 (gint, row_count);
+  row_ints = g_new0 (gint, row_count);
+  row_strings = g_new0 (gchar *, row_count);
+
+  for (i = 0; i < row_count; i++)
+  {
+    int an_int;
+    char *a_string;
+    row_indices[i] = i;
+    switch (sort_column)
+    {
+    case GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN:
+      gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_order_store), &iter,
+          sort_column, &an_int, -1);
+      row_ints[i] = an_int;
+      row_strings[i] = NULL;
+      break;
+    case GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN:
+    case GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_COLUMN:
+      gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_order_store), &iter,
+          sort_column, &a_string, -1);
+      row_strings[i] = a_string;
+      break;
+    default:
+      row_strings[i] = NULL;
+      break;
+    }
+    if (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL (
+      ctx->ns_order_store), &iter))
+      break;
+  }
+
+  while (sorted != GNUNET_YES)
+  {
+    sorted = GNUNET_YES;
+    for (i = 0; i < row_count - 1; i++)
+    {
+      int cmp_result;
+
+      switch (sort_column)
+      {
+      case GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN:
+        cmp_result = row_ints[i] <= row_ints[i + 1] ? 0 : 1;
+        break;
+      case GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN:
+      case GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_COLUMN:
+        /* FIXME: name can be UTF-8-encoded, use UTF-8-aware comparison func */
+        cmp_result = strcmp (row_strings[i], row_strings[i + 1]);
+        break;
+      default:
+        break;
+      }
+
+      if (((ctx->sort_direction <= 0) && (cmp_result <= 0)) ||
+          ((ctx->sort_direction > 0) && (cmp_result > 0)))
+      {
+        int tmp_int, tmp_index;
+        char *tmp_string;
+        tmp_index = row_indices[i];
+        tmp_int = row_ints[i];
+        tmp_string = row_strings[i];
+        row_indices[i] = row_indices[i + 1];
+        row_ints[i] = row_ints[i + 1];
+        row_strings[i] = row_strings[i + 1];
+        row_ints[i + 1] = tmp_int;
+        row_strings[i + 1] = tmp_string;
+        row_indices[i + 1] = tmp_index;
+        sorted = GNUNET_NO;
+      }
+    }
+  }
+
+  gtk_list_store_reorder (ctx->ns_order_store, row_indices);
+
+  g_free (row_indices);
+  for (i = 0; i < row_count; i++)
+    g_free (row_strings[i]);
+  g_free (row_strings);
+  g_free (row_ints);
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_order_column_clicked_cb (
+    GtkTreeViewColumn *treeviewcolumn, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  if (treeviewcolumn == ctx->order_rank)
+  {
+    sort_order_list (ctx, GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN);
+  }
+  else if (treeviewcolumn == ctx->order_name)
+  {
+    sort_order_list (ctx, GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN);
+  }
+  else if (treeviewcolumn == ctx->order_id)
+  {
+    sort_order_list (ctx, GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_COLUMN);
+  }
+}
+
+void
+GNUNET_GTK_namespace_manager_known_namespaces_delete_button_clicked_cb (
+    GtkButton *button, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter iter;
+  GNUNET_HashCode *nsid;
+  GtkTreeRowReference *order_row, *known_row;
+  struct GNUNET_CONTAINER_MetaData *md;
+
+  if (FALSE == gtk_tree_selection_get_selected (ctx->known_ns_sel, NULL, &iter))
+    return;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, &nsid,
+      GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, &order_row,
+      GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, &md,
+      -1);
+  GNUNET_CONTAINER_meta_data_destroy (md);
+  gtk_list_store_remove (ctx->known_ns_store, &iter);
+  if (order_row != NULL)
+  {
+    if (GNUNET_OK == GNUNET_GTK_get_iter_from_reference (order_row, &iter))
+    {
+      gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_order_store), &iter,
+        GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN, &known_row, -1);
+      gtk_list_store_remove (ctx->ns_order_store, &iter);
+      if (known_row != NULL)
+        gtk_tree_row_reference_free (known_row);
+    }
+    gtk_tree_row_reference_free (order_row);
+  }
+  GNUNET_free (nsid);
+
+  gtk_list_store_clear (ctx->ns_details_store);
+  GNUNET_CONTAINER_meta_data_destroy (ctx->uneditable_md);
+  ctx->uneditable_md = NULL;
+}
+
+void
+GNUNET_GTK_namespace_manager_known_namespaces_swap_rank_button_clicked_cb (
+    GtkButton *button, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter known_iter, order_iter;
+  GNUNET_HashCode *nsid;
+  int32_t old_rank, new_rank;
+  GtkTreeRowReference *order_row, *known_row;
+
+  if (FALSE == gtk_tree_selection_get_selected (ctx->known_ns_sel, NULL, &known_iter))
+    return;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &known_iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, &nsid,
+      GNUNET_GTK_KNOWN_NAMESPACES_RANK_COLUMN, &old_rank,
+      GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, &order_row,
+      -1);
+
+  if (old_rank == 0)
+    new_rank = -1;
+  else
+    new_rank = -old_rank;
+
+  gtk_list_store_set (ctx->known_ns_store, &known_iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_RANK_COLUMN, new_rank,
+      GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, NULL,
+      -1);
+
+  if (order_row != NULL)
+  {
+    if (new_rank < 0)
+    {
+      if (GNUNET_OK == GNUNET_GTK_get_iter_from_reference (order_row, &order_iter))
+      {
+        gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_order_store), &order_iter,
+            GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN, &known_row, -1);
+        gtk_list_store_remove (ctx->ns_order_store, &order_iter);
+        if (known_row != NULL)
+          gtk_tree_row_reference_free (known_row);
+      }
+    }
+    gtk_tree_row_reference_free (order_row);
+  }
+  if (new_rank >= 0)
+  {
+    char *name, *identifier;
+    if (order_row != NULL)
+    {
+      /* This should not happen */
+      GNUNET_break (0);
+    }
+    gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &known_iter,
+        GNUNET_GTK_KNOWN_NAMESPACES_NAME_COLUMN, &name,
+        GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_COLUMN, &identifier,
+        -1);
+    known_row = GNUNET_GTK_get_reference_from_iter (GTK_TREE_MODEL (
+        ctx->known_ns_store), &known_iter);
+    gtk_list_store_insert_with_values (ctx->ns_order_store, &order_iter, G_MAXINT,
+        GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN, new_rank,
+        GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN, name,
+        GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_COLUMN, identifier,
+        GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_BIN_COLUMN, nsid,
+        GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN, known_row,
+        -1);
+    g_free (name);
+    g_free (identifier);
+    order_row = GNUNET_GTK_get_reference_from_iter (GTK_TREE_MODEL (ctx->ns_order_store),
+        &order_iter);
+    gtk_list_store_set (ctx->known_ns_store, &known_iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, order_row, -1);
+  }
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_order_apply_clicked_cb (
+    GtkButton *button, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter iter;
+  gint i;
+  gint row_count;
+  GtkTreeRowReference *known_row;
+
+  row_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (
+      ctx->ns_order_store), NULL);
+  if (TRUE != gtk_tree_model_get_iter_first (GTK_TREE_MODEL (
+      ctx->ns_order_store), &iter))
+    return;
+
+  for (i = 0; i < row_count; i++)
+  {
+    gtk_list_store_set (ctx->ns_order_store, &iter,
+        GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN, i, -1);
+    gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_order_store), &iter,
+        GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN, &known_row, -1);
+    if (known_row == NULL)
+    {
+      /* This is not supposed to happen. What to do? */
+      GNUNET_break (0);
+    }
+    else
+    {
+      GtkTreeIter known_iter;
+      if (GNUNET_OK == GNUNET_GTK_get_iter_from_reference (known_row, &known_iter))
+      {
+        gtk_list_store_set (ctx->known_ns_store, &known_iter,
+            GNUNET_GTK_KNOWN_NAMESPACES_RANK_COLUMN, i,
+            -1);
+      }
+    }
+    if (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL (
+      ctx->ns_order_store), &iter))
+      break;
+  }
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_details_add_button_clicked_cb (
+    GtkButton *button, gpointer user_data)
+{
+  /* FIXME: add a row to the details list. Disabled at the moment, since
+   * metadata type selection is not implemented.
+   */
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_details_delete_button_clicked_cb (
+    GtkButton *button, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter iter;
+
+  if (FALSE == gtk_tree_selection_get_selected (ctx->ns_details_sel, NULL, &iter))
+    return;
+
+  gtk_list_store_remove (ctx->ns_details_store, &iter);
+  gtk_widget_set_sensitive (ctx->details_apply_button, TRUE);
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_details_apply_button_clicked_cb (
+    GtkButton *button, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter iter, known_iter;
+  gint i;
+  gint row_count;
+  struct GNUNET_CONTAINER_MetaData *md, *old_md;
+  char *plugin_name;
+  int type;
+  char *type_name;
+  int format;
+  char *data_mime_type;
+  char *data;
+
+  row_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (
+      ctx->ns_details_store), NULL);
+  if ((row_count > 0) && (TRUE != gtk_tree_model_get_iter_first (
+      GTK_TREE_MODEL (ctx->ns_details_store), &iter)))
+  {
+    /* This should not happen */
+    return;
+  }
+
+  if (FALSE == gtk_tree_selection_get_selected (ctx->known_ns_sel, NULL,
+      &known_iter))
+  {
+    /* This should not happen */
+    return;
+  }
+
+  md = GNUNET_CONTAINER_meta_data_create ();
+
+  for (i = 0; i < row_count; i++)
+  {
+    gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_details_store), &iter,
+        GNUNET_GTK_NAMESPACE_DETAILS_PLUGIN_NAME_COLUMN, &plugin_name,
+        GNUNET_GTK_NAMESPACE_DETAILS_TYPE_BIN_COLUMN, &type,
+        GNUNET_GTK_NAMESPACE_DETAILS_TYPE_COLUMN, &type_name,
+        GNUNET_GTK_NAMESPACE_DETAILS_FORMAT_COLUMN, &format,
+        GNUNET_GTK_NAMESPACE_DETAILS_DATA_MIME_COLUMN, &data_mime_type,
+        GNUNET_GTK_NAMESPACE_DETAILS_VALUE_COLUMN, &data,
+        -1);
+    GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
+        type, format, data_mime_type, data, strlen (data) + 1);
+    g_free (plugin_name);
+    g_free (type_name);
+    g_free (data_mime_type);
+    g_free (data);
+    if (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL (
+      ctx->ns_details_store), &iter))
+      break;
+  }
+  GNUNET_CONTAINER_meta_data_merge (md,
+      (const struct GNUNET_CONTAINER_MetaData *) ctx->uneditable_md);
+
+  gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &known_iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, &old_md,
+      -1);
+  GNUNET_CONTAINER_meta_data_destroy (old_md);
+  gtk_list_store_set (ctx->known_ns_store, &known_iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, md,
+      -1);
+  gtk_widget_set_sensitive (ctx->details_apply_button, FALSE);
+}
+
+void
+GNUNET_GTK_namespace_manager_name_text_editing_started_cb (
+    GtkCellRenderer *renderer, GtkCellEditable *editable, gchar *path,
+    gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+
+  if (GTK_IS_ENTRY (editable)) 
+  {
+    GtkTreePath *tree_path;
+    GtkTreeIter iter;
+    char *non_unique_name = NULL;
+    GtkEntry *entry = GTK_ENTRY (editable);
+
+    tree_path = gtk_tree_path_new_from_string (path);
+    if (tree_path != NULL)
+    {
+      if (gtk_tree_model_get_iter (GTK_TREE_MODEL (ctx->known_ns_store),
+          &iter, tree_path))
+      {
+        gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &iter,
+            GNUNET_GTK_KNOWN_NAMESPACES_NON_UNIQUE_NAME_COLUMN, &non_unique_name,
+            -1);
+      }
+      gtk_tree_path_free (tree_path);
+    }
+    if (non_unique_name == NULL)
+    {
+      gtk_cell_editable_editing_done (editable);
+      return;
+    }
+    gtk_entry_set_text (entry, non_unique_name);
+    g_free (non_unique_name);
+  }
+}
+
+void
+GNUNET_GTK_namespace_manager_name_text_edited_cb (
+    GtkCellRendererText *renderer, gchar *path, gchar *new_text,
+    gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  char *unique_name;
+  GtkTreeRowReference *order_row;
+
+  if (strlen (new_text) == 0)
+    return;
+
+  GtkTreePath *tree_path;
+  GtkTreeIter iter;
+  tree_path = gtk_tree_path_new_from_string (path);
+  if (tree_path != NULL)
+  {
+    if (gtk_tree_model_get_iter (GTK_TREE_MODEL (ctx->known_ns_store),
+        &iter, tree_path))
+    {
+      GNUNET_HashCode *nsid;
+      gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &iter,
+          GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, &nsid,
+          GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, &order_row,
+          -1);
+
+      unique_name = GNUNET_PSEUDONYM_name_uniquify (
+          GNUNET_FS_GTK_get_configuration (), nsid, new_text, NULL);
+
+      gtk_list_store_set (ctx->known_ns_store, &iter,
+          GNUNET_GTK_KNOWN_NAMESPACES_NON_UNIQUE_NAME_COLUMN, new_text,
+          GNUNET_GTK_KNOWN_NAMESPACES_NAME_COLUMN, unique_name,
+          -1);
+
+      if (order_row != NULL)
+      {
+        GtkTreeIter order_iter;
+        if (GNUNET_OK == GNUNET_GTK_get_iter_from_reference (order_row, &order_iter))
+        {
+          gtk_list_store_set (ctx->ns_order_store, &order_iter,
+            GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN, unique_name,
+            -1);
+        }
+      }
+
+      GNUNET_free (unique_name);
+    }
+    gtk_tree_path_free (tree_path);
+  }
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_details_type_text_edited_cb (
+    GtkCellRendererText *renderer, gchar *path, gchar *new_text,
+    gpointer user_data)
+{
+  /* Changing metadata type is more difficult than simply entering a
+   * new string, see publication editing dialog. So this is disabled
+   * for now. In fact, it's not going to be a Text renderer when it's done.
+   */
+  /*
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = cls;
+  GtkTreeIter iter;
+  if (gtk_tree_model_get_iter (GTK_TREE_MODEL (ctx->ns_details_store),
+      &iter, path))
+  {
+    gtk_list_store_set (ctx->ns_details_store, &iter,
+        GNUNET_GTK_NAMESPACE_DETAILS_TYPE_COLUMN, new_text,
+        -1);
+  }
+  */
+}
+
+void
+GNUNET_GTK_namespace_manager_namespace_details_value_text_edited_cb (
+    GtkCellRendererText *renderer, gchar *path, gchar *new_text,
+    gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreePath *tree_path;
+  GtkTreeIter iter;
+  tree_path = gtk_tree_path_new_from_string (path);
+  if (tree_path != NULL)
+  {
+    if (gtk_tree_model_get_iter (GTK_TREE_MODEL (ctx->ns_details_store),
+        &iter, tree_path))
+    {
+      char *old_text;
+      gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_details_store), &iter,
+          GNUNET_GTK_NAMESPACE_DETAILS_VALUE_COLUMN, &old_text,
+          -1);
+      if (strcmp (old_text, new_text) != 0)
+      {
+        gtk_list_store_set (ctx->ns_details_store, &iter,
+            GNUNET_GTK_NAMESPACE_DETAILS_VALUE_COLUMN, new_text,
+            -1);
+        gtk_widget_set_sensitive (ctx->details_apply_button, TRUE);
+      }
+      g_free (old_text);
+    }
+    gtk_tree_path_free (tree_path);
+  }
+}
+
+/**
+ * Iterator over all known pseudonyms.
+ * Populate "known ns" and "ns order" lists.
+ *
+ * @param cls closure
+ * @param pseudonym hash code of public key of pseudonym
+ * @param md meta data known about the pseudonym
+ * @param rating the local rating of the pseudonym
+ * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
+ */
+static int
+populate_known_ns_list (void *cls, const GNUNET_HashCode *pseudonym,
+    const char *name, const char *unique_name,
+    const struct GNUNET_CONTAINER_MetaData *md, int rating)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = cls;
+  GNUNET_HashCode *nsid;
+  struct GNUNET_CRYPTO_HashAsciiEncoded identifier;
+  GtkTreeIter known_iter, order_iter;
+  struct GNUNET_CONTAINER_MetaData *md_copy;
+  char *non_null_name, *non_null_unique_name;
+
+  nsid = GNUNET_malloc (sizeof (GNUNET_HashCode));
+  *nsid = *pseudonym;
+
+  GNUNET_CRYPTO_hash_to_enc (nsid, &identifier);
+
+  GNUNET_PSEUDONYM_get_info (GNUNET_FS_GTK_get_configuration (),
+                             nsid, NULL, NULL, &non_null_name, NULL);
+  non_null_unique_name = GNUNET_PSEUDONYM_name_uniquify (
+      GNUNET_FS_GTK_get_configuration (), nsid, non_null_name, NULL);
+
+  md_copy = GNUNET_CONTAINER_meta_data_duplicate (md);
+
+  gtk_list_store_insert_with_values (ctx->known_ns_store, &known_iter, G_MAXINT,
+      GNUNET_GTK_KNOWN_NAMESPACES_IS_MINE_COLUMN, FALSE,
+      GNUNET_GTK_KNOWN_NAMESPACES_RANK_COLUMN, rating,
+      GNUNET_GTK_KNOWN_NAMESPACES_NAME_COLUMN, non_null_unique_name,
+      GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_COLUMN, identifier.encoding,
+      GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, nsid,
+      GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, NULL,
+      GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, md_copy,
+      GNUNET_GTK_KNOWN_NAMESPACES_NON_UNIQUE_NAME_COLUMN, non_null_name,
+      -1);
+
+  if (rating >= 0)
+  {
+    GtkTreeRowReference *rr = GNUNET_GTK_get_reference_from_iter (GTK_TREE_MODEL (
+        ctx->known_ns_store), &known_iter);
+    gtk_list_store_insert_with_values (ctx->ns_order_store, &order_iter, G_MAXINT,
+        GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN, rating,
+        GNUNET_GTK_NAMESPACE_ORDER_NAME_COLUMN, non_null_unique_name,
+        GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_COLUMN, identifier.encoding,
+        GNUNET_GTK_NAMESPACE_ORDER_IDENTIFIER_BIN_COLUMN, nsid,
+        GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN, rr,
+        -1);
+    rr = GNUNET_GTK_get_reference_from_iter (GTK_TREE_MODEL (ctx->ns_order_store),
+        &order_iter);
+    gtk_list_store_set (ctx->known_ns_store, &known_iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, rr, -1);
+  }
+  GNUNET_free (non_null_name);
+  GNUNET_free (non_null_unique_name);
+
+  return GNUNET_OK;
+}
+
+static void
+apply_known_ns_changes (struct GNUNET_GTK_NamespaceManagerContext *ctx)
+{
+  GtkTreeIter iter;
+  gint i;
+  gint row_count;
+  GNUNET_HashCode *nsid;
+  char *name;
+  int32_t rank;
+  struct GNUNET_CONTAINER_MetaData *md;
+
+  row_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (
+      ctx->known_ns_store), NULL);
+  if (TRUE != gtk_tree_model_get_iter_first (GTK_TREE_MODEL (
+      ctx->known_ns_store), &iter))
+    return;
+
+  for (i = 0; i < row_count; i++)
+  {
+    gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &iter,
+        GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, &nsid,
+        GNUNET_GTK_KNOWN_NAMESPACES_NON_UNIQUE_NAME_COLUMN, &name,
+        GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, &md,
+        GNUNET_GTK_KNOWN_NAMESPACES_RANK_COLUMN, &rank,
+        -1);
+    GNUNET_PSEUDONYM_set_info (GNUNET_FS_GTK_get_configuration (),
+        nsid, name, md, rank);
+    g_free (name);
+    if (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL (
+      ctx->known_ns_store), &iter))
+      break;
+  }
+}
+
+static void
+free_lists_contents (struct GNUNET_GTK_NamespaceManagerContext *ctx)
+{
+  GtkTreeIter iter;
+  gint i;
+  gint row_count;
+  GNUNET_HashCode *nsid;
+  GtkTreeRowReference *order_row;
+  struct GNUNET_CONTAINER_MetaData *md;
+
+  row_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (
+      ctx->known_ns_store), NULL);
+  if (TRUE != gtk_tree_model_get_iter_first (GTK_TREE_MODEL (
+      ctx->known_ns_store), &iter))
+    return;
+
+  for (i = 0; i < row_count; i++)
+  {
+    gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &iter,
+        GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, &nsid,
+        GNUNET_GTK_KNOWN_NAMESPACES_ORDER_ROW_REFERENCE_COLUMN, &order_row,
+        GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, &md,
+        -1);
+    if (order_row != NULL)
+    {
+      GtkTreeIter order_iter;
+      if (GNUNET_OK == GNUNET_GTK_get_iter_from_reference (order_row, &order_iter))
+      {
+        GtkTreeRowReference *known_row;
+        gtk_tree_model_get (GTK_TREE_MODEL (ctx->ns_order_store), &order_iter,
+            GNUNET_GTK_NAMESPACE_ORDER_KNOWN_ROW_REFERENCE_COLUMN, &known_row,
+            -1);
+        if (known_row != NULL)
+          gtk_tree_row_reference_free (known_row);
+      }
+      gtk_tree_row_reference_free (order_row);
+    }
+    GNUNET_CONTAINER_meta_data_destroy (md);
+    GNUNET_free (nsid);
+    if (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL (
+      ctx->known_ns_store), &iter))
+      break;
+  }
+
+  gtk_list_store_clear (ctx->ns_order_store);
+  gtk_list_store_clear (ctx->known_ns_store);
+  gtk_list_store_clear (ctx->ns_details_store);
+  if (ctx->uneditable_md != NULL)
+    GNUNET_CONTAINER_meta_data_destroy (ctx->uneditable_md);
+}
+
+void
+GNUNET_GTK_namespace_manager_dialog_response_cb (GtkDialog *dialog,
+    gint response_id, gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  switch (response_id)
+  {
+  case GTK_RESPONSE_APPLY:
+  case GTK_RESPONSE_OK:
+    apply_known_ns_changes (ctx);
+    break;
+  default:
+    break;
+  }
+  switch (response_id)
+  {
+  case GTK_RESPONSE_APPLY:
+    break;
+  case GTK_RESPONSE_OK:
+  case GTK_RESPONSE_CANCEL:
+  default:
+    free_lists_contents (ctx);
+    gtk_widget_destroy (GTK_WIDGET (ctx->namespace_manager));
+    GNUNET_free (ctx);
+    ns_manager = NULL;
+  }
+}
+
+
+static gboolean
+mark_as_mine (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+    gpointer data)
+{
+  const GNUNET_HashCode *mine_id = data;
+  const GNUNET_HashCode *ns_id;
+
+  gtk_tree_model_get (model, iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_IDENTIFIER_BIN_COLUMN, &ns_id, -1);
+
+  if (memcmp (ns_id, mine_id, sizeof (GNUNET_HashCode)) != 0)
+    return FALSE;
+
+  gtk_list_store_set (GTK_LIST_STORE (model), iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_IS_MINE_COLUMN, TRUE, -1);
+  return TRUE;
+}
+
+/**
+ * Callback with information about local (!) namespaces.
+ * Contains the names of the local namespace and the global
+ * ID.
+ *
+ * @param cls closure
+ * @param name human-readable identifier of the namespace
+ * @param id hash identifier for the namespace
+ */
+static void
+mark_namespaces_as_mine (void *cls, const char *name,
+    const GNUNET_HashCode * id)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = cls;
+
+  gtk_tree_model_foreach (GTK_TREE_MODEL (ctx->known_ns_store), mark_as_mine,
+      (gpointer) id);
+}
+
+/**
+ * Type of a function that libextractor calls for each
+ * meta data item found.
+ *
+ * @param cls closure (user-defined)
+ * @param plugin_name name of the plugin that produced this value;
+ *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
+ *        used in the main libextractor library and yielding
+ *        meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data 
+ * @param data_mime_type mime-type of data (not of the original file);
+ *        can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue extracting, 1 to abort
+ */ 
+static int
+populate_details_list (void *cls, const char *plugin_name,
+    enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format,
+    const char *data_mime_type, const char *data, size_t data_len)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = cls;
+  const char *type_name;
+  char *data_utf8;
+
+  if (format == EXTRACTOR_METAFORMAT_UTF8 ||
+      format == EXTRACTOR_METAFORMAT_C_STRING)
+  {
+    type_name = EXTRACTOR_metatype_to_string (type);
+    /* TODO: translate type_name using dgettext? */
+    data_utf8 = GNUNET_FS_GTK_dubious_meta_to_utf8 (format, data, data_len);
+    if (data != NULL)
+    {
+      gtk_list_store_insert_with_values (ctx->ns_details_store, NULL, G_MAXINT,
+          GNUNET_GTK_NAMESPACE_DETAILS_PLUGIN_NAME_COLUMN, plugin_name,
+          GNUNET_GTK_NAMESPACE_DETAILS_TYPE_BIN_COLUMN, type,
+          GNUNET_GTK_NAMESPACE_DETAILS_TYPE_COLUMN, type_name,
+          GNUNET_GTK_NAMESPACE_DETAILS_FORMAT_COLUMN,
+          EXTRACTOR_METAFORMAT_UTF8,
+          GNUNET_GTK_NAMESPACE_DETAILS_DATA_MIME_COLUMN, data_mime_type,
+          GNUNET_GTK_NAMESPACE_DETAILS_VALUE_COLUMN, data_utf8,
+          -1);
+      GNUNET_free (data_utf8);
+      return 0;
+    }
+  }
+  GNUNET_CONTAINER_meta_data_insert (ctx->uneditable_md,
+      plugin_name, type, format, data_mime_type, data, data_len);
+  return 0;
+}
+
+static void
+ns_details_selection_changed (GtkTreeSelection *treeselection,
+    gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter iter;
+
+  if (FALSE == gtk_tree_selection_get_selected (ctx->ns_details_sel, NULL, &iter))
+  {
+    gtk_widget_set_sensitive (ctx->details_delete_button, FALSE);
+    return;
+  }
+
+  gtk_widget_set_sensitive (ctx->details_delete_button, TRUE);
+}
+
+static void
+known_ns_selection_changed (GtkTreeSelection *treeselection,
+    gpointer user_data)
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx = user_data;
+  GtkTreeIter iter;
+  const struct GNUNET_CONTAINER_MetaData *md;
+
+  if (FALSE == gtk_tree_selection_get_selected (ctx->known_ns_sel, NULL, &iter))
+    return;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (ctx->known_ns_store), &iter,
+      GNUNET_GTK_KNOWN_NAMESPACES_METADATA_COLUMN, &md,
+      -1);
+  if (ctx->uneditable_md != NULL)
+    GNUNET_CONTAINER_meta_data_clear (ctx->uneditable_md);
+  else
+    ctx->uneditable_md = GNUNET_CONTAINER_meta_data_create ();
+  gtk_list_store_clear (ctx->ns_details_store);
+  gtk_widget_set_sensitive (ctx->details_apply_button, FALSE);
+  GNUNET_CONTAINER_meta_data_iterate ((const struct GNUNET_CONTAINER_MetaData *) md,
+      populate_details_list, ctx);
+  gtk_widget_set_sensitive (ctx->details_apply_button, FALSE);
+}
+
+void
+GNUNET_GTK_namespace_manager_open ()
+{
+  struct GNUNET_GTK_NamespaceManagerContext *ctx;
+
+  if (ns_manager != NULL)
+    return;
+
+  ctx = GNUNET_malloc (sizeof (struct GNUNET_GTK_NamespaceManagerContext));
+  ctx->builder = GNUNET_GTK_get_new_builder ("gnunet_fs_gtk_namespace_manager.glade", ctx);
+  if (ctx->builder == NULL)
+  {
+    GNUNET_break (0);
+    GNUNET_free (ctx);
+    return;
+  }
+
+  /* initialize widget references */
+  ctx->known_ns = GTK_WIDGET (gtk_builder_get_object (ctx->builder,
+      "GNUNET_GTK_namespace_manager_known_treeview"));
+  ctx->ns_order = GTK_WIDGET (gtk_builder_get_object (ctx->builder,
+      "GNUNET_GTK_namespace_manager_namespace_order_treeview"));
+  ctx->ns_details = GTK_WIDGET (gtk_builder_get_object (ctx->builder,
+      "GNUNET_GTK_namespace_manager_namespace_details_treeview"));
+  ctx->known_ns_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (
+      ctx->known_ns));
+  ctx->ns_order_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (
+      ctx->ns_order));
+  ctx->ns_details_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (
+      ctx->ns_details));
+  ctx->ns_order_store = GTK_LIST_STORE (gtk_tree_view_get_model (
+      GTK_TREE_VIEW (ctx->ns_order)));
+  ctx->known_ns_store = GTK_LIST_STORE (gtk_tree_view_get_model (
+      GTK_TREE_VIEW (ctx->known_ns)));
+  ctx->ns_details_store = GTK_LIST_STORE (gtk_tree_view_get_model (
+      GTK_TREE_VIEW (ctx->ns_details)));
+  ctx->namespace_manager = GTK_WINDOW (gtk_builder_get_object (
+      ctx->builder, "GNUNET_GTK_namespace_manager_dialog"));
+  ctx->details_apply_button = GTK_WIDGET (gtk_builder_get_object (ctx->builder,
+      "GNUNET_GTK_namespace_manager_namespace_details_apply_button"));
+  ctx->details_delete_button = GTK_WIDGET (gtk_builder_get_object (ctx->builder,
+      "GNUNET_GTK_namespace_manager_namespace_details_delete_button"));
+  ctx->order_rank = gtk_tree_view_get_column (GTK_TREE_VIEW (ctx->ns_order), 0);
+  ctx->order_name = gtk_tree_view_get_column (GTK_TREE_VIEW (ctx->ns_order), 1);
+  ctx->order_id = gtk_tree_view_get_column (GTK_TREE_VIEW (ctx->ns_order), 2);
+
+  /* connect signals; FIXME-GTK3: these could be connected with (modern) Glade */
+  g_signal_connect (G_OBJECT (ctx->known_ns_sel), "changed",
+      G_CALLBACK (known_ns_selection_changed), ctx);
+  g_signal_connect (G_OBJECT (ctx->ns_details_sel), "changed",
+      G_CALLBACK (ns_details_selection_changed), ctx);
+
+  /* populate namespace model */
+  (void) GNUNET_PSEUDONYM_list_all (GNUNET_FS_GTK_get_configuration (),
+      populate_known_ns_list, ctx);
+
+  /* mark our own namespaces as such */
+  GNUNET_FS_namespace_list (GNUNET_FS_GTK_get_fs_handle (),
+      mark_namespaces_as_mine, ctx);
+
+  /* sort namespace order list by rank (initially) */
+  sort_order_list (ctx, GNUNET_GTK_NAMESPACE_ORDER_RANK_COLUMN);
+
+  ns_manager = ctx;
+
+  gtk_widget_set_sensitive (ctx->details_apply_button, FALSE);
+
+  /* show dialog */
+  gtk_window_present (GTK_WINDOW (ctx->namespace_manager));
+}
+
+/* end of gnunet-fs-gtk_namespace_manager.c */
diff --git a/src/fs/gnunet-fs-gtk_namespace_manager.h b/src/fs/gnunet-fs-gtk_namespace_manager.h
new file mode 100644
index 0000000..abc265e
--- /dev/null
+++ b/src/fs/gnunet-fs-gtk_namespace_manager.h
@@ -0,0 +1,35 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010, 2012 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
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs/gnunet-fs-gtk_namespace_manager.h
+ * @author LRN
+ */
+
+#ifndef GNUNET_FS_GTK_NAMESPACE_MANAGER_H
+#define GNUNET_FS_GTK_NAMESPACE_MANAGER_H
+
+#include "gnunet-fs-gtk_common.h"
+
+void
+GNUNET_GTK_namespace_manager_open ();
+
+#endif /* GNUNET_FS_GTK_NAMESPACE_MANAGER_H */
+/* end of gnunet-fs-gtk_namespace_manager.c */
-- 
1.7.4

0001-Namespace-manager.patch (85,041 bytes)   

Relationships

related to 0002132 closedChristian Grothoff gnunet-gtk can't do much with namespace search results 
related to 0003023 closedChristian Grothoff gnunet-gtk gnunet-identity-gtk advertising dialog is not finished 
parent of 0002936 closedChristian Grothoff GNUnet integrate GNS with identity service 
parent of 0003024 closedChristian Grothoff gnunet-gtk when we discover a namespace advertisement, we need to update the sks-fs zone accordingly 
child of 0001966 closedLRN gnunet-gtk GUI master-issue 

Activities

LRN

2011-12-13 12:42

developer   ~0005076

Dropdown list can show all and any information, so there's no need to access full meta-data via menu. Also, information can be put into tooltips.

Deletion might not be feasible to implement separately. As per 0001951, there will be a separate dialog/tab for namespace editing, a rightclick menu on a namespace in the search namespaces list might just open that tab instead of deleting namespaces right here.

LRN

2012-02-23 17:05

developer   ~0005496

Uploaded gnunet_namespace_manaer_draft_01.png - a draft for namespace management dialog.

In "Known namespaces" list user is able to edit namespace descriptions (but not IDs, obviously), check/uncheck the toggle button that adds/removes namespace from the popup list. "Is mine" depends on whether we hold the key to that namespace or not.

"Selected namespace details" is a listview populated with information about selected namespace. Will be editable (to some extent?).

The listview in the bottom half presents a sortable (by dragging, by ModifierKey+Arrow combinations, and by clicking on column headers) list of namespaces that have "offer" checked in the top list. Determintes the order in which namespaces are shown in popup dialog. The information about the order (and the ids and metadata of the namespaces that should be shown) must be stored somewhere. Config file? Or a special file in ~/.gnunet/data/ ?

Christian Grothoff

2012-02-23 17:47

manager   ~0005497

(05:45:49 PM) grothoff-office: LRN: I think the dialog for 0001952 you propose looks fine.
(05:46:00 PM) grothoff-office: Now, IIRC we have all of the storage features you talk about already.
(05:46:40 PM) grothoff-office: Look at src/include/gnunet_pseudonym_lib.h -- the 'rank' can be used for the sorting, metadata is also already there.

LRN

2012-02-25 14:35

developer   ~0005517

Uploaded gnunet_namespace_manager_draft_02.png - the second draft.

Details list is now at the bottom (more space here - easier to see and edit namespace metadata), and re-ordering list is to the right (no real need for space there).

There's a "Delete" button to make GNUnet node "forget" about namespaces that it discovered. Prevents namespace list pollution.
Problem: GNUnet node will find deleted namespaces again, if you search for them (or if you search for something else, but these namespaces are published with keywords that make them pop up in searches, even if the have no relevance, i.e. - namespace advertisement spam).
Adding a list of banned namespaces doesn't strike me as a good solutions (more GUI elements; and namespace spammer can just generate more namespaces with different keys - can't ban them all).
I'll try to make new namespaces as unobtrusive as possible - put them at the bottom of the list initially; automatically increase rating of namespaces that the user searched in; allow user to boost a rank of a namespace from the search results tab (that is, you tell GNUnet which namespaces are good as you discover them; the rest of the namespaces you find will be remembered, but will stay at the bottom of the list).

So, about rating (or rank): all namespaces start with a rank INT_MIN (-2^32), your own namespaces start with a rank 0. Namespaces with positive rank closer to zero are at the top of the list. Namespaces with positive rank farther away from zero are farther away from the top of the list. Namespaces with negative rank are at the bottom of the list, the ones with lowest negative rank are at the very bottom.
This way it's easy to put something at the top (give it a rank 0), or at the bottom (give it a rank INT_MIN).

Rank can be increased or decreased for any namespace in places where namespaces can be seen (namespace popup list in main window, namespaces found via search), there will be context menu for that (see above).
The ultimate rank-changer is the re-ordering list. Namespaces with non-negative rank are put there, and can be sorted. After the order of namespaces is acceptable, the user can press "Apply" to convert the order into ranks. Item at the top of the re-order list will be given rank 0, next item will get 1, and so on.
There will probably be a way to quickly give namespaces a positive or a negative rank to put them on re-ordering list, and to remove them from there (editing rank value by hand is non-intuitive; i'd say - a button that gives positive rank to namespaces with negative rank, and negative rank - to namespaces with non-negative rank; its label will change depending on which namespace is selected).

"Add" and "Delete" buttons are for metadata items insertion and removal. Unlike Publication Editing dialog, there's no dropdown list of possible metadata types. Not sure if it is needed here.

LRN

2012-02-29 13:58

developer   ~0005540

Uploaded 0001-Change-pseudonym-management.patch and 0001-Namespace-manager.patch
One changes pseudonym management API in core, another implements the namespace manager dialog.

Christian Grothoff

2012-03-01 09:26

manager   ~0005547

First two patches committed as SVN 20154 and 20155.

Christian Grothoff

2012-03-05 01:43

manager   ~0005561

Ok, a few comments on what we have in SVN HEAD. First of all, why do you list the namespaces twice? I'd have combined the namespace reorder area on the right with the main namespace list on the left. In fact, just leave out the 'apply' button (always auto-apply) and don't even display the 'rank' (just use it for sorting, and don't allow sorting by name). That would make it very easy: one list, I can reorder within that list (and the order will be remembered), and lots of GUI elements that are no more (rank, apply button, swap rank -- which I still don't get, btw.).

LRN

2012-03-05 06:41

developer   ~0005563

Namespace list on the left will, eventually, be FULL of various namespaces discovered over the years. Sorting it allows users to quickly access various namespaces in that list (i also hope that start-typing-to-search also works, if it doesn't - it should).
Using that list ALSO for rank-applying is not going to be nice, since LOTS of irrelevant namespaces will make it difficult to re-order the other namespaces correctly.
Which is why there's a big namespace list and small sorting list, which only gets namespaces with non-negative ranks. Swap rank button is a way to quickly swap namespace rank from positive to negative and back (this is faster than double-clicking on rank column and entering a new rank, although that isn't even enabled).
Another possibility is to split this in a different way:
One big namespace list with namespaces that have non-negative rank - on the left, and another small list with namespaces that have negative rank - on the right. You'd drag namespaces from the right list to the left list, that will function as rank swapping (although to do this in a proper way you still need to have "move to the right" and "move to the left" buttons for accessibility reasons). So, in the end you end up with even more widgets.
Now, removing "Apply" button for namespace order MIGHT be a good idea, but i would like to hold it off until we are able to ask users about it. Applying rank changes immediately means that you can't roll back your re-ordering. At the moment you can - just sort namespaces by rank again, and they will be re-ordered the way they were when you opened the dialog. Only after you press "Apply" will their ranks change.

Christian Grothoff

2012-03-09 22:34

manager   ~0005590

Ok, I see your point(s). I still think the dialog & code need some work. (Starting with adding meta data and adding comments to the code...).

Also, the buttons are all always sensitive (both delete, swap rank), even if no elements are selected. Finally, you have two apply buttons -- wouldn't it at least be OK to only have one? What's the difference between the two apply and the "OK" button then exactly? As you see, I'm still confused about this dialog...

LRN

2012-03-09 23:07

developer   ~0005591

One "Apply" applies metadata changes. Every time you select a namespace in Known Namespace List, the Metadata List is re-populated with the metadata for that namespace. You can change that metadata, but the changes won't stick until you click "Apply" (selecting a different namespace without clicking "Apply" will discard metadata changes).
The other "Apply" applies rank order.
...
And ALL that is NOT going to stick until you press "OK" (apply changes, close the dialog) or the third "Apply" button (apply the changes, don't close the dialog).

So yeah, 3 "Apply" buttons.
Any suggestions?

Christian Grothoff

2012-03-09 23:38

manager   ~0005592

I'm currently thinking that one "apply" button is fine, but that one than applies *all* changes. Furthermore, the "OK" button must *also* apply all changes. So the difference between apply and OK is that OK also closes the dialog (so OK is the same as pushing first apply and then cancel).

That's also the only consistent thing with other UI guidelines that I've found. We can then later possibly choose to make the 'apply' button invisible if that seems better based on usability studies.

In any case, more than one apply button, or apply buttons not with ok/cancel are NOT ok, and having changes not fully applied (after pushing apply and then cancel) *also* violates all user interface guidelines that I've found.

Christian Grothoff

2013-08-11 23:29

manager   ~0007339

Just an update -- most of the namespace stuff was moved to gnunet-identity-gtk with the birth of the identity service (as it's not just about namespaces anymore, but about egos). The namespace advertising in gnunet-identity-gtk is still not implemented, and the ability to NICELY discover/select namespaces in gnunet-fs-gtk (especially GNS integration) likely also still needs work.

Christian Grothoff

2013-09-05 18:39

manager   ~0007411

Last edited: 2013-09-07 21:17

"monitor_zone_records" still needs to be implemented in gnunet-fs-gtk, but then we theoretically have an integration between FS-SKS and GNS working.

Still to do:
0) monitor zone
1) update fs-gns zone when we discover namespace advertisements (0003024)
2) finish gnunet-identity-gtk (0003023)

Christian Grothoff

2013-09-06 11:33

manager   ~0007412

monitor_zone_records was implemented in SVN 29055.

Christian Grothoff

2013-09-08 20:16

manager   ~0007428

After 0003024, we should test this end-to-end before closing this bug...

Christian Grothoff

2013-09-15 21:45

manager   ~0007454

Seems to work now (after minor fixes).

Issue History

Date Modified Username Field Change
2011-11-22 20:34 Christian Grothoff New Issue
2011-11-22 20:39 Christian Grothoff Status new => confirmed
2011-11-26 18:23 Christian Grothoff Relationship added child of 0001966
2011-12-01 10:12 Christian Grothoff Target Version => 0.9.2
2011-12-13 12:42 LRN Note Added: 0005076
2011-12-13 23:15 Christian Grothoff Assigned To => LRN
2011-12-13 23:15 Christian Grothoff Status confirmed => assigned
2012-01-22 21:19 Christian Grothoff Target Version 0.9.2 => 0.9.3
2012-02-03 23:01 Christian Grothoff Relationship added related to 0002132
2012-02-03 23:08 Christian Grothoff Relationship added child of 0001949
2012-02-05 19:53 Christian Grothoff Relationship deleted child of 0001949
2012-02-23 16:59 LRN File Added: gnunet_namespace_manaer_draft_01.png
2012-02-23 17:05 LRN Note Added: 0005496
2012-02-23 17:47 Christian Grothoff Note Added: 0005497
2012-02-25 14:35 LRN File Added: gnunet_namespace_manager_draft_02.png
2012-02-25 14:35 LRN Note Added: 0005517
2012-02-29 13:56 LRN File Added: 0001-Change-pseudonym-management.patch
2012-02-29 13:56 LRN File Added: 0001-Namespace-manager.patch
2012-02-29 13:58 LRN Note Added: 0005540
2012-03-01 09:26 Christian Grothoff Note Added: 0005547
2012-03-05 01:43 Christian Grothoff Note Added: 0005561
2012-03-05 06:41 LRN Note Added: 0005563
2012-03-09 22:34 Christian Grothoff Note Added: 0005590
2012-03-09 23:07 LRN Note Added: 0005591
2012-03-09 23:38 Christian Grothoff Note Added: 0005592
2012-04-26 19:22 Christian Grothoff Target Version 0.9.3 => 0.9.4
2012-06-17 12:49 Christian Grothoff Assigned To LRN =>
2012-06-17 12:49 Christian Grothoff Status assigned => confirmed
2012-06-17 12:49 Christian Grothoff Target Version 0.9.4 =>
2012-06-18 22:56 Christian Grothoff Target Version => 0.9.5
2012-11-06 23:40 Christian Grothoff Target Version 0.9.5 => 0.10.0
2013-07-11 00:07 Christian Grothoff Assigned To => Christian Grothoff
2013-07-11 00:07 Christian Grothoff Priority normal => high
2013-07-11 00:07 Christian Grothoff Status confirmed => assigned
2013-07-13 21:06 Christian Grothoff Relationship added parent of 0002936
2013-08-11 23:29 Christian Grothoff Note Added: 0007339
2013-09-05 18:39 Christian Grothoff Note Added: 0007411
2013-09-05 18:51 Christian Grothoff Note Edited: 0007411
2013-09-06 11:33 Christian Grothoff Note Added: 0007412
2013-09-06 11:36 Christian Grothoff Note Edited: 0007411
2013-09-06 11:36 Christian Grothoff Relationship added related to 0003023
2013-09-07 21:17 Christian Grothoff Note Edited: 0007411
2013-09-07 21:17 Christian Grothoff Relationship added parent of 0003024
2013-09-08 20:16 Christian Grothoff Note Added: 0007428
2013-09-15 21:45 Christian Grothoff Note Added: 0007454
2013-09-15 21:45 Christian Grothoff Status assigned => resolved
2013-09-15 21:45 Christian Grothoff Fixed in Version => 0.10.0
2013-09-15 21:45 Christian Grothoff Resolution open => fixed
2013-12-24 20:54 Christian Grothoff Status resolved => closed