81e85a54a6217793679d8a6cf56eb88e562d5f33
[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  * ID of the task checking the intervall between to hostlist tests
163  */
164 static GNUNET_SCHEDULER_TaskIdentifier testing_intervall_task;
165
166 /**
167  * Amount of time we wait between hostlist downloads.
168  */
169 static struct GNUNET_TIME_Relative hostlist_delay;
170
171 /**
172  * Set to GNUNET_YES if the current URL had some problems.
173  */ 
174 static int bogus_url;
175
176 /**
177  * Number of active connections (according to core service).
178  */
179 static unsigned int connection_count;
180
181 /**
182  * At what time MUST the current hostlist request be done?
183  */
184 static struct GNUNET_TIME_Absolute end_time;
185
186 /**
187  * Head of the linked list used to store hostlists
188  */
189 static struct Hostlist * linked_list_head;
190
191 /**
192  *  Tail of the linked list used to store hostlists
193  */
194 static struct Hostlist * linked_list_tail;
195
196 /**
197  *  Current hostlist used for downloading
198  */
199 static struct Hostlist * current_hostlist;
200
201 /**
202  *  Size of the linke list  used to store hostlists
203  */
204 static unsigned int linked_list_size;
205
206 /**
207  * Head of the linked list used to store hostlists
208  */
209 static struct Hostlist * hostlist_to_test;
210
211 static int testing_hostlist;
212
213 static int testing_allowed;
214
215 static int download_in_progress;
216
217 /**
218  * Value saying if preconfigured  is used
219  */
220 static unsigned int use_preconfigured_list;
221
222 /**
223  * Set if we are allowed to learn new hostlists and use them
224  */
225 static int learning;
226
227 /**
228  * Value saying how many valid HELLO messages were obtained during download
229  */
230 static unsigned int hellos_obtained;
231
232 /**
233  * Value saying if hostlist download was successful
234  */
235 static unsigned int download_successful;
236
237 /**
238  * Process downloaded bits by calling callback on each HELLO.
239  *
240  * @param ptr buffer with downloaded data
241  * @param size size of a record
242  * @param nmemb number of records downloaded
243  * @param ctx unused
244  * @return number of bytes that were processed (always size*nmemb)
245  */
246 static size_t
247 download_hostlist_processor (void *ptr, 
248                              size_t size, 
249                              size_t nmemb, 
250                              void *ctx)
251 {
252   const char * cbuf = ptr;
253   const struct GNUNET_MessageHeader *msg;
254   size_t total;
255   size_t cpy;
256   size_t left;
257   uint16_t msize;
258
259   total = size * nmemb;
260   if ( (total == 0) || (bogus_url) )
261     {
262       return total;  /* ok, no data or bogus data */
263     }
264   GNUNET_STATISTICS_update (stats, 
265                             gettext_noop ("# bytes downloaded from hostlist servers"), 
266                             (int64_t) total, 
267                             GNUNET_NO);  
268   left = total;
269   while ( (left > 0) ||
270           (download_pos > 0) )
271     {
272       cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
273       memcpy (&download_buffer[download_pos],
274               cbuf,
275               cpy);      
276       cbuf += cpy;
277       download_pos += cpy;
278       left -= cpy;
279       if (download_pos < sizeof(struct GNUNET_MessageHeader))
280         {
281           GNUNET_assert (left == 0);
282           break;
283         }
284       msg = (const struct GNUNET_MessageHeader *) download_buffer;
285       msize = ntohs(msg->size);
286       if (msize < sizeof(struct GNUNET_MessageHeader))
287         {        
288           GNUNET_STATISTICS_update (stats, 
289                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
290                                     1, 
291                                     GNUNET_NO);  
292           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
293                       _("Invalid `%s' message received from hostlist at `%s'\n"),
294                       "HELLO",
295                       current_url); 
296           bogus_url = 1;
297           return total;
298         }
299       if (download_pos < msize)
300         {
301           GNUNET_assert (left == 0);
302           break;
303         }
304       if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
305         {
306 #if DEBUG_HOSTLIST_CLIENT
307           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308                       "Received valid `%s' message from hostlist server.\n",
309                       "HELLO");
310 #endif
311           GNUNET_STATISTICS_update (stats, 
312                                     gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
313                                     1, 
314                                     GNUNET_NO);
315           hellos_obtained++;
316           GNUNET_TRANSPORT_offer_hello (transport, msg);
317         }
318       else
319         {
320           GNUNET_STATISTICS_update (stats, 
321                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
322                                     1, 
323                                     GNUNET_NO);  
324           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
325                       _("Invalid `%s' message received from hostlist at `%s'\n"),
326                       "HELLO",
327                       current_url);
328           bogus_url = GNUNET_YES;
329           return total;
330         }
331       memmove (download_buffer,
332                &download_buffer[msize],
333                download_pos - msize);
334       download_pos -= msize;
335     }
336   return total;
337 }
338
339
340 /**
341  * Obtain a hostlist URL that we should use.
342  *
343  * @return NULL if there is no URL available
344  */
345 static char *
346 get_bootstrap_url ()
347 {
348   char *servers;
349   char *ret;
350   size_t urls;
351   size_t pos;
352
353   if (GNUNET_OK != 
354       GNUNET_CONFIGURATION_get_value_string (cfg,
355                                              "HOSTLIST",
356                                              "SERVERS",
357                                              &servers))
358     {
359       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
360                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
361                   "SERVERS", "HOSTLIST");
362       return NULL;
363     }
364
365   urls = 0;
366   if (strlen (servers) > 0)
367     {
368       urls++;
369       pos = strlen (servers) - 1;
370       while (pos > 0)
371         {
372           if (servers[pos] == ' ')
373             urls++;
374           pos--;
375         }
376     }
377   if (urls == 0)
378     {
379       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
380                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
381                   "SERVERS", "HOSTLIST");
382       GNUNET_free (servers);
383       return NULL;
384     }
385
386   urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
387   pos = strlen (servers) - 1;
388   while (pos > 0)
389     {
390       if (servers[pos] == ' ')
391         {
392           urls--;
393           servers[pos] = '\0';
394         }
395       if (urls == 0)
396         {
397           pos++;
398           break;
399         }
400       pos--;    
401     }
402   ret = GNUNET_strdup (&servers[pos]);
403   GNUNET_free (servers);
404   return ret;
405 }
406
407 /**
408  * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
409  * @return uri to use, NULL if there is no URL available
410  */
411 static char *
412 get_list_url ()
413 {
414   uint32_t index;
415   unsigned int counter;
416   struct Hostlist * pos;
417
418   if ( GNUNET_NO == learning)
419   {
420     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421                 "Using preconfigured bootstrap server\n");
422     current_hostlist = NULL;
423     return get_bootstrap_url();
424   }
425
426   if ( ( GNUNET_YES == testing_hostlist) && (NULL != hostlist_to_test) )
427   {
428     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
429                 "Testing new advertised hostlist if it is obtainable\n");
430     current_hostlist = hostlist_to_test;
431     return strdup(hostlist_to_test->hostlist_uri);
432   }
433
434   if ( (GNUNET_YES == use_preconfigured_list) ||
435        (linked_list_size == 0) )
436   {
437     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438                 "Using preconfigured bootstrap server\n");
439     current_hostlist = NULL;
440     return get_bootstrap_url();
441   }
442   index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
443   counter = 0;
444   pos = linked_list_head;
445   while ( counter < index )
446     {
447       pos = pos->next;
448       counter ++;
449     }
450   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
451               "Using learned hostlist `%s'\n", pos->hostlist_uri);
452   current_hostlist = pos;
453   return strdup(pos->hostlist_uri);
454 }
455
456
457 #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);
458
459 /**
460  * Method to load persistent hostlist file during hostlist client shutdown
461  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
462  */
463 static void save_hostlist_file ( int shutdown );
464
465 /**
466  * add val2 to val1 with overflow check
467  * @param val1 value 1
468  * @param val2 value 2
469  * @return result
470  */
471 static uint64_t checked_add (uint64_t val1, uint64_t val2)
472 {
473   static uint64_t temp;
474   static uint64_t maxv;
475
476   maxv = 0;
477   maxv--;
478
479   temp = val1+val2;
480   if ( temp < val1)
481     return maxv;
482   else
483     return temp;
484 }
485
486 /**
487  * Subtract val2 from val1 with underflow check
488  * @param val1 value 1
489  * @param val2 value 2
490  * @return result
491  */
492 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
493 {
494   if ( val1 <= val2)
495     return 0;
496   else
497     return (val1-val2);
498 }
499
500 /**
501  * Method to check if URI is in hostlist linked list
502  * @param uri uri to check
503  * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
504  */
505 static int
506 linked_list_contains (const char * uri)
507 {
508   struct Hostlist * pos;
509
510   pos = linked_list_head;
511   while (pos != NULL)
512     {
513       if (0 == strcmp(pos->hostlist_uri, uri) )
514         return GNUNET_YES;
515       pos = pos->next;
516     }
517   return GNUNET_NO;
518 }
519
520
521 /**
522  * Method returning the uri with the lowest quality in the datastore
523  * @return hostlist with lowest quality
524  */
525 static struct Hostlist *
526 linked_list_get_lowest_quality ( )
527 {
528   struct Hostlist * pos;
529   struct Hostlist * lowest;
530
531   if (linked_list_size == 0)
532     return NULL;
533   lowest = linked_list_head;
534   pos = linked_list_head->next;
535   while (pos != NULL)
536     {
537       if (pos->quality < lowest->quality)
538         lowest = pos;
539       pos = pos->next;
540     }
541   return lowest;
542 }
543
544
545 /**
546  * Method to insert a hostlist into the datastore. If datastore contains maximum number of elements, the elements with lowest quality is dismissed
547  */
548 static void
549 insert_hostlist ( void )
550 {
551   GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
552   linked_list_size++;
553
554   GNUNET_STATISTICS_set (stats,
555                          gettext_noop("# advertised hostlist URIs"),
556                          linked_list_size,
557                          GNUNET_NO);
558
559   if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
560     return;
561
562   /* No free entries available, replace existing entry  */
563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564               "Removing lowest quality entry\n" );
565   struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567               "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
568               lowest_quality->hostlist_uri,
569               (unsigned long long) lowest_quality->quality);
570   GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
571   linked_list_size--;
572
573   GNUNET_STATISTICS_set (stats,
574                          gettext_noop("# advertised hostlist URIs"),
575                          linked_list_size,
576                          GNUNET_NO);
577
578   GNUNET_free (lowest_quality);
579
580   testing_hostlist = GNUNET_NO;
581   return;
582 }
583
584
585 /**
586  * Method updating hostlist statistics
587  */
588 static void update_hostlist ( )
589 {
590   char *stat;
591   if ( ((use_preconfigured_list == GNUNET_NO) && ( NULL != current_hostlist )) ||
592        ((testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
593   {
594     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
595                 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
596      current_hostlist->hello_count = hellos_obtained;
597      current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
598      current_hostlist->quality = checked_add ( current_hostlist->quality, (hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
599      if ( GNUNET_YES == download_successful )
600      {
601        current_hostlist->times_used++;
602        current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
603        GNUNET_asprintf (&stat,
604                         gettext_noop("# advertised URI `%s' downloaded"),
605                         current_hostlist->hostlist_uri);
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   download_in_progress = GNUNET_NO;
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               counter ++;
817               GNUNET_break (msg != NULL);
818               if (msg == NULL)
819                 break;
820               switch (msg->msg)
821                 {
822                 case CURLMSG_DONE:
823                   if ( (msg->data.result != CURLE_OK) &&
824                        (msg->data.result != CURLE_GOT_NOTHING) )                       
825                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
826                                _("%s failed for `%s' at %s:%d: `%s'\n"),
827                                "curl_multi_perform", 
828                                current_url,
829                                __FILE__,
830                                __LINE__,
831                                curl_easy_strerror (msg->data.result));            
832                   else
833                     {
834                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
835                                 _("Download of hostlist `%s' completed.\n"),
836                                 current_url);
837                     download_successful = GNUNET_YES;
838                     update_hostlist();
839                     if (GNUNET_YES == testing_hostlist)
840                      {
841                       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
842                                         _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
843                       insert_hostlist();
844                       hostlist_to_test = NULL;
845                       testing_hostlist = GNUNET_NO;
846                      }
847                     }
848                   clean_up ();
849                   return;
850                 default:
851                   break;
852                 }
853
854             }
855           while (running > 0);
856         }
857     }
858   while (mret == CURLM_CALL_MULTI_PERFORM);
859   if (mret != CURLM_OK)
860     {
861       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
862                   _("%s failed at %s:%d: `%s'\n"),
863                   "curl_multi_perform", __FILE__, __LINE__,
864                   curl_multi_strerror (mret));
865       clean_up ();
866     }
867   run_multi ();
868 }
869
870
871 /**
872  * Main function that will download a hostlist and process its
873  * data.
874  */
875 static void
876 download_hostlist () 
877 {
878   CURLcode ret;
879   CURLMcode mret;
880
881
882   current_url = get_list_url ();
883   if (current_url == NULL)
884     return;
885   curl = curl_easy_init ();
886   multi = NULL;
887   if (curl == NULL)
888     {
889       GNUNET_break (0);
890       clean_up ();
891       return;
892     }
893   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
894               _("Bootstrapping using hostlist at `%s'.\n"), 
895               current_url);
896
897   download_in_progress = GNUNET_YES;
898   download_successful = GNUNET_NO;
899   hellos_obtained = 0;
900
901   GNUNET_STATISTICS_update (stats, 
902                             gettext_noop ("# hostlist downloads initiated"), 
903                             1, 
904                             GNUNET_NO);  
905   if (proxy != NULL)
906     CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);    
907   download_pos = 0;
908   bogus_url = 0;
909   CURL_EASY_SETOPT (curl,
910                     CURLOPT_WRITEFUNCTION, 
911                     &download_hostlist_processor);
912   if (ret != CURLE_OK)
913     {
914       clean_up ();
915       return;
916     }
917   CURL_EASY_SETOPT (curl,
918                     CURLOPT_WRITEDATA, 
919                     NULL);
920   if (ret != CURLE_OK)
921     {
922       clean_up ();
923       return;
924     }
925   CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
926   CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
927   /* no need to abort if the above failed */
928   CURL_EASY_SETOPT (curl, 
929                     CURLOPT_URL, 
930                     current_url);
931   if (ret != CURLE_OK)
932     {
933       clean_up ();
934       return;
935     }
936   CURL_EASY_SETOPT (curl, 
937                     CURLOPT_FAILONERROR, 
938                     1);
939 #if 0
940   CURL_EASY_SETOPT (curl, 
941                     CURLOPT_VERBOSE, 
942                     1);
943 #endif
944   CURL_EASY_SETOPT (curl, 
945                     CURLOPT_BUFFERSIZE, 
946                     GNUNET_SERVER_MAX_MESSAGE_SIZE);
947   if (0 == strncmp (current_url, "http", 4))
948     CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
949   CURL_EASY_SETOPT (curl, 
950                     CURLOPT_CONNECTTIMEOUT, 
951                     60L);
952   CURL_EASY_SETOPT (curl, 
953                     CURLOPT_TIMEOUT, 
954                     60L);
955 #if 0
956   /* this should no longer be needed; we're now single-threaded! */
957   CURL_EASY_SETOPT (curl,
958                     CURLOPT_NOSIGNAL, 
959                     1);
960 #endif
961   multi = curl_multi_init ();
962   if (multi == NULL)
963     {
964       GNUNET_break (0);
965       clean_up ();
966       return;
967     }
968   mret = curl_multi_add_handle (multi, curl);
969   if (mret != CURLM_OK)
970     {
971       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
972                   _("%s failed at %s:%d: `%s'\n"),
973                   "curl_multi_add_handle", __FILE__, __LINE__,
974                   curl_multi_strerror (mret));
975       mret = curl_multi_cleanup (multi);
976       if (mret != CURLM_OK)
977         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
978                     _("%s failed at %s:%d: `%s'\n"),
979                     "curl_multi_cleanup", __FILE__, __LINE__,
980                     curl_multi_strerror (mret));
981       multi = NULL;
982       clean_up ();
983       return;
984     }
985   end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
986   run_multi ();
987 }  
988
989
990 static void
991 download_dispatcher (void *cls,
992             const struct GNUNET_SCHEDULER_TaskContext *tc)
993 {
994    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
995              "Download is initiated...\n");
996    if ( GNUNET_NO == download_in_progress )
997    {
998      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
999                "Download can start immediately...\n");
1000      download_hostlist();
1001    }
1002    else
1003    {
1004      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1005                "Download in progess, have to wait...\n");
1006      GNUNET_SCHEDULER_add_delayed (sched,
1007           WAITING_INTERVALL,
1008           &download_dispatcher,
1009           NULL);
1010    }
1011 }
1012
1013 /**
1014  * Task that checks if we should try to download a hostlist.
1015  * If so, we initiate the download, otherwise we schedule
1016  * this task again for a later time.
1017  */
1018 static void
1019 check_task (void *cls,
1020             const struct GNUNET_SCHEDULER_TaskContext *tc)
1021 {
1022   current_task = GNUNET_SCHEDULER_NO_TASK;
1023   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1024     return;
1025
1026   if (connection_count < MIN_CONNECTIONS)
1027   {
1028     GNUNET_SCHEDULER_add_now (sched,
1029                               &download_dispatcher,
1030                               NULL);
1031   }
1032
1033   static int once;
1034   struct GNUNET_TIME_Relative delay;
1035
1036   if (stats == NULL)
1037     {
1038       curl_global_cleanup ();
1039       return; /* in shutdown */
1040     }
1041   delay = hostlist_delay;
1042   if (hostlist_delay.value == 0)
1043     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1044   else
1045     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1046   if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
1047     hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1048                                                     (1 + connection_count));
1049   GNUNET_STATISTICS_set (stats,
1050                          gettext_noop("# seconds between hostlist downloads"),
1051                          hostlist_delay.value,
1052                          GNUNET_YES);
1053   if (0 == once)
1054     {
1055       delay = GNUNET_TIME_UNIT_ZERO;
1056       once = 1;
1057     }  
1058   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1059               _("Have %u/%u connections.  Will consider downloading hostlist in %llums\n"),
1060               connection_count,
1061               MIN_CONNECTIONS,
1062               (unsigned long long) delay.value);
1063   current_task = GNUNET_SCHEDULER_add_delayed (sched,
1064                                                delay,
1065                                                &check_task,
1066                                                NULL);
1067 }
1068
1069 /**
1070  * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1071  * cls closure
1072  * tc TaskContext
1073  */
1074 static void
1075 testing_intervall_reset (void *cls,
1076             const struct GNUNET_SCHEDULER_TaskContext *tc)
1077 {
1078    testing_allowed = GNUNET_OK;
1079    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080              "Testing new hostlist advertisements is allowed again\n");
1081 }
1082
1083
1084 /**
1085  * Task that writes hostlist entries to a file on a regular base
1086  * cls closure
1087  * tc TaskContext
1088  */
1089 static void
1090 hostlist_saving_task (void *cls,
1091             const struct GNUNET_SCHEDULER_TaskContext *tc)
1092 {
1093   saving_task = GNUNET_SCHEDULER_NO_TASK;
1094   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1095     return;
1096   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1097               _("Scheduled saving of hostlists\n"));
1098   save_hostlist_file ( GNUNET_NO );
1099
1100   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1101               _("Hostlists will be saved to file again in %llums\n"),
1102               (unsigned long long) SAVING_INTERVALL.value);
1103   saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1104                                                SAVING_INTERVALL,
1105                                                &hostlist_saving_task,
1106                                                NULL);
1107 }
1108
1109 /**
1110  * Method called whenever a given peer connects.
1111  *
1112  * @param cls closure
1113  * @param peer peer identity this notification is about
1114  * @param latency reported latency of the connection with 'other'
1115  * @param distance reported distance (DV) to 'other' 
1116  */
1117 static void
1118 connect_handler (void *cls,
1119                  const struct
1120                  GNUNET_PeerIdentity * peer,
1121                  struct GNUNET_TIME_Relative latency,
1122                  uint32_t distance)
1123 {
1124   connection_count++;
1125   GNUNET_STATISTICS_update (stats, 
1126                             gettext_noop ("# active connections"), 
1127                             1, 
1128                             GNUNET_NO);  
1129 }
1130
1131
1132 /**
1133  * Method called whenever a given peer disconnects.
1134  *
1135  * @param cls closure
1136  * @param peer peer identity this notification is about
1137  */
1138 static void
1139 disconnect_handler (void *cls,
1140                     const struct
1141                     GNUNET_PeerIdentity * peer)
1142 {
1143   connection_count--;
1144   GNUNET_STATISTICS_update (stats, 
1145                             gettext_noop ("# active connections"), 
1146                             -1, 
1147                             GNUNET_NO);  
1148 }
1149
1150 /**
1151  * Method called whenever an advertisement message arrives.
1152  *
1153  * @param cls closure (always NULL)
1154  * @param peer the peer sending the message
1155  * @param message the actual message
1156  * @param latency latency
1157  * @param distance distance
1158  * @return GNUNET_OK to keep the connection open,
1159  *         GNUNET_SYSERR to close it (signal serious error)
1160  */
1161 static int
1162 advertisement_handler (void *cls,
1163     const struct GNUNET_PeerIdentity * peer,
1164     const struct GNUNET_MessageHeader * message,
1165     struct GNUNET_TIME_Relative latency,
1166     uint32_t distance)
1167 {
1168   size_t size;
1169   size_t uri_size;
1170   const struct GNUNET_MessageHeader * incoming;
1171   const char *uri;
1172   struct Hostlist * hostlist;
1173
1174   GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1175   size = ntohs (message->size);
1176   if (size <= sizeof(struct GNUNET_MessageHeader))
1177     {
1178       GNUNET_break_op (0);
1179       return GNUNET_SYSERR;
1180     }
1181   incoming = (const struct GNUNET_MessageHeader *) message;
1182   uri = (const char*) &incoming[1];
1183   uri_size = size - sizeof (struct GNUNET_MessageHeader);
1184   if (uri [uri_size - 1] != '\0')
1185     {
1186       GNUNET_break_op (0);
1187       return GNUNET_SYSERR;
1188     }
1189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1190               "Hostlist client recieved advertisement from `%s' containing URI `%s'\n", 
1191               GNUNET_i2s (peer), 
1192               uri);
1193   if (GNUNET_NO != linked_list_contains (uri))
1194     {
1195       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1196                 "URI `%s' is already known\n",
1197                 uri);
1198       return GNUNET_OK;
1199     }
1200
1201   if ( GNUNET_NO == testing_allowed )
1202     {
1203       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1204                 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1205       return GNUNET_SYSERR;
1206     }
1207   if ( GNUNET_YES == testing_hostlist )
1208     {
1209       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1210                 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1211       return GNUNET_SYSERR;
1212     }
1213
1214   hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1215   hostlist->hostlist_uri = (const char*) &hostlist[1];
1216   memcpy (&hostlist[1], uri, uri_size);
1217   hostlist->time_creation = GNUNET_TIME_absolute_get();
1218   hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1219   hostlist->quality = HOSTLIST_INITIAL;
1220   hostlist_to_test = hostlist;
1221
1222   testing_hostlist = GNUNET_YES;
1223   testing_allowed = GNUNET_NO;
1224   testing_intervall_task = GNUNET_SCHEDULER_add_delayed (sched,
1225                                                          TESTING_INTERVALL,
1226                                                          &testing_intervall_reset,
1227                                                          NULL);
1228
1229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1230             "Testing new hostlist advertisements is locked for the next %u ms\n",
1231             TESTING_INTERVALL);
1232
1233   testing_intervall_task = GNUNET_SCHEDULER_add_now (sched,
1234                                                      &download_dispatcher,
1235                                                      NULL);
1236
1237   return GNUNET_OK;
1238 }
1239
1240
1241
1242 /**
1243  * Continuation called by the statistics code once 
1244  * we go the stat.  Initiates hostlist download scheduling.
1245  *
1246  * @param cls closure
1247  * @param success GNUNET_OK if statistics were
1248  *        successfully obtained, GNUNET_SYSERR if not.
1249  */
1250 static void
1251 primary_task (void *cls, int success)
1252 {
1253   if (stats == NULL)
1254     return; /* in shutdown */
1255 #if DEBUG_HOSTLIST_CLIENT
1256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1257               "Statistics request done, scheduling hostlist download\n");
1258 #endif
1259   current_task = GNUNET_SCHEDULER_add_now (sched,
1260                                            &check_task,
1261                                            NULL);
1262 }
1263
1264
1265 static int
1266 process_stat (void *cls,
1267               const char *subsystem,
1268               const char *name,
1269               uint64_t value,
1270               int is_persistent)
1271 {
1272   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1273               _("Initial time between hostlist downloads is %llums\n"),
1274               (unsigned long long) value);
1275   hostlist_delay.value = value;
1276   return GNUNET_OK;
1277 }
1278
1279 /**
1280  * Method to load persistent hostlist file during hostlist client startup
1281  */
1282 static void 
1283 load_hostlist_file ()
1284 {
1285   char *filename;
1286   char *uri;
1287   char *emsg;
1288   struct Hostlist * hostlist;
1289   uri = NULL;
1290   uint32_t times_used;
1291   uint32_t hellos_returned;
1292   uint64_t quality;
1293   uint64_t last_used;
1294   uint64_t created;
1295   uint32_t counter;
1296
1297   if (GNUNET_OK !=
1298       GNUNET_CONFIGURATION_get_value_string (cfg,
1299                                              "HOSTLIST",
1300                                              "HOSTLISTFILE",
1301                                              &filename))
1302     {
1303       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1304                   _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1305                   "HOSTLISTFILE", "HOSTLIST");
1306       return;
1307     }
1308
1309   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1310               _("Loading saved hostlist entries from file `%s' \n"), filename);
1311   if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1312   {
1313     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1314                 _("Hostlist file `%s' is not existing\n"), filename);
1315     GNUNET_free ( filename );
1316     return;
1317   }
1318
1319   struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1320   if (NULL == rh)
1321     {
1322       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1323                   _("Could not open file `%s' for reading to load hostlists: %s\n"), 
1324                   filename,
1325                   STRERROR (errno));
1326       GNUNET_free (filename);
1327       return;
1328     }
1329
1330   counter = 0;
1331   while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1332           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used)) &&
1333           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1334           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1335           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1336           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1337     {
1338       hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1339       hostlist->hello_count = hellos_returned;
1340       hostlist->hostlist_uri = (const char *) &hostlist[1];
1341       memcpy (&hostlist[1], uri, strlen(uri)+1);
1342       hostlist->quality = quality;
1343       hostlist->time_creation.value = created;
1344       hostlist->time_last_usage.value = last_used;
1345       GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1346       linked_list_size++;
1347       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1348                   "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1349       GNUNET_free (uri);
1350       uri = NULL;
1351       counter++;
1352       if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1353     }
1354
1355   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1356               _("%u hostlist URIs loaded from file\n"), counter);
1357   GNUNET_STATISTICS_set (stats,
1358                          gettext_noop("# hostlist URIs read from file"),
1359                          counter,
1360                          GNUNET_YES);
1361   GNUNET_STATISTICS_set (stats,
1362                          gettext_noop("# advertised hostlist URIs"),
1363                          linked_list_size,
1364                          GNUNET_NO);
1365
1366   GNUNET_free_non_null (uri);
1367   emsg = NULL;
1368   GNUNET_BIO_read_close (rh, &emsg);
1369   if (emsg != NULL)
1370     GNUNET_free (emsg);
1371   GNUNET_free (filename);
1372 }
1373
1374
1375 /**
1376  * Method to save persistent hostlist file during hostlist client shutdown
1377  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1378  */
1379 static void save_hostlist_file ( int shutdown )
1380 {
1381   char *filename;
1382   struct Hostlist *pos;
1383   struct GNUNET_BIO_WriteHandle * wh;
1384   int ok;
1385   uint32_t counter;
1386
1387   if (GNUNET_OK !=
1388       GNUNET_CONFIGURATION_get_value_string (cfg,
1389                                              "HOSTLIST",
1390                                              "HOSTLISTFILE",
1391                                              &filename))
1392     {
1393       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1394                   _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1395                   "HOSTLISTFILE", "HOSTLIST");
1396       return;
1397     }
1398   wh = GNUNET_BIO_write_open (filename);
1399   if ( NULL == wh)
1400     {
1401       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1402                   _("Could not open file `%s' for writing to save hostlists: %s\n"),
1403                   filename,
1404                   STRERROR (errno));
1405       GNUNET_free (filename);
1406       return;
1407     }
1408   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1409               _("Writing %u hostlist URIs to `%s'\n" ),
1410               linked_list_size, filename);
1411
1412   /* add code to write hostlists to file using bio */
1413   ok = GNUNET_YES;
1414   counter = 0;
1415   while (NULL != (pos = linked_list_head))
1416     {
1417       if ( GNUNET_YES == shutdown)
1418       {
1419         GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1420         linked_list_size--;
1421       }
1422       if (GNUNET_YES == ok)
1423         {
1424           if ( (GNUNET_OK !=
1425                 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1426                (GNUNET_OK !=
1427                 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1428                (GNUNET_OK !=
1429                 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1430                (GNUNET_OK !=
1431                 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1432                (GNUNET_OK !=
1433                 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1434                (GNUNET_OK !=
1435                 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1436             {
1437               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1438                           _("Error writing hostlist URIs to file `%s'\n"),
1439                           filename);
1440               ok = GNUNET_NO;
1441             }
1442         }
1443
1444       if ( GNUNET_YES == shutdown)
1445         GNUNET_free (pos);
1446       counter ++;
1447       if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1448     }  
1449   GNUNET_STATISTICS_set (stats,
1450                          gettext_noop("# hostlist URIs written to file"),
1451                          counter,
1452                          GNUNET_YES);
1453
1454   if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1455     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1456                 _("Error writing hostlist URIs to file `%s'\n"),
1457                 filename);
1458   GNUNET_free (filename);
1459 }
1460
1461 /**
1462  * Start downloading hostlists from hostlist servers as necessary.
1463  */
1464 int
1465 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1466                               struct GNUNET_SCHEDULER_Handle *s,
1467                               struct GNUNET_STATISTICS_Handle *st,
1468                               GNUNET_CORE_ConnectEventHandler *ch,
1469                               GNUNET_CORE_DisconnectEventHandler *dh,
1470                               GNUNET_CORE_MessageCallback *msgh,
1471                               int learn)
1472 {
1473   char *filename;
1474   int result;
1475
1476   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1477     {
1478       GNUNET_break (0);
1479       return GNUNET_SYSERR;
1480     }
1481   transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1482   if (NULL == transport)
1483     {
1484       curl_global_cleanup ();
1485       return GNUNET_SYSERR;
1486     }
1487   cfg = c;
1488   sched = s;
1489   stats = st;
1490   if (GNUNET_OK !=
1491       GNUNET_CONFIGURATION_get_value_string (cfg,
1492                                              "HOSTLIST",
1493                                              "HTTP-PROXY", 
1494                                              &proxy))
1495     proxy = NULL;
1496   learning = learn;
1497   *ch = &connect_handler;
1498   *dh = &disconnect_handler;
1499   linked_list_head = NULL;
1500   linked_list_tail = NULL;
1501   use_preconfigured_list = GNUNET_YES;
1502   testing_hostlist = GNUNET_NO;
1503   testing_allowed = GNUNET_YES;
1504
1505   if ( GNUNET_YES == learning )
1506   {
1507     *msgh = &advertisement_handler;
1508     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1509               _("Learning is enabled on this peer\n"));
1510     load_hostlist_file ();
1511     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1512               _("Hostlists will be saved to file again in  %llums\n"),
1513               (unsigned long long) SAVING_INTERVALL.value);
1514     saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1515                                                SAVING_INTERVALL,
1516                                                &hostlist_saving_task,
1517                                                NULL);
1518   }
1519   else
1520   {
1521     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1522               _("Learning is not enabled on this peer\n"));
1523     *msgh = NULL;
1524     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1525                                                             "HOSTLIST",
1526                                                             "HOSTLISTFILE",
1527                                                             &filename))
1528     {
1529     if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1530       {
1531         result = remove (filename);
1532         if (result == 0)
1533         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1534               _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1535         else
1536           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1537                 _("Hostlist file `%s' could not be removed\n"),filename);
1538       }
1539     }
1540     GNUNET_free ( filename );
1541   }
1542   GNUNET_STATISTICS_get (stats,
1543                          "hostlist",
1544                          gettext_noop("# seconds between hostlist downloads"),
1545                          GNUNET_TIME_UNIT_MINUTES,
1546                          &primary_task,
1547                          &process_stat,
1548                          NULL);
1549   return GNUNET_OK;
1550 }
1551
1552
1553 /**
1554  * Stop downloading hostlists from hostlist servers as necessary.
1555  */
1556 void
1557 GNUNET_HOSTLIST_client_stop ()
1558 {
1559 #if DEBUG_HOSTLIST_CLIENT
1560   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1561               "Hostlist client shutdown\n");
1562 #endif
1563   if ( GNUNET_YES == learning )
1564     save_hostlist_file ( GNUNET_YES );
1565
1566   if (current_task != GNUNET_SCHEDULER_NO_TASK)
1567     {
1568       GNUNET_SCHEDULER_cancel (sched,
1569                                current_task);
1570       curl_global_cleanup ();
1571     }
1572   if (transport != NULL)
1573     {
1574       GNUNET_TRANSPORT_disconnect (transport);
1575       transport = NULL;
1576     }
1577   GNUNET_assert (NULL == transport);
1578   GNUNET_free_non_null (proxy);
1579   proxy = NULL;
1580   cfg = NULL;
1581   sched = NULL;
1582 }
1583
1584 /* end of hostlist-client.c */