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