remove send on connect
[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 /**
152  * Amount of time we wait between hostlist downloads.
153  */
154 static struct GNUNET_TIME_Relative hostlist_delay;
155
156 /**
157  * ID of the task, checking if hostlist download should take plate
158  */
159 static GNUNET_SCHEDULER_TaskIdentifier ti_check_download;
160
161 /**
162  * ID of the task downloading the hostlist
163  */
164 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
165
166 /**
167  * ID of the task saving the hostlsit in a regular intervall
168  */
169 static GNUNET_SCHEDULER_TaskIdentifier ti_saving_task;
170
171 /**
172  * ID of the task called to initiate a download
173  */
174 static GNUNET_SCHEDULER_TaskIdentifier ti_download_dispatcher_task;
175
176 /**
177  * ID of the task controlling the locking between two hostlist tests
178  */
179 static GNUNET_SCHEDULER_TaskIdentifier ti_testing_intervall_task;
180
181 /**
182  * At what time MUST the current hostlist request be done?
183  */
184 static struct GNUNET_TIME_Absolute end_time;
185
186 /**
187  * Head of the linked list used to store hostlists
188  */
189 static struct Hostlist * linked_list_head;
190
191 /**
192  *  Tail of the linked list used to store hostlists
193  */
194 static struct Hostlist * linked_list_tail;
195
196 /**
197  *  Current hostlist used for downloading
198  */
199 static struct Hostlist * current_hostlist;
200
201 /**
202  *  Size of the linke list  used to store hostlists
203  */
204 static unsigned int linked_list_size;
205
206 /**
207  * Head of the linked list used to store hostlists
208  */
209 static struct Hostlist * hostlist_to_test;
210
211 /**
212  * Set to GNUNET_YES if the current URL had some problems.
213  */
214 static int stat_bogus_url;
215
216 /**
217  * Value controlling if a hostlist is tested at the moment
218  */
219 static int stat_testing_hostlist;
220
221 /**
222  * Value controlling if a hostlist testing is allowed at the moment
223  */
224 static int stat_testing_allowed;
225
226 /**
227  * Value controlling if a hostlist download is running at the moment
228  */
229 static int stat_download_in_progress;
230
231 /**
232  * Value saying if a preconfigured bootstrap server is used
233  */
234 static unsigned int stat_use_bootstrap;
235 /**
236  * Set if we are allowed to learn new hostlists and use them
237  */
238 static int stat_learning;
239
240 /**
241  * Value saying if hostlist download was successful
242  */
243 static unsigned int stat_download_successful;
244
245 /**
246  * Value saying how many valid HELLO messages were obtained during download
247  */
248 static unsigned int stat_hellos_obtained;
249
250 /**
251  * Number of active connections (according to core service).
252  */
253 static unsigned int stat_connection_count;
254
255
256 /**
257  * Process downloaded bits by calling callback on each HELLO.
258  *
259  * @param ptr buffer with downloaded data
260  * @param size size of a record
261  * @param nmemb number of records downloaded
262  * @param ctx unused
263  * @return number of bytes that were processed (always size*nmemb)
264  */
265 static size_t
266 callback_download (void *ptr, 
267                    size_t size, 
268                    size_t nmemb, 
269                    void *ctx)
270 {
271   static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
272   const char * cbuf = ptr;
273   const struct GNUNET_MessageHeader *msg;
274   size_t total;
275   size_t cpy;
276   size_t left;
277   uint16_t msize;
278
279   total = size * nmemb;
280   stat_bytes_downloaded += total;
281   if ( (total == 0) || (stat_bogus_url) )
282     {
283       return total;  /* ok, no data or bogus data */
284     }
285
286   GNUNET_STATISTICS_update (stats, 
287                             gettext_noop ("# bytes downloaded from hostlist servers"), 
288                             (int64_t) total, 
289                             GNUNET_NO);  
290   left = total;
291   while ( (left > 0) ||
292           (download_pos > 0) )
293     {
294       cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - download_pos);
295       memcpy (&download_buffer[download_pos],
296               cbuf,
297               cpy);      
298       cbuf += cpy;
299       download_pos += cpy;
300       left -= cpy;
301       if (download_pos < sizeof(struct GNUNET_MessageHeader))
302         {
303           GNUNET_assert (left == 0);
304           break;
305         }
306       msg = (const struct GNUNET_MessageHeader *) download_buffer;
307       msize = ntohs(msg->size);
308       if (msize < sizeof(struct GNUNET_MessageHeader))
309         {        
310           GNUNET_STATISTICS_update (stats, 
311                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
312                                     1, 
313                                     GNUNET_NO);  
314           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
315                       _("Invalid `%s' message received from hostlist at `%s'\n"),
316                       "HELLO",
317                       current_url);
318           stat_hellos_obtained++;
319           stat_bogus_url = 1;
320           return total;
321         }
322       if (download_pos < msize)
323         {
324           GNUNET_assert (left == 0);
325           break;
326         }
327       if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
328         {
329 #if DEBUG_HOSTLIST_CLIENT
330           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331                       "Received valid `%s' message from hostlist server.\n",
332                       "HELLO");
333 #endif
334           GNUNET_STATISTICS_update (stats, 
335                                     gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
336                                     1, 
337                                     GNUNET_NO);
338           stat_hellos_obtained++;
339           GNUNET_TRANSPORT_offer_hello (transport, msg, NULL, NULL);
340         }
341       else
342         {
343           GNUNET_STATISTICS_update (stats, 
344                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
345                                     1, 
346                                     GNUNET_NO);  
347           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
348                       _("Invalid `%s' message received from hostlist at `%s'\n"),
349                       "HELLO",
350                       current_url);
351           stat_bogus_url = GNUNET_YES;
352           stat_hellos_obtained++;
353           return total;
354         }
355       memmove (download_buffer,
356                &download_buffer[msize],
357                download_pos - msize);
358       download_pos -= msize;
359     }
360   return total;
361 }
362
363
364 /**
365  * Obtain a hostlist URL that we should use.
366  *
367  * @return NULL if there is no URL available
368  */
369 static char *
370 get_bootstrap_server ()
371 {
372   char *servers;
373   char *ret;
374   size_t urls;
375   size_t pos;
376
377   if (GNUNET_OK != 
378       GNUNET_CONFIGURATION_get_value_string (cfg,
379                                              "HOSTLIST",
380                                              "SERVERS",
381                                              &servers))
382     {
383       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
384                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
385                   "SERVERS", "HOSTLIST");
386       return NULL;
387     }
388
389   urls = 0;
390   if (strlen (servers) > 0)
391     {
392       urls++;
393       pos = strlen (servers) - 1;
394       while (pos > 0)
395         {
396           if (servers[pos] == ' ')
397             urls++;
398           pos--;
399         }
400     }
401   if (urls == 0)
402     {
403       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
404                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
405                   "SERVERS", "HOSTLIST");
406       GNUNET_free (servers);
407       return NULL;
408     }
409
410   urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
411   pos = strlen (servers) - 1;
412   while (pos > 0)
413     {
414       if (servers[pos] == ' ')
415         {
416           urls--;
417           servers[pos] = '\0';
418         }
419       if (urls == 0)
420         {
421           pos++;
422           break;
423         }
424       pos--;    
425     }
426   ret = GNUNET_strdup (&servers[pos]);
427   GNUNET_free (servers);
428   return ret;
429 }
430
431 /**
432  * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
433  * @return uri to use, NULL if there is no URL available
434  */
435 static char *
436 download_get_url ()
437 {
438   uint32_t index;
439   unsigned int counter;
440   struct Hostlist * pos;
441
442   if ( GNUNET_NO == stat_learning)
443   {
444     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
445                 "Using preconfigured bootstrap server\n");
446     current_hostlist = NULL;
447     return get_bootstrap_server();
448   }
449
450   if ( ( GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test) )
451   {
452     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453                 "Testing new advertised hostlist if it is obtainable\n");
454     current_hostlist = hostlist_to_test;
455     return strdup(hostlist_to_test->hostlist_uri);
456   }
457
458   if ( (GNUNET_YES == stat_use_bootstrap) ||
459        (linked_list_size == 0) )
460   {
461     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
462                 "Using preconfigured bootstrap server\n");
463     current_hostlist = NULL;
464     return get_bootstrap_server();
465   }
466   index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 
467                                     linked_list_size);
468   counter = 0;
469   pos = linked_list_head;
470   while ( counter < index )
471     {
472       pos = pos->next;
473       counter ++;
474     }
475   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476               "Using learned hostlist `%s'\n", pos->hostlist_uri);
477   current_hostlist = pos;
478   return strdup(pos->hostlist_uri);
479 }
480
481
482 #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)
483
484
485 /**
486  * Method to save hostlist to a file during hostlist client shutdown
487  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
488  */
489 static void save_hostlist_file ( int shutdown );
490
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 
520 checked_sub (uint64_t val1, 
521              uint64_t val2)
522 {
523   if ( val1 <= val2)
524     return 0;
525   else
526     return (val1-val2);
527 }
528
529 /**
530  * Method to check if  a URI is in hostlist linked list
531  * @param uri uri to check
532  * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
533  */
534 static int
535 linked_list_contains (const char * uri)
536 {
537   struct Hostlist * pos;
538
539   pos = linked_list_head;
540   while (pos != NULL)
541     {
542       if (0 == strcmp(pos->hostlist_uri, uri) )
543         return GNUNET_YES;
544       pos = pos->next;
545     }
546   return GNUNET_NO;
547 }
548
549
550 /**
551  * Method returning the hostlist element with the lowest quality in the datastore
552  * @return hostlist with lowest quality
553  */
554 static struct Hostlist *
555 linked_list_get_lowest_quality ( )
556 {
557   struct Hostlist * pos;
558   struct Hostlist * lowest;
559
560   if (linked_list_size == 0)
561     return NULL;
562   lowest = linked_list_head;
563   pos = linked_list_head->next;
564   while (pos != NULL)
565     {
566       if (pos->quality < lowest->quality)
567         lowest = pos;
568       pos = pos->next;
569     }
570   return lowest;
571 }
572
573
574 /**
575  * Method to insert a hostlist into the datastore. If datastore
576  * contains maximum number of elements, the elements with lowest
577  * quality is dismissed
578  */
579 static void
580 insert_hostlist ()
581 {
582   struct Hostlist * lowest_quality;
583
584   if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
585     {
586       /* No free entries available, replace existing entry  */
587       lowest_quality = linked_list_get_lowest_quality();
588       GNUNET_assert (lowest_quality != NULL);
589       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590                   "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
591                   lowest_quality->hostlist_uri,
592                   (unsigned long long) lowest_quality->quality);
593       GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
594       linked_list_size--;
595       GNUNET_free (lowest_quality);
596     }
597   GNUNET_CONTAINER_DLL_insert(linked_list_head,
598                               linked_list_tail,
599                               hostlist_to_test);
600   linked_list_size++;
601   GNUNET_STATISTICS_set (stats,
602                          gettext_noop("# advertised hostlist URIs"),
603                          linked_list_size,
604                          GNUNET_NO);
605   stat_testing_hostlist = GNUNET_NO;
606 }
607
608
609 /**
610  * Method updating hostlist statistics
611  */
612 static void update_hostlist ( )
613 {
614   char *stat;
615   if ( ((stat_use_bootstrap == GNUNET_NO) && ( NULL != current_hostlist )) ||
616        ((stat_testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
617   {
618     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
619                 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
620      current_hostlist->hello_count = stat_hellos_obtained;
621      current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
622      current_hostlist->quality = checked_add ( current_hostlist->quality, (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
623      if ( GNUNET_YES == stat_download_successful )
624      {
625        current_hostlist->times_used++;
626        current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
627        GNUNET_asprintf (&stat,
628                         gettext_noop("# advertised URI `%s' downloaded"),
629                         current_hostlist->hostlist_uri);
630
631        GNUNET_STATISTICS_update ( stats,
632                                   stat,
633                                   1,
634                                   GNUNET_YES);
635        GNUNET_free (stat);
636      }
637      else
638        current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
639   }
640   current_hostlist = NULL;
641   /* Alternating the usage of preconfigured and learned hostlists */
642
643   if (stat_testing_hostlist == GNUNET_YES)
644     return;
645
646   if ( GNUNET_YES == stat_learning)
647     {
648     if (stat_use_bootstrap == GNUNET_YES)
649       stat_use_bootstrap = GNUNET_NO;
650     else
651       stat_use_bootstrap = GNUNET_YES;
652     }
653   else
654     stat_use_bootstrap = GNUNET_YES;
655 }
656
657 /**
658  * Clean up the state from the task that downloaded the
659  * hostlist and schedule the next task.
660  */
661 static void 
662 clean_up ()
663 {
664   CURLMcode mret;
665
666   if ( (stat_testing_hostlist == GNUNET_YES) && 
667        (GNUNET_NO == stat_download_successful) &&
668        (NULL != hostlist_to_test))
669   {
670     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
671                 _("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),
672                 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 /**
716  * Task that is run when we are ready to receive more data from the hostlist
717  * server.
718  *
719  * @param cls closure, unused
720  * @param tc task context, unused
721  */
722 static void
723 task_download (void *cls,
724                const struct GNUNET_SCHEDULER_TaskContext *tc);
725
726
727 /**
728  * Ask CURL for the select set and then schedule the
729  * receiving task with the scheduler.
730  */
731 static void
732 download_prepare ()
733 {
734   CURLMcode mret;
735   fd_set rs;
736   fd_set ws;
737   fd_set es;
738   int max;
739   struct GNUNET_NETWORK_FDSet *grs;
740   struct GNUNET_NETWORK_FDSet *gws;
741   long timeout;
742   struct GNUNET_TIME_Relative rtime;
743
744   max = -1;
745   FD_ZERO (&rs);
746   FD_ZERO (&ws);
747   FD_ZERO (&es);
748   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
749   if (mret != CURLM_OK)
750     {
751       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
752                   _("%s failed at %s:%d: `%s'\n"),
753                   "curl_multi_fdset", __FILE__, __LINE__,
754                   curl_multi_strerror (mret));
755       clean_up ();
756       return;
757     }
758   mret = curl_multi_timeout (multi, &timeout);
759   if (mret != CURLM_OK)
760     {
761       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
762                   _("%s failed at %s:%d: `%s'\n"),
763                   "curl_multi_timeout", __FILE__, __LINE__,
764                   curl_multi_strerror (mret));
765       clean_up ();
766       return;
767     }
768   rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
769                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
770                                                                    timeout));
771   grs = GNUNET_NETWORK_fdset_create ();
772   gws = GNUNET_NETWORK_fdset_create ();
773   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
774   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
775 #if DEBUG_HOSTLIST_CLIENT
776   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
777               "Scheduling task for hostlist download using cURL\n");
778 #endif
779   ti_download
780     = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
781                                    GNUNET_SCHEDULER_NO_TASK,
782                                    rtime,
783                                    grs,
784                                    gws,
785                                    &task_download,
786                                    multi);
787   GNUNET_NETWORK_fdset_destroy (gws);
788   GNUNET_NETWORK_fdset_destroy (grs);
789 }
790
791
792 /**
793  * Task that is run when we are ready to receive more data from the hostlist
794  * server. 
795  *
796  * @param cls closure, unused
797  * @param tc task context, unused
798  */
799 static void
800 task_download (void *cls,
801                const struct GNUNET_SCHEDULER_TaskContext *tc)
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).rel_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"),
840                     MAX_BYTES_PER_HOSTLISTS);
841         clean_up();
842         return;
843         }
844       mret = curl_multi_perform (multi, &running);
845       if (running == 0)
846         {
847           do
848             {
849               msg = curl_multi_info_read (multi, &running);
850               GNUNET_break (msg != NULL);
851               if (msg == NULL)
852                 break;
853               switch (msg->msg)
854                 {
855                 case CURLMSG_DONE:
856                   if ( (msg->data.result != CURLE_OK) &&
857                        (msg->data.result != CURLE_GOT_NOTHING) )                       
858                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
859                                _("%s failed for `%s' at %s:%d: `%s'\n"),
860                                "curl_multi_perform", 
861                                current_url,
862                                __FILE__,
863                                __LINE__,
864                                curl_easy_strerror (msg->data.result));            
865                   else
866                     {
867                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
868                                 _("Download of hostlist `%s' completed.\n"),
869                                 current_url);
870                     stat_download_successful = GNUNET_YES;
871                     update_hostlist();
872                     if (GNUNET_YES == stat_testing_hostlist)
873                      {
874                        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
875                                    _("Adding successfully tested hostlist `%s' datastore.\n"),
876                                    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 (WAITING_INTERVALL,
1046                                                               &task_download_dispatcher,
1047                                                               NULL);
1048    }
1049 }
1050
1051 /**
1052  * Task that checks if we should try to download a hostlist.
1053  * If so, we initiate the download, otherwise we schedule
1054  * this task again for a later time.
1055  */
1056 static void
1057 task_check (void *cls,
1058             const struct GNUNET_SCHEDULER_TaskContext *tc)
1059 {
1060   static int once;
1061   struct GNUNET_TIME_Relative delay;
1062
1063   ti_check_download = GNUNET_SCHEDULER_NO_TASK;
1064   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1065     return;
1066
1067   if (stat_connection_count < MIN_CONNECTIONS)
1068   {
1069     ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (&task_download_dispatcher,
1070                                                           NULL);
1071   }
1072
1073   if (stats == NULL)
1074   {
1075     curl_global_cleanup ();
1076     return; /* in shutdown */
1077   }
1078   delay = hostlist_delay;
1079   if (hostlist_delay.rel_value == 0)
1080     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1081   else
1082     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1083   if (hostlist_delay.rel_value > GNUNET_TIME_UNIT_HOURS.rel_value * (1 + stat_connection_count))
1084     hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1085                                                     (1 + stat_connection_count));
1086   GNUNET_STATISTICS_set (stats,
1087                          gettext_noop("# milliseconds between hostlist downloads"),
1088                          hostlist_delay.rel_value,
1089                          GNUNET_YES);
1090   if (0 == once)
1091     {
1092       delay = GNUNET_TIME_UNIT_ZERO;
1093       once = 1;
1094     }  
1095   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1096               _("Have %u/%u connections.  Will consider downloading hostlist in %llums\n"),
1097               stat_connection_count,
1098               MIN_CONNECTIONS,
1099               (unsigned long long) delay.rel_value);
1100   ti_check_download = GNUNET_SCHEDULER_add_delayed (delay,
1101                                                &task_check,
1102                                                NULL);
1103 }
1104
1105
1106 /**
1107  * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1108  *
1109  * @param cls closure
1110  * @param tc TaskContext
1111  */
1112 static void
1113 task_testing_intervall_reset (void *cls,
1114             const struct GNUNET_SCHEDULER_TaskContext *tc)
1115 {
1116   ti_testing_intervall_task = GNUNET_SCHEDULER_NO_TASK;
1117   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1118     return;
1119    stat_testing_allowed = GNUNET_OK;
1120    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121              "Testing new hostlist advertisements is allowed again\n");
1122 }
1123
1124
1125 /**
1126  * Task that writes hostlist entries to a file on a regular base
1127  *
1128  * @param cls closure
1129  * @param 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.rel_value);
1145   ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL,
1146                                                &task_hostlist_saving,
1147                                                NULL);
1148 }
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 atsi performance data
1157  */
1158 static void
1159 handler_connect (void *cls,
1160                  const struct
1161                  GNUNET_PeerIdentity * peer,
1162                  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1163 {
1164   GNUNET_assert (stat_connection_count < UINT_MAX);
1165   stat_connection_count++;
1166   GNUNET_STATISTICS_update (stats, 
1167                             gettext_noop ("# active connections"), 
1168                             1, 
1169                             GNUNET_NO);
1170 }
1171
1172
1173 /**
1174  * Method called whenever a given peer disconnects.
1175  *
1176  * @param cls closure
1177  * @param peer peer identity this notification is about
1178  */
1179 static void
1180 handler_disconnect (void *cls,
1181                     const struct
1182                     GNUNET_PeerIdentity * peer)
1183 {
1184   GNUNET_assert (stat_connection_count > 0);
1185   stat_connection_count--;
1186   GNUNET_STATISTICS_update (stats,
1187                             gettext_noop ("# active connections"),
1188                             -1,
1189                             GNUNET_NO);
1190 }
1191
1192
1193 /**
1194  * Method called whenever an advertisement message arrives.
1195  *
1196  * @param cls closure (always NULL)
1197  * @param peer the peer sending the message
1198  * @param message the actual message
1199  * @param atsi performance data
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                        const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1208 {
1209   size_t size;
1210   size_t uri_size;
1211   const struct GNUNET_MessageHeader * incoming;
1212   const char *uri;
1213   struct Hostlist * hostlist;
1214
1215   GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1216   size = ntohs (message->size);
1217   if (size <= sizeof(struct GNUNET_MessageHeader))
1218     {
1219       GNUNET_break_op (0);
1220       return GNUNET_SYSERR;
1221     }
1222   incoming = (const struct GNUNET_MessageHeader *) message;
1223   uri = (const char*) &incoming[1];
1224   uri_size = size - sizeof (struct GNUNET_MessageHeader);
1225   if (uri [uri_size - 1] != '\0')
1226     {
1227       GNUNET_break_op (0);
1228       return GNUNET_SYSERR;
1229     }
1230   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1231               "Hostlist client recieved advertisement from `%s' containing URI `%s'\n", 
1232               GNUNET_i2s (peer), 
1233               uri);
1234   if (GNUNET_NO != linked_list_contains (uri))
1235     {
1236       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1237                 "URI `%s' is already known\n",
1238                 uri);
1239       return GNUNET_OK;
1240     }
1241
1242   if ( GNUNET_NO == stat_testing_allowed )
1243     {
1244       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1245                 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1246       return GNUNET_SYSERR;
1247     }
1248   if ( GNUNET_YES == stat_testing_hostlist )
1249     {
1250       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1251                 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1252       return GNUNET_SYSERR;
1253     }
1254
1255   hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1256   hostlist->hostlist_uri = (const char*) &hostlist[1];
1257   memcpy (&hostlist[1], uri, uri_size);
1258   hostlist->time_creation = GNUNET_TIME_absolute_get();
1259   hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1260   hostlist->quality = HOSTLIST_INITIAL;
1261   hostlist_to_test = hostlist;
1262
1263   stat_testing_hostlist = GNUNET_YES;
1264   stat_testing_allowed = GNUNET_NO;
1265   ti_testing_intervall_task = GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1266                                                          &task_testing_intervall_reset,
1267                                                          NULL);
1268
1269   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1270             "Testing new hostlist advertisements is locked for the next %u ms\n",
1271             TESTING_INTERVAL.rel_value);
1272
1273   ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (&task_download_dispatcher,
1274                                                      NULL);
1275
1276   return GNUNET_OK;
1277 }
1278
1279
1280
1281 /**
1282  * Continuation called by the statistics code once 
1283  * we go the stat.  Initiates hostlist download scheduling.
1284  *
1285  * @param cls closure
1286  * @param success GNUNET_OK if statistics were
1287  *        successfully obtained, GNUNET_SYSERR if not.
1288  */
1289 static void
1290 primary_task (void *cls, int success)
1291 {
1292   if (stats == NULL)
1293     return; /* in shutdown */
1294 #if DEBUG_HOSTLIST_CLIENT
1295   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1296               "Statistics request done, scheduling hostlist download\n");
1297 #endif
1298   ti_check_download = GNUNET_SCHEDULER_add_now (&task_check,
1299                                            NULL);
1300 }
1301
1302
1303 static int
1304 process_stat (void *cls,
1305               const char *subsystem,
1306               const char *name,
1307               uint64_t value,
1308               int is_persistent)
1309 {
1310   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1311               _("Initial time between hostlist downloads is %llums\n"),
1312               (unsigned long long) value);
1313   hostlist_delay.rel_value = value;
1314   return GNUNET_OK;
1315 }
1316
1317 /**
1318  * Method to load persistent hostlist file during hostlist client startup
1319  */
1320 static void 
1321 load_hostlist_file ()
1322 {
1323   char *filename;
1324   char *uri;
1325   char *emsg;
1326   struct Hostlist * hostlist;
1327   uri = NULL;
1328   uint32_t times_used;
1329   uint32_t hellos_returned;
1330   uint64_t quality;
1331   uint64_t last_used;
1332   uint64_t created;
1333   uint32_t counter;
1334
1335   if (GNUNET_OK !=
1336                   GNUNET_CONFIGURATION_get_value_filename (cfg,
1337                                                    "HOSTLIST",
1338                                                    "HOSTLISTFILE",
1339                                                    &filename))
1340     {
1341       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1342                   _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1343                   "HOSTLISTFILE", "HOSTLIST");
1344       return;
1345     }
1346
1347   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1348               _("Loading saved hostlist entries from file `%s' \n"), filename);
1349   if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1350   {
1351     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1352                 _("Hostlist file `%s' is not existing\n"), filename);
1353     GNUNET_free ( filename );
1354     return;
1355   }
1356
1357   struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1358   if (NULL == rh)
1359     {
1360       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1361                   _("Could not open file `%s' for reading to load hostlists: %s\n"), 
1362                   filename,
1363                   STRERROR (errno));
1364       GNUNET_free (filename);
1365       return;
1366     }
1367
1368   counter = 0;
1369   while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1370           (NULL != uri) &&
1371           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &times_used)) &&
1372           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1373           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1374           (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1375           (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1376     {
1377       hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1378       hostlist->hello_count = hellos_returned;
1379       hostlist->hostlist_uri = (const char *) &hostlist[1];
1380       memcpy (&hostlist[1], uri, strlen(uri)+1);
1381       hostlist->quality = quality;
1382       hostlist->time_creation.abs_value = created;
1383       hostlist->time_last_usage.abs_value = last_used;
1384       GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1385       linked_list_size++;
1386       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1387                   "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1388       GNUNET_free (uri);
1389       uri = NULL;
1390       counter++;
1391       if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1392     }
1393
1394   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1395               _("%u hostlist URIs loaded from file\n"), counter);
1396   GNUNET_STATISTICS_set (stats,
1397                          gettext_noop("# hostlist URIs read from file"),
1398                          counter,
1399                          GNUNET_YES);
1400   GNUNET_STATISTICS_set (stats,
1401                          gettext_noop("# advertised hostlist URIs"),
1402                          linked_list_size,
1403                          GNUNET_NO);
1404
1405   GNUNET_free_non_null (uri);
1406   emsg = NULL;
1407   GNUNET_BIO_read_close (rh, &emsg);
1408   if (emsg != NULL)
1409     GNUNET_free (emsg);
1410   GNUNET_free (filename);
1411 }
1412
1413
1414 /**
1415  * Method to save persistent hostlist file during hostlist client shutdown
1416  * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1417  */
1418 static void save_hostlist_file ( int shutdown )
1419 {
1420   char *filename;
1421   struct Hostlist *pos;
1422   struct GNUNET_BIO_WriteHandle * wh;
1423   int ok;
1424   uint32_t counter;
1425
1426   if (GNUNET_OK !=
1427       GNUNET_CONFIGURATION_get_value_filename (cfg,
1428                                              "HOSTLIST",
1429                                              "HOSTLISTFILE",
1430                                              &filename))
1431     {
1432       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1433                   _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1434                   "HOSTLISTFILE", "HOSTLIST");
1435       return;
1436     }
1437   if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1438     {
1439       GNUNET_free (filename);
1440       return;
1441     }
1442   wh = GNUNET_BIO_write_open (filename);
1443   if ( NULL == wh)
1444     {
1445       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1446                   _("Could not open file `%s' for writing to save hostlists: %s\n"),
1447                   filename,
1448                   STRERROR (errno));
1449       GNUNET_free (filename);
1450       return;
1451     }
1452   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1453               _("Writing %u hostlist URIs to `%s'\n" ),
1454               linked_list_size, filename);
1455   /* add code to write hostlists to file using bio */
1456   ok = GNUNET_YES;
1457   counter = 0;
1458   while (NULL != (pos = linked_list_head))
1459     {
1460       if ( GNUNET_YES == shutdown)
1461       {
1462         GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1463         linked_list_size--;
1464       }
1465       if (GNUNET_YES == ok)
1466         {
1467           if ( (GNUNET_OK !=
1468                 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1469                (GNUNET_OK !=
1470                 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1471                (GNUNET_OK !=
1472                 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1473                (GNUNET_OK !=
1474                 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value)) ||
1475                (GNUNET_OK !=
1476                 GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value)) ||
1477                (GNUNET_OK !=
1478                 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1479             {
1480               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1481                           _("Error writing hostlist URIs to file `%s'\n"),
1482                           filename);
1483               ok = GNUNET_NO;
1484             }
1485         }
1486
1487       if ( GNUNET_YES == shutdown)
1488         GNUNET_free (pos);
1489       counter ++;
1490       if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1491     }  
1492   GNUNET_STATISTICS_set (stats,
1493                          gettext_noop("# hostlist URIs written to file"),
1494                          counter,
1495                          GNUNET_YES);
1496
1497   if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1498     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1499                 _("Error writing hostlist URIs to file `%s'\n"),
1500                 filename);
1501   GNUNET_free (filename);
1502 }
1503
1504 /**
1505  * Start downloading hostlists from hostlist servers as necessary.
1506  */
1507 int
1508 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
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 (c, NULL, NULL, NULL, NULL, NULL);
1524   if (NULL == transport)
1525     {
1526       curl_global_cleanup ();
1527       return GNUNET_SYSERR;
1528     }
1529   cfg = c;
1530   stats = st;
1531   if (GNUNET_OK !=
1532       GNUNET_CONFIGURATION_get_value_string (cfg,
1533                                              "HOSTLIST",
1534                                              "HTTP-PROXY", 
1535                                              &proxy))
1536     proxy = NULL;
1537   stat_learning = learn;
1538   *ch = &handler_connect;
1539   *dh = &handler_disconnect;
1540   linked_list_head = NULL;
1541   linked_list_tail = NULL;
1542   stat_use_bootstrap = GNUNET_YES;
1543   stat_testing_hostlist = GNUNET_NO;
1544   stat_testing_allowed = GNUNET_YES;
1545
1546   if ( GNUNET_YES == stat_learning )
1547   {
1548     *msgh = &handler_advertisement;
1549     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1550               _("Learning is enabled on this peer\n"));
1551     load_hostlist_file ();
1552     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1553               _("Hostlists will be saved to file again in  %llums\n"),
1554               (unsigned long long) SAVING_INTERVALL.rel_value);
1555     ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL,
1556                                                    &task_hostlist_saving,
1557                                                    NULL);
1558   }
1559   else
1560   {
1561     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1562               _("Learning is not enabled on this peer\n"));
1563     *msgh = NULL;
1564     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1565                                                             "HOSTLIST",
1566                                                             "HOSTLISTFILE",
1567                                                             &filename))
1568     {
1569     if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1570       {
1571         result = remove (filename);
1572         if (result == 0)
1573         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1574                     _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1575                     filename);
1576         else
1577           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1578                       _("Hostlist file `%s' could not be removed\n"),
1579                       filename);
1580       }
1581     }
1582     GNUNET_free ( filename );
1583   }
1584   GNUNET_STATISTICS_get (stats,
1585                          "hostlist",
1586                          gettext_noop("# milliseconds 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 (ti_saving_task);
1613     }
1614
1615   if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1616     {
1617       GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1618     }
1619   if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1620     {
1621       GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1622     }
1623   if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1624     {
1625       GNUNET_SCHEDULER_cancel (ti_download);
1626     }
1627   if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1628     {
1629       GNUNET_SCHEDULER_cancel (ti_check_download);
1630       curl_global_cleanup ();
1631     }
1632   if (transport != NULL)
1633     {
1634       GNUNET_TRANSPORT_disconnect (transport);
1635       transport = NULL;
1636     }
1637   GNUNET_assert (NULL == transport);
1638   GNUNET_free_non_null (proxy);
1639   proxy = NULL;
1640   cfg = NULL;
1641 }
1642
1643 /* end of hostlist-client.c */