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