LRN: Use GNUNET_strdup() instead of strdup()
[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  */
1111 static void
1112 handler_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
1113                  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1114 {
1115   GNUNET_assert (stat_connection_count < UINT_MAX);
1116   stat_connection_count++;
1117   GNUNET_STATISTICS_update (stats, gettext_noop ("# active connections"), 1,
1118                             GNUNET_NO);
1119 }
1120
1121
1122 /**
1123  * Method called whenever a given peer disconnects.
1124  *
1125  * @param cls closure
1126  * @param peer peer identity this notification is about
1127  */
1128 static void
1129 handler_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1130 {
1131   GNUNET_assert (stat_connection_count > 0);
1132   stat_connection_count--;
1133   GNUNET_STATISTICS_update (stats, gettext_noop ("# active connections"), -1,
1134                             GNUNET_NO);
1135 }
1136
1137
1138 /**
1139  * Method called whenever an advertisement message arrives.
1140  *
1141  * @param cls closure (always NULL)
1142  * @param peer the peer sending the message
1143  * @param message the actual message
1144  * @param atsi performance data
1145  * @return GNUNET_OK to keep the connection open,
1146  *         GNUNET_SYSERR to close it (signal serious error)
1147  */
1148 static int
1149 handler_advertisement (void *cls, const struct GNUNET_PeerIdentity *peer,
1150                        const struct GNUNET_MessageHeader *message,
1151                        const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1152 {
1153   size_t size;
1154   size_t uri_size;
1155   const struct GNUNET_MessageHeader *incoming;
1156   const char *uri;
1157   struct Hostlist *hostlist;
1158
1159   GNUNET_assert (ntohs (message->type) ==
1160                  GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1161   size = ntohs (message->size);
1162   if (size <= sizeof (struct GNUNET_MessageHeader))
1163   {
1164     GNUNET_break_op (0);
1165     return GNUNET_SYSERR;
1166   }
1167   incoming = (const struct GNUNET_MessageHeader *) message;
1168   uri = (const char *) &incoming[1];
1169   uri_size = size - sizeof (struct GNUNET_MessageHeader);
1170   if (uri[uri_size - 1] != '\0')
1171   {
1172     GNUNET_break_op (0);
1173     return GNUNET_SYSERR;
1174   }
1175   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176               "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1177               GNUNET_i2s (peer), uri);
1178   if (GNUNET_NO != linked_list_contains (uri))
1179   {
1180     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1181     return GNUNET_OK;
1182   }
1183
1184   if (GNUNET_NO == stat_testing_allowed)
1185   {
1186     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1187                 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1188     return GNUNET_SYSERR;
1189   }
1190   if (GNUNET_YES == stat_testing_hostlist)
1191   {
1192     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1193                 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1194     return GNUNET_SYSERR;
1195   }
1196
1197   hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1198   hostlist->hostlist_uri = (const char *) &hostlist[1];
1199   memcpy (&hostlist[1], uri, uri_size);
1200   hostlist->time_creation = GNUNET_TIME_absolute_get ();
1201   hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero ();
1202   hostlist->quality = HOSTLIST_INITIAL;
1203   hostlist_to_test = hostlist;
1204
1205   stat_testing_hostlist = GNUNET_YES;
1206   stat_testing_allowed = GNUNET_NO;
1207   ti_testing_intervall_task =
1208       GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1209                                     &task_testing_intervall_reset, NULL);
1210
1211   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1212               "Testing new hostlist advertisements is locked for the next %u ms\n",
1213               TESTING_INTERVAL.rel_value);
1214
1215   ti_download_dispatcher_task =
1216       GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1217
1218   return GNUNET_OK;
1219 }
1220
1221
1222
1223 /**
1224  * Continuation called by the statistics code once
1225  * we go the stat.  Initiates hostlist download scheduling.
1226  *
1227  * @param cls closure
1228  * @param success GNUNET_OK if statistics were
1229  *        successfully obtained, GNUNET_SYSERR if not.
1230  */
1231 static void
1232 primary_task (void *cls, int success)
1233 {
1234   if (stats == NULL)
1235     return;                     /* in shutdown */
1236 #if DEBUG_HOSTLIST_CLIENT
1237   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1238               "Statistics request done, scheduling hostlist download\n");
1239 #endif
1240   ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1241 }
1242
1243
1244 static int
1245 process_stat (void *cls, const char *subsystem, const char *name,
1246               uint64_t value, int is_persistent)
1247 {
1248   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1249               _("Initial time between hostlist downloads is %llums\n"),
1250               (unsigned long long) value);
1251   hostlist_delay.rel_value = value;
1252   return GNUNET_OK;
1253 }
1254
1255 /**
1256  * Method to load persistent hostlist file during hostlist client startup
1257  */
1258 static void
1259 load_hostlist_file ()
1260 {
1261   char *filename;
1262   char *uri;
1263   char *emsg;
1264   struct Hostlist *hostlist;
1265
1266   uri = NULL;
1267   uint32_t times_used;
1268   uint32_t hellos_returned;
1269   uint64_t quality;
1270   uint64_t last_used;
1271   uint64_t created;
1272   uint32_t counter;
1273
1274   if (GNUNET_OK !=
1275       GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1276                                                &filename))
1277   {
1278     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1279                 _
1280                 ("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1281                 "HOSTLISTFILE", "HOSTLIST");
1282     return;
1283   }
1284
1285   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1286               _("Loading saved hostlist entries from file `%s' \n"), filename);
1287   if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1288   {
1289     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1290                 _("Hostlist file `%s' is not existing\n"), filename);
1291     GNUNET_free (filename);
1292     return;
1293   }
1294
1295   struct GNUNET_BIO_ReadHandle *rh = GNUNET_BIO_read_open (filename);
1296
1297   if (NULL == rh)
1298   {
1299     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1300                 _
1301                 ("Could not open file `%s' for reading to load hostlists: %s\n"),
1302                 filename, STRERROR (errno));
1303     GNUNET_free (filename);
1304     return;
1305   }
1306
1307   counter = 0;
1308   while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1309          (NULL != uri) && (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used))
1310          && (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1311          (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1312          (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1313          (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)))
1314   {
1315     hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1316     hostlist->hello_count = hellos_returned;
1317     hostlist->hostlist_uri = (const char *) &hostlist[1];
1318     memcpy (&hostlist[1], uri, strlen (uri) + 1);
1319     hostlist->quality = quality;
1320     hostlist->time_creation.abs_value = created;
1321     hostlist->time_last_usage.abs_value = last_used;
1322     GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1323     linked_list_size++;
1324     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1325                 "Added hostlist entry eith URI `%s' \n",
1326                 hostlist->hostlist_uri);
1327     GNUNET_free (uri);
1328     uri = NULL;
1329     counter++;
1330     if (counter >= MAX_NUMBER_HOSTLISTS)
1331       break;
1332   }
1333
1334   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%u hostlist URIs loaded from file\n"),
1335               counter);
1336   GNUNET_STATISTICS_set (stats, gettext_noop ("# hostlist URIs read from file"),
1337                          counter, GNUNET_YES);
1338   GNUNET_STATISTICS_set (stats, gettext_noop ("# advertised hostlist URIs"),
1339                          linked_list_size, GNUNET_NO);
1340
1341   GNUNET_free_non_null (uri);
1342   emsg = NULL;
1343   GNUNET_BIO_read_close (rh, &emsg);
1344   if (emsg != NULL)
1345     GNUNET_free (emsg);
1346   GNUNET_free (filename);
1347 }
1348
1349
1350 /**
1351  * Method to save persistent hostlist file during hostlist client shutdown
1352  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1353  */
1354 static void
1355 save_hostlist_file (int shutdown)
1356 {
1357   char *filename;
1358   struct Hostlist *pos;
1359   struct GNUNET_BIO_WriteHandle *wh;
1360   int ok;
1361   uint32_t counter;
1362
1363   if (GNUNET_OK !=
1364       GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1365                                                &filename))
1366   {
1367     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1368                 _
1369                 ("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1370                 "HOSTLISTFILE", "HOSTLIST");
1371     return;
1372   }
1373   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1374   {
1375     GNUNET_free (filename);
1376     return;
1377   }
1378   wh = GNUNET_BIO_write_open (filename);
1379   if (NULL == wh)
1380   {
1381     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1382                 _
1383                 ("Could not open file `%s' for writing to save hostlists: %s\n"),
1384                 filename, STRERROR (errno));
1385     GNUNET_free (filename);
1386     return;
1387   }
1388   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Writing %u hostlist URIs to `%s'\n"),
1389               linked_list_size, filename);
1390   /* add code to write hostlists to file using bio */
1391   ok = GNUNET_YES;
1392   counter = 0;
1393   while (NULL != (pos = linked_list_head))
1394   {
1395     if (GNUNET_YES == shutdown)
1396     {
1397       GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1398       linked_list_size--;
1399     }
1400     if (GNUNET_YES == ok)
1401     {
1402       if ((GNUNET_OK != GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1403           (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1404           (GNUNET_OK != GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1405           (GNUNET_OK !=
1406            GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value)) ||
1407           (GNUNET_OK !=
1408            GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value)) ||
1409           (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1410       {
1411         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1412                     _("Error writing hostlist URIs to file `%s'\n"), filename);
1413         ok = GNUNET_NO;
1414       }
1415     }
1416
1417     if (GNUNET_YES == shutdown)
1418       GNUNET_free (pos);
1419     counter++;
1420     if (counter >= MAX_NUMBER_HOSTLISTS)
1421       break;
1422   }
1423   GNUNET_STATISTICS_set (stats,
1424                          gettext_noop ("# hostlist URIs written to file"),
1425                          counter, GNUNET_YES);
1426
1427   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1428     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1429                 _("Error writing hostlist URIs to file `%s'\n"), filename);
1430   GNUNET_free (filename);
1431 }
1432
1433 /**
1434  * Start downloading hostlists from hostlist servers as necessary.
1435  */
1436 int
1437 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1438                               struct GNUNET_STATISTICS_Handle *st,
1439                               GNUNET_CORE_ConnectEventHandler *ch,
1440                               GNUNET_CORE_DisconnectEventHandler *dh,
1441                               GNUNET_CORE_MessageCallback *msgh, int learn)
1442 {
1443   char *filename;
1444   int result;
1445
1446   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1447   {
1448     GNUNET_break (0);
1449     return GNUNET_SYSERR;
1450   }
1451   transport = GNUNET_TRANSPORT_connect (c, NULL, NULL, NULL, NULL, NULL);
1452   if (NULL == transport)
1453   {
1454     curl_global_cleanup ();
1455     return GNUNET_SYSERR;
1456   }
1457   cfg = c;
1458   stats = st;
1459   if (GNUNET_OK !=
1460       GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST", "HTTP-PROXY",
1461                                              &proxy))
1462     proxy = NULL;
1463   stat_learning = learn;
1464   *ch = &handler_connect;
1465   *dh = &handler_disconnect;
1466   linked_list_head = NULL;
1467   linked_list_tail = NULL;
1468   stat_use_bootstrap = GNUNET_YES;
1469   stat_testing_hostlist = GNUNET_NO;
1470   stat_testing_allowed = GNUNET_YES;
1471
1472   if (GNUNET_YES == stat_learning)
1473   {
1474     *msgh = &handler_advertisement;
1475     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1476                 _("Learning is enabled on this peer\n"));
1477     load_hostlist_file ();
1478     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1479                 _("Hostlists will be saved to file again in  %llums\n"),
1480                 (unsigned long long) SAVING_INTERVALL.rel_value);
1481     ti_saving_task =
1482         GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL, &task_hostlist_saving,
1483                                       NULL);
1484   }
1485   else
1486   {
1487     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1488                 _("Learning is not enabled on this peer\n"));
1489     *msgh = NULL;
1490     if (GNUNET_OK ==
1491         GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST",
1492                                                  "HOSTLISTFILE", &filename))
1493     {
1494       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1495       {
1496         result = remove (filename);
1497         if (result == 0)
1498           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1499                       _
1500                       ("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1501                       filename);
1502         else
1503           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1504                       _("Hostlist file `%s' could not be removed\n"), filename);
1505       }
1506     }
1507     GNUNET_free (filename);
1508   }
1509   GNUNET_STATISTICS_get (stats, "hostlist",
1510                          gettext_noop
1511                          ("# milliseconds between hostlist downloads"),
1512                          GNUNET_TIME_UNIT_MINUTES, &primary_task, &process_stat,
1513                          NULL);
1514   return GNUNET_OK;
1515 }
1516
1517
1518 /**
1519  * Stop downloading hostlists from hostlist servers as necessary.
1520  */
1521 void
1522 GNUNET_HOSTLIST_client_stop ()
1523 {
1524   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1525 #if DEBUG_HOSTLIST_CLIENT
1526   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1527 #endif
1528   if (GNUNET_YES == stat_learning)
1529     save_hostlist_file (GNUNET_YES);
1530
1531   if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1532   {
1533     GNUNET_SCHEDULER_cancel (ti_saving_task);
1534   }
1535
1536   if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1537   {
1538     GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1539   }
1540   if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1541   {
1542     GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1543   }
1544   if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1545   {
1546     GNUNET_SCHEDULER_cancel (ti_download);
1547   }
1548   if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1549   {
1550     GNUNET_SCHEDULER_cancel (ti_check_download);
1551     curl_global_cleanup ();
1552   }
1553   if (transport != NULL)
1554   {
1555     GNUNET_TRANSPORT_disconnect (transport);
1556     transport = NULL;
1557   }
1558   GNUNET_assert (NULL == transport);
1559   GNUNET_free_non_null (proxy);
1560   proxy = NULL;
1561   cfg = NULL;
1562 }
1563
1564 /* end of hostlist-client.c */