make clang static analysis happy
[oweals/gnunet.git] / src / hostlist / hostlist-client.c
index 9af75fec2b3b118dee7b599a85a50871c1bfa364..90fdfb63a3d35e59525b8217490c45c91b937c11 100644 (file)
@@ -22,6 +22,7 @@
  * @file hostlist/hostlist-client.c
  * @brief hostlist support.  Downloads HELLOs via HTTP.
  * @author Christian Grothoff
+ * @author Matthias Wachs
  */
 
 #include "platform.h"
 #include "gnunet_hello_lib.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
+#include "gnunet-daemon-hostlist.h"
 #include <curl/curl.h>
+#include "gnunet_common.h"
+#include "gnunet_bio_lib.h"
 
 #define DEBUG_HOSTLIST_CLIENT GNUNET_YES
 
+
 /**
  * Number of connections that we must have to NOT download
  * hostlists anymore.
  */
 #define MIN_CONNECTIONS 4
 
+/**
+ * A single hostlist obtained by hostlist advertisements
+ */
+struct Hostlist
+{
+  /**
+   * previous entry, used to manage entries in a double linked list
+   */
+  struct Hostlist * prev;
+
+  /**
+   * next entry, used to manage entries in a double linked list
+   */
+  struct Hostlist * next;
+
+  /**
+   * URI where hostlist can be obtained
+   */
+  const char *hostlist_uri;
+
+  /**
+   * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
+   * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
+   * intial value = HOSTLIST_INITIAL
+   * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
+   * increased every successful download by number of obtained HELLO messages
+   * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
+   */
+  uint64_t quality;
+
+  /**
+   * Time the hostlist advertisement was recieved and the entry was created
+   */
+  struct GNUNET_TIME_Absolute time_creation;
+
+  /**
+   * Last time the hostlist was obtained
+   */
+  struct GNUNET_TIME_Absolute time_last_usage;
+
+  /**
+   * Number of HELLO messages obtained during last download
+   */
+  uint32_t hello_count;
+
+  /**
+   * Number of times the hostlist was successfully obtained
+   */
+  uint32_t times_used;
+
+};
+
+
 /**
  * Our configuration.
  */
@@ -95,6 +153,11 @@ static CURLM *multi;
  */
 static GNUNET_SCHEDULER_TaskIdentifier current_task;
 
+/**
+ * ID of the current hostlist saving task scheduled.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier saving_task;
+
 /**
  * Amount of time we wait between hostlist downloads.
  */
@@ -115,6 +178,45 @@ static unsigned int connection_count;
  */
 static struct GNUNET_TIME_Absolute end_time;
 
+/**
+ * Head of the linked list used to store hostlists
+ */
+static struct Hostlist * linked_list_head;
+
+/**
+ *  Tail of the linked list used to store hostlists
+ */
+static struct Hostlist * linked_list_tail;
+
+/**
+ *  Current hostlist used for downloading
+ */
+static struct Hostlist * current_hostlist;
+
+/**
+ *  Size of the linke list  used to store hostlists
+ */
+static unsigned int linked_list_size;
+
+/**
+ * Value saying if preconfigured  is used
+ */
+static unsigned int use_preconfigured_list;
+
+/**
+ * Set if we are allowed to learn new hostlists and use them
+ */
+static int learning;
+
+/**
+ * Value saying how many valid HELLO messages were obtained during download
+ */
+static unsigned int hellos_obtained;
+
+/**
+ * Value saying if hostlist download was successful
+ */
+static unsigned int download_successful;
 
 /**
  * Process downloaded bits by calling callback on each HELLO.
@@ -193,7 +295,8 @@ download_hostlist_processor (void *ptr,
          GNUNET_STATISTICS_update (stats, 
                                    gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
                                    1, 
-                                   GNUNET_NO);  
+                                   GNUNET_NO);
+         hellos_obtained++;
          GNUNET_TRANSPORT_offer_hello (transport, msg);
        }
       else
@@ -224,7 +327,7 @@ download_hostlist_processor (void *ptr,
  * @return NULL if there is no URL available
  */
 static char *
-get_url ()
+get_bootstrap_url ()
 {
   char *servers;
   char *ret;
@@ -233,13 +336,13 @@ get_url ()
 
   if (GNUNET_OK != 
       GNUNET_CONFIGURATION_get_value_string (cfg,
-                                            "HOSTLIST",
-                                            "SERVERS",
-                                            &servers))
+                                             "HOSTLIST",
+                                             "SERVERS",
+                                             &servers))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
-                 "SERVERS", "HOSTLIST");
+                  _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
+                  "SERVERS", "HOSTLIST");
       return NULL;
     }
 
@@ -258,8 +361,8 @@ get_url ()
   if (urls == 0)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
-                 "SERVERS", "HOSTLIST");
+                  _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
+                  "SERVERS", "HOSTLIST");
       GNUNET_free (servers);
       return NULL;
     }
@@ -285,6 +388,46 @@ get_url ()
   return ret;
 }
 
+/**
+ * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
+ * @return uri to use, NULL if there is no URL available
+ */
+static char *
+get_list_url ()
+{
+  uint32_t index;
+  unsigned int counter;
+  struct Hostlist * pos;
+
+  if ( GNUNET_NO == learning)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Using preconfigured bootstrap server\n");
+    current_hostlist = NULL;
+    return get_bootstrap_url();
+  }
+  if ( (GNUNET_YES == use_preconfigured_list) ||
+       (linked_list_size == 0) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Using preconfigured bootstrap server\n");
+    current_hostlist = NULL;
+    return get_bootstrap_url();
+  }
+  index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
+  counter = 0;
+  pos = linked_list_head;
+  while ( counter < index )
+    {
+      pos = pos->next;
+      counter ++;
+    }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Using learned hostlist `%s'\n", pos->hostlist_uri);
+  current_hostlist = pos;
+  return strdup(pos->hostlist_uri);
+}
+
 
 #define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
 
@@ -296,6 +439,91 @@ get_url ()
 static void
 schedule_hostlist_task (void);
 
+/**
+ * Method to load persistent hostlist file during hostlist client shutdown
+ * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
+ */
+static void save_hostlist_file ( int shutdown );
+
+/**
+ * add val2 to val1 with overflow check
+ * @param val1 value 1
+ * @param val2 value 2
+ * @return result
+ */
+static uint64_t checked_add (uint64_t val1, uint64_t val2)
+{
+  static uint64_t temp;
+  static uint64_t maxv;
+
+  maxv = 0;
+  maxv--;
+
+  temp = val1+val2;
+  if ( temp < val1)
+    return maxv;
+  else
+    return temp;
+}
+
+/**
+ * Subtract val2 from val1 with underflow check
+ * @param val1 value 1
+ * @param val2 value 2
+ * @return result
+ */
+static uint64_t checked_sub (uint64_t val1, uint64_t val2)
+{
+  if ( val1 <= val2)
+    return 0;
+  else
+    return (val1-val2);
+}
+
+
+/**
+ * Method updating hostlist statistics
+ */
+static void update_hostlist ( )
+{
+  char *stat;
+  if ( (use_preconfigured_list == GNUNET_NO) && ( NULL != current_hostlist ) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
+     current_hostlist->hello_count = hellos_obtained;
+     current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
+     current_hostlist->quality = checked_add ( current_hostlist->quality, (hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
+     if ( GNUNET_YES == download_successful )
+     {
+       current_hostlist->times_used++;
+       current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
+       GNUNET_asprintf (&stat,
+                        gettext_noop("# advertised URI `%s' downloaded"),
+                        current_hostlist->hostlist_uri);
+
+       GNUNET_STATISTICS_update ( stats,
+                                  stat,
+                                  1,
+                                  GNUNET_YES);
+       GNUNET_free (stat);
+     }
+     else
+       current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
+  }
+  current_hostlist = NULL;
+  /* Alternating the usage of preconfigured and learned hostlists */
+
+  if ( GNUNET_YES == learning)
+    {
+    if (use_preconfigured_list == GNUNET_YES)
+      use_preconfigured_list = GNUNET_NO;
+    else
+      use_preconfigured_list = GNUNET_YES;
+    }
+  else
+    use_preconfigured_list = GNUNET_YES;
+}
 
 /**
  * Clean up the state from the task that downloaded the
@@ -331,6 +559,7 @@ clean_up ()
     }  
   GNUNET_free_non_null (current_url);
   current_url = NULL;
+
   schedule_hostlist_task ();
 }
 
@@ -340,7 +569,7 @@ clean_up ()
  * receiving task with the scheduler.
  */
 static void
-run_multi ();
+run_multi (void);
 
 
 /**
@@ -365,6 +594,7 @@ multi_ready (void *cls,
                  "Shutdown requested while trying to download hostlist from `%s'\n",
                  current_url);
 #endif
+      update_hostlist();
       clean_up ();
       return;
     }
@@ -373,6 +603,7 @@ multi_ready (void *cls,
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                  _("Timeout trying to download hostlist from `%s'\n"),
                  current_url);
+      update_hostlist();
       clean_up ();
       return;
     }
@@ -405,9 +636,13 @@ multi_ready (void *cls,
                               __LINE__,
                               curl_easy_strerror (msg->data.result));            
                  else
+                   {
                    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                                _("Download of hostlist `%s' completed.\n"),
                                current_url);
+                   download_successful = GNUNET_YES;
+                   update_hostlist();
+                   }
                  clean_up ();
                  return;
                default:
@@ -506,6 +741,9 @@ download_hostlist ()
   CURLcode ret;
   CURLMcode mret;
 
+  current_url = get_list_url ();
+  if (current_url == NULL)
+    return;
   curl = curl_easy_init ();
   multi = NULL;
   if (curl == NULL)
@@ -514,10 +752,11 @@ download_hostlist ()
       clean_up ();
       return;
     }
-  current_url = get_url ();
   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
              _("Bootstrapping using hostlist at `%s'.\n"), 
              current_url);
+  hellos_obtained = 0;
+  download_successful = GNUNET_NO;
   GNUNET_STATISTICS_update (stats, 
                            gettext_noop ("# hostlist downloads initiated"), 
                            1, 
@@ -669,6 +908,30 @@ schedule_hostlist_task ()
                                               NULL);
 }
 
+/**
+ * Task that writes hostlist entries to a file on a regular base
+ * cls closure
+ * tc TaskContext
+ */
+static void
+hostlist_saving_task (void *cls,
+            const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  saving_task = GNUNET_SCHEDULER_NO_TASK;
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Scheduled saving of hostlists\n"));
+  save_hostlist_file ( GNUNET_NO );
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Hostlists will be saved to file again in %llums\n"),
+              (unsigned long long) SAVING_INTERVALL.value);
+  saving_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                               SAVING_INTERVALL,
+                                               &hostlist_saving_task,
+                                               NULL);
+}
 
 /**
  * Method called whenever a given peer connects.
@@ -711,12 +974,57 @@ disconnect_handler (void *cls,
                            GNUNET_NO);  
 }
 
+
+/**
+ * Method to check if URI is in hostlist linked list
+ * @param uri uri to check
+ * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
+ */
+static int 
+linked_list_contains (const char * uri)
+{
+  struct Hostlist * pos;
+
+  pos = linked_list_head;
+  while (pos != NULL)
+    {
+      if (0 == strcmp(pos->hostlist_uri, uri) ) 
+       return GNUNET_YES;
+      pos = pos->next;
+    }
+  return GNUNET_NO;
+}
+
+
+/* linked_list_? */
+static struct Hostlist *
+linked_list_get_lowest_quality ( )
+{
+  struct Hostlist * pos;
+  struct Hostlist * lowest;
+
+  if (linked_list_size == 0)
+    return NULL;
+  lowest = linked_list_head;
+  pos = linked_list_head->next;
+  while (pos != NULL)
+    {
+      if (pos->quality < lowest->quality) 
+       lowest = pos;
+      pos = pos->next;
+    }
+  return lowest;
+}
+
+
 /**
  * Method called whenever an advertisement message arrives.
  *
  * @param cls closure (always NULL)
- * @param client identification of the client
+ * @param peer the peer sending the message
  * @param message the actual message
+ * @param latency latency
+ * @param distance distance
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
@@ -727,18 +1035,77 @@ advertisement_handler (void *cls,
     struct GNUNET_TIME_Relative latency,
     uint32_t distance)
 {
-  int size = ntohs (message->size);
-  int type = ntohs (message->type);
-  if ( type != GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT)
-    return GNUNET_NO;
-#if DEBUG_HOSTLIST_CLIENT
+  size_t size;
+  size_t uri_size;
+  const struct GNUNET_MessageHeader * incoming;
+  const char *uri;
+  struct Hostlist * hostlist;
+
+  GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
+  size = ntohs (message->size);
+  if (size <= sizeof(struct GNUNET_MessageHeader))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  incoming = (const struct GNUNET_MessageHeader *) message;
+  uri = (const char*) &incoming[1];
+  uri_size = size - sizeof (struct GNUNET_MessageHeader);
+  if (uri [uri_size - 1] != '\0')
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Hostlist client recieved advertisement from `%s' containing URI `%s'\n", 
+             GNUNET_i2s (peer), 
+             uri);
+  if (GNUNET_NO != linked_list_contains (uri))
+    {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Hostlist client recieved advertisement message, size %u, type %u\n",size,type);
-#endif
+                "URI `%s' is already known\n",
+                uri);
+      return GNUNET_OK;
+    }
+  hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
+  hostlist->hostlist_uri = (const char*) &hostlist[1];
+  memcpy (&hostlist[1], uri, uri_size);
+  hostlist->time_creation = GNUNET_TIME_absolute_get();
+  hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
+  hostlist->quality = HOSTLIST_INITIAL;  
+
+  GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
+  linked_list_size++;
+  
+  GNUNET_STATISTICS_set (stats,
+                         gettext_noop("# advertised hostlist URIs"),
+                         linked_list_size,
+                         GNUNET_NO);
+
+  if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
+    return GNUNET_OK;
+
+  /* No free entries available, replace existing entry  */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Removing lowest quality entry\n" );  
+  struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Hostlist with URI `%s' has the worst quality of all with value %llu\n", 
+             lowest_quality->hostlist_uri,
+             (unsigned long long) lowest_quality->quality);
+  GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
+  linked_list_size--;
+
+  GNUNET_STATISTICS_set (stats,
+                         gettext_noop("# advertised hostlist URIs"),
+                         linked_list_size,
+                         GNUNET_NO);
 
-     return GNUNET_YES;
+  GNUNET_free (lowest_quality);
+  return GNUNET_OK;
 }
 
+
 /**
  * Continuation called by the statistics code once 
  * we go the stat.  Initiates hostlist download scheduling.
@@ -774,6 +1141,187 @@ process_stat (void *cls,
   return GNUNET_OK;
 }
 
+/**
+ * Method to load persistent hostlist file during hostlist client startup
+ */
+static void 
+load_hostlist_file ()
+{
+  char *filename;
+  char *uri;
+  char *emsg;
+  struct Hostlist * hostlist;
+  uri = NULL;
+  uint32_t times_used;
+  uint32_t hellos_returned;
+  uint64_t quality;
+  uint64_t last_used;
+  uint64_t created;
+  uint32_t counter;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "HOSTLIST",
+                                             "HOSTLISTFILE",
+                                             &filename))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
+                  "HOSTLISTFILE", "HOSTLIST");
+      return;
+    }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Loading saved hostlist entries from file `%s' \n"), filename);
+  if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                _("Hostlist file `%s' is not existing\n"), filename);
+    GNUNET_free ( filename );
+    return;
+  }
+
+  struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
+  if (NULL == rh)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  _("Could not open file `%s' for reading to load hostlists: %s\n"), 
+                 filename,
+                 STRERROR (errno));
+      GNUNET_free (filename);
+      return;
+    }
+
+  counter = 0;
+  while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
+         (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used)) &&
+         (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
+         (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
+         (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
+         (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
+    {
+      hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
+      hostlist->hello_count = hellos_returned;
+      hostlist->hostlist_uri = (const char *) &hostlist[1];
+      memcpy (&hostlist[1], uri, strlen(uri)+1);
+      hostlist->quality = quality;
+      hostlist->time_creation.value = created;
+      hostlist->time_last_usage.value = last_used;
+      GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
+      linked_list_size++;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
+      GNUNET_free (uri);
+      uri = NULL;
+      counter++;
+      if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
+    }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("%u hostlist URIs loaded from file\n"), counter);
+  GNUNET_STATISTICS_set (stats,
+                         gettext_noop("# hostlist URIs read from file"),
+                         counter,
+                         GNUNET_YES);
+  GNUNET_STATISTICS_set (stats,
+                         gettext_noop("# advertised hostlist URIs"),
+                         linked_list_size,
+                         GNUNET_NO);
+
+  GNUNET_free_non_null (uri);
+  emsg = NULL;
+  GNUNET_BIO_read_close (rh, &emsg);
+  if (emsg != NULL)
+    GNUNET_free (emsg);
+  GNUNET_free (filename);
+}
+
+
+/**
+ * Method to save persistent hostlist file during hostlist client shutdown
+ * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
+ */
+static void save_hostlist_file ( int shutdown )
+{
+  char *filename;
+  struct Hostlist *pos;
+  struct GNUNET_BIO_WriteHandle * wh;
+  int ok;
+  uint32_t counter;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "HOSTLIST",
+                                             "HOSTLISTFILE",
+                                             &filename))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
+                  "HOSTLISTFILE", "HOSTLIST");
+      return;
+    }
+  wh = GNUNET_BIO_write_open (filename);
+  if ( NULL == wh)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Could not open file `%s' for writing to save hostlists: %s\n"),
+                  filename,
+                 STRERROR (errno));
+      GNUNET_free (filename);
+      return;
+    }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Writing %u hostlist URIs to `%s'\n" ),
+              linked_list_size, filename);
+
+  /* add code to write hostlists to file using bio */
+  ok = GNUNET_YES;
+  counter = 0;
+  while (NULL != (pos = linked_list_head))
+    {
+      if ( GNUNET_YES == shutdown)
+      {
+        GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
+        linked_list_size--;
+      }
+      if (GNUNET_YES == ok)
+       {
+         if ( (GNUNET_OK !=
+               GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
+              (GNUNET_OK !=
+               GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
+              (GNUNET_OK !=
+               GNUNET_BIO_write_int64 (wh, pos->quality)) ||
+               (GNUNET_OK !=
+                GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
+               (GNUNET_OK !=
+                GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
+              (GNUNET_OK !=
+               GNUNET_BIO_write_int32 (wh, pos->hello_count)))
+           {
+             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                         _("Error writing hostlist URIs to file `%s'\n"),
+                         filename);
+             ok = GNUNET_NO;
+           }
+       }
+
+      if ( GNUNET_YES == shutdown)
+        GNUNET_free (pos);
+      counter ++;
+      if ( counter >= MAX_NUMBER_HOSTLISTS) break;
+    }  
+  GNUNET_STATISTICS_set (stats,
+                         gettext_noop("# hostlist URIs written to file"),
+                         counter,
+                         GNUNET_YES);
+
+  if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Error writing hostlist URIs to file `%s'\n"),
+               filename);
+  GNUNET_free (filename);
+}
 
 /**
  * Start downloading hostlists from hostlist servers as necessary.
@@ -784,8 +1332,12 @@ GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
                              struct GNUNET_STATISTICS_Handle *st,
                              GNUNET_CORE_ConnectEventHandler *ch,
                              GNUNET_CORE_DisconnectEventHandler *dh,
-                             GNUNET_CORE_MessageCallback *msgh)
+                             GNUNET_CORE_MessageCallback *msgh,
+                             int learn)
 {
+  char *filename;
+  int result;
+
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
     {
       GNUNET_break (0);
@@ -806,9 +1358,50 @@ GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
                                             "HTTP-PROXY", 
                                             &proxy))
     proxy = NULL;
+  learning = learn;
   *ch = &connect_handler;
   *dh = &disconnect_handler;
-  *msgh = &advertisement_handler;
+  linked_list_head = NULL;
+  linked_list_tail = NULL;
+  use_preconfigured_list = GNUNET_YES;
+
+  if ( GNUNET_YES == learning )
+  {
+    *msgh = &advertisement_handler;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Learning is enabled on this peer\n"));
+    load_hostlist_file ();
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Hostlists will be saved to file again in  %llums\n"),
+              (unsigned long long) SAVING_INTERVALL.value);
+    saving_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                               SAVING_INTERVALL,
+                                               &hostlist_saving_task,
+                                               NULL);
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Learning is not enabled on this peer\n"));
+    *msgh = NULL;
+    if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
+                                                            "HOSTLIST",
+                                                            "HOSTLISTFILE",
+                                                            &filename))
+    {
+    if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
+      {
+        result = remove (filename);
+        if (result == 0)
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
+        else
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Hostlist file `%s' could not be removed\n"),filename);
+      }
+    }
+    GNUNET_free ( filename );
+  }
   GNUNET_STATISTICS_get (stats,
                         "hostlist",
                         gettext_noop("# seconds between hostlist downloads"),
@@ -830,6 +1423,9 @@ GNUNET_HOSTLIST_client_stop ()
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Hostlist client shutdown\n");
 #endif
+  if ( GNUNET_YES == learning )
+    save_hostlist_file ( GNUNET_YES );
+
   if (current_task != GNUNET_SCHEDULER_NO_TASK)
     {
       GNUNET_SCHEDULER_cancel (sched,