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