da4c29d710e0e7a673ec1df37aff135a5f269aec
[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 testing_hostlist;
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
417   if ( ( GNUNET_YES == testing_hostlist) && (NULL != hostlist_to_test) )
418   {
419     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420                 "Testing new advertised hostlist if it is obtainable\n");
421     return strdup(hostlist_to_test->hostlist_uri);
422   }
423
424   if ( (GNUNET_YES == use_preconfigured_list) ||
425        (linked_list_size == 0) )
426   {
427     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428                 "Using preconfigured bootstrap server\n");
429     current_hostlist = NULL;
430     return get_bootstrap_url();
431   }
432   index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
433   counter = 0;
434   pos = linked_list_head;
435   while ( counter < index )
436     {
437       pos = pos->next;
438       counter ++;
439     }
440   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441               "Using learned hostlist `%s'\n", pos->hostlist_uri);
442   current_hostlist = pos;
443   return strdup(pos->hostlist_uri);
444 }
445
446
447 #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);
448
449
450 /**
451  * Schedule the background task that will (possibly)
452  * download a hostlist.
453  */
454 static void
455 schedule_hostlist_task (void);
456
457 /**
458  * Method to load persistent hostlist file during hostlist client shutdown
459  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
460  */
461 static void save_hostlist_file ( int shutdown );
462
463 /**
464  * add val2 to val1 with overflow check
465  * @param val1 value 1
466  * @param val2 value 2
467  * @return result
468  */
469 static uint64_t checked_add (uint64_t val1, uint64_t val2)
470 {
471   static uint64_t temp;
472   static uint64_t maxv;
473
474   maxv = 0;
475   maxv--;
476
477   temp = val1+val2;
478   if ( temp < val1)
479     return maxv;
480   else
481     return temp;
482 }
483
484 /**
485  * Subtract val2 from val1 with underflow check
486  * @param val1 value 1
487  * @param val2 value 2
488  * @return result
489  */
490 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
491 {
492   if ( val1 <= val2)
493     return 0;
494   else
495     return (val1-val2);
496 }
497
498 /**
499  * Method to check if URI is in hostlist linked list
500  * @param uri uri to check
501  * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
502  */
503 static int
504 linked_list_contains (const char * uri)
505 {
506   struct Hostlist * pos;
507
508   pos = linked_list_head;
509   while (pos != NULL)
510     {
511       if (0 == strcmp(pos->hostlist_uri, uri) )
512         return GNUNET_YES;
513       pos = pos->next;
514     }
515   return GNUNET_NO;
516 }
517
518
519 /* linked_list_? */
520 static struct Hostlist *
521 linked_list_get_lowest_quality ( )
522 {
523   struct Hostlist * pos;
524   struct Hostlist * lowest;
525
526   if (linked_list_size == 0)
527     return NULL;
528   lowest = linked_list_head;
529   pos = linked_list_head->next;
530   while (pos != NULL)
531     {
532       if (pos->quality < lowest->quality)
533         lowest = pos;
534       pos = pos->next;
535     }
536   return lowest;
537 }
538
539
540 /**
541  * Task that checks if we should try to download a hostlist.
542  * If so, we initiate the download, otherwise we schedule
543  * this task again for a later time.
544  */
545 static void
546 insert_hostlist ( void )
547 {
548   GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
549   linked_list_size++;
550
551   GNUNET_STATISTICS_set (stats,
552                          gettext_noop("# advertised hostlist URIs"),
553                          linked_list_size,
554                          GNUNET_NO);
555
556   if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
557     return;
558
559   /* No free entries available, replace existing entry  */
560   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
561               "Removing lowest quality entry\n" );
562   struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564               "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
565               lowest_quality->hostlist_uri,
566               (unsigned long long) lowest_quality->quality);
567   GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
568   linked_list_size--;
569
570   GNUNET_STATISTICS_set (stats,
571                          gettext_noop("# advertised hostlist URIs"),
572                          linked_list_size,
573                          GNUNET_NO);
574
575   GNUNET_free (lowest_quality);
576
577   testing_hostlist = GNUNET_NO;
578   return;
579 }
580
581 /**
582  * Method updating hostlist statistics
583  */
584 static void update_hostlist ( )
585 {
586   char *stat;
587   if ( (use_preconfigured_list == GNUNET_NO) && ( NULL != current_hostlist ) )
588   {
589     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
590                 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
591      current_hostlist->hello_count = hellos_obtained;
592      current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
593      current_hostlist->quality = checked_add ( current_hostlist->quality, (hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
594      if ( GNUNET_YES == download_successful )
595      {
596        current_hostlist->times_used++;
597        current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
598        GNUNET_asprintf (&stat,
599                         gettext_noop("# advertised URI `%s' downloaded"),
600                         current_hostlist->hostlist_uri);
601
602        GNUNET_STATISTICS_update ( stats,
603                                   stat,
604                                   1,
605                                   GNUNET_YES);
606        GNUNET_free (stat);
607      }
608      else
609        current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
610   }
611   current_hostlist = NULL;
612   /* Alternating the usage of preconfigured and learned hostlists */
613
614   if ( GNUNET_YES == learning)
615     {
616     if (use_preconfigured_list == GNUNET_YES)
617       use_preconfigured_list = GNUNET_NO;
618     else
619       use_preconfigured_list = GNUNET_YES;
620     }
621   else
622     use_preconfigured_list = GNUNET_YES;
623 }
624
625 /**
626  * Clean up the state from the task that downloaded the
627  * hostlist and schedule the next task.
628  */
629 static void 
630 clean_up ()
631 {
632   CURLMcode mret;
633
634   if (multi != NULL)
635     {
636       mret = curl_multi_remove_handle (multi, curl);
637       if (mret != CURLM_OK)
638         {
639           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
640                       _("%s failed at %s:%d: `%s'\n"),
641                       "curl_multi_remove_handle", __FILE__, __LINE__,
642                       curl_multi_strerror (mret));
643         }
644       mret = curl_multi_cleanup (multi);
645       if (mret != CURLM_OK)
646         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
647                     _("%s failed at %s:%d: `%s'\n"),
648                     "curl_multi_cleanup", __FILE__, __LINE__,
649                     curl_multi_strerror (mret));
650       multi = NULL;
651     }
652   if (curl != NULL)
653     {
654       curl_easy_cleanup (curl);
655       curl = NULL;
656     }  
657   GNUNET_free_non_null (current_url);
658   current_url = NULL;
659
660   schedule_hostlist_task ();
661 }
662
663 /**
664  * Task that is run when we are ready to receive more data from the hostlist
665  * server.
666  *
667  * @param cls closure, unused
668  * @param tc task context, unused
669  */
670 static void
671 multi_ready (void *cls,
672              const struct GNUNET_SCHEDULER_TaskContext *tc);
673
674 /**
675  * Ask CURL for the select set and then schedule the
676  * receiving task with the scheduler.
677  */
678 static void
679 run_multi ()
680 {
681   CURLMcode mret;
682   fd_set rs;
683   fd_set ws;
684   fd_set es;
685   int max;
686   struct GNUNET_NETWORK_FDSet *grs;
687   struct GNUNET_NETWORK_FDSet *gws;
688   long timeout;
689   struct GNUNET_TIME_Relative rtime;
690
691   max = -1;
692   FD_ZERO (&rs);
693   FD_ZERO (&ws);
694   FD_ZERO (&es);
695   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
696   if (mret != CURLM_OK)
697     {
698       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
699                   _("%s failed at %s:%d: `%s'\n"),
700                   "curl_multi_fdset", __FILE__, __LINE__,
701                   curl_multi_strerror (mret));
702       clean_up ();
703       return;
704     }
705   mret = curl_multi_timeout (multi, &timeout);
706   if (mret != CURLM_OK)
707     {
708       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
709                   _("%s failed at %s:%d: `%s'\n"),
710                   "curl_multi_timeout", __FILE__, __LINE__,
711                   curl_multi_strerror (mret));
712       clean_up ();
713       return;
714     }
715   rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
716                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
717                                                                    timeout));
718   grs = GNUNET_NETWORK_fdset_create ();
719   gws = GNUNET_NETWORK_fdset_create ();
720   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
721   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
722 #if DEBUG_HOSTLIST_CLIENT
723   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
724               "Scheduling task for hostlist download using cURL\n");
725 #endif
726   current_task
727     = GNUNET_SCHEDULER_add_select (sched,
728                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
729                                    GNUNET_SCHEDULER_NO_TASK,
730                                    rtime,
731                                    grs,
732                                    gws,
733                                    &multi_ready,
734                                    multi);
735   GNUNET_NETWORK_fdset_destroy (gws);
736   GNUNET_NETWORK_fdset_destroy (grs);
737 }
738
739
740 /**
741  * Task that is run when we are ready to receive more data from the hostlist
742  * server. 
743  *
744  * @param cls closure, unused
745  * @param tc task context, unused
746  */
747 static void
748 multi_ready (void *cls,
749              const struct GNUNET_SCHEDULER_TaskContext *tc)
750 {
751   unsigned int counter;
752   int running;
753   struct CURLMsg *msg;
754   CURLMcode mret;
755   
756   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
757     {
758 #if DEBUG_HOSTLIST_CLIENT
759       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
760                   "Shutdown requested while trying to download hostlist from `%s'\n",
761                   current_url);
762 #endif
763       update_hostlist();
764       clean_up ();
765       return;
766     }
767   if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
768     {
769       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
770                   _("Timeout trying to download hostlist from `%s'\n"),
771                   current_url);
772       update_hostlist();
773       clean_up ();
774       return;
775     }
776 #if DEBUG_HOSTLIST_CLIENT
777   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778               "Ready for processing hostlist client request\n");
779 #endif
780   counter = 0;
781   do 
782     {
783       running = 0;
784       mret = curl_multi_perform (multi, &running);
785       if (running == 0)
786         {
787           do
788             {
789
790               msg = curl_multi_info_read (multi, &running);
791               /*
792               if (counter >= MAX_HELLO_PER_HOSTLISTS)
793               {
794                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
795                             _("Maximum number of %u HELLOs per hostlist exceeded, stopping download...\n"),
796                             MAX_HELLO_PER_HOSTLISTS);
797                 clean_up ();
798                 break;
799               }*/
800               counter ++;
801               GNUNET_break (msg != NULL);
802               if (msg == NULL)
803                 break;
804               switch (msg->msg)
805                 {
806                 case CURLMSG_DONE:
807                   if ( (msg->data.result != CURLE_OK) &&
808                        (msg->data.result != CURLE_GOT_NOTHING) )                       
809                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
810                                _("%s failed for `%s' at %s:%d: `%s'\n"),
811                                "curl_multi_perform", 
812                                current_url,
813                                __FILE__,
814                                __LINE__,
815                                curl_easy_strerror (msg->data.result));            
816                   else
817                     {
818                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
819                                 _("Download of hostlist `%s' completed.\n"),
820                                 current_url);
821                     download_successful = GNUNET_YES;
822                     if (GNUNET_YES == testing_hostlist)
823                      {
824                       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
825                                         _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
826                       insert_hostlist();
827                       testing_hostlist = GNUNET_NO;
828                      }
829                     update_hostlist();
830                     }
831                   clean_up ();
832                   return;
833                 default:
834                   break;
835                 }
836
837             }
838           while (running > 0);
839         }
840     }
841   while (mret == CURLM_CALL_MULTI_PERFORM);
842   if (mret != CURLM_OK)
843     {
844       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
845                   _("%s failed at %s:%d: `%s'\n"),
846                   "curl_multi_perform", __FILE__, __LINE__,
847                   curl_multi_strerror (mret));
848       clean_up ();
849     }
850   run_multi ();
851 }
852
853
854 /**
855  * Main function that will download a hostlist and process its
856  * data.
857  */
858 static void
859 download_hostlist () 
860 {
861   CURLcode ret;
862   CURLMcode mret;
863
864   current_url = get_list_url ();
865   if (current_url == NULL)
866     return;
867   curl = curl_easy_init ();
868   multi = NULL;
869   if (curl == NULL)
870     {
871       GNUNET_break (0);
872       clean_up ();
873       return;
874     }
875   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
876               _("Bootstrapping using hostlist at `%s'.\n"), 
877               current_url);
878   hellos_obtained = 0;
879   download_successful = GNUNET_NO;
880   GNUNET_STATISTICS_update (stats, 
881                             gettext_noop ("# hostlist downloads initiated"), 
882                             1, 
883                             GNUNET_NO);  
884   if (proxy != NULL)
885     CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);    
886   download_pos = 0;
887   bogus_url = 0;
888   CURL_EASY_SETOPT (curl,
889                     CURLOPT_WRITEFUNCTION, 
890                     &download_hostlist_processor);
891   if (ret != CURLE_OK)
892     {
893       clean_up ();
894       return;
895     }
896   CURL_EASY_SETOPT (curl,
897                     CURLOPT_WRITEDATA, 
898                     NULL);
899   if (ret != CURLE_OK)
900     {
901       clean_up ();
902       return;
903     }
904   CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
905   CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
906   /* no need to abort if the above failed */
907   CURL_EASY_SETOPT (curl, 
908                     CURLOPT_URL, 
909                     current_url);
910   if (ret != CURLE_OK)
911     {
912       clean_up ();
913       return;
914     }
915   CURL_EASY_SETOPT (curl, 
916                     CURLOPT_FAILONERROR, 
917                     1);
918 #if 0
919   CURL_EASY_SETOPT (curl, 
920                     CURLOPT_VERBOSE, 
921                     1);
922 #endif
923   CURL_EASY_SETOPT (curl, 
924                     CURLOPT_BUFFERSIZE, 
925                     GNUNET_SERVER_MAX_MESSAGE_SIZE);
926   if (0 == strncmp (current_url, "http", 4))
927     CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
928   CURL_EASY_SETOPT (curl, 
929                     CURLOPT_CONNECTTIMEOUT, 
930                     60L);
931   CURL_EASY_SETOPT (curl, 
932                     CURLOPT_TIMEOUT, 
933                     60L);
934 #if 0
935   /* this should no longer be needed; we're now single-threaded! */
936   CURL_EASY_SETOPT (curl,
937                     CURLOPT_NOSIGNAL, 
938                     1);
939 #endif
940   multi = curl_multi_init ();
941   if (multi == NULL)
942     {
943       GNUNET_break (0);
944       clean_up ();
945       return;
946     }
947   mret = curl_multi_add_handle (multi, curl);
948   if (mret != CURLM_OK)
949     {
950       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
951                   _("%s failed at %s:%d: `%s'\n"),
952                   "curl_multi_add_handle", __FILE__, __LINE__,
953                   curl_multi_strerror (mret));
954       mret = curl_multi_cleanup (multi);
955       if (mret != CURLM_OK)
956         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
957                     _("%s failed at %s:%d: `%s'\n"),
958                     "curl_multi_cleanup", __FILE__, __LINE__,
959                     curl_multi_strerror (mret));
960       multi = NULL;
961       clean_up ();
962       return;
963     }
964   end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
965   run_multi ();
966 }  
967
968
969 /**
970  * Task that checks if we should try to download a hostlist.
971  * If so, we initiate the download, otherwise we schedule
972  * this task again for a later time.
973  */
974 static void
975 check_task (void *cls,
976             const struct GNUNET_SCHEDULER_TaskContext *tc)
977 {
978   current_task = GNUNET_SCHEDULER_NO_TASK;
979   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
980     return;
981   if (connection_count < MIN_CONNECTIONS)
982     download_hostlist ();
983   else
984     schedule_hostlist_task ();
985 }
986
987
988 /**
989  * Compute when we should check the next time about downloading
990  * a hostlist; then schedule the task accordingly.
991  */
992 static void
993 schedule_hostlist_task ()
994 {
995   static int once;
996   struct GNUNET_TIME_Relative delay;
997
998   if (stats == NULL)
999     {
1000       curl_global_cleanup ();
1001       return; /* in shutdown */
1002     }
1003   delay = hostlist_delay;
1004   if (hostlist_delay.value == 0)
1005     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1006   else
1007     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1008   if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
1009     hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1010                                                     (1 + connection_count));
1011   GNUNET_STATISTICS_set (stats,
1012                          gettext_noop("# seconds between hostlist downloads"),
1013                          hostlist_delay.value,
1014                          GNUNET_YES);
1015   if (0 == once)
1016     {
1017       delay = GNUNET_TIME_UNIT_ZERO;
1018       once = 1;
1019     }  
1020   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1021               _("Have %u/%u connections.  Will consider downloading hostlist in %llums\n"),
1022               connection_count,
1023               MIN_CONNECTIONS,
1024               (unsigned long long) delay.value);
1025   current_task = GNUNET_SCHEDULER_add_delayed (sched,
1026                                                delay,
1027                                                &check_task,
1028                                                NULL);
1029 }
1030
1031 /**
1032  * Task that writes hostlist entries to a file on a regular base
1033  * cls closure
1034  * tc TaskContext
1035  */
1036 static void
1037 hostlist_saving_task (void *cls,
1038             const struct GNUNET_SCHEDULER_TaskContext *tc)
1039 {
1040   saving_task = GNUNET_SCHEDULER_NO_TASK;
1041   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1042     return;
1043   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1044               _("Scheduled saving of hostlists\n"));
1045   save_hostlist_file ( GNUNET_NO );
1046
1047   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1048               _("Hostlists will be saved to file again in %llums\n"),
1049               (unsigned long long) SAVING_INTERVALL.value);
1050   saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1051                                                SAVING_INTERVALL,
1052                                                &hostlist_saving_task,
1053                                                NULL);
1054 }
1055
1056 /**
1057  * Method called whenever a given peer connects.
1058  *
1059  * @param cls closure
1060  * @param peer peer identity this notification is about
1061  * @param latency reported latency of the connection with 'other'
1062  * @param distance reported distance (DV) to 'other' 
1063  */
1064 static void
1065 connect_handler (void *cls,
1066                  const struct
1067                  GNUNET_PeerIdentity * peer,
1068                  struct GNUNET_TIME_Relative latency,
1069                  uint32_t distance)
1070 {
1071   connection_count++;
1072   GNUNET_STATISTICS_update (stats, 
1073                             gettext_noop ("# active connections"), 
1074                             1, 
1075                             GNUNET_NO);  
1076 }
1077
1078
1079 /**
1080  * Method called whenever a given peer disconnects.
1081  *
1082  * @param cls closure
1083  * @param peer peer identity this notification is about
1084  */
1085 static void
1086 disconnect_handler (void *cls,
1087                     const struct
1088                     GNUNET_PeerIdentity * peer)
1089 {
1090   connection_count--;
1091   GNUNET_STATISTICS_update (stats, 
1092                             gettext_noop ("# active connections"), 
1093                             -1, 
1094                             GNUNET_NO);  
1095 }
1096
1097
1098 /**
1099  * Method called whenever an advertisement message arrives.
1100  *
1101  * @param cls closure (always NULL)
1102  * @param peer the peer sending the message
1103  * @param message the actual message
1104  * @param latency latency
1105  * @param distance distance
1106  * @return GNUNET_OK to keep the connection open,
1107  *         GNUNET_SYSERR to close it (signal serious error)
1108  */
1109 static int
1110 advertisement_handler (void *cls,
1111     const struct GNUNET_PeerIdentity * peer,
1112     const struct GNUNET_MessageHeader * message,
1113     struct GNUNET_TIME_Relative latency,
1114     uint32_t distance)
1115 {
1116   size_t size;
1117   size_t uri_size;
1118   const struct GNUNET_MessageHeader * incoming;
1119   const char *uri;
1120   struct Hostlist * hostlist;
1121
1122   GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1123   size = ntohs (message->size);
1124   if (size <= sizeof(struct GNUNET_MessageHeader))
1125     {
1126       GNUNET_break_op (0);
1127       return GNUNET_SYSERR;
1128     }
1129   incoming = (const struct GNUNET_MessageHeader *) message;
1130   uri = (const char*) &incoming[1];
1131   uri_size = size - sizeof (struct GNUNET_MessageHeader);
1132   if (uri [uri_size - 1] != '\0')
1133     {
1134       GNUNET_break_op (0);
1135       return GNUNET_SYSERR;
1136     }
1137   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138               "Hostlist client recieved advertisement from `%s' containing URI `%s'\n", 
1139               GNUNET_i2s (peer), 
1140               uri);
1141   if (GNUNET_NO != linked_list_contains (uri))
1142     {
1143       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1144                 "URI `%s' is already known\n",
1145                 uri);
1146       return GNUNET_OK;
1147     }
1148
1149   if ( GNUNET_YES == testing_hostlist )
1150     {
1151       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1152                 "Currently not accepting adverts\n");
1153       return GNUNET_OK;
1154     }
1155
1156   hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1157   hostlist->hostlist_uri = (const char*) &hostlist[1];
1158   memcpy (&hostlist[1], uri, uri_size);
1159   hostlist->time_creation = GNUNET_TIME_absolute_get();
1160   hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1161   hostlist->quality = HOSTLIST_INITIAL;
1162   hostlist_to_test = hostlist;
1163
1164   testing_hostlist = GNUNET_YES;
1165   return GNUNET_OK;
1166 }
1167
1168
1169
1170 /**
1171  * Continuation called by the statistics code once 
1172  * we go the stat.  Initiates hostlist download scheduling.
1173  *
1174  * @param cls closure
1175  * @param success GNUNET_OK if statistics were
1176  *        successfully obtained, GNUNET_SYSERR if not.
1177  */
1178 static void
1179 primary_task (void *cls, int success)
1180 {
1181   if (stats == NULL)
1182     return; /* in shutdown */
1183 #if DEBUG_HOSTLIST_CLIENT
1184   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1185               "Statistics request done, scheduling hostlist download\n");
1186 #endif
1187   schedule_hostlist_task ();
1188 }
1189
1190
1191 static int
1192 process_stat (void *cls,
1193               const char *subsystem,
1194               const char *name,
1195               uint64_t value,
1196               int is_persistent)
1197 {
1198   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1199               _("Initial time between hostlist downloads is %llums\n"),
1200               (unsigned long long) value);
1201   hostlist_delay.value = value;
1202   return GNUNET_OK;
1203 }
1204
1205 /**
1206  * Method to load persistent hostlist file during hostlist client startup
1207  */
1208 static void 
1209 load_hostlist_file ()
1210 {
1211   char *filename;
1212   char *uri;
1213   char *emsg;
1214   struct Hostlist * hostlist;
1215   uri = NULL;
1216   uint32_t times_used;
1217   uint32_t hellos_returned;
1218   uint64_t quality;
1219   uint64_t last_used;
1220   uint64_t created;
1221   uint32_t counter;
1222
1223   if (GNUNET_OK !=
1224       GNUNET_CONFIGURATION_get_value_string (cfg,
1225                                              "HOSTLIST",
1226                                              "HOSTLISTFILE",
1227                                              &filename))
1228     {
1229       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1230                   _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1231                   "HOSTLISTFILE", "HOSTLIST");
1232       return;
1233     }
1234
1235   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1236               _("Loading saved hostlist entries from file `%s' \n"), filename);
1237   if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1238   {
1239     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1240                 _("Hostlist file `%s' is not existing\n"), filename);
1241     GNUNET_free ( filename );
1242     return;
1243   }
1244
1245   struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1246   if (NULL == rh)
1247     {
1248       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1249                   _("Could not open file `%s' for reading to load hostlists: %s\n"), 
1250                   filename,
1251                   STRERROR (errno));
1252       GNUNET_free (filename);
1253       return;
1254     }
1255
1256   counter = 0;
1257   while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1258           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used)) &&
1259           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1260           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1261           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1262           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1263     {
1264       hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1265       hostlist->hello_count = hellos_returned;
1266       hostlist->hostlist_uri = (const char *) &hostlist[1];
1267       memcpy (&hostlist[1], uri, strlen(uri)+1);
1268       hostlist->quality = quality;
1269       hostlist->time_creation.value = created;
1270       hostlist->time_last_usage.value = last_used;
1271       GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1272       linked_list_size++;
1273       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1274                   "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1275       GNUNET_free (uri);
1276       uri = NULL;
1277       counter++;
1278       if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1279     }
1280
1281   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1282               _("%u hostlist URIs loaded from file\n"), counter);
1283   GNUNET_STATISTICS_set (stats,
1284                          gettext_noop("# hostlist URIs read from file"),
1285                          counter,
1286                          GNUNET_YES);
1287   GNUNET_STATISTICS_set (stats,
1288                          gettext_noop("# advertised hostlist URIs"),
1289                          linked_list_size,
1290                          GNUNET_NO);
1291
1292   GNUNET_free_non_null (uri);
1293   emsg = NULL;
1294   GNUNET_BIO_read_close (rh, &emsg);
1295   if (emsg != NULL)
1296     GNUNET_free (emsg);
1297   GNUNET_free (filename);
1298 }
1299
1300
1301 /**
1302  * Method to save persistent hostlist file during hostlist client shutdown
1303  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1304  */
1305 static void save_hostlist_file ( int shutdown )
1306 {
1307   char *filename;
1308   struct Hostlist *pos;
1309   struct GNUNET_BIO_WriteHandle * wh;
1310   int ok;
1311   uint32_t counter;
1312
1313   if (GNUNET_OK !=
1314       GNUNET_CONFIGURATION_get_value_string (cfg,
1315                                              "HOSTLIST",
1316                                              "HOSTLISTFILE",
1317                                              &filename))
1318     {
1319       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1320                   _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1321                   "HOSTLISTFILE", "HOSTLIST");
1322       return;
1323     }
1324   wh = GNUNET_BIO_write_open (filename);
1325   if ( NULL == wh)
1326     {
1327       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1328                   _("Could not open file `%s' for writing to save hostlists: %s\n"),
1329                   filename,
1330                   STRERROR (errno));
1331       GNUNET_free (filename);
1332       return;
1333     }
1334   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1335               _("Writing %u hostlist URIs to `%s'\n" ),
1336               linked_list_size, filename);
1337
1338   /* add code to write hostlists to file using bio */
1339   ok = GNUNET_YES;
1340   counter = 0;
1341   while (NULL != (pos = linked_list_head))
1342     {
1343       if ( GNUNET_YES == shutdown)
1344       {
1345         GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1346         linked_list_size--;
1347       }
1348       if (GNUNET_YES == ok)
1349         {
1350           if ( (GNUNET_OK !=
1351                 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1352                (GNUNET_OK !=
1353                 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1354                (GNUNET_OK !=
1355                 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1356                (GNUNET_OK !=
1357                 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1358                (GNUNET_OK !=
1359                 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1360                (GNUNET_OK !=
1361                 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1362             {
1363               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1364                           _("Error writing hostlist URIs to file `%s'\n"),
1365                           filename);
1366               ok = GNUNET_NO;
1367             }
1368         }
1369
1370       if ( GNUNET_YES == shutdown)
1371         GNUNET_free (pos);
1372       counter ++;
1373       if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1374     }  
1375   GNUNET_STATISTICS_set (stats,
1376                          gettext_noop("# hostlist URIs written to file"),
1377                          counter,
1378                          GNUNET_YES);
1379
1380   if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1381     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1382                 _("Error writing hostlist URIs to file `%s'\n"),
1383                 filename);
1384   GNUNET_free (filename);
1385 }
1386
1387 /**
1388  * Start downloading hostlists from hostlist servers as necessary.
1389  */
1390 int
1391 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1392                               struct GNUNET_SCHEDULER_Handle *s,
1393                               struct GNUNET_STATISTICS_Handle *st,
1394                               GNUNET_CORE_ConnectEventHandler *ch,
1395                               GNUNET_CORE_DisconnectEventHandler *dh,
1396                               GNUNET_CORE_MessageCallback *msgh,
1397                               int learn)
1398 {
1399   char *filename;
1400   int result;
1401
1402   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1403     {
1404       GNUNET_break (0);
1405       return GNUNET_SYSERR;
1406     }
1407   transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1408   if (NULL == transport)
1409     {
1410       curl_global_cleanup ();
1411       return GNUNET_SYSERR;
1412     }
1413   cfg = c;
1414   sched = s;
1415   stats = st;
1416   if (GNUNET_OK !=
1417       GNUNET_CONFIGURATION_get_value_string (cfg,
1418                                              "HOSTLIST",
1419                                              "HTTP-PROXY", 
1420                                              &proxy))
1421     proxy = NULL;
1422   learning = learn;
1423   *ch = &connect_handler;
1424   *dh = &disconnect_handler;
1425   linked_list_head = NULL;
1426   linked_list_tail = NULL;
1427   use_preconfigured_list = GNUNET_YES;
1428   testing_hostlist = GNUNET_NO;
1429
1430   if ( GNUNET_YES == learning )
1431   {
1432     *msgh = &advertisement_handler;
1433     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1434               _("Learning is enabled on this peer\n"));
1435     load_hostlist_file ();
1436     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1437               _("Hostlists will be saved to file again in  %llums\n"),
1438               (unsigned long long) SAVING_INTERVALL.value);
1439     saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1440                                                SAVING_INTERVALL,
1441                                                &hostlist_saving_task,
1442                                                NULL);
1443   }
1444   else
1445   {
1446     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1447               _("Learning is not enabled on this peer\n"));
1448     *msgh = NULL;
1449     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1450                                                             "HOSTLIST",
1451                                                             "HOSTLISTFILE",
1452                                                             &filename))
1453     {
1454     if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1455       {
1456         result = remove (filename);
1457         if (result == 0)
1458         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1459               _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1460         else
1461           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1462                 _("Hostlist file `%s' could not be removed\n"),filename);
1463       }
1464     }
1465     GNUNET_free ( filename );
1466   }
1467   GNUNET_STATISTICS_get (stats,
1468                          "hostlist",
1469                          gettext_noop("# seconds between hostlist downloads"),
1470                          GNUNET_TIME_UNIT_MINUTES,
1471                          &primary_task,
1472                          &process_stat,
1473                          NULL);
1474   return GNUNET_OK;
1475 }
1476
1477
1478 /**
1479  * Stop downloading hostlists from hostlist servers as necessary.
1480  */
1481 void
1482 GNUNET_HOSTLIST_client_stop ()
1483 {
1484 #if DEBUG_HOSTLIST_CLIENT
1485   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1486               "Hostlist client shutdown\n");
1487 #endif
1488   if ( GNUNET_YES == learning )
1489     save_hostlist_file ( GNUNET_YES );
1490
1491   if (current_task != GNUNET_SCHEDULER_NO_TASK)
1492     {
1493       GNUNET_SCHEDULER_cancel (sched,
1494                                current_task);
1495       curl_global_cleanup ();
1496     }
1497   if (transport != NULL)
1498     {
1499       GNUNET_TRANSPORT_disconnect (transport);
1500       transport = NULL;
1501     }
1502   GNUNET_assert (NULL == transport);
1503   GNUNET_free_non_null (proxy);
1504   proxy = NULL;
1505   cfg = NULL;
1506   sched = NULL;
1507 }
1508
1509 /* end of hostlist-client.c */