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