(no commit message)
[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_NO
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  *
153  */
154 static uint32_t stat_bytes_downloaded;
155 /**
156  * Amount of time we wait between hostlist downloads.
157  */
158 static struct GNUNET_TIME_Relative hostlist_delay;
159
160 /**
161  * ID of the task, checking if hostlist download should take plate
162  */
163 static GNUNET_SCHEDULER_TaskIdentifier ti_check_download;
164
165 /**
166  * ID of the task downloading the hostlist
167  */
168 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
169
170 /**
171  * ID of the task saving the hostlsit in a regular intervall
172  */
173 static GNUNET_SCHEDULER_TaskIdentifier ti_saving_task;
174
175 /**
176  * ID of the task called to initiate a download
177  */
178 static GNUNET_SCHEDULER_TaskIdentifier ti_download_dispatcher_task;
179
180 /**
181  * ID of the task controlling the locking between two hostlist tests
182  */
183 static GNUNET_SCHEDULER_TaskIdentifier ti_testing_intervall_task;
184
185 /**
186  * At what time MUST the current hostlist request be done?
187  */
188 static struct GNUNET_TIME_Absolute end_time;
189
190 /**
191  * Head of the linked list used to store hostlists
192  */
193 static struct Hostlist * linked_list_head;
194
195 /**
196  *  Tail of the linked list used to store hostlists
197  */
198 static struct Hostlist * linked_list_tail;
199
200 /**
201  *  Current hostlist used for downloading
202  */
203 static struct Hostlist * current_hostlist;
204
205 /**
206  *  Size of the linke list  used to store hostlists
207  */
208 static unsigned int linked_list_size;
209
210 /**
211  * Head of the linked list used to store hostlists
212  */
213 static struct Hostlist * hostlist_to_test;
214
215 /**
216  * Set to GNUNET_YES if the current URL had some problems.
217  */
218 static int stat_bogus_url;
219
220 /**
221  * Value controlling if a hostlist is tested at the moment
222  */
223 static int stat_testing_hostlist;
224
225 /**
226  * Value controlling if a hostlist testing is allowed at the moment
227  */
228 static int stat_testing_allowed;
229
230 /**
231  * Value controlling if a hostlist download is running at the moment
232  */
233 static int stat_download_in_progress;
234
235 /**
236  * Value saying if a preconfigured bootstrap server is used
237  */
238 static unsigned int stat_use_bootstrap;
239 /**
240  * Set if we are allowed to learn new hostlists and use them
241  */
242 static int stat_learning;
243
244 /**
245  * Value saying if hostlist download was successful
246  */
247 static unsigned int stat_download_successful;
248
249 /**
250  * Value saying how many valid HELLO messages were obtained during download
251  */
252 static unsigned int stat_hellos_obtained;
253
254 /**
255  * Number of active connections (according to core service).
256  */
257 static unsigned int stat_connection_count;
258
259
260 /**
261  * Process downloaded bits by calling callback on each HELLO.
262  *
263  * @param ptr buffer with downloaded data
264  * @param size size of a record
265  * @param nmemb number of records downloaded
266  * @param ctx unused
267  * @return number of bytes that were processed (always size*nmemb)
268  */
269 static size_t
270 callback_download (void *ptr, 
271                              size_t size, 
272                              size_t nmemb, 
273                              void *ctx)
274 {
275   const char * cbuf = ptr;
276   const struct GNUNET_MessageHeader *msg;
277   size_t total;
278   size_t cpy;
279   size_t left;
280   uint16_t msize;
281
282   total = size * nmemb;
283   stat_bytes_downloaded += total;
284   if ( (total == 0) || (stat_bogus_url) )
285     {
286       return total;  /* ok, no data or bogus data */
287     }
288
289   GNUNET_STATISTICS_update (stats, 
290                             gettext_noop ("# bytes downloaded from hostlist servers"), 
291                             (int64_t) total, 
292                             GNUNET_NO);  
293   left = total;
294   while ( (left > 0) ||
295           (download_pos > 0) )
296     {
297       cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
298       memcpy (&download_buffer[download_pos],
299               cbuf,
300               cpy);      
301       cbuf += cpy;
302       download_pos += cpy;
303       left -= cpy;
304       if (download_pos < sizeof(struct GNUNET_MessageHeader))
305         {
306           GNUNET_assert (left == 0);
307           break;
308         }
309       msg = (const struct GNUNET_MessageHeader *) download_buffer;
310       msize = ntohs(msg->size);
311       if (msize < sizeof(struct GNUNET_MessageHeader))
312         {        
313           GNUNET_STATISTICS_update (stats, 
314                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
315                                     1, 
316                                     GNUNET_NO);  
317           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
318                       _("Invalid `%s' message received from hostlist at `%s'\n"),
319                       "HELLO",
320                       current_url);
321           stat_hellos_obtained++;
322           stat_bogus_url = 1;
323           return total;
324         }
325       if (download_pos < msize)
326         {
327           GNUNET_assert (left == 0);
328           break;
329         }
330       if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
331         {
332 #if DEBUG_HOSTLIST_CLIENT
333           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334                       "Received valid `%s' message from hostlist server.\n",
335                       "HELLO");
336 #endif
337           GNUNET_STATISTICS_update (stats, 
338                                     gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
339                                     1, 
340                                     GNUNET_NO);
341           stat_hellos_obtained++;
342           GNUNET_TRANSPORT_offer_hello (transport, msg);
343         }
344       else
345         {
346           GNUNET_STATISTICS_update (stats, 
347                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
348                                     1, 
349                                     GNUNET_NO);  
350           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
351                       _("Invalid `%s' message received from hostlist at `%s'\n"),
352                       "HELLO",
353                       current_url);
354           stat_bogus_url = GNUNET_YES;
355           stat_hellos_obtained++;
356           return total;
357         }
358       memmove (download_buffer,
359                &download_buffer[msize],
360                download_pos - msize);
361       download_pos -= msize;
362     }
363   return total;
364 }
365
366
367 /**
368  * Obtain a hostlist URL that we should use.
369  *
370  * @return NULL if there is no URL available
371  */
372 static char *
373 get_bootstrap_server ()
374 {
375   char *servers;
376   char *ret;
377   size_t urls;
378   size_t pos;
379
380   if (GNUNET_OK != 
381       GNUNET_CONFIGURATION_get_value_string (cfg,
382                                              "HOSTLIST",
383                                              "SERVERS",
384                                              &servers))
385     {
386       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
387                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
388                   "SERVERS", "HOSTLIST");
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 (GNUNET_ERROR_TYPE_WARNING,
407                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
408                   "SERVERS", "HOSTLIST");
409       GNUNET_free (servers);
410       return NULL;
411     }
412
413   urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
414   pos = strlen (servers) - 1;
415   while (pos > 0)
416     {
417       if (servers[pos] == ' ')
418         {
419           urls--;
420           servers[pos] = '\0';
421         }
422       if (urls == 0)
423         {
424           pos++;
425           break;
426         }
427       pos--;    
428     }
429   ret = GNUNET_strdup (&servers[pos]);
430   GNUNET_free (servers);
431   return ret;
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 strdup(hostlist_to_test->hostlist_uri);
459   }
460
461   if ( (GNUNET_YES == stat_use_bootstrap) ||
462        (linked_list_size == 0) )
463   {
464     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465                 "Using preconfigured bootstrap server\n");
466     current_hostlist = NULL;
467     return get_bootstrap_server();
468   }
469   index = 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,
478               "Using learned hostlist `%s'\n", pos->hostlist_uri);
479   current_hostlist = pos;
480   return strdup(pos->hostlist_uri);
481 }
482
483
484 #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);
485
486 /**
487  * Method to save hostlist to a file during hostlist client shutdown
488  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
489  */
490 static void save_hostlist_file ( int shutdown );
491
492 /**
493  * add val2 to val1 with overflow check
494  * @param val1 value 1
495  * @param val2 value 2
496  * @return result
497  */
498 static uint64_t checked_add (uint64_t val1, uint64_t val2)
499 {
500   static uint64_t temp;
501   static uint64_t maxv;
502
503   maxv = 0;
504   maxv--;
505
506   temp = val1+val2;
507   if ( temp < val1)
508     return maxv;
509   else
510     return temp;
511 }
512
513 /**
514  * Subtract val2 from val1 with underflow check
515  * @param val1 value 1
516  * @param val2 value 2
517  * @return result
518  */
519 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
520 {
521   if ( val1 <= val2)
522     return 0;
523   else
524     return (val1-val2);
525 }
526
527 /**
528  * Method to check if  a URI is in hostlist linked list
529  * @param uri uri to check
530  * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
531  */
532 static int
533 linked_list_contains (const char * uri)
534 {
535   struct Hostlist * pos;
536
537   pos = linked_list_head;
538   while (pos != NULL)
539     {
540       if (0 == strcmp(pos->hostlist_uri, uri) )
541         return GNUNET_YES;
542       pos = pos->next;
543     }
544   return GNUNET_NO;
545 }
546
547
548 /**
549  * Method returning the hostlist element with the lowest quality in the datastore
550  * @return hostlist with lowest quality
551  */
552 static struct Hostlist *
553 linked_list_get_lowest_quality ( )
554 {
555   struct Hostlist * pos;
556   struct Hostlist * lowest;
557
558   if (linked_list_size == 0)
559     return NULL;
560   lowest = linked_list_head;
561   pos = linked_list_head->next;
562   while (pos != NULL)
563     {
564       if (pos->quality < lowest->quality)
565         lowest = pos;
566       pos = pos->next;
567     }
568   return lowest;
569 }
570
571
572 /**
573  * Method to insert a hostlist into the datastore. If datastore contains maximum number of elements, the elements with lowest quality is dismissed
574  */
575 static void
576 insert_hostlist ( void )
577 {
578   GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
579   linked_list_size++;
580
581   GNUNET_STATISTICS_set (stats,
582                          gettext_noop("# advertised hostlist URIs"),
583                          linked_list_size,
584                          GNUNET_NO);
585
586   if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
587     return;
588
589   /* No free entries available, replace existing entry  */
590   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591               "Removing lowest quality entry\n" );
592   struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
593   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
594               "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
595               lowest_quality->hostlist_uri,
596               (unsigned long long) lowest_quality->quality);
597   GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
598   linked_list_size--;
599
600   GNUNET_STATISTICS_set (stats,
601                          gettext_noop("# advertised hostlist URIs"),
602                          linked_list_size,
603                          GNUNET_NO);
604
605   GNUNET_free (lowest_quality);
606
607   stat_testing_hostlist = GNUNET_NO;
608   return;
609 }
610
611
612 /**
613  * Method updating hostlist statistics
614  */
615 static void update_hostlist ( )
616 {
617   char *stat;
618   if ( ((stat_use_bootstrap == GNUNET_NO) && ( NULL != current_hostlist )) ||
619        ((stat_testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
620   {
621     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
622                 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
623      current_hostlist->hello_count = stat_hellos_obtained;
624      current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
625      current_hostlist->quality = checked_add ( current_hostlist->quality, (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
626      if ( GNUNET_YES == stat_download_successful )
627      {
628        current_hostlist->times_used++;
629        current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
630        GNUNET_asprintf (&stat,
631                         gettext_noop("# advertised URI `%s' downloaded"),
632                         current_hostlist->hostlist_uri);
633
634        GNUNET_STATISTICS_update ( stats,
635                                   stat,
636                                   1,
637                                   GNUNET_YES);
638        GNUNET_free (stat);
639      }
640      else
641        current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
642   }
643   current_hostlist = NULL;
644   /* Alternating the usage of preconfigured and learned hostlists */
645
646   if (stat_testing_hostlist == GNUNET_YES)
647     return;
648
649   if ( GNUNET_YES == stat_learning)
650     {
651     if (stat_use_bootstrap == GNUNET_YES)
652       stat_use_bootstrap = GNUNET_NO;
653     else
654       stat_use_bootstrap = GNUNET_YES;
655     }
656   else
657     stat_use_bootstrap = GNUNET_YES;
658 }
659
660 /**
661  * Clean up the state from the task that downloaded the
662  * hostlist and schedule the next task.
663  */
664 static void 
665 clean_up ()
666 {
667   CURLMcode mret;
668
669   if ( ( stat_testing_hostlist == GNUNET_YES ) && ( GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
670   {
671     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
672                 _("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),hostlist_to_test->hostlist_uri);
673   }
674
675   if ( stat_testing_hostlist == GNUNET_YES )
676     {
677     stat_testing_hostlist = GNUNET_NO;
678     }
679   if ( NULL != hostlist_to_test)
680   {
681     GNUNET_free (hostlist_to_test);
682     hostlist_to_test = NULL;
683   }
684
685   if (multi != NULL)
686     {
687       mret = curl_multi_remove_handle (multi, curl);
688       if (mret != CURLM_OK)
689         {
690           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
691                       _("%s failed at %s:%d: `%s'\n"),
692                       "curl_multi_remove_handle", __FILE__, __LINE__,
693                       curl_multi_strerror (mret));
694         }
695       mret = curl_multi_cleanup (multi);
696       if (mret != CURLM_OK)
697         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
698                     _("%s failed at %s:%d: `%s'\n"),
699                     "curl_multi_cleanup", __FILE__, __LINE__,
700                     curl_multi_strerror (mret));
701       multi = NULL;
702     }
703   if (curl != NULL)
704     {
705       curl_easy_cleanup (curl);
706       curl = NULL;
707     }  
708   GNUNET_free_non_null (current_url);
709   current_url = NULL;
710   stat_bytes_downloaded = 0;
711   stat_download_in_progress = GNUNET_NO;
712 }
713
714 /**
715  * Task that is run when we are ready to receive more data from the hostlist
716  * server.
717  *
718  * @param cls closure, unused
719  * @param tc task context, unused
720  */
721 static void
722 task_download (void *cls,
723              const struct GNUNET_SCHEDULER_TaskContext *tc);
724
725 /**
726  * Ask CURL for the select set and then schedule the
727  * receiving task with the scheduler.
728  */
729 static void
730 download_prepare ()
731 {
732   CURLMcode mret;
733   fd_set rs;
734   fd_set ws;
735   fd_set es;
736   int max;
737   struct GNUNET_NETWORK_FDSet *grs;
738   struct GNUNET_NETWORK_FDSet *gws;
739   long timeout;
740   struct GNUNET_TIME_Relative rtime;
741
742   max = -1;
743   FD_ZERO (&rs);
744   FD_ZERO (&ws);
745   FD_ZERO (&es);
746   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
747   if (mret != CURLM_OK)
748     {
749       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
750                   _("%s failed at %s:%d: `%s'\n"),
751                   "curl_multi_fdset", __FILE__, __LINE__,
752                   curl_multi_strerror (mret));
753       clean_up ();
754       return;
755     }
756   mret = curl_multi_timeout (multi, &timeout);
757   if (mret != CURLM_OK)
758     {
759       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
760                   _("%s failed at %s:%d: `%s'\n"),
761                   "curl_multi_timeout", __FILE__, __LINE__,
762                   curl_multi_strerror (mret));
763       clean_up ();
764       return;
765     }
766   rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
767                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
768                                                                    timeout));
769   grs = GNUNET_NETWORK_fdset_create ();
770   gws = GNUNET_NETWORK_fdset_create ();
771   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
772   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
773 #if DEBUG_HOSTLIST_CLIENT
774   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775               "Scheduling task for hostlist download using cURL\n");
776 #endif
777   ti_download
778     = GNUNET_SCHEDULER_add_select (sched,
779                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
780                                    GNUNET_SCHEDULER_NO_TASK,
781                                    rtime,
782                                    grs,
783                                    gws,
784                                    &task_download,
785                                    multi);
786   GNUNET_NETWORK_fdset_destroy (gws);
787   GNUNET_NETWORK_fdset_destroy (grs);
788 }
789
790
791 /**
792  * Task that is run when we are ready to receive more data from the hostlist
793  * server. 
794  *
795  * @param cls closure, unused
796  * @param tc task context, unused
797  */
798 static void
799 task_download (void *cls,
800              const struct GNUNET_SCHEDULER_TaskContext *tc)
801 {
802
803   int running;
804   struct CURLMsg *msg;
805   CURLMcode mret;
806   
807   ti_download = GNUNET_SCHEDULER_NO_TASK;
808   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
809     {
810 #if DEBUG_HOSTLIST_CLIENT
811       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812                   "Shutdown requested while trying to download hostlist from `%s'\n",
813                   current_url);
814 #endif
815       update_hostlist();
816       clean_up ();
817       return;
818     }
819   if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
820     {
821       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
822                   _("Timeout trying to download hostlist from `%s'\n"),
823                   current_url);
824       update_hostlist();
825       clean_up ();
826       return;
827     }
828 #if DEBUG_HOSTLIST_CLIENT
829   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
830               "Ready for processing hostlist client request\n");
831 #endif
832
833   do 
834     {
835       running = 0;
836       if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
837         {
838         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
839                                   _("Download limit of %u bytes exceeded, stopping download\n"),MAX_BYTES_PER_HOSTLISTS);
840         clean_up();
841         return;
842         }
843       mret = curl_multi_perform (multi, &running);
844       if (running == 0)
845         {
846           do
847             {
848
849
850               msg = curl_multi_info_read (multi, &running);
851               GNUNET_break (msg != NULL);
852               if (msg == NULL)
853                 break;
854               switch (msg->msg)
855                 {
856                 case CURLMSG_DONE:
857                   if ( (msg->data.result != CURLE_OK) &&
858                        (msg->data.result != CURLE_GOT_NOTHING) )                       
859                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
860                                _("%s failed for `%s' at %s:%d: `%s'\n"),
861                                "curl_multi_perform", 
862                                current_url,
863                                __FILE__,
864                                __LINE__,
865                                curl_easy_strerror (msg->data.result));            
866                   else
867                     {
868                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
869                                 _("Download of hostlist `%s' completed.\n"),
870                                 current_url);
871                     stat_download_successful = GNUNET_YES;
872                     update_hostlist();
873                     if (GNUNET_YES == stat_testing_hostlist)
874                      {
875                       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
876                                         _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
877                       insert_hostlist();
878                       hostlist_to_test = NULL;
879                       stat_testing_hostlist = GNUNET_NO;
880                      }
881                     }
882                   clean_up ();
883                   return;
884                 default:
885                   break;
886                 }
887
888             }
889           while ( (running > 0) );
890         }
891     }
892   while (mret == CURLM_CALL_MULTI_PERFORM);
893
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   stat_bytes_downloaded = 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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1602               "Hostlist client shutdown\n");
1603 #if DEBUG_HOSTLIST_CLIENT
1604   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1605               "Hostlist client shutdown\n");
1606 #endif
1607   if ( GNUNET_YES == stat_learning )
1608     save_hostlist_file ( GNUNET_YES );
1609
1610   if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1611     {
1612       GNUNET_SCHEDULER_cancel (sched,
1613           ti_saving_task);
1614     }
1615
1616   if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1617     {
1618       GNUNET_SCHEDULER_cancel (sched,
1619           ti_download_dispatcher_task);
1620     }
1621   if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1622     {
1623       GNUNET_SCHEDULER_cancel (sched,
1624           ti_testing_intervall_task);
1625     }
1626   if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1627     {
1628       GNUNET_SCHEDULER_cancel (sched,
1629                                ti_download);
1630     }
1631   if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1632     {
1633       GNUNET_SCHEDULER_cancel (sched,
1634                                ti_check_download);
1635       curl_global_cleanup ();
1636     }
1637   if (transport != NULL)
1638     {
1639       GNUNET_TRANSPORT_disconnect (transport);
1640       transport = NULL;
1641     }
1642   GNUNET_assert (NULL == transport);
1643   GNUNET_free_non_null (proxy);
1644   proxy = NULL;
1645   cfg = NULL;
1646   sched = NULL;
1647 }
1648
1649 /* end of hostlist-client.c */