9e86053fc3afbe7ecfd2779c774166edfbf69e09
[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  */
26
27 #include "platform.h"
28 #include "hostlist-client.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_transport_service.h"
33 #include "gnunet-daemon-hostlist.h"
34 #include <curl/curl.h>
35
36 #define DEBUG_HOSTLIST_CLIENT GNUNET_YES
37
38 /**
39  * Number of connections that we must have to NOT download
40  * hostlists anymore.
41  */
42 #define MIN_CONNECTIONS 4
43
44 /**
45  * Our configuration.
46  */
47 static const struct GNUNET_CONFIGURATION_Handle *cfg;
48
49 /**
50  * Our scheduler.
51  */
52 static struct GNUNET_SCHEDULER_Handle *sched;
53
54 /**
55  * Statistics handle.
56  */
57 struct GNUNET_STATISTICS_Handle *stats; 
58
59 /**
60  * Transport handle.
61  */
62 struct GNUNET_TRANSPORT_Handle *transport;
63                        
64 /**
65  * Proxy that we are using (can be NULL).
66  */
67 static char *proxy;
68
69 /**
70  * Buffer for data downloaded via HTTP.
71  */
72 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
73
74 /**
75  * Number of bytes valid in 'download_buffer'.
76  */
77 static size_t download_pos;
78
79 /**
80  * Current URL that we are using.
81  */
82 static char *current_url;
83
84 /**
85  * Current CURL handle.
86  */
87 static CURL *curl;
88
89 /**
90  * Current multi-CURL handle.
91  */
92 static CURLM *multi;
93
94 /**
95  * ID of the current task scheduled.
96  */
97 static GNUNET_SCHEDULER_TaskIdentifier current_task;
98
99 /**
100  * Amount of time we wait between hostlist downloads.
101  */
102 static struct GNUNET_TIME_Relative hostlist_delay;
103
104 /**
105  * Set to GNUNET_YES if the current URL had some problems.
106  */ 
107 static int bogus_url;
108
109 /**
110  * Number of active connections (according to core service).
111  */
112 static unsigned int connection_count;
113
114 /**
115  * At what time MUST the current hostlist request be done?
116  */
117 static struct GNUNET_TIME_Absolute end_time;
118
119
120 /**
121  * Process downloaded bits by calling callback on each HELLO.
122  *
123  * @param ptr buffer with downloaded data
124  * @param size size of a record
125  * @param nmemb number of records downloaded
126  * @param ctx unused
127  * @return number of bytes that were processed (always size*nmemb)
128  */
129 static size_t
130 download_hostlist_processor (void *ptr, 
131                              size_t size, 
132                              size_t nmemb, 
133                              void *ctx)
134 {
135   const char * cbuf = ptr;
136   const struct GNUNET_MessageHeader *msg;
137   size_t total;
138   size_t cpy;
139   size_t left;
140   uint16_t msize;
141
142   total = size * nmemb;
143   if ( (total == 0) || (bogus_url) )
144     {
145       return total;  /* ok, no data or bogus data */
146     }
147   GNUNET_STATISTICS_update (stats, 
148                             gettext_noop ("# bytes downloaded from hostlist servers"), 
149                             (int64_t) total, 
150                             GNUNET_NO);  
151   left = total;
152   while ( (left > 0) ||
153           (download_pos > 0) )
154     {
155       cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
156       memcpy (&download_buffer[download_pos],
157               cbuf,
158               cpy);      
159       cbuf += cpy;
160       download_pos += cpy;
161       left -= cpy;
162       if (download_pos < sizeof(struct GNUNET_MessageHeader))
163         {
164           GNUNET_assert (left == 0);
165           break;
166         }
167       msg = (const struct GNUNET_MessageHeader *) download_buffer;
168       msize = ntohs(msg->size);
169       if (msize < sizeof(struct GNUNET_MessageHeader))
170         {        
171           GNUNET_STATISTICS_update (stats, 
172                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
173                                     1, 
174                                     GNUNET_NO);  
175           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
176                       _("Invalid `%s' message received from hostlist at `%s'\n"),
177                       "HELLO",
178                       current_url); 
179           bogus_url = 1;
180           return total;
181         }
182       if (download_pos < msize)
183         {
184           GNUNET_assert (left == 0);
185           break;
186         }
187       if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
188         {
189 #if DEBUG_HOSTLIST_CLIENT
190           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191                       "Received valid `%s' message from hostlist server.\n",
192                       "HELLO");
193 #endif
194           GNUNET_STATISTICS_update (stats, 
195                                     gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
196                                     1, 
197                                     GNUNET_NO);  
198           GNUNET_TRANSPORT_offer_hello (transport, msg);
199         }
200       else
201         {
202           GNUNET_STATISTICS_update (stats, 
203                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
204                                     1, 
205                                     GNUNET_NO);  
206           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
207                       _("Invalid `%s' message received from hostlist at `%s'\n"),
208                       "HELLO",
209                       current_url);
210           bogus_url = GNUNET_YES;
211           return total;
212         }
213       memmove (download_buffer,
214                &download_buffer[msize],
215                download_pos - msize);
216       download_pos -= msize;
217     }
218   return total;
219 }
220
221
222 /**
223  * Obtain a hostlist URL that we should use.
224  *
225  * @return NULL if there is no URL available
226  */
227 static char *
228 get_url ()
229 {
230   char *servers;
231   char *ret;
232   size_t urls;
233   size_t pos;
234
235   if (GNUNET_OK != 
236       GNUNET_CONFIGURATION_get_value_string (cfg,
237                                              "HOSTLIST",
238                                              "SERVERS",
239                                              &servers))
240     {
241       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
242                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
243                   "SERVERS", "HOSTLIST");
244       return NULL;
245     }
246
247   urls = 0;
248   if (strlen (servers) > 0)
249     {
250       urls++;
251       pos = strlen (servers) - 1;
252       while (pos > 0)
253         {
254           if (servers[pos] == ' ')
255             urls++;
256           pos--;
257         }
258     }
259   if (urls == 0)
260     {
261       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
262                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
263                   "SERVERS", "HOSTLIST");
264       GNUNET_free (servers);
265       return NULL;
266     }
267
268   urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
269   pos = strlen (servers) - 1;
270   while (pos > 0)
271     {
272       if (servers[pos] == ' ')
273         {
274           urls--;
275           servers[pos] = '\0';
276         }
277       if (urls == 0)
278         {
279           pos++;
280           break;
281         }
282       pos--;    
283     }
284   ret = GNUNET_strdup (&servers[pos]);
285   GNUNET_free (servers);
286   return ret;
287 }
288
289
290 #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);
291
292
293 /**
294  * Schedule the background task that will (possibly)
295  * download a hostlist.
296  */
297 static void
298 schedule_hostlist_task (void);
299
300
301 /**
302  * Clean up the state from the task that downloaded the
303  * hostlist and schedule the next task.
304  */
305 static void 
306 clean_up ()
307 {
308   CURLMcode mret;
309
310   if (multi != NULL)
311     {
312       mret = curl_multi_remove_handle (multi, curl);
313       if (mret != CURLM_OK)
314         {
315           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
316                       _("%s failed at %s:%d: `%s'\n"),
317                       "curl_multi_remove_handle", __FILE__, __LINE__,
318                       curl_multi_strerror (mret));
319         }
320       mret = curl_multi_cleanup (multi);
321       if (mret != CURLM_OK)
322         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
323                     _("%s failed at %s:%d: `%s'\n"),
324                     "curl_multi_cleanup", __FILE__, __LINE__,
325                     curl_multi_strerror (mret));
326       multi = NULL;
327     }
328   if (curl != NULL)
329     {
330       curl_easy_cleanup (curl);
331       curl = NULL;
332     }  
333   GNUNET_free_non_null (current_url);
334   current_url = NULL;
335   schedule_hostlist_task ();
336 }
337
338
339 /**
340  * Ask CURL for the select set and then schedule the
341  * receiving task with the scheduler.
342  */
343 static void
344 run_multi ();
345
346
347 /**
348  * Task that is run when we are ready to receive more data from the hostlist
349  * server. 
350  *
351  * @param cls closure, unused
352  * @param tc task context, unused
353  */
354 static void
355 multi_ready (void *cls,
356              const struct GNUNET_SCHEDULER_TaskContext *tc)
357 {
358   int running;
359   struct CURLMsg *msg;
360   CURLMcode mret;
361   
362   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
363     {
364 #if DEBUG_HOSTLIST_CLIENT
365       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366                   "Shutdown requested while trying to download hostlist from `%s'\n",
367                   current_url);
368 #endif
369       clean_up ();
370       return;
371     }
372   if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
373     {
374       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
375                   _("Timeout trying to download hostlist from `%s'\n"),
376                   current_url);
377       clean_up ();
378       return;
379     }
380 #if DEBUG_HOSTLIST_CLIENT
381   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382               "Ready for processing hostlist client request\n");
383 #endif
384   do 
385     {
386       running = 0;
387       mret = curl_multi_perform (multi, &running);
388       if (running == 0)
389         {
390           do
391             {
392               msg = curl_multi_info_read (multi, &running);
393               GNUNET_break (msg != NULL);
394               if (msg == NULL)
395                 break;
396               switch (msg->msg)
397                 {
398                 case CURLMSG_DONE:
399                   if ( (msg->data.result != CURLE_OK) &&
400                        (msg->data.result != CURLE_GOT_NOTHING) )                       
401                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
402                                _("%s failed for `%s' at %s:%d: `%s'\n"),
403                                "curl_multi_perform", 
404                                current_url,
405                                __FILE__,
406                                __LINE__,
407                                curl_easy_strerror (msg->data.result));            
408                   else
409                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
410                                 _("Download of hostlist `%s' completed.\n"),
411                                 current_url);
412                   clean_up ();
413                   return;
414                 default:
415                   break;
416                 }
417             }
418           while (running > 0);
419         }
420     }
421   while (mret == CURLM_CALL_MULTI_PERFORM);
422   if (mret != CURLM_OK)
423     {
424       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
425                   _("%s failed at %s:%d: `%s'\n"),
426                   "curl_multi_perform", __FILE__, __LINE__,
427                   curl_multi_strerror (mret));
428       clean_up ();
429     }
430   run_multi ();
431 }
432
433
434 /**
435  * Ask CURL for the select set and then schedule the
436  * receiving task with the scheduler.
437  */
438 static void
439 run_multi () 
440 {
441   CURLMcode mret;
442   fd_set rs;
443   fd_set ws;
444   fd_set es;
445   int max;
446   struct GNUNET_NETWORK_FDSet *grs;
447   struct GNUNET_NETWORK_FDSet *gws;
448   long timeout;
449   struct GNUNET_TIME_Relative rtime;
450   
451   max = -1;
452   FD_ZERO (&rs);
453   FD_ZERO (&ws);
454   FD_ZERO (&es);
455   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
456   if (mret != CURLM_OK)
457     {
458       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
459                   _("%s failed at %s:%d: `%s'\n"),
460                   "curl_multi_fdset", __FILE__, __LINE__,
461                   curl_multi_strerror (mret));
462       clean_up ();
463       return;
464     }
465   mret = curl_multi_timeout (multi, &timeout);
466   if (mret != CURLM_OK)
467     {
468       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469                   _("%s failed at %s:%d: `%s'\n"),
470                   "curl_multi_timeout", __FILE__, __LINE__,
471                   curl_multi_strerror (mret));
472       clean_up ();
473       return;
474     }
475   rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
476                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
477                                                                    timeout));
478   grs = GNUNET_NETWORK_fdset_create ();
479   gws = GNUNET_NETWORK_fdset_create ();
480   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
481   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);  
482 #if DEBUG_HOSTLIST_CLIENT
483   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
484               "Scheduling task for hostlist download using cURL\n");
485 #endif
486   current_task 
487     = GNUNET_SCHEDULER_add_select (sched,
488                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
489                                    GNUNET_SCHEDULER_NO_TASK,
490                                    rtime,
491                                    grs,
492                                    gws,
493                                    &multi_ready,
494                                    multi);
495   GNUNET_NETWORK_fdset_destroy (gws);
496   GNUNET_NETWORK_fdset_destroy (grs);
497 }
498
499
500 /**
501  * Main function that will download a hostlist and process its
502  * data.
503  */
504 static void
505 download_hostlist () 
506 {
507   CURLcode ret;
508   CURLMcode mret;
509
510   curl = curl_easy_init ();
511   multi = NULL;
512   if (curl == NULL)
513     {
514       GNUNET_break (0);
515       clean_up ();
516       return;
517     }
518   current_url = get_url ();
519   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
520               _("Bootstrapping using hostlist at `%s'.\n"), 
521               current_url);
522   GNUNET_STATISTICS_update (stats, 
523                             gettext_noop ("# hostlist downloads initiated"), 
524                             1, 
525                             GNUNET_NO);  
526   if (proxy != NULL)
527     CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);    
528   download_pos = 0;
529   bogus_url = 0;
530   CURL_EASY_SETOPT (curl,
531                     CURLOPT_WRITEFUNCTION, 
532                     &download_hostlist_processor);
533   if (ret != CURLE_OK)
534     {
535       clean_up ();
536       return;
537     }
538   CURL_EASY_SETOPT (curl,
539                     CURLOPT_WRITEDATA, 
540                     NULL);
541   if (ret != CURLE_OK)
542     {
543       clean_up ();
544       return;
545     }
546   CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
547   CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
548   /* no need to abort if the above failed */
549   CURL_EASY_SETOPT (curl, 
550                     CURLOPT_URL, 
551                     current_url);
552   if (ret != CURLE_OK)
553     {
554       clean_up ();
555       return;
556     }
557   CURL_EASY_SETOPT (curl, 
558                     CURLOPT_FAILONERROR, 
559                     1);
560 #if 0
561   CURL_EASY_SETOPT (curl, 
562                     CURLOPT_VERBOSE, 
563                     1);
564 #endif
565   CURL_EASY_SETOPT (curl, 
566                     CURLOPT_BUFFERSIZE, 
567                     GNUNET_SERVER_MAX_MESSAGE_SIZE);
568   if (0 == strncmp (current_url, "http", 4))
569     CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
570   CURL_EASY_SETOPT (curl, 
571                     CURLOPT_CONNECTTIMEOUT, 
572                     60L);
573   CURL_EASY_SETOPT (curl, 
574                     CURLOPT_TIMEOUT, 
575                     60L);
576 #if 0
577   /* this should no longer be needed; we're now single-threaded! */
578   CURL_EASY_SETOPT (curl,
579                     CURLOPT_NOSIGNAL, 
580                     1);
581 #endif
582   multi = curl_multi_init ();
583   if (multi == NULL)
584     {
585       GNUNET_break (0);
586       clean_up ();
587       return;
588     }
589   mret = curl_multi_add_handle (multi, curl);
590   if (mret != CURLM_OK)
591     {
592       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
593                   _("%s failed at %s:%d: `%s'\n"),
594                   "curl_multi_add_handle", __FILE__, __LINE__,
595                   curl_multi_strerror (mret));
596       mret = curl_multi_cleanup (multi);
597       if (mret != CURLM_OK)
598         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
599                     _("%s failed at %s:%d: `%s'\n"),
600                     "curl_multi_cleanup", __FILE__, __LINE__,
601                     curl_multi_strerror (mret));
602       multi = NULL;
603       clean_up ();
604       return;
605     }
606   end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
607   run_multi ();
608 }  
609
610
611 /**
612  * Task that checks if we should try to download a hostlist.
613  * If so, we initiate the download, otherwise we schedule
614  * this task again for a later time.
615  */
616 static void
617 check_task (void *cls,
618             const struct GNUNET_SCHEDULER_TaskContext *tc)
619 {
620   current_task = GNUNET_SCHEDULER_NO_TASK;
621   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
622     return;
623   if (connection_count < MIN_CONNECTIONS)
624     download_hostlist ();
625   else
626     schedule_hostlist_task ();
627 }
628
629
630 /**
631  * Compute when we should check the next time about downloading
632  * a hostlist; then schedule the task accordingly.
633  */
634 static void
635 schedule_hostlist_task ()
636 {
637   static int once;
638   struct GNUNET_TIME_Relative delay;
639
640   if (stats == NULL)
641     {
642       curl_global_cleanup ();
643       return; /* in shutdown */
644     }
645   delay = hostlist_delay;
646   if (hostlist_delay.value == 0)
647     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
648   else
649     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
650   if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
651     hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
652                                                     (1 + connection_count));
653   GNUNET_STATISTICS_set (stats,
654                          gettext_noop("# seconds between hostlist downloads"),
655                          hostlist_delay.value,
656                          GNUNET_YES);
657   if (0 == once)
658     {
659       delay = GNUNET_TIME_UNIT_ZERO;
660       once = 1;
661     }  
662   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
663               _("Have %u/%u connections.  Will consider downloading hostlist in %llums\n"),
664               connection_count,
665               MIN_CONNECTIONS,
666               (unsigned long long) delay.value);
667   current_task = GNUNET_SCHEDULER_add_delayed (sched,
668                                                delay,
669                                                &check_task,
670                                                NULL);
671 }
672
673
674 /**
675  * Method called whenever a given peer connects.
676  *
677  * @param cls closure
678  * @param peer peer identity this notification is about
679  * @param latency reported latency of the connection with 'other'
680  * @param distance reported distance (DV) to 'other' 
681  */
682 static void
683 connect_handler (void *cls,
684                  const struct
685                  GNUNET_PeerIdentity * peer,
686                  struct GNUNET_TIME_Relative latency,
687                  uint32_t distance)
688 {
689   connection_count++;
690   GNUNET_STATISTICS_update (stats, 
691                             gettext_noop ("# active connections"), 
692                             1, 
693                             GNUNET_NO);  
694 }
695
696
697 /**
698  * Method called whenever a given peer disconnects.
699  *
700  * @param cls closure
701  * @param peer peer identity this notification is about
702  */
703 static void
704 disconnect_handler (void *cls,
705                     const struct
706                     GNUNET_PeerIdentity * peer)
707 {
708   connection_count--;
709   GNUNET_STATISTICS_update (stats, 
710                             gettext_noop ("# active connections"), 
711                             -1, 
712                             GNUNET_NO);  
713 }
714
715 /**
716  * Method called whenever an advertisement message arrives.
717  *
718  * @param cls closure (always NULL)
719  * @param client identification of the client
720  * @param message the actual message
721  * @return GNUNET_OK to keep the connection open,
722  *         GNUNET_SYSERR to close it (signal serious error)
723  */
724 static int
725 advertisement_handler (void *cls,
726     const struct GNUNET_PeerIdentity * peer,
727     const struct GNUNET_MessageHeader * message,
728     struct GNUNET_TIME_Relative latency,
729     uint32_t distance)
730 {
731   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732               "Hostlist client recieved advertisement, checking message: %s\n");
733   int size = ntohs (message->size);
734   int uri_size = size - sizeof ( struct GNUNET_HOSTLIST_ADV_Message );
735   int type = ntohs (message->type);
736   char * uri = GNUNET_malloc ( uri_size );
737
738   if ( type != GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT)
739     return GNUNET_NO;
740
741   const struct GNUNET_HOSTLIST_ADV_Message * incoming = (const struct GNUNET_HOSTLIST_ADV_Message *) message;
742   //struct GNUNET_HOSTLIST_ADV_Message * msg = message;
743   memcpy ( uri, &incoming[1], uri_size );
744   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745               "Hostlist client recieved advertisement uri: %s\n", uri);
746   #if DEBUG_HOSTLIST_CLIENT
747       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748                   "Hostlist client recieved advertisement message, type %u, message size %u, headersize %u, uri length %u, uri: %s\n",type,size,sizeof( struct GNUNET_HOSTLIST_ADV_Message ),uri_size,uri);
749 #endif
750
751
752      return GNUNET_YES;
753 }
754
755 /**
756  * Continuation called by the statistics code once 
757  * we go the stat.  Initiates hostlist download scheduling.
758  *
759  * @param cls closure
760  * @param success GNUNET_OK if statistics were
761  *        successfully obtained, GNUNET_SYSERR if not.
762  */
763 static void
764 primary_task (void *cls, int success)
765 {
766   if (stats == NULL)
767     return; /* in shutdown */
768 #if DEBUG_HOSTLIST_CLIENT
769   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
770               "Statistics request done, scheduling hostlist download\n");
771 #endif
772   schedule_hostlist_task ();
773 }
774
775
776 static int
777 process_stat (void *cls,
778               const char *subsystem,
779               const char *name,
780               uint64_t value,
781               int is_persistent)
782 {
783   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
784               _("Initial time between hostlist downloads is %llums\n"),
785               (unsigned long long) value);
786   hostlist_delay.value = value;
787   return GNUNET_OK;
788 }
789
790
791 /**
792  * Start downloading hostlists from hostlist servers as necessary.
793  */
794 int
795 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
796                               struct GNUNET_SCHEDULER_Handle *s,
797                               struct GNUNET_STATISTICS_Handle *st,
798                               GNUNET_CORE_ConnectEventHandler *ch,
799                               GNUNET_CORE_DisconnectEventHandler *dh,
800                               GNUNET_CORE_MessageCallback *msgh)
801 {
802   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
803     {
804       GNUNET_break (0);
805       return GNUNET_SYSERR;
806     }
807   transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
808   if (NULL == transport)
809     {
810       curl_global_cleanup ();
811       return GNUNET_SYSERR;
812     }
813   cfg = c;
814   sched = s;
815   stats = st;
816   if (GNUNET_OK !=
817       GNUNET_CONFIGURATION_get_value_string (cfg,
818                                              "HOSTLIST",
819                                              "HTTP-PROXY", 
820                                              &proxy))
821     proxy = NULL;
822   *ch = &connect_handler;
823   *dh = &disconnect_handler;
824   *msgh = &advertisement_handler;
825   GNUNET_STATISTICS_get (stats,
826                          "hostlist",
827                          gettext_noop("# seconds between hostlist downloads"),
828                          GNUNET_TIME_UNIT_MINUTES,
829                          &primary_task,
830                          &process_stat,
831                          NULL);
832   return GNUNET_OK;
833 }
834
835
836 /**
837  * Stop downloading hostlists from hostlist servers as necessary.
838  */
839 void
840 GNUNET_HOSTLIST_client_stop ()
841 {
842 #if DEBUG_HOSTLIST_CLIENT
843   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
844               "Hostlist client shutdown\n");
845 #endif
846   if (current_task != GNUNET_SCHEDULER_NO_TASK)
847     {
848       GNUNET_SCHEDULER_cancel (sched,
849                                current_task);
850       curl_global_cleanup ();
851     }
852   if (transport != NULL)
853     {
854       GNUNET_TRANSPORT_disconnect (transport);
855       transport = NULL;
856     }
857   GNUNET_assert (NULL == transport);
858   GNUNET_free_non_null (proxy);
859   proxy = NULL;
860   cfg = NULL;
861   sched = NULL;
862 }
863
864 /* end of hostlist-client.c */