ab52c3a3fb79b3ff9612cf5f28ac889f1484e932
[oweals/gnunet.git] / src / hostlist / hostlist-client.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file hostlist/hostlist-client.c
23  * @brief hostlist support.  Downloads HELLOs via HTTP.
24  * @author Christian Grothoff
25  * @author Matthias Wachs
26  */
27
28 #include "platform.h"
29 #include "hostlist-client.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet-daemon-hostlist.h"
35 #include <curl/curl.h>
36 #include "gnunet_common.h"
37 #include "gnunet_bio_lib.h"
38
39 #define DEBUG_HOSTLIST_CLIENT GNUNET_YES
40
41
42 /**
43  * Number of connections that we must have to NOT download
44  * hostlists anymore.
45  */
46 #define MIN_CONNECTIONS 4
47
48 /**
49  * A single hostlist obtained by hostlist advertisements
50  */
51 struct Hostlist
52 {
53   /**
54    * previous entry, used to manage entries in a double linked list
55    */
56   struct Hostlist * prev;
57
58   /**
59    * next entry, used to manage entries in a double linked list
60    */
61   struct Hostlist * next;
62
63   /**
64    * URI where hostlist can be obtained
65    */
66   const char *hostlist_uri;
67
68   /**
69    * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
70    * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
71    * intial value = HOSTLIST_INITIAL
72    * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
73    * increased every successful download by number of obtained HELLO messages
74    * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
75    */
76   uint64_t quality;
77
78   /**
79    * Time the hostlist advertisement was recieved and the entry was created
80    */
81   struct GNUNET_TIME_Absolute time_creation;
82
83   /**
84    * Last time the hostlist was obtained
85    */
86   struct GNUNET_TIME_Absolute time_last_usage;
87
88   /**
89    * Number of HELLO messages obtained during last download
90    */
91   uint32_t hello_count;
92
93   /**
94    * Number of times the hostlist was successfully obtained
95    */
96   uint32_t times_used;
97
98 };
99
100
101 /**
102  * Our configuration.
103  */
104 static const struct GNUNET_CONFIGURATION_Handle *cfg;
105
106 /**
107  * Our scheduler.
108  */
109 static struct GNUNET_SCHEDULER_Handle *sched;
110
111 /**
112  * Statistics handle.
113  */
114 struct GNUNET_STATISTICS_Handle *stats; 
115
116 /**
117  * Transport handle.
118  */
119 struct GNUNET_TRANSPORT_Handle *transport;
120                        
121 /**
122  * Proxy that we are using (can be NULL).
123  */
124 static char *proxy;
125
126 /**
127  * Buffer for data downloaded via HTTP.
128  */
129 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
130
131 /**
132  * Number of bytes valid in 'download_buffer'.
133  */
134 static size_t download_pos;
135
136 /**
137  * Current URL that we are using.
138  */
139 static char *current_url;
140
141 /**
142  * Current CURL handle.
143  */
144 static CURL *curl;
145
146 /**
147  * Current multi-CURL handle.
148  */
149 static CURLM *multi;
150
151 /**
152  * ID of the current task scheduled.
153  */
154 static GNUNET_SCHEDULER_TaskIdentifier current_task;
155
156 /**
157  * ID of the current hostlist saving task scheduled.
158  */
159 static GNUNET_SCHEDULER_TaskIdentifier saving_task;
160
161 /**
162  * Amount of time we wait between hostlist downloads.
163  */
164 static struct GNUNET_TIME_Relative hostlist_delay;
165
166 /**
167  * Set to GNUNET_YES if the current URL had some problems.
168  */ 
169 static int bogus_url;
170
171 /**
172  * Number of active connections (according to core service).
173  */
174 static unsigned int connection_count;
175
176 /**
177  * At what time MUST the current hostlist request be done?
178  */
179 static struct GNUNET_TIME_Absolute end_time;
180
181 /**
182  * Head of the linked list used to store hostlists
183  */
184 static struct Hostlist * linked_list_head;
185
186 /**
187  *  Tail of the linked list used to store hostlists
188  */
189 static struct Hostlist * linked_list_tail;
190
191 /**
192  *  Current hostlist used for downloading
193  */
194 static struct Hostlist * current_hostlist;
195
196 /**
197  *  Size of the linke list  used to store hostlists
198  */
199 static unsigned int linked_list_size;
200
201 /**
202  * Head of the linked list used to store hostlists
203  */
204 static struct Hostlist * hostlist_to_test;
205
206 static int test_locked;
207
208 /**
209  * Value saying if preconfigured  is used
210  */
211 static unsigned int use_preconfigured_list;
212
213 /**
214  * Set if we are allowed to learn new hostlists and use them
215  */
216 static int learning;
217
218 /**
219  * Value saying how many valid HELLO messages were obtained during download
220  */
221 static unsigned int hellos_obtained;
222
223 /**
224  * Value saying if hostlist download was successful
225  */
226 static unsigned int download_successful;
227
228 /**
229  * Process downloaded bits by calling callback on each HELLO.
230  *
231  * @param ptr buffer with downloaded data
232  * @param size size of a record
233  * @param nmemb number of records downloaded
234  * @param ctx unused
235  * @return number of bytes that were processed (always size*nmemb)
236  */
237 static size_t
238 download_hostlist_processor (void *ptr, 
239                              size_t size, 
240                              size_t nmemb, 
241                              void *ctx)
242 {
243   const char * cbuf = ptr;
244   const struct GNUNET_MessageHeader *msg;
245   size_t total;
246   size_t cpy;
247   size_t left;
248   uint16_t msize;
249
250   total = size * nmemb;
251   if ( (total == 0) || (bogus_url) )
252     {
253       return total;  /* ok, no data or bogus data */
254     }
255   GNUNET_STATISTICS_update (stats, 
256                             gettext_noop ("# bytes downloaded from hostlist servers"), 
257                             (int64_t) total, 
258                             GNUNET_NO);  
259   left = total;
260   while ( (left > 0) ||
261           (download_pos > 0) )
262     {
263       cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
264       memcpy (&download_buffer[download_pos],
265               cbuf,
266               cpy);      
267       cbuf += cpy;
268       download_pos += cpy;
269       left -= cpy;
270       if (download_pos < sizeof(struct GNUNET_MessageHeader))
271         {
272           GNUNET_assert (left == 0);
273           break;
274         }
275       msg = (const struct GNUNET_MessageHeader *) download_buffer;
276       msize = ntohs(msg->size);
277       if (msize < sizeof(struct GNUNET_MessageHeader))
278         {        
279           GNUNET_STATISTICS_update (stats, 
280                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
281                                     1, 
282                                     GNUNET_NO);  
283           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284                       _("Invalid `%s' message received from hostlist at `%s'\n"),
285                       "HELLO",
286                       current_url); 
287           bogus_url = 1;
288           return total;
289         }
290       if (download_pos < msize)
291         {
292           GNUNET_assert (left == 0);
293           break;
294         }
295       if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
296         {
297 #if DEBUG_HOSTLIST_CLIENT
298           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299                       "Received valid `%s' message from hostlist server.\n",
300                       "HELLO");
301 #endif
302           GNUNET_STATISTICS_update (stats, 
303                                     gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
304                                     1, 
305                                     GNUNET_NO);
306           hellos_obtained++;
307           GNUNET_TRANSPORT_offer_hello (transport, msg);
308         }
309       else
310         {
311           GNUNET_STATISTICS_update (stats, 
312                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
313                                     1, 
314                                     GNUNET_NO);  
315           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
316                       _("Invalid `%s' message received from hostlist at `%s'\n"),
317                       "HELLO",
318                       current_url);
319           bogus_url = GNUNET_YES;
320           return total;
321         }
322       memmove (download_buffer,
323                &download_buffer[msize],
324                download_pos - msize);
325       download_pos -= msize;
326     }
327   return total;
328 }
329
330
331 /**
332  * Obtain a hostlist URL that we should use.
333  *
334  * @return NULL if there is no URL available
335  */
336 static char *
337 get_bootstrap_url ()
338 {
339   char *servers;
340   char *ret;
341   size_t urls;
342   size_t pos;
343
344   if (GNUNET_OK != 
345       GNUNET_CONFIGURATION_get_value_string (cfg,
346                                              "HOSTLIST",
347                                              "SERVERS",
348                                              &servers))
349     {
350       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
351                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
352                   "SERVERS", "HOSTLIST");
353       return NULL;
354     }
355
356   urls = 0;
357   if (strlen (servers) > 0)
358     {
359       urls++;
360       pos = strlen (servers) - 1;
361       while (pos > 0)
362         {
363           if (servers[pos] == ' ')
364             urls++;
365           pos--;
366         }
367     }
368   if (urls == 0)
369     {
370       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
372                   "SERVERS", "HOSTLIST");
373       GNUNET_free (servers);
374       return NULL;
375     }
376
377   urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
378   pos = strlen (servers) - 1;
379   while (pos > 0)
380     {
381       if (servers[pos] == ' ')
382         {
383           urls--;
384           servers[pos] = '\0';
385         }
386       if (urls == 0)
387         {
388           pos++;
389           break;
390         }
391       pos--;    
392     }
393   ret = GNUNET_strdup (&servers[pos]);
394   GNUNET_free (servers);
395   return ret;
396 }
397
398 /**
399  * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
400  * @return uri to use, NULL if there is no URL available
401  */
402 static char *
403 get_list_url ()
404 {
405   uint32_t index;
406   unsigned int counter;
407   struct Hostlist * pos;
408
409   if ( GNUNET_NO == learning)
410   {
411     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412                 "Using preconfigured bootstrap server\n");
413     current_hostlist = NULL;
414     return get_bootstrap_url();
415   }
416   if ( (GNUNET_YES == use_preconfigured_list) ||
417        (linked_list_size == 0) )
418   {
419     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420                 "Using preconfigured bootstrap server\n");
421     current_hostlist = NULL;
422     return get_bootstrap_url();
423   }
424   index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
425   counter = 0;
426   pos = linked_list_head;
427   while ( counter < index )
428     {
429       pos = pos->next;
430       counter ++;
431     }
432   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433               "Using learned hostlist `%s'\n", pos->hostlist_uri);
434   current_hostlist = pos;
435   return strdup(pos->hostlist_uri);
436 }
437
438
439 #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);
440
441
442 /**
443  * Schedule the background task that will (possibly)
444  * download a hostlist.
445  */
446 static void
447 schedule_hostlist_task (void);
448
449 /**
450  * Method to load persistent hostlist file during hostlist client shutdown
451  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
452  */
453 static void save_hostlist_file ( int shutdown );
454
455 /**
456  * add val2 to val1 with overflow check
457  * @param val1 value 1
458  * @param val2 value 2
459  * @return result
460  */
461 static uint64_t checked_add (uint64_t val1, uint64_t val2)
462 {
463   static uint64_t temp;
464   static uint64_t maxv;
465
466   maxv = 0;
467   maxv--;
468
469   temp = val1+val2;
470   if ( temp < val1)
471     return maxv;
472   else
473     return temp;
474 }
475
476 /**
477  * Subtract val2 from val1 with underflow check
478  * @param val1 value 1
479  * @param val2 value 2
480  * @return result
481  */
482 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
483 {
484   if ( val1 <= val2)
485     return 0;
486   else
487     return (val1-val2);
488 }
489
490
491 /**
492  * Method updating hostlist statistics
493  */
494 static void update_hostlist ( )
495 {
496   char *stat;
497   if ( (use_preconfigured_list == GNUNET_NO) && ( NULL != current_hostlist ) )
498   {
499     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
500                 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
501      current_hostlist->hello_count = hellos_obtained;
502      current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
503      current_hostlist->quality = checked_add ( current_hostlist->quality, (hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
504      if ( GNUNET_YES == download_successful )
505      {
506        current_hostlist->times_used++;
507        current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
508        GNUNET_asprintf (&stat,
509                         gettext_noop("# advertised URI `%s' downloaded"),
510                         current_hostlist->hostlist_uri);
511
512        GNUNET_STATISTICS_update ( stats,
513                                   stat,
514                                   1,
515                                   GNUNET_YES);
516        GNUNET_free (stat);
517      }
518      else
519        current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
520   }
521   current_hostlist = NULL;
522   /* Alternating the usage of preconfigured and learned hostlists */
523
524   if ( GNUNET_YES == learning)
525     {
526     if (use_preconfigured_list == GNUNET_YES)
527       use_preconfigured_list = GNUNET_NO;
528     else
529       use_preconfigured_list = GNUNET_YES;
530     }
531   else
532     use_preconfigured_list = GNUNET_YES;
533 }
534
535 /**
536  * Clean up the state from the task that downloaded the
537  * hostlist and schedule the next task.
538  */
539 static void 
540 clean_up ()
541 {
542   CURLMcode mret;
543
544   if (multi != NULL)
545     {
546       mret = curl_multi_remove_handle (multi, curl);
547       if (mret != CURLM_OK)
548         {
549           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
550                       _("%s failed at %s:%d: `%s'\n"),
551                       "curl_multi_remove_handle", __FILE__, __LINE__,
552                       curl_multi_strerror (mret));
553         }
554       mret = curl_multi_cleanup (multi);
555       if (mret != CURLM_OK)
556         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
557                     _("%s failed at %s:%d: `%s'\n"),
558                     "curl_multi_cleanup", __FILE__, __LINE__,
559                     curl_multi_strerror (mret));
560       multi = NULL;
561     }
562   if (curl != NULL)
563     {
564       curl_easy_cleanup (curl);
565       curl = NULL;
566     }  
567   GNUNET_free_non_null (current_url);
568   current_url = NULL;
569
570   schedule_hostlist_task ();
571 }
572
573 /**
574  * Task that is run when we are ready to receive more data from the hostlist
575  * server.
576  *
577  * @param cls closure, unused
578  * @param tc task context, unused
579  */
580 static void
581 multi_ready (void *cls,
582              const struct GNUNET_SCHEDULER_TaskContext *tc);
583
584 /**
585  * Ask CURL for the select set and then schedule the
586  * receiving task with the scheduler.
587  */
588 static void
589 run_multi ()
590 {
591   CURLMcode mret;
592   fd_set rs;
593   fd_set ws;
594   fd_set es;
595   int max;
596   struct GNUNET_NETWORK_FDSet *grs;
597   struct GNUNET_NETWORK_FDSet *gws;
598   long timeout;
599   struct GNUNET_TIME_Relative rtime;
600
601   max = -1;
602   FD_ZERO (&rs);
603   FD_ZERO (&ws);
604   FD_ZERO (&es);
605   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
606   if (mret != CURLM_OK)
607     {
608       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
609                   _("%s failed at %s:%d: `%s'\n"),
610                   "curl_multi_fdset", __FILE__, __LINE__,
611                   curl_multi_strerror (mret));
612       clean_up ();
613       return;
614     }
615   mret = curl_multi_timeout (multi, &timeout);
616   if (mret != CURLM_OK)
617     {
618       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619                   _("%s failed at %s:%d: `%s'\n"),
620                   "curl_multi_timeout", __FILE__, __LINE__,
621                   curl_multi_strerror (mret));
622       clean_up ();
623       return;
624     }
625   rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
626                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
627                                                                    timeout));
628   grs = GNUNET_NETWORK_fdset_create ();
629   gws = GNUNET_NETWORK_fdset_create ();
630   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
631   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
632 #if DEBUG_HOSTLIST_CLIENT
633   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634               "Scheduling task for hostlist download using cURL\n");
635 #endif
636   current_task
637     = GNUNET_SCHEDULER_add_select (sched,
638                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
639                                    GNUNET_SCHEDULER_NO_TASK,
640                                    rtime,
641                                    grs,
642                                    gws,
643                                    &multi_ready,
644                                    multi);
645   GNUNET_NETWORK_fdset_destroy (gws);
646   GNUNET_NETWORK_fdset_destroy (grs);
647 }
648
649
650 /**
651  * Task that is run when we are ready to receive more data from the hostlist
652  * server. 
653  *
654  * @param cls closure, unused
655  * @param tc task context, unused
656  */
657 static void
658 multi_ready (void *cls,
659              const struct GNUNET_SCHEDULER_TaskContext *tc)
660 {
661   unsigned int counter;
662   int running;
663   struct CURLMsg *msg;
664   CURLMcode mret;
665   
666   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
667     {
668 #if DEBUG_HOSTLIST_CLIENT
669       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
670                   "Shutdown requested while trying to download hostlist from `%s'\n",
671                   current_url);
672 #endif
673       update_hostlist();
674       clean_up ();
675       return;
676     }
677   if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
678     {
679       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
680                   _("Timeout trying to download hostlist from `%s'\n"),
681                   current_url);
682       update_hostlist();
683       clean_up ();
684       return;
685     }
686 #if DEBUG_HOSTLIST_CLIENT
687   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
688               "Ready for processing hostlist client request\n");
689 #endif
690   counter = 0;
691   do 
692     {
693       running = 0;
694       mret = curl_multi_perform (multi, &running);
695       if (running == 0)
696         {
697           do
698             {
699
700               msg = curl_multi_info_read (multi, &running);
701               /*
702               if (counter >= MAX_HELLO_PER_HOSTLISTS)
703               {
704                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
705                             _("Maximum number of %u HELLOs per hostlist exceeded, stopping download...\n"),
706                             MAX_HELLO_PER_HOSTLISTS);
707                 clean_up ();
708                 break;
709               }*/
710               counter ++;
711               GNUNET_break (msg != NULL);
712               if (msg == NULL)
713                 break;
714               switch (msg->msg)
715                 {
716                 case CURLMSG_DONE:
717                   if ( (msg->data.result != CURLE_OK) &&
718                        (msg->data.result != CURLE_GOT_NOTHING) )                       
719                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
720                                _("%s failed for `%s' at %s:%d: `%s'\n"),
721                                "curl_multi_perform", 
722                                current_url,
723                                __FILE__,
724                                __LINE__,
725                                curl_easy_strerror (msg->data.result));            
726                   else
727                     {
728                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
729                                 _("Download of hostlist `%s' completed.\n"),
730                                 current_url);
731                     download_successful = GNUNET_YES;
732                     update_hostlist();
733                     }
734                   clean_up ();
735                   return;
736                 default:
737                   break;
738                 }
739
740             }
741           while (running > 0);
742         }
743     }
744   while (mret == CURLM_CALL_MULTI_PERFORM);
745   if (mret != CURLM_OK)
746     {
747       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
748                   _("%s failed at %s:%d: `%s'\n"),
749                   "curl_multi_perform", __FILE__, __LINE__,
750                   curl_multi_strerror (mret));
751       clean_up ();
752     }
753   run_multi ();
754 }
755
756
757 /**
758  * Main function that will download a hostlist and process its
759  * data.
760  */
761 static void
762 download_hostlist () 
763 {
764   CURLcode ret;
765   CURLMcode mret;
766
767   current_url = get_list_url ();
768   if (current_url == NULL)
769     return;
770   curl = curl_easy_init ();
771   multi = NULL;
772   if (curl == NULL)
773     {
774       GNUNET_break (0);
775       clean_up ();
776       return;
777     }
778   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
779               _("Bootstrapping using hostlist at `%s'.\n"), 
780               current_url);
781   hellos_obtained = 0;
782   download_successful = GNUNET_NO;
783   GNUNET_STATISTICS_update (stats, 
784                             gettext_noop ("# hostlist downloads initiated"), 
785                             1, 
786                             GNUNET_NO);  
787   if (proxy != NULL)
788     CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);    
789   download_pos = 0;
790   bogus_url = 0;
791   CURL_EASY_SETOPT (curl,
792                     CURLOPT_WRITEFUNCTION, 
793                     &download_hostlist_processor);
794   if (ret != CURLE_OK)
795     {
796       clean_up ();
797       return;
798     }
799   CURL_EASY_SETOPT (curl,
800                     CURLOPT_WRITEDATA, 
801                     NULL);
802   if (ret != CURLE_OK)
803     {
804       clean_up ();
805       return;
806     }
807   CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
808   CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
809   /* no need to abort if the above failed */
810   CURL_EASY_SETOPT (curl, 
811                     CURLOPT_URL, 
812                     current_url);
813   if (ret != CURLE_OK)
814     {
815       clean_up ();
816       return;
817     }
818   CURL_EASY_SETOPT (curl, 
819                     CURLOPT_FAILONERROR, 
820                     1);
821 #if 0
822   CURL_EASY_SETOPT (curl, 
823                     CURLOPT_VERBOSE, 
824                     1);
825 #endif
826   CURL_EASY_SETOPT (curl, 
827                     CURLOPT_BUFFERSIZE, 
828                     GNUNET_SERVER_MAX_MESSAGE_SIZE);
829   if (0 == strncmp (current_url, "http", 4))
830     CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
831   CURL_EASY_SETOPT (curl, 
832                     CURLOPT_CONNECTTIMEOUT, 
833                     60L);
834   CURL_EASY_SETOPT (curl, 
835                     CURLOPT_TIMEOUT, 
836                     60L);
837 #if 0
838   /* this should no longer be needed; we're now single-threaded! */
839   CURL_EASY_SETOPT (curl,
840                     CURLOPT_NOSIGNAL, 
841                     1);
842 #endif
843   multi = curl_multi_init ();
844   if (multi == NULL)
845     {
846       GNUNET_break (0);
847       clean_up ();
848       return;
849     }
850   mret = curl_multi_add_handle (multi, curl);
851   if (mret != CURLM_OK)
852     {
853       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
854                   _("%s failed at %s:%d: `%s'\n"),
855                   "curl_multi_add_handle", __FILE__, __LINE__,
856                   curl_multi_strerror (mret));
857       mret = curl_multi_cleanup (multi);
858       if (mret != CURLM_OK)
859         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
860                     _("%s failed at %s:%d: `%s'\n"),
861                     "curl_multi_cleanup", __FILE__, __LINE__,
862                     curl_multi_strerror (mret));
863       multi = NULL;
864       clean_up ();
865       return;
866     }
867   end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
868   run_multi ();
869 }  
870
871
872 /**
873  * Task that checks if we should try to download a hostlist.
874  * If so, we initiate the download, otherwise we schedule
875  * this task again for a later time.
876  */
877 static void
878 check_task (void *cls,
879             const struct GNUNET_SCHEDULER_TaskContext *tc)
880 {
881   current_task = GNUNET_SCHEDULER_NO_TASK;
882   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
883     return;
884   if (connection_count < MIN_CONNECTIONS)
885     download_hostlist ();
886   else
887     schedule_hostlist_task ();
888 }
889
890
891 /**
892  * Compute when we should check the next time about downloading
893  * a hostlist; then schedule the task accordingly.
894  */
895 static void
896 schedule_hostlist_task ()
897 {
898   static int once;
899   struct GNUNET_TIME_Relative delay;
900
901   if (stats == NULL)
902     {
903       curl_global_cleanup ();
904       return; /* in shutdown */
905     }
906   delay = hostlist_delay;
907   if (hostlist_delay.value == 0)
908     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
909   else
910     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
911   if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
912     hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
913                                                     (1 + connection_count));
914   GNUNET_STATISTICS_set (stats,
915                          gettext_noop("# seconds between hostlist downloads"),
916                          hostlist_delay.value,
917                          GNUNET_YES);
918   if (0 == once)
919     {
920       delay = GNUNET_TIME_UNIT_ZERO;
921       once = 1;
922     }  
923   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
924               _("Have %u/%u connections.  Will consider downloading hostlist in %llums\n"),
925               connection_count,
926               MIN_CONNECTIONS,
927               (unsigned long long) delay.value);
928   current_task = GNUNET_SCHEDULER_add_delayed (sched,
929                                                delay,
930                                                &check_task,
931                                                NULL);
932 }
933
934 /**
935  * Task that writes hostlist entries to a file on a regular base
936  * cls closure
937  * tc TaskContext
938  */
939 static void
940 hostlist_saving_task (void *cls,
941             const struct GNUNET_SCHEDULER_TaskContext *tc)
942 {
943   saving_task = GNUNET_SCHEDULER_NO_TASK;
944   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
945     return;
946   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
947               _("Scheduled saving of hostlists\n"));
948   save_hostlist_file ( GNUNET_NO );
949
950   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
951               _("Hostlists will be saved to file again in %llums\n"),
952               (unsigned long long) SAVING_INTERVALL.value);
953   saving_task = GNUNET_SCHEDULER_add_delayed (sched,
954                                                SAVING_INTERVALL,
955                                                &hostlist_saving_task,
956                                                NULL);
957 }
958
959 /**
960  * Method called whenever a given peer connects.
961  *
962  * @param cls closure
963  * @param peer peer identity this notification is about
964  * @param latency reported latency of the connection with 'other'
965  * @param distance reported distance (DV) to 'other' 
966  */
967 static void
968 connect_handler (void *cls,
969                  const struct
970                  GNUNET_PeerIdentity * peer,
971                  struct GNUNET_TIME_Relative latency,
972                  uint32_t distance)
973 {
974   connection_count++;
975   GNUNET_STATISTICS_update (stats, 
976                             gettext_noop ("# active connections"), 
977                             1, 
978                             GNUNET_NO);  
979 }
980
981
982 /**
983  * Method called whenever a given peer disconnects.
984  *
985  * @param cls closure
986  * @param peer peer identity this notification is about
987  */
988 static void
989 disconnect_handler (void *cls,
990                     const struct
991                     GNUNET_PeerIdentity * peer)
992 {
993   connection_count--;
994   GNUNET_STATISTICS_update (stats, 
995                             gettext_noop ("# active connections"), 
996                             -1, 
997                             GNUNET_NO);  
998 }
999
1000
1001 /**
1002  * Method to check if URI is in hostlist linked list
1003  * @param uri uri to check
1004  * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
1005  */
1006 static int 
1007 linked_list_contains (const char * uri)
1008 {
1009   struct Hostlist * pos;
1010
1011   pos = linked_list_head;
1012   while (pos != NULL)
1013     {
1014       if (0 == strcmp(pos->hostlist_uri, uri) ) 
1015         return GNUNET_YES;
1016       pos = pos->next;
1017     }
1018   return GNUNET_NO;
1019 }
1020
1021
1022 /* linked_list_? */
1023 static struct Hostlist *
1024 linked_list_get_lowest_quality ( )
1025 {
1026   struct Hostlist * pos;
1027   struct Hostlist * lowest;
1028
1029   if (linked_list_size == 0)
1030     return NULL;
1031   lowest = linked_list_head;
1032   pos = linked_list_head->next;
1033   while (pos != NULL)
1034     {
1035       if (pos->quality < lowest->quality) 
1036         lowest = pos;
1037       pos = pos->next;
1038     }
1039   return lowest;
1040 }
1041
1042 /**
1043  * Task that checks if we should try to download a hostlist.
1044  * If so, we initiate the download, otherwise we schedule
1045  * this task again for a later time.
1046  */
1047 static void
1048 check_hostlist_task (void *cls,
1049             const struct GNUNET_SCHEDULER_TaskContext *tc)
1050 {
1051   GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
1052   linked_list_size++;
1053
1054   GNUNET_STATISTICS_set (stats,
1055                          gettext_noop("# advertised hostlist URIs"),
1056                          linked_list_size,
1057                          GNUNET_NO);
1058
1059   if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
1060     return;
1061
1062   /* No free entries available, replace existing entry  */
1063   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1064               "Removing lowest quality entry\n" );
1065   struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
1066   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1067               "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
1068               lowest_quality->hostlist_uri,
1069               (unsigned long long) lowest_quality->quality);
1070   GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
1071   linked_list_size--;
1072
1073   GNUNET_STATISTICS_set (stats,
1074                          gettext_noop("# advertised hostlist URIs"),
1075                          linked_list_size,
1076                          GNUNET_NO);
1077
1078   GNUNET_free (lowest_quality);
1079
1080   test_locked = GNUNET_NO;
1081   return;
1082 }
1083
1084 /**
1085  * Method called whenever an advertisement message arrives.
1086  *
1087  * @param cls closure (always NULL)
1088  * @param peer the peer sending the message
1089  * @param message the actual message
1090  * @param latency latency
1091  * @param distance distance
1092  * @return GNUNET_OK to keep the connection open,
1093  *         GNUNET_SYSERR to close it (signal serious error)
1094  */
1095 static int
1096 advertisement_handler (void *cls,
1097     const struct GNUNET_PeerIdentity * peer,
1098     const struct GNUNET_MessageHeader * message,
1099     struct GNUNET_TIME_Relative latency,
1100     uint32_t distance)
1101 {
1102   size_t size;
1103   size_t uri_size;
1104   const struct GNUNET_MessageHeader * incoming;
1105   const char *uri;
1106   struct Hostlist * hostlist;
1107
1108   GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1109   size = ntohs (message->size);
1110   if (size <= sizeof(struct GNUNET_MessageHeader))
1111     {
1112       GNUNET_break_op (0);
1113       return GNUNET_SYSERR;
1114     }
1115   incoming = (const struct GNUNET_MessageHeader *) message;
1116   uri = (const char*) &incoming[1];
1117   uri_size = size - sizeof (struct GNUNET_MessageHeader);
1118   if (uri [uri_size - 1] != '\0')
1119     {
1120       GNUNET_break_op (0);
1121       return GNUNET_SYSERR;
1122     }
1123   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1124               "Hostlist client recieved advertisement from `%s' containing URI `%s'\n", 
1125               GNUNET_i2s (peer), 
1126               uri);
1127   if (GNUNET_NO != linked_list_contains (uri))
1128     {
1129       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1130                 "URI `%s' is already known\n",
1131                 uri);
1132       return GNUNET_OK;
1133     }
1134
1135   if ( GNUNET_YES == test_locked )
1136     {
1137       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138                 "Currently not accepting adverts\n");
1139       return GNUNET_OK;
1140     }
1141
1142   hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1143   hostlist->hostlist_uri = (const char*) &hostlist[1];
1144   memcpy (&hostlist[1], uri, uri_size);
1145   hostlist->time_creation = GNUNET_TIME_absolute_get();
1146   hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1147   hostlist->quality = HOSTLIST_INITIAL;
1148   hostlist_to_test = hostlist;
1149
1150   test_locked = GNUNET_YES;
1151
1152
1153   GNUNET_SCHEDULER_add_now (sched,
1154                            &check_hostlist_task,
1155                            NULL);
1156 }
1157
1158
1159
1160 /**
1161  * Continuation called by the statistics code once 
1162  * we go the stat.  Initiates hostlist download scheduling.
1163  *
1164  * @param cls closure
1165  * @param success GNUNET_OK if statistics were
1166  *        successfully obtained, GNUNET_SYSERR if not.
1167  */
1168 static void
1169 primary_task (void *cls, int success)
1170 {
1171   if (stats == NULL)
1172     return; /* in shutdown */
1173 #if DEBUG_HOSTLIST_CLIENT
1174   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1175               "Statistics request done, scheduling hostlist download\n");
1176 #endif
1177   schedule_hostlist_task ();
1178 }
1179
1180
1181 static int
1182 process_stat (void *cls,
1183               const char *subsystem,
1184               const char *name,
1185               uint64_t value,
1186               int is_persistent)
1187 {
1188   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1189               _("Initial time between hostlist downloads is %llums\n"),
1190               (unsigned long long) value);
1191   hostlist_delay.value = value;
1192   return GNUNET_OK;
1193 }
1194
1195 /**
1196  * Method to load persistent hostlist file during hostlist client startup
1197  */
1198 static void 
1199 load_hostlist_file ()
1200 {
1201   char *filename;
1202   char *uri;
1203   char *emsg;
1204   struct Hostlist * hostlist;
1205   uri = NULL;
1206   uint32_t times_used;
1207   uint32_t hellos_returned;
1208   uint64_t quality;
1209   uint64_t last_used;
1210   uint64_t created;
1211   uint32_t counter;
1212
1213   if (GNUNET_OK !=
1214       GNUNET_CONFIGURATION_get_value_string (cfg,
1215                                              "HOSTLIST",
1216                                              "HOSTLISTFILE",
1217                                              &filename))
1218     {
1219       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1220                   _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1221                   "HOSTLISTFILE", "HOSTLIST");
1222       return;
1223     }
1224
1225   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1226               _("Loading saved hostlist entries from file `%s' \n"), filename);
1227   if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1228   {
1229     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1230                 _("Hostlist file `%s' is not existing\n"), filename);
1231     GNUNET_free ( filename );
1232     return;
1233   }
1234
1235   struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1236   if (NULL == rh)
1237     {
1238       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1239                   _("Could not open file `%s' for reading to load hostlists: %s\n"), 
1240                   filename,
1241                   STRERROR (errno));
1242       GNUNET_free (filename);
1243       return;
1244     }
1245
1246   counter = 0;
1247   while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1248           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used)) &&
1249           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1250           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1251           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1252           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1253     {
1254       hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1255       hostlist->hello_count = hellos_returned;
1256       hostlist->hostlist_uri = (const char *) &hostlist[1];
1257       memcpy (&hostlist[1], uri, strlen(uri)+1);
1258       hostlist->quality = quality;
1259       hostlist->time_creation.value = created;
1260       hostlist->time_last_usage.value = last_used;
1261       GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1262       linked_list_size++;
1263       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1264                   "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1265       GNUNET_free (uri);
1266       uri = NULL;
1267       counter++;
1268       if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1269     }
1270
1271   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1272               _("%u hostlist URIs loaded from file\n"), counter);
1273   GNUNET_STATISTICS_set (stats,
1274                          gettext_noop("# hostlist URIs read from file"),
1275                          counter,
1276                          GNUNET_YES);
1277   GNUNET_STATISTICS_set (stats,
1278                          gettext_noop("# advertised hostlist URIs"),
1279                          linked_list_size,
1280                          GNUNET_NO);
1281
1282   GNUNET_free_non_null (uri);
1283   emsg = NULL;
1284   GNUNET_BIO_read_close (rh, &emsg);
1285   if (emsg != NULL)
1286     GNUNET_free (emsg);
1287   GNUNET_free (filename);
1288 }
1289
1290
1291 /**
1292  * Method to save persistent hostlist file during hostlist client shutdown
1293  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1294  */
1295 static void save_hostlist_file ( int shutdown )
1296 {
1297   char *filename;
1298   struct Hostlist *pos;
1299   struct GNUNET_BIO_WriteHandle * wh;
1300   int ok;
1301   uint32_t counter;
1302
1303   if (GNUNET_OK !=
1304       GNUNET_CONFIGURATION_get_value_string (cfg,
1305                                              "HOSTLIST",
1306                                              "HOSTLISTFILE",
1307                                              &filename))
1308     {
1309       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1310                   _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1311                   "HOSTLISTFILE", "HOSTLIST");
1312       return;
1313     }
1314   wh = GNUNET_BIO_write_open (filename);
1315   if ( NULL == wh)
1316     {
1317       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1318                   _("Could not open file `%s' for writing to save hostlists: %s\n"),
1319                   filename,
1320                   STRERROR (errno));
1321       GNUNET_free (filename);
1322       return;
1323     }
1324   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1325               _("Writing %u hostlist URIs to `%s'\n" ),
1326               linked_list_size, filename);
1327
1328   /* add code to write hostlists to file using bio */
1329   ok = GNUNET_YES;
1330   counter = 0;
1331   while (NULL != (pos = linked_list_head))
1332     {
1333       if ( GNUNET_YES == shutdown)
1334       {
1335         GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1336         linked_list_size--;
1337       }
1338       if (GNUNET_YES == ok)
1339         {
1340           if ( (GNUNET_OK !=
1341                 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1342                (GNUNET_OK !=
1343                 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1344                (GNUNET_OK !=
1345                 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1346                (GNUNET_OK !=
1347                 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1348                (GNUNET_OK !=
1349                 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1350                (GNUNET_OK !=
1351                 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1352             {
1353               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1354                           _("Error writing hostlist URIs to file `%s'\n"),
1355                           filename);
1356               ok = GNUNET_NO;
1357             }
1358         }
1359
1360       if ( GNUNET_YES == shutdown)
1361         GNUNET_free (pos);
1362       counter ++;
1363       if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1364     }  
1365   GNUNET_STATISTICS_set (stats,
1366                          gettext_noop("# hostlist URIs written to file"),
1367                          counter,
1368                          GNUNET_YES);
1369
1370   if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1371     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1372                 _("Error writing hostlist URIs to file `%s'\n"),
1373                 filename);
1374   GNUNET_free (filename);
1375 }
1376
1377 /**
1378  * Start downloading hostlists from hostlist servers as necessary.
1379  */
1380 int
1381 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1382                               struct GNUNET_SCHEDULER_Handle *s,
1383                               struct GNUNET_STATISTICS_Handle *st,
1384                               GNUNET_CORE_ConnectEventHandler *ch,
1385                               GNUNET_CORE_DisconnectEventHandler *dh,
1386                               GNUNET_CORE_MessageCallback *msgh,
1387                               int learn)
1388 {
1389   char *filename;
1390   int result;
1391
1392   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1393     {
1394       GNUNET_break (0);
1395       return GNUNET_SYSERR;
1396     }
1397   transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1398   if (NULL == transport)
1399     {
1400       curl_global_cleanup ();
1401       return GNUNET_SYSERR;
1402     }
1403   cfg = c;
1404   sched = s;
1405   stats = st;
1406   if (GNUNET_OK !=
1407       GNUNET_CONFIGURATION_get_value_string (cfg,
1408                                              "HOSTLIST",
1409                                              "HTTP-PROXY", 
1410                                              &proxy))
1411     proxy = NULL;
1412   learning = learn;
1413   *ch = &connect_handler;
1414   *dh = &disconnect_handler;
1415   linked_list_head = NULL;
1416   linked_list_tail = NULL;
1417   use_preconfigured_list = GNUNET_YES;
1418   test_locked = GNUNET_NO;
1419
1420   if ( GNUNET_YES == learning )
1421   {
1422     *msgh = &advertisement_handler;
1423     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1424               _("Learning is enabled on this peer\n"));
1425     load_hostlist_file ();
1426     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1427               _("Hostlists will be saved to file again in  %llums\n"),
1428               (unsigned long long) SAVING_INTERVALL.value);
1429     saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1430                                                SAVING_INTERVALL,
1431                                                &hostlist_saving_task,
1432                                                NULL);
1433   }
1434   else
1435   {
1436     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1437               _("Learning is not enabled on this peer\n"));
1438     *msgh = NULL;
1439     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1440                                                             "HOSTLIST",
1441                                                             "HOSTLISTFILE",
1442                                                             &filename))
1443     {
1444     if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1445       {
1446         result = remove (filename);
1447         if (result == 0)
1448         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1449               _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1450         else
1451           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1452                 _("Hostlist file `%s' could not be removed\n"),filename);
1453       }
1454     }
1455     GNUNET_free ( filename );
1456   }
1457   GNUNET_STATISTICS_get (stats,
1458                          "hostlist",
1459                          gettext_noop("# seconds between hostlist downloads"),
1460                          GNUNET_TIME_UNIT_MINUTES,
1461                          &primary_task,
1462                          &process_stat,
1463                          NULL);
1464   return GNUNET_OK;
1465 }
1466
1467
1468 /**
1469  * Stop downloading hostlists from hostlist servers as necessary.
1470  */
1471 void
1472 GNUNET_HOSTLIST_client_stop ()
1473 {
1474 #if DEBUG_HOSTLIST_CLIENT
1475   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1476               "Hostlist client shutdown\n");
1477 #endif
1478   if ( GNUNET_YES == learning )
1479     save_hostlist_file ( GNUNET_YES );
1480
1481   if (current_task != GNUNET_SCHEDULER_NO_TASK)
1482     {
1483       GNUNET_SCHEDULER_cancel (sched,
1484                                current_task);
1485       curl_global_cleanup ();
1486     }
1487   if (transport != NULL)
1488     {
1489       GNUNET_TRANSPORT_disconnect (transport);
1490       transport = NULL;
1491     }
1492   GNUNET_assert (NULL == transport);
1493   GNUNET_free_non_null (proxy);
1494   proxy = NULL;
1495   cfg = NULL;
1496   sched = NULL;
1497 }
1498
1499 /* end of hostlist-client.c */