2f3e2c8e16dabe04179a1a6a82eac9f8550452f0
[oweals/gnunet.git] / src / peerinfo / gnunet-service-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 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 peerinfo/gnunet-service-peerinfo.c
23  * @brief maintains list of known peers
24  *
25  * Code to maintain the list of currently known hosts (in memory
26  * structure of data/hosts/ and data/credit/).
27  *
28  * @author Christian Grothoff
29  *
30  * TODO:
31  * - HostEntries are never 'free'd (add expiration, upper bound?)
32  */
33
34 #include "platform.h"
35 #include "gnunet_crypto_lib.h"
36 #include "gnunet_disk_lib.h"
37 #include "gnunet_hello_lib.h"
38 #include "gnunet_protocols.h"
39 #include "gnunet_service_lib.h"
40 #include "peerinfo.h"
41
42 /**
43  * How often do we scan the HOST_DIR for new entries?
44  */
45 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
46
47 /**
48  * How often do we flush trust values to disk?
49  */
50 #define TRUST_FLUSH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
51
52 /**
53  * How often do we discard old entries in data/hosts/?
54  */
55 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
56
57 /**
58  * In-memory cache of known hosts.
59  */
60 struct HostEntry
61 {
62
63   /**
64    * This is a linked list.
65    */
66   struct HostEntry *next;
67
68   /**
69    * Identity of the peer.
70    */
71   struct GNUNET_PeerIdentity identity;
72
73   /**
74    * Hello for the peer (can be NULL)
75    */
76   struct GNUNET_HELLO_Message *hello;
77
78   /**
79    * Trust rating for this peer
80    */
81   uint32_t trust;
82
83   /**
84    * Trust rating for this peer on disk.
85    */
86   uint32_t disk_trust;
87
88 };
89
90
91 /**
92  * Entries that we still need to tell the client about.
93  */
94 struct PendingEntry
95 {
96
97   /**
98    * This is a linked list.
99    */
100   struct PendingEntry *next;
101
102   /**
103    * Entry to tell the client about.
104    */
105   struct HostEntry *he;
106
107 };
108
109
110 /**
111  * Clients to notify of changes to the peer information.
112  */
113 struct NotifyList
114 {
115
116   /**
117    * This is a linked list.
118    */
119   struct NotifyList *next;
120
121   /**
122    * Client to notify.
123    */ 
124   struct GNUNET_SERVER_Client *client;
125
126   /**
127    * Notifications pending for this entry.
128    */
129   struct PendingEntry *pending;
130
131   /**
132    * Handle for a transmit ready request.
133    */
134   struct GNUNET_CONNECTION_TransmitHandle *transmit_ctx;
135
136 };
137
138
139 /**
140  * The in-memory list of known hosts.
141  */
142 static struct HostEntry *hosts;
143
144 /**
145  * Clients to immediately notify about all changes.
146  */
147 static struct NotifyList *notify_list;
148
149 /**
150  * Directory where the hellos are stored in (data/hosts)
151  */
152 static char *networkIdDirectory;
153
154 /**
155  * Where do we store trust information?
156  */
157 static char *trustDirectory;
158
159
160 /**
161  * Transmit peer information messages from the pending queue
162  * to the client.
163  *
164  * @param cls the 'struct NotifyList' that we are processing
165  * @param size number of bytes we can transmit
166  * @param vbuf where to write the messages
167  * @return number of bytes written to vbuf
168  */
169 static size_t
170 transmit_pending_notification (void *cls,
171                                size_t size,
172                                void *vbuf)
173 {
174   struct NotifyList *nl = cls;
175   char *buf = vbuf;
176   struct PendingEntry *pos;
177   struct PendingEntry *next;
178   struct InfoMessage im;
179   uint16_t hs;
180   size_t left;
181
182   nl->transmit_ctx = NULL;
183   next = nl->pending;
184   pos = nl->pending;
185   left = size;
186   while (pos != NULL) 
187     {
188       hs = (pos->he->hello == NULL) ? 0 : GNUNET_HELLO_size (pos->he->hello);
189       if (left < sizeof (struct InfoMessage) + hs)
190         break;
191       next = pos->next;
192       im.header.size = htons (hs + sizeof (struct InfoMessage));
193       im.header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
194       im.trust = htonl (pos->he->trust);
195       im.peer = pos->he->identity;
196       memcpy (&buf[size - left], &im, sizeof (struct InfoMessage));      
197       memcpy (&buf[size - left + sizeof (struct InfoMessage)], pos->he->hello, hs);
198       left -= hs + sizeof (struct InfoMessage);
199       GNUNET_free (pos);
200       pos = next;      
201     }
202   nl->pending = next;
203   if (nl->pending != NULL)
204     {
205       nl->transmit_ctx 
206         = GNUNET_SERVER_notify_transmit_ready (nl->client,
207                                                sizeof (struct InfoMessage) + hs,
208                                                GNUNET_TIME_UNIT_FOREVER_REL,
209                                                &transmit_pending_notification,
210                                                nl);
211     }
212   return size - left;
213 }
214
215
216
217 /**
218  * Notify client about host change.  Checks if the
219  * respective host entry is already in the list of things
220  * to send to the client, and if not, adds it.  Also
221  * triggers a new request for transmission if the pending
222  * list was previously empty.
223  *
224  * @param nl client to notify
225  * @param he entry to notify about
226  */
227 static void
228 do_notify (struct NotifyList *nl,
229            struct HostEntry *he)
230 {
231   struct PendingEntry *pe;
232   uint16_t hsize;
233
234   pe = nl->pending;
235   while (NULL != pe)
236     {
237       if (pe->he == he)
238         return; /* already in list */
239       pe = pe->next;
240     }
241   pe = GNUNET_malloc (sizeof (struct PendingEntry));
242   pe->next = nl->pending;
243   pe->he = he;
244   nl->pending = pe;
245   if (nl->transmit_ctx != NULL)
246     return; /* already trying to transmit */
247   hsize = (he->hello == NULL) ? 0 : GNUNET_HELLO_size (he->hello);
248   nl->transmit_ctx = GNUNET_SERVER_notify_transmit_ready (nl->client,
249                                                           sizeof (struct InfoMessage) + hsize,
250                                                           GNUNET_TIME_UNIT_FOREVER_REL,
251                                                           &transmit_pending_notification,
252                                                           nl);
253 }
254
255
256 /**
257  * Notify all clients in the notify list about the
258  * given host entry changing.
259  */
260 static void
261 notify_all (struct HostEntry *he)
262 {
263   struct NotifyList *nl;
264
265   nl = notify_list;
266   while (NULL != nl)
267     {
268       do_notify (nl, he);
269       nl = nl->next;
270     }
271 }
272
273
274 /**
275  * Address iterator that causes expired entries to be discarded.
276  *
277  * @param cls pointer to the current time
278  * @param tname name of the transport
279  * @param expiration expiration time for the address
280  * @param addr the address
281  * @param addrlen length of addr in bytes
282  * @return GNUNET_NO if expiration smaller than the current time
283  */
284 static int
285 discard_expired (void *cls,
286                  const char *tname,
287                  struct GNUNET_TIME_Absolute expiration,
288                  const void *addr, size_t addrlen)
289 {
290   const struct GNUNET_TIME_Absolute *now = cls;
291   if (now->value > expiration.value)
292     return GNUNET_NO;
293   return GNUNET_OK;
294 }
295
296
297 /**
298  * Get the filename under which we would store the GNUNET_HELLO_Message
299  * for the given host and protocol.
300  * @return filename of the form DIRECTORY/HOSTID
301  */
302 static char *
303 get_host_filename (const struct GNUNET_PeerIdentity *id)
304 {
305   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
306   char *fn;
307
308   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
309   GNUNET_asprintf (&fn,
310                    "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil);
311   return fn;
312 }
313
314
315 /**
316  * Get the filename under which we would store the GNUNET_HELLO_Message
317  * for the given host and protocol.
318  * @return filename of the form DIRECTORY/HOSTID
319  */
320 static char *
321 get_trust_filename (const struct GNUNET_PeerIdentity *id)
322 {
323   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
324   char *fn;
325
326   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
327   GNUNET_asprintf (&fn, "%s%s%s", trustDirectory, DIR_SEPARATOR_STR, &fil);
328   return fn;
329 }
330
331 /**
332  * Find the host entry for the given peer.  Call
333  * only when synchronized!
334  * @return NULL if not found
335  */
336 static struct HostEntry *
337 lookup_host_entry (const struct GNUNET_PeerIdentity *id)
338 {
339   struct HostEntry *pos;
340
341   pos = hosts;
342   while ((pos != NULL) &&
343          (0 !=
344           memcmp (id, &pos->identity, sizeof (struct GNUNET_PeerIdentity))))
345     pos = pos->next;
346   return pos;
347 }
348
349
350 /**
351  * Add a host to the list.
352  *
353  * @param identity the identity of the host
354  */
355 static void
356 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
357 {
358   struct HostEntry *entry;
359   char *fn;
360   uint32_t trust;
361   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
362   const struct GNUNET_HELLO_Message *hello;
363   struct GNUNET_HELLO_Message *hello_clean;
364   int size;
365   struct GNUNET_TIME_Absolute now;
366
367   entry = lookup_host_entry (identity);
368   if (entry != NULL)
369     return;
370   entry = GNUNET_malloc (sizeof (struct HostEntry));
371   entry->identity = *identity;
372   fn = get_trust_filename (identity);
373   if ((GNUNET_DISK_file_test (fn) == GNUNET_YES) &&
374       (sizeof (trust) == GNUNET_DISK_fn_read (fn, &trust, sizeof (trust))))
375     entry->disk_trust = entry->trust = ntohl (trust);
376   GNUNET_free (fn);
377
378   fn = get_host_filename (identity);
379   if (GNUNET_DISK_file_test (fn) == GNUNET_YES)
380     {
381       size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
382       hello = (const struct GNUNET_HELLO_Message *) buffer;
383       if ( (size < sizeof (struct GNUNET_MessageHeader)) ||
384            (size != ntohs((((const struct GNUNET_MessageHeader*) hello)->size))) ||
385            (size != GNUNET_HELLO_size (hello)) )
386         {
387           GNUNET_break (0);
388           if (0 != UNLINK (fn))
389             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
390                                       "unlink",
391                                       fn);
392         }
393       else
394         {
395           now = GNUNET_TIME_absolute_get ();
396           hello_clean = GNUNET_HELLO_iterate_addresses (hello,
397                                                         GNUNET_YES,
398                                                         &discard_expired, &now);
399           entry->hello = hello_clean;
400         }
401     }
402   GNUNET_free (fn);
403   entry->next = hosts;
404   hosts = entry;
405   notify_all (entry);
406 }
407
408
409 /**
410  * Increase the host credit by a value.
411  *
412  * @param hostId is the identity of the host
413  * @param value is the int value by which the
414  *  host credit is to be increased or decreased
415  * @returns the actual change in trust (positive or negative)
416  */
417 static int
418 change_host_trust (const struct GNUNET_PeerIdentity *hostId, int value)
419 {
420   struct HostEntry *host;
421   unsigned int old_trust;
422
423   if (value == 0)
424     return 0;
425   host = lookup_host_entry (hostId);
426   if (host == NULL)
427     {
428       add_host_to_known_hosts (hostId);
429       host = lookup_host_entry (hostId);
430     }
431   GNUNET_assert (host != NULL);
432   old_trust = host->trust;
433   if (value > 0)
434     {
435       if (host->trust + value < host->trust)
436         {
437           value = ((uint32_t) - 1) - host->trust;
438           host->trust = (uint32_t) - 1; /* maximized */
439         }
440       else
441         host->trust += value;
442     }
443   else
444     {
445       if (host->trust < -value)
446         {
447           value = -host->trust;
448           host->trust = 0;
449         }
450       else
451         host->trust += value;
452     }
453   if (host->trust != old_trust)
454     notify_all (host);
455   return value;
456 }
457
458
459 /**
460  * Remove a file that should not be there.  LOG
461  * success or failure.
462  */
463 static void
464 remove_garbage (const char *fullname)
465 {
466   if (0 == UNLINK (fullname))
467     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
468                 _
469                 ("File `%s' in directory `%s' does not match naming convention. "
470                  "Removed.\n"), fullname, networkIdDirectory);
471   else
472     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
473                               GNUNET_ERROR_TYPE_BULK, "unlink", fullname);
474 }
475
476
477 static int
478 hosts_directory_scan_callback (void *cls, const char *fullname)
479 {
480   unsigned int *matched = cls;
481   struct GNUNET_PeerIdentity identity;
482   const char *filename;
483
484   if (GNUNET_DISK_file_test (fullname) != GNUNET_YES)
485     return GNUNET_OK;           /* ignore non-files */
486   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
487     {
488       remove_garbage (fullname);
489       return GNUNET_OK;
490     }
491   filename =
492     &fullname[strlen (fullname) -
493               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1];
494   if (filename[-1] != DIR_SEPARATOR)
495     {
496       remove_garbage (fullname);
497       return GNUNET_OK;
498     }
499   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename,
500                                                    &identity.hashPubKey))
501     {
502       remove_garbage (fullname);
503       return GNUNET_OK;
504     }
505   (*matched)++;
506   add_host_to_known_hosts (&identity);
507   return GNUNET_OK;
508 }
509
510
511 /**
512  * Call this method periodically to scan data/hosts for new hosts.
513  */
514 static void
515 cron_scan_directory_data_hosts (void *cls,
516                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
517 {
518   static unsigned int retries;
519   unsigned int count;
520
521   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
522     return;
523   count = 0;
524   GNUNET_DISK_directory_create (networkIdDirectory);
525   GNUNET_DISK_directory_scan (networkIdDirectory,
526                               &hosts_directory_scan_callback, &count);
527   if ((0 == count) && (0 == (++retries & 31)))
528     GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
529                 GNUNET_ERROR_TYPE_BULK,
530                 _("Still no peers found in `%s'!\n"), networkIdDirectory);
531   GNUNET_SCHEDULER_add_delayed (tc->sched,
532                                 DATA_HOST_FREQ,
533                                 &cron_scan_directory_data_hosts, NULL);
534 }
535
536
537 /**
538  * Bind a host address (hello) to a hostId.
539  *
540  * @param peer the peer for which this is a hello
541  * @param hello the verified (!) hello message
542  */
543 static void
544 bind_address (const struct GNUNET_PeerIdentity *peer,
545               const struct GNUNET_HELLO_Message *hello)
546 {
547   char *fn;
548   struct HostEntry *host;
549   struct GNUNET_HELLO_Message *mrg;
550
551   add_host_to_known_hosts (peer);
552   host = lookup_host_entry (peer);
553   GNUNET_assert (host != NULL);
554   if (host->hello == NULL)
555     {
556       host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
557       memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
558     }
559   else
560     {
561       mrg = GNUNET_HELLO_merge (host->hello, hello);
562       /* FIXME: check if old and merged hello are equal,
563          and if so, bail out early... */
564       GNUNET_free (host->hello);
565       host->hello = mrg;
566     }
567   fn = get_host_filename (peer);
568   GNUNET_DISK_directory_create_for_file (fn);
569   GNUNET_DISK_fn_write (fn, 
570                         host->hello, 
571                         GNUNET_HELLO_size (host->hello),
572                         GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
573                         | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
574   GNUNET_free (fn);
575   notify_all (host);
576 }
577
578
579 /**
580  * Do transmit info either for only the host matching the given
581  * argument or for all known hosts and change their trust values by
582  * the given delta.
583  *
584  * @param only NULL to hit all hosts, otherwise specifies a particular target
585  * @param trust_change how much should the trust be changed
586  * @param client who is making the request (and will thus receive our confirmation)
587  */
588 static void
589 send_to_each_host (const struct GNUNET_PeerIdentity *only,
590                    int trust_change, struct GNUNET_SERVER_Client *client)
591 {
592   struct HostEntry *pos;
593   struct InfoMessage *im;
594   const struct GNUNET_MessageHeader *end;
595   uint16_t hs;
596   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
597   struct GNUNET_SERVER_TransmitContext *tc;
598
599   tc = GNUNET_SERVER_transmit_context_create (client);
600   pos = hosts;
601   while (pos != NULL)
602     {
603       if ((only == NULL) ||
604           (0 ==
605            memcmp (only, &pos->identity,
606                    sizeof (struct GNUNET_PeerIdentity))))
607         {
608           change_host_trust (&pos->identity, trust_change);
609           hs = 0;
610           im = (struct InfoMessage *) buf;
611           if (pos->hello != NULL)
612             {
613               hs = GNUNET_HELLO_size (pos->hello);
614               GNUNET_assert (hs <
615                              GNUNET_SERVER_MAX_MESSAGE_SIZE -
616                              sizeof (struct InfoMessage));
617               memcpy (&im[1], pos->hello, hs);
618             }
619           im->trust = htonl (pos->trust);
620           im->peer = pos->identity;
621           end = &im->header;
622           GNUNET_SERVER_transmit_context_append (tc,
623                                                  &end[1],
624                                                  hs +
625                                                  sizeof (struct InfoMessage) -
626                                                  sizeof (struct
627                                                          GNUNET_MessageHeader),
628                                                  GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
629         }
630       pos = pos->next;
631     }
632   GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
633                                          GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
634   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
635 }
636
637
638 /**
639  * Write host-trust information to a file - flush the buffer entry!
640  * Assumes synchronized access.
641  */
642 static void
643 flush_trust (struct HostEntry *host)
644 {
645   char *fn;
646   uint32_t trust;
647
648   if (host->trust == host->disk_trust)
649     return;                     /* unchanged */
650   fn = get_trust_filename (&host->identity);
651   if (host->trust == 0)
652     {
653       if ((0 != UNLINK (fn)) && (errno != ENOENT))
654         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
655                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);
656     }
657   else
658     {
659       trust = htonl (host->trust);
660       if (sizeof(uint32_t) == GNUNET_DISK_fn_write (fn, &trust, 
661                                                     sizeof(uint32_t),
662                                                     GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
663                                                     | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ))
664         host->disk_trust = host->trust;
665     }
666   GNUNET_free (fn);
667 }
668
669 /**
670  * Call this method periodically to scan data/hosts for new hosts.
671  */
672 static void
673 cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
674 {
675   struct HostEntry *pos;
676
677   pos = hosts;
678   while (pos != NULL)
679     {
680       flush_trust (pos);
681       pos = pos->next;
682     }
683   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
684     return;
685   GNUNET_SCHEDULER_add_delayed (tc->sched,
686                                 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
687 }
688
689
690 /**
691  * @brief delete expired HELLO entries in data/hosts/
692  */
693 static int
694 discard_hosts_helper (void *cls, const char *fn)
695 {
696   struct GNUNET_TIME_Absolute *now = cls;
697   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
698   const struct GNUNET_HELLO_Message *hello;
699   struct GNUNET_HELLO_Message *new_hello;
700   int size;
701
702   size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
703   if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn)))
704     {
705       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
706                                 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
707       return GNUNET_OK;
708     }
709   hello = (const struct GNUNET_HELLO_Message *) buffer;
710   new_hello = GNUNET_HELLO_iterate_addresses (hello,
711                                               GNUNET_YES,
712                                               &discard_expired, now);
713   if ((new_hello == NULL) && (0 != UNLINK (fn)))
714     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
715                               GNUNET_ERROR_TYPE_BULK, "unlink", fn);
716   if (new_hello != NULL)
717     {
718       GNUNET_DISK_fn_write (fn, 
719                             new_hello,
720                             GNUNET_HELLO_size (new_hello),
721                             GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
722                             | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
723       GNUNET_free (new_hello);
724     }
725   return GNUNET_OK;
726 }
727
728
729 /**
730  * Call this method periodically to scan data/hosts for new hosts.
731  */
732 static void
733 cron_clean_data_hosts (void *cls,
734                        const struct GNUNET_SCHEDULER_TaskContext *tc)
735 {
736   struct GNUNET_TIME_Absolute now;
737
738   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
739     return;
740   now = GNUNET_TIME_absolute_get ();
741   GNUNET_DISK_directory_scan (networkIdDirectory,
742                               &discard_hosts_helper, &now);
743
744   GNUNET_SCHEDULER_add_delayed (tc->sched,
745                                 DATA_HOST_CLEAN_FREQ,
746                                 &cron_clean_data_hosts, NULL);
747 }
748
749
750 /**
751  * Handle ADD-message.
752  *
753  * @param cls closure
754  * @param client identification of the client
755  * @param message the actual message
756  */
757 static void
758 handle_add (void *cls,
759             struct GNUNET_SERVER_Client *client,
760             const struct GNUNET_MessageHeader *message)
761 {
762   const struct PeerAddMessage *pam;
763   const struct GNUNET_MessageHeader *hello;
764   uint16_t size;
765
766   size = ntohs (message->size);
767   if (size <
768       sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader))
769     {
770       GNUNET_break (0);
771       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
772       return;
773     }
774   pam = (const struct PeerAddMessage *) message;
775   hello = (const struct GNUNET_MessageHeader *) &pam[1];
776   if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size))
777     {
778       GNUNET_break (0);
779       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
780       return;
781     }
782   bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello);
783   GNUNET_SERVER_receive_done (client, GNUNET_OK);
784 }
785
786
787 /**
788  * Handle GET-message.
789  *
790  * @param cls closure
791  * @param client identification of the client
792  * @param message the actual message
793  */
794 static void
795 handle_get (void *cls,
796             struct GNUNET_SERVER_Client *client,
797             const struct GNUNET_MessageHeader *message)
798 {
799   const struct ListPeerMessage *lpm;
800
801   lpm = (const struct ListPeerMessage *) message;
802   send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client);
803 }
804
805
806 /**
807  * Handle GET-ALL-message.
808  *
809  * @param cls closure
810  * @param client identification of the client
811  * @param message the actual message
812  */
813 static void
814 handle_get_all (void *cls,
815                 struct GNUNET_SERVER_Client *client,
816                 const struct GNUNET_MessageHeader *message)
817 {
818   const struct ListAllPeersMessage *lpm;
819
820   lpm = (const struct ListAllPeersMessage *) message;
821   send_to_each_host (NULL, ntohl (lpm->trust_change), client);
822 }
823
824
825 /**
826  * Handle NOTIFY-message.
827  *
828  * @param cls closure
829  * @param client identification of the client
830  * @param message the actual message
831  */
832 static void
833 handle_notify (void *cls,
834             struct GNUNET_SERVER_Client *client,
835             const struct GNUNET_MessageHeader *message)
836 {
837   struct NotifyList *nl;
838   struct HostEntry *pos;
839
840   nl = GNUNET_malloc (sizeof (struct NotifyList));
841   nl->next = notify_list;
842   nl->client = client;
843   GNUNET_SERVER_client_keep (client);  
844   notify_list = nl;
845   pos = hosts;
846   while (NULL != pos)
847     {
848       do_notify (nl, pos);
849       pos = pos->next;
850     }
851 }
852
853
854 /**
855  * List of handlers for the messages understood by this
856  * service.
857  */
858 static struct GNUNET_SERVER_MessageHandler handlers[] = {
859   {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0},
860   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
861    sizeof (struct ListPeerMessage)},
862   {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
863    sizeof (struct ListAllPeersMessage)},
864   {&handle_notify, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
865    sizeof (struct GNUNET_MessageHeader)},
866   {NULL, NULL, 0, 0}
867 };
868
869
870 /**
871  * Function that is called when a client disconnects.
872  */
873 static void
874 notify_disconnect (void *cls,
875                    struct GNUNET_SERVER_Client *client)
876 {
877   struct NotifyList *pos;
878   struct NotifyList *prev;
879   struct NotifyList *next;
880   struct PendingEntry *p;
881
882   pos = notify_list;
883   prev = NULL;
884   while (pos != NULL)
885     {
886       next = pos->next;
887       if (pos->client == client)
888         {
889           while (NULL != (p = pos->pending))
890             {
891               pos->pending = p->next;
892               GNUNET_free (p);
893             }
894           if (pos->transmit_ctx != NULL)
895             {
896               GNUNET_CONNECTION_notify_transmit_ready_cancel (pos->transmit_ctx);
897               pos->transmit_ctx = NULL;
898             }
899           if (prev == NULL)
900             notify_list = next;
901           else
902             prev->next = next;
903           GNUNET_SERVER_client_drop (client);
904           GNUNET_free (pos);
905         }
906       else
907         {
908           prev = pos;
909         }
910       pos = next;
911     }
912
913 }
914
915
916 /**
917  * Process statistics requests.
918  *
919  * @param cls closure
920  * @param sched scheduler to use
921  * @param server the initialized server
922  * @param cfg configuration to use
923  */
924 static void
925 run (void *cls,
926      struct GNUNET_SCHEDULER_Handle *sched,
927      struct GNUNET_SERVER_Handle *server,
928      const struct GNUNET_CONFIGURATION_Handle *cfg)
929 {
930   GNUNET_assert (GNUNET_OK ==
931                  GNUNET_CONFIGURATION_get_value_filename (cfg,
932                                                           "peerinfo",
933                                                           "HOSTS",
934                                                           &networkIdDirectory));
935   GNUNET_assert (GNUNET_OK ==
936                  GNUNET_CONFIGURATION_get_value_filename (cfg,
937                                                           "peerinfo",
938                                                           "TRUST",
939                                                           &trustDirectory));
940   GNUNET_DISK_directory_create (networkIdDirectory);
941   GNUNET_DISK_directory_create (trustDirectory);
942   GNUNET_SCHEDULER_add_with_priority (sched,
943                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
944                                       &cron_scan_directory_data_hosts, NULL);
945   GNUNET_SCHEDULER_add_with_priority (sched,
946                                       GNUNET_SCHEDULER_PRIORITY_HIGH,
947                                       &cron_flush_trust, NULL);
948   GNUNET_SCHEDULER_add_with_priority (sched,
949                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
950                                       &cron_clean_data_hosts, NULL);
951   GNUNET_SERVER_disconnect_notify (server, &notify_disconnect, NULL);
952   GNUNET_SERVER_add_handlers (server, handlers);
953 }
954
955
956 /**
957  * The main function for the statistics service.
958  *
959  * @param argc number of arguments from the command line
960  * @param argv command line arguments
961  * @return 0 ok, 1 on error
962  */
963 int
964 main (int argc, char *const *argv)
965 {
966   int ret;
967
968   ret = (GNUNET_OK ==
969          GNUNET_SERVICE_run (argc,
970                              argv,
971                               "peerinfo",
972                              GNUNET_SERVICE_OPTION_NONE,
973                              &run, NULL)) ? 0 : 1;
974   GNUNET_free_non_null (networkIdDirectory);
975   GNUNET_free_non_null (trustDirectory);
976   return ret;
977 }
978
979
980 /* end of gnunet-service-peerinfo.c */