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