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