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