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