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