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