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