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