small API change: do no longer pass rarely needed GNUNET_SCHEDULER_TaskContext to...
[oweals/gnunet.git] / src / hostlist / gnunet-daemon-hostlist_client.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2010, 2014 GNUnet e.V.
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
779
780 /**
781  * Ask CURL for the select set and then schedule the
782  * receiving task with the scheduler.
783  */
784 static void
785 download_prepare ()
786 {
787   CURLMcode mret;
788   fd_set rs;
789   fd_set ws;
790   fd_set es;
791   int max;
792   struct GNUNET_NETWORK_FDSet *grs;
793   struct GNUNET_NETWORK_FDSet *gws;
794   long timeout;
795   struct GNUNET_TIME_Relative rtime;
796
797   max = -1;
798   FD_ZERO (&rs);
799   FD_ZERO (&ws);
800   FD_ZERO (&es);
801   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
802   if (mret != CURLM_OK)
803   {
804     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
805                 "curl_multi_fdset", __FILE__, __LINE__,
806                 curl_multi_strerror (mret));
807     clean_up ();
808     return;
809   }
810   mret = curl_multi_timeout (multi, &timeout);
811   if (mret != CURLM_OK)
812   {
813     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
814                 "curl_multi_timeout", __FILE__, __LINE__,
815                 curl_multi_strerror (mret));
816     clean_up ();
817     return;
818   }
819   rtime =
820       GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
821                                 GNUNET_TIME_relative_multiply
822                                 (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
823   grs = GNUNET_NETWORK_fdset_create ();
824   gws = GNUNET_NETWORK_fdset_create ();
825   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
826   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
827   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828               "Scheduling task for hostlist download using cURL\n");
829   ti_download =
830       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
831                                    rtime, grs, gws,
832                                    &task_download, multi);
833   GNUNET_NETWORK_fdset_destroy (gws);
834   GNUNET_NETWORK_fdset_destroy (grs);
835 }
836
837
838 /**
839  * Task that is run when we are ready to receive more data from the hostlist
840  * server.
841  *
842  * @param cls closure, unused
843  */
844 static void
845 task_download (void *cls)
846 {
847   int running;
848   struct CURLMsg *msg;
849   CURLMcode mret;
850   const struct GNUNET_SCHEDULER_TaskContext *tc;
851
852   ti_download = NULL;
853   tc = GNUNET_SCHEDULER_get_task_context ();
854   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
855   {
856     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
857                 "Shutdown requested while trying to download hostlist from `%s'\n",
858                 current_url);
859     update_hostlist ();
860     clean_up ();
861     return;
862   }
863   if (0 == GNUNET_TIME_absolute_get_remaining (end_time).rel_value_us)
864   {
865     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
866                 _("Timeout trying to download hostlist from `%s'\n"),
867                 current_url);
868     update_hostlist ();
869     clean_up ();
870     return;
871   }
872   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
873               "Ready for processing hostlist client request\n");
874   do
875   {
876     running = 0;
877     if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
878     {
879       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
880                   _("Download limit of %u bytes exceeded, stopping download\n"),
881                   MAX_BYTES_PER_HOSTLISTS);
882       clean_up ();
883       return;
884     }
885     mret = curl_multi_perform (multi, &running);
886     if (running == 0)
887     {
888       do
889       {
890         msg = curl_multi_info_read (multi, &running);
891         GNUNET_break (msg != NULL);
892         if (msg == NULL)
893           break;
894         switch (msg->msg)
895         {
896         case CURLMSG_DONE:
897           if ((msg->data.result != CURLE_OK) &&
898               (msg->data.result != CURLE_GOT_NOTHING))
899             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
900                         _("Download of hostlist from `%s' failed: `%s'\n"),
901                         current_url,
902                         curl_easy_strerror (msg->data.result));
903           else
904           {
905             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
906                         _("Download of hostlist `%s' completed.\n"),
907                         current_url);
908             stat_download_successful = GNUNET_YES;
909             update_hostlist ();
910             if (GNUNET_YES == stat_testing_hostlist)
911             {
912               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
913                           _
914                           ("Adding successfully tested hostlist `%s' datastore.\n"),
915                           current_url);
916               insert_hostlist ();
917               hostlist_to_test = NULL;
918               stat_testing_hostlist = GNUNET_NO;
919             }
920           }
921           clean_up ();
922           return;
923         default:
924           break;
925         }
926
927       }
928       while ((running > 0));
929     }
930   }
931   while (mret == CURLM_CALL_MULTI_PERFORM);
932
933   if (mret != CURLM_OK)
934   {
935     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s failed at %s:%d: `%s'\n"),
936                 "curl_multi_perform", __FILE__, __LINE__,
937                 curl_multi_strerror (mret));
938     clean_up ();
939   }
940   download_prepare ();
941 }
942
943
944 /**
945  * Main function that will download a hostlist and process its
946  * data.
947  */
948 static void
949 download_hostlist ()
950 {
951   CURLcode ret;
952   CURLMcode mret;
953
954
955   current_url = download_get_url ();
956   if (current_url == NULL)
957     return;
958   curl = curl_easy_init ();
959   multi = NULL;
960   if (curl == NULL)
961   {
962     GNUNET_break (0);
963     clean_up ();
964     return;
965   }
966   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
967               _("Bootstrapping using hostlist at `%s'.\n"), current_url);
968
969   stat_download_in_progress = GNUNET_YES;
970   stat_download_successful = GNUNET_NO;
971   stat_hellos_obtained = 0;
972   stat_bytes_downloaded = 0;
973
974   GNUNET_STATISTICS_update (stats,
975                             gettext_noop ("# hostlist downloads initiated"), 1,
976                             GNUNET_NO);
977   if (NULL != proxy)
978   {
979     CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
980     CURL_EASY_SETOPT (curl, CURLOPT_PROXYTYPE, proxy_type);
981     if (NULL != proxy_username)
982       CURL_EASY_SETOPT (curl, CURLOPT_PROXYUSERNAME, proxy_username);
983     if (NULL != proxy_password)
984       CURL_EASY_SETOPT (curl, CURLOPT_PROXYPASSWORD, proxy_password);
985   }
986   download_pos = 0;
987   stat_bogus_url = 0;
988   CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
989   if (ret != CURLE_OK)
990   {
991     clean_up ();
992     return;
993   }
994   CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
995   if (ret != CURLE_OK)
996   {
997     clean_up ();
998     return;
999   }
1000   CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
1001   CURL_EASY_SETOPT (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1002   CURL_EASY_SETOPT (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1003   CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
1004   /* no need to abort if the above failed */
1005   CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
1006   if (ret != CURLE_OK)
1007   {
1008     clean_up ();
1009     return;
1010   }
1011   CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
1012 #if 0
1013   CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
1014 #endif
1015   CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1016   if (0 == strncmp (current_url, "http", 4))
1017     CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
1018   CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
1019   CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
1020   multi = curl_multi_init ();
1021   if (multi == NULL)
1022   {
1023     GNUNET_break (0);
1024     /* clean_up (); */
1025     return;
1026   }
1027   mret = curl_multi_add_handle (multi, curl);
1028   if (mret != CURLM_OK)
1029   {
1030     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1031                 "curl_multi_add_handle", __FILE__, __LINE__,
1032                 curl_multi_strerror (mret));
1033     mret = curl_multi_cleanup (multi);
1034     if (mret != CURLM_OK)
1035       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1036                   "curl_multi_cleanup", __FILE__, __LINE__,
1037                   curl_multi_strerror (mret));
1038     multi = NULL;
1039     clean_up ();
1040     return;
1041   }
1042   end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1043   download_prepare ();
1044 }
1045
1046
1047 static void
1048 task_download_dispatcher (void *cls)
1049 {
1050   const struct GNUNET_SCHEDULER_TaskContext *tc;
1051
1052   ti_download_dispatcher_task = NULL;
1053   tc = GNUNET_SCHEDULER_get_task_context ();
1054   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1055     return;
1056   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1057   if (GNUNET_NO == stat_download_in_progress)
1058   {
1059     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1060     download_hostlist ();
1061   }
1062   else
1063   {
1064     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065                 "Download in progess, have to wait...\n");
1066     ti_download_dispatcher_task =
1067         GNUNET_SCHEDULER_add_delayed (WAITING_INTERVAL,
1068                                       &task_download_dispatcher, NULL);
1069   }
1070 }
1071
1072
1073 /**
1074  * Task that checks if we should try to download a hostlist.
1075  * If so, we initiate the download, otherwise we schedule
1076  * this task again for a later time.
1077  */
1078 static void
1079 task_check (void *cls)
1080 {
1081   static int once;
1082   struct GNUNET_TIME_Relative delay;
1083   const struct GNUNET_SCHEDULER_TaskContext *tc;
1084
1085   ti_check_download = NULL;
1086   tc = GNUNET_SCHEDULER_get_task_context ();
1087   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1088     return;
1089   if (stats == NULL)
1090   {
1091     curl_global_cleanup ();
1092     return;                     /* in shutdown */
1093   }
1094   if ( (stat_connection_count < MIN_CONNECTIONS) &&
1095        (NULL == ti_download_dispatcher_task) )
1096     ti_download_dispatcher_task =
1097         GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1098
1099   delay = hostlist_delay;
1100   if (0 == hostlist_delay.rel_value_us)
1101     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1102   else
1103     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1104   if (hostlist_delay.rel_value_us >
1105       GNUNET_TIME_UNIT_HOURS.rel_value_us * (1 + stat_connection_count))
1106     hostlist_delay =
1107         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1108                                        (1 + stat_connection_count));
1109   GNUNET_STATISTICS_set (stats,
1110                          gettext_noop
1111                          ("# milliseconds between hostlist downloads"),
1112                          hostlist_delay.rel_value_us / 1000LL,
1113                          GNUNET_YES);
1114   if (0 == once)
1115   {
1116     delay = GNUNET_TIME_UNIT_ZERO;
1117     once = 1;
1118   }
1119   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1120               _("Have %u/%u connections.  Will consider downloading hostlist in %s\n"),
1121               stat_connection_count, MIN_CONNECTIONS,
1122               GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
1123   ti_check_download = GNUNET_SCHEDULER_add_delayed (delay, &task_check, NULL);
1124 }
1125
1126
1127 /**
1128  * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1129  *
1130  * @param cls closure
1131  */
1132 static void
1133 task_testing_intervall_reset (void *cls)
1134 {
1135   const struct GNUNET_SCHEDULER_TaskContext *tc;
1136
1137   ti_testing_intervall_task = NULL;
1138   tc = GNUNET_SCHEDULER_get_task_context ();
1139   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1140     return;
1141   stat_testing_allowed = GNUNET_OK;
1142   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1143               "Testing new hostlist advertisements is allowed again\n");
1144 }
1145
1146
1147 /**
1148  * Task that writes hostlist entries to a file on a regular base
1149  *
1150  * @param cls closure
1151  */
1152 static void
1153 task_hostlist_saving (void *cls)
1154 {
1155   const struct GNUNET_SCHEDULER_TaskContext *tc;
1156
1157   ti_saving_task = NULL;
1158   tc = GNUNET_SCHEDULER_get_task_context ();
1159   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1160     return;
1161   save_hostlist_file (GNUNET_NO);
1162
1163   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1164               "Hostlists will be saved to file again in %s\n",
1165               GNUNET_STRINGS_relative_time_to_string(SAVING_INTERVAL, GNUNET_YES));
1166   ti_saving_task =
1167       GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL, &task_hostlist_saving,
1168                                     NULL);
1169 }
1170
1171
1172 /**
1173  * Method called whenever a given peer connects.
1174  *
1175  * @param cls closure
1176  * @param peer peer identity this notification is about
1177  */
1178 static void
1179 handler_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
1180 {
1181   GNUNET_assert (stat_connection_count < UINT_MAX);
1182   stat_connection_count++;
1183   GNUNET_STATISTICS_update (stats, gettext_noop ("# active connections"), 1,
1184                             GNUNET_NO);
1185 }
1186
1187
1188 /**
1189  * Method called whenever a given peer disconnects.
1190  *
1191  * @param cls closure
1192  * @param peer peer identity this notification is about
1193  */
1194 static void
1195 handler_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1196 {
1197   GNUNET_assert (stat_connection_count > 0);
1198   stat_connection_count--;
1199   GNUNET_STATISTICS_update (stats, gettext_noop ("# active connections"), -1,
1200                             GNUNET_NO);
1201 }
1202
1203
1204 /**
1205  * Method called whenever an advertisement message arrives.
1206  *
1207  * @param cls closure (always NULL)
1208  * @param peer the peer sending the message
1209  * @param message the actual message
1210  * @return #GNUNET_OK to keep the connection open,
1211  *         #GNUNET_SYSERR to close it (signal serious error)
1212  */
1213 static int
1214 handler_advertisement (void *cls, const struct GNUNET_PeerIdentity *peer,
1215                        const struct GNUNET_MessageHeader *message)
1216 {
1217   size_t size;
1218   size_t uri_size;
1219   const struct GNUNET_MessageHeader *incoming;
1220   const char *uri;
1221   struct Hostlist *hostlist;
1222
1223   GNUNET_assert (ntohs (message->type) ==
1224                  GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1225   size = ntohs (message->size);
1226   if (size <= sizeof (struct GNUNET_MessageHeader))
1227   {
1228     GNUNET_break_op (0);
1229     return GNUNET_SYSERR;
1230   }
1231   incoming = (const struct GNUNET_MessageHeader *) message;
1232   uri = (const char *) &incoming[1];
1233   uri_size = size - sizeof (struct GNUNET_MessageHeader);
1234   if (uri[uri_size - 1] != '\0')
1235   {
1236     GNUNET_break_op (0);
1237     return GNUNET_SYSERR;
1238   }
1239   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240               "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1241               GNUNET_i2s (peer), uri);
1242   if (GNUNET_NO != linked_list_contains (uri))
1243   {
1244     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1245     return GNUNET_OK;
1246   }
1247
1248   if (GNUNET_NO == stat_testing_allowed)
1249   {
1250     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1251                 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1252     return GNUNET_SYSERR;
1253   }
1254   if (GNUNET_YES == stat_testing_hostlist)
1255   {
1256     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1257                 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1258     return GNUNET_SYSERR;
1259   }
1260
1261   hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1262   hostlist->hostlist_uri = (const char *) &hostlist[1];
1263   memcpy (&hostlist[1], uri, uri_size);
1264   hostlist->time_creation = GNUNET_TIME_absolute_get ();
1265   hostlist->quality = HOSTLIST_INITIAL;
1266   hostlist_to_test = hostlist;
1267
1268   stat_testing_hostlist = GNUNET_YES;
1269   stat_testing_allowed = GNUNET_NO;
1270   ti_testing_intervall_task =
1271       GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1272                                     &task_testing_intervall_reset, NULL);
1273
1274   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1275               "Testing new hostlist advertisements is locked for the next %s\n",
1276               GNUNET_STRINGS_relative_time_to_string (TESTING_INTERVAL,
1277                                                       GNUNET_YES));
1278
1279   ti_download_dispatcher_task =
1280       GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1281
1282   return GNUNET_OK;
1283 }
1284
1285
1286 /**
1287  * Continuation called by the statistics code once
1288  * we go the stat.  Initiates hostlist download scheduling.
1289  *
1290  * @param cls closure
1291  * @param success #GNUNET_OK if statistics were
1292  *        successfully obtained, #GNUNET_SYSERR if not.
1293  */
1294 static void
1295 primary_task (void *cls, int success)
1296 {
1297   sget = NULL;
1298   GNUNET_assert (NULL != stats);
1299   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1300               "Statistics request done, scheduling hostlist download\n");
1301   ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1302 }
1303
1304
1305 /**
1306  * We've received the previous delay value from statistics.  Remember it.
1307  *
1308  * @param cls NULL, unused
1309  * @param subsystem should be "hostlist", unused
1310  * @param name will be "milliseconds between hostlist downloads", unused
1311  * @param value previous delay value, in milliseconds (!)
1312  * @param is_persistent unused, will be #GNUNET_YES
1313  */
1314 static int
1315 process_stat (void *cls,
1316               const char *subsystem,
1317               const char *name,
1318               uint64_t value,
1319               int is_persistent)
1320 {
1321   hostlist_delay.rel_value_us = value * 1000LL;
1322   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1323               "Initial time between hostlist downloads is %s\n",
1324               GNUNET_STRINGS_relative_time_to_string (hostlist_delay,
1325                                                       GNUNET_YES));
1326   return GNUNET_OK;
1327 }
1328
1329
1330 /**
1331  * Method to load persistent hostlist file during hostlist client startup
1332  */
1333 static void
1334 load_hostlist_file ()
1335 {
1336   char *filename;
1337   char *uri;
1338   char *emsg;
1339   struct Hostlist *hostlist;
1340   uint32_t times_used;
1341   uint32_t hellos_returned;
1342   uint64_t quality;
1343   uint64_t last_used;
1344   uint64_t created;
1345   uint32_t counter;
1346   struct GNUNET_BIO_ReadHandle *rh;
1347
1348   uri = NULL;
1349   if (GNUNET_OK !=
1350       GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1351                                                &filename))
1352   {
1353     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1354                                "hostlist", "HOSTLISTFILE");
1355     return;
1356   }
1357
1358   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1359               _("Loading saved hostlist entries from file `%s' \n"),
1360               filename);
1361   if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1362   {
1363     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1364                 _("Hostlist file `%s' does not exist\n"), filename);
1365     GNUNET_free (filename);
1366     return;
1367   }
1368
1369   rh = GNUNET_BIO_read_open (filename);
1370   if (NULL == rh)
1371   {
1372     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1373                 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1374                 filename, STRERROR (errno));
1375     GNUNET_free (filename);
1376     return;
1377   }
1378
1379   counter = 0;
1380   while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1381          (NULL != uri) && (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used))
1382          && (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1383          (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1384          (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1385          (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)))
1386   {
1387     hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1388     hostlist->hello_count = hellos_returned;
1389     hostlist->hostlist_uri = (const char *) &hostlist[1];
1390     memcpy (&hostlist[1], uri, strlen (uri) + 1);
1391     hostlist->quality = quality;
1392     hostlist->time_creation.abs_value_us = created;
1393     hostlist->time_last_usage.abs_value_us = last_used;
1394     GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1395     linked_list_size++;
1396     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1397                 "Added hostlist entry eith URI `%s' \n",
1398                 hostlist->hostlist_uri);
1399     GNUNET_free (uri);
1400     uri = NULL;
1401     counter++;
1402     if (counter >= MAX_NUMBER_HOSTLISTS)
1403       break;
1404   }
1405
1406   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1407               _("%u hostlist URIs loaded from file\n"),
1408               counter);
1409   GNUNET_STATISTICS_set (stats,
1410                          gettext_noop ("# hostlist URIs read from file"),
1411                          counter, GNUNET_YES);
1412   GNUNET_STATISTICS_set (stats,
1413                          gettext_noop ("# advertised hostlist URIs"),
1414                          linked_list_size, GNUNET_NO);
1415
1416   GNUNET_free_non_null (uri);
1417   emsg = NULL;
1418   (void) GNUNET_BIO_read_close (rh, &emsg);
1419   if (emsg != NULL)
1420     GNUNET_free (emsg);
1421   GNUNET_free (filename);
1422 }
1423
1424
1425 /**
1426  * Method to save persistent hostlist file during hostlist client shutdown
1427  *
1428  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1429  */
1430 static void
1431 save_hostlist_file (int shutdown)
1432 {
1433   char *filename;
1434   struct Hostlist *pos;
1435   struct GNUNET_BIO_WriteHandle *wh;
1436   int ok;
1437   uint32_t counter;
1438
1439   if (GNUNET_OK !=
1440       GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1441                                                &filename))
1442   {
1443     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1444                                "hostlist", "HOSTLISTFILE");
1445     return;
1446   }
1447   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1448   {
1449     GNUNET_free (filename);
1450     return;
1451   }
1452   wh = GNUNET_BIO_write_open (filename);
1453   if (NULL == wh)
1454   {
1455     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1456                 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1457                 filename,
1458                 STRERROR (errno));
1459     GNUNET_free (filename);
1460     return;
1461   }
1462   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1463               _("Writing %u hostlist URIs to `%s'\n"),
1464               linked_list_size, filename);
1465   /* add code to write hostlists to file using bio */
1466   ok = GNUNET_YES;
1467   counter = 0;
1468   while (NULL != (pos = linked_list_head))
1469   {
1470     if (GNUNET_YES == shutdown)
1471     {
1472       GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1473       linked_list_size--;
1474     }
1475     if (GNUNET_YES == ok)
1476     {
1477       if ((GNUNET_OK != GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1478           (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1479           (GNUNET_OK != GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1480           (GNUNET_OK !=
1481            GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value_us)) ||
1482           (GNUNET_OK !=
1483            GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value_us)) ||
1484           (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1485       {
1486         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1487                     _("Error writing hostlist URIs to file `%s'\n"), filename);
1488         ok = GNUNET_NO;
1489       }
1490     }
1491
1492     if (GNUNET_YES == shutdown)
1493       GNUNET_free (pos);
1494     counter++;
1495     if (counter >= MAX_NUMBER_HOSTLISTS)
1496       break;
1497   }
1498   GNUNET_STATISTICS_set (stats,
1499                          gettext_noop ("# hostlist URIs written to file"),
1500                          counter, GNUNET_YES);
1501
1502   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1503     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1504                 _("Error writing hostlist URIs to file `%s'\n"), filename);
1505   GNUNET_free (filename);
1506 }
1507
1508
1509 /**
1510  * Start downloading hostlists from hostlist servers as necessary.
1511  *
1512  * @param c configuration to use
1513  * @param st statistics handle to use
1514  * @param[out] ch set to handler for CORE connect events
1515  * @param[out] dh set to handler for CORE disconnect events
1516  * @param[out] msgh set to handler for CORE advertisement messages
1517  * @param learn should we learn hostlist URLs from CORE
1518  * @return #GNUNET_OK on success
1519  */
1520 int
1521 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1522                               struct GNUNET_STATISTICS_Handle *st,
1523                               GNUNET_CORE_ConnectEventHandler *ch,
1524                               GNUNET_CORE_DisconnectEventHandler *dh,
1525                               GNUNET_CORE_MessageCallback *msgh,
1526                               int learn)
1527 {
1528   char *filename;
1529   char *proxytype_str;
1530   int result;
1531
1532   GNUNET_assert (NULL != st);
1533   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1534   {
1535     GNUNET_break (0);
1536     return GNUNET_SYSERR;
1537   }
1538   transport = GNUNET_TRANSPORT_connect (c, NULL, NULL, NULL, NULL, NULL);
1539   if (NULL == transport)
1540   {
1541     GNUNET_break (0);
1542     curl_global_cleanup ();
1543     return GNUNET_SYSERR;
1544   }
1545   cfg = c;
1546   stats = st;
1547
1548   /* Read proxy configuration */
1549   if (GNUNET_OK ==
1550       GNUNET_CONFIGURATION_get_value_string (cfg,
1551                                              "HOSTLIST", "PROXY", &proxy))
1552   {
1553     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1554                      "Found proxy host: `%s'\n",
1555                      proxy);
1556     /* proxy username */
1557     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1558         "HOSTLIST", "PROXY_USERNAME", &proxy_username))
1559     {
1560       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1561                        "Found proxy username name: `%s'\n",
1562                        proxy_username);
1563     }
1564
1565     /* proxy password */
1566     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1567         "HOSTLIST", "PROXY_PASSWORD", &proxy_password))
1568     {
1569       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1570                        "Found proxy password name: `%s'\n",
1571                        proxy_password);
1572     }
1573
1574     /* proxy type */
1575     if (GNUNET_OK ==
1576         GNUNET_CONFIGURATION_get_value_string (cfg,
1577                                                "HOSTLIST",
1578                                                "PROXY_TYPE",
1579                                                &proxytype_str))
1580     {
1581       GNUNET_STRINGS_utf8_toupper (proxytype_str,
1582                                    proxytype_str);
1583       proxy_type = CURLPROXY_HTTP;
1584       if (0 == strcmp (proxytype_str, "HTTP"))
1585         proxy_type = CURLPROXY_HTTP;
1586       else if (0 == strcmp (proxytype_str, "HTTP_1_0"))
1587         proxy_type = CURLPROXY_HTTP_1_0;
1588       else if (0 == strcmp (proxytype_str, "SOCKS4"))
1589         proxy_type = CURLPROXY_SOCKS4;
1590       else if (0 == strcmp (proxytype_str, "SOCKS5"))
1591         proxy_type = CURLPROXY_SOCKS5;
1592       else if (0 == strcmp (proxytype_str, "SOCKS4A"))
1593         proxy_type = CURLPROXY_SOCKS4A;
1594       else if (0 == strcmp (proxytype_str, "SOCKS5_HOSTNAME"))
1595         proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1596       else
1597       {
1598         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1599                     _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
1600                     proxytype_str);
1601         GNUNET_free (proxytype_str);
1602         GNUNET_free (proxy);
1603         proxy = NULL;
1604         GNUNET_free_non_null (proxy_username);
1605         proxy_username = NULL;
1606         GNUNET_free_non_null (proxy_password);
1607         proxy_password = NULL;
1608
1609         return GNUNET_SYSERR;
1610       }
1611     }
1612     GNUNET_free_non_null (proxytype_str);
1613   }
1614
1615   stat_learning = learn;
1616   *ch = &handler_connect;
1617   *dh = &handler_disconnect;
1618   linked_list_head = NULL;
1619   linked_list_tail = NULL;
1620   stat_use_bootstrap = GNUNET_YES;
1621   stat_testing_hostlist = GNUNET_NO;
1622   stat_testing_allowed = GNUNET_YES;
1623
1624   if (GNUNET_YES == stat_learning)
1625   {
1626     *msgh = &handler_advertisement;
1627     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1628                 _("Learning is enabled on this peer\n"));
1629     load_hostlist_file ();
1630     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1631                 "Hostlists will be saved to file again in %s\n",
1632                 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL, GNUNET_YES));
1633     ti_saving_task =
1634         GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1635                                       &task_hostlist_saving,
1636                                       NULL);
1637   }
1638   else
1639   {
1640     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1641                 _("Learning is not enabled on this peer\n"));
1642     *msgh = NULL;
1643     if (GNUNET_OK ==
1644         GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST",
1645                                                  "HOSTLISTFILE", &filename))
1646     {
1647       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1648       {
1649         result = remove (filename);
1650         if (0 == result)
1651           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1652                       _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1653                       filename);
1654         else
1655           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1656                                     "remove",
1657                                     filename);
1658       }
1659     }
1660     GNUNET_free (filename);
1661   }
1662   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1663               "Loading stats value on hostlist download frequency\n");
1664   sget = GNUNET_STATISTICS_get (stats, "hostlist",
1665                                 gettext_noop
1666                                 ("# milliseconds between hostlist downloads"),
1667                                 GNUNET_TIME_UNIT_MINUTES,
1668                                 &primary_task,
1669                                 &process_stat,
1670                                 NULL);
1671   if (NULL == sget)
1672   {
1673     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1674                 "Statistics request failed, scheduling hostlist download\n");
1675     ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1676   }
1677   return GNUNET_OK;
1678 }
1679
1680
1681 /**
1682  * Stop downloading hostlists from hostlist servers as necessary.
1683  */
1684 void
1685 GNUNET_HOSTLIST_client_stop ()
1686 {
1687   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1688   if (NULL != sget)
1689   {
1690     GNUNET_STATISTICS_get_cancel (sget);
1691     sget = NULL;
1692   }
1693   stats = NULL;
1694   if (GNUNET_YES == stat_learning)
1695     save_hostlist_file (GNUNET_YES);
1696   if (ti_saving_task != NULL)
1697   {
1698     GNUNET_SCHEDULER_cancel (ti_saving_task);
1699     ti_saving_task = NULL;
1700   }
1701
1702   if (ti_download_dispatcher_task != NULL)
1703     {
1704     GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1705     ti_download_dispatcher_task = NULL;
1706   }
1707   if (ti_testing_intervall_task != NULL)
1708   {
1709     GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1710     ti_testing_intervall_task = NULL;
1711   }
1712   if (ti_download != NULL)
1713   {
1714     GNUNET_SCHEDULER_cancel (ti_download);
1715     ti_download = NULL;
1716   }
1717   if (ti_check_download != NULL)
1718   {
1719     GNUNET_SCHEDULER_cancel (ti_check_download);
1720     ti_check_download = NULL;
1721     curl_global_cleanup ();
1722   }
1723   if (NULL != transport)
1724   {
1725     GNUNET_TRANSPORT_disconnect (transport);
1726     transport = NULL;
1727   }
1728   GNUNET_free_non_null (proxy);
1729   proxy = NULL;
1730   GNUNET_free_non_null (proxy_username);
1731   proxy_username = NULL;
1732   GNUNET_free_non_null (proxy_password);
1733   proxy_password = NULL;
1734
1735   cfg = NULL;
1736 }
1737
1738 /* end of gnunet-daemon-hostlist_client.c */