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