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