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