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