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