15832ca8023dd627233b9036fcb4ce57a63ca947
[oweals/gnunet.git] / src / peerinfo / peerinfo_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 2009 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/peerinfo_api.c
23  * @brief API to access peerinfo service
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - document NEW API implementation
28  * - add timeout for iteration
29  * - implement cancellation of iteration
30  * - prevent transmit during receive!
31  */
32 #include "platform.h"
33 #include "gnunet_client_lib.h"
34 #include "gnunet_container_lib.h"
35 #include "gnunet_peerinfo_service.h"
36 #include "gnunet_protocols.h"
37 #include "gnunet_time_lib.h"
38 #include "peerinfo.h"
39
40 /**
41  * Function to call after transmission has succeeded.
42  * 
43  * @param cls closure
44  * @param success GNUNET_OK if transmission worked, GNUNET_SYSERR on error
45  */
46 typedef void (*TransmissionContinuation)(void *cls,
47                                          int success);
48
49
50 /**
51  * Entry in the transmission queue to PEERINFO service.
52  */
53 struct TransmissionQueueEntry
54 {
55   /**
56    * This is a linked list.
57    */
58   struct TransmissionQueueEntry *next;
59   
60   /**
61    * This is a linked list.
62    */
63   struct TransmissionQueueEntry *prev;
64
65   /**
66    * Function to call after request has been transmitted, or NULL (in which
67    * case we must consider sending the next entry immediately).
68    */
69   TransmissionContinuation cont;
70   
71   /**
72    * Closure for 'cont'.
73    */
74   void *cont_cls;
75
76   /**
77    * When this request times out.
78    */
79   struct GNUNET_TIME_Absolute timeout;
80
81   /**
82    * Number of bytes of the request message (follows after this struct).
83    */
84   size_t size;
85
86 };
87
88
89 /**
90  * Handle to the peerinfo service.
91  */
92 struct GNUNET_PEERINFO_Handle
93 {
94   /**
95    * Our configuration.
96    */
97   const struct GNUNET_CONFIGURATION_Handle *cfg;
98
99   /**
100    * Our scheduler.
101    */
102   struct GNUNET_SCHEDULER_Handle *sched;
103
104   /**
105    * Connection to the service.
106    */
107   struct GNUNET_CLIENT_Connection *client;
108
109   /**
110    * Head of transmission queue.
111    */
112   struct TransmissionQueueEntry *tq_head;
113
114   /**
115    * Tail of transmission queue.
116    */
117   struct TransmissionQueueEntry *tq_tail;
118
119   /**
120    * Handle for the current transmission request, or NULL if none is pending.
121    */
122   struct GNUNET_CLIENT_TransmitHandle *th;
123
124 };
125
126
127 /**
128  * Connect to the peerinfo service.
129  *
130  * @param cfg configuration to use
131  * @param sched scheduler to use
132  * @return NULL on error (configuration related, actual connection
133  *         etablishment may happen asynchronously).
134  */
135 struct GNUNET_PEERINFO_Handle *
136 GNUNET_PEERINFO_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
137                          struct GNUNET_SCHEDULER_Handle *sched)
138 {
139   struct GNUNET_CLIENT_Connection *client;
140   struct GNUNET_PEERINFO_Handle *ret;
141
142   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
143   if (client == NULL)
144     return NULL;
145   ret = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_Handle));
146   ret->client = client;
147   ret->cfg = cfg;
148   ret->sched = sched;
149   return ret;
150 }
151
152
153 /**
154  * Disconnect from the peerinfo service.  Note that all iterators must
155  * have completed or have been cancelled by the time this function is
156  * called (otherwise, calling this function is a serious error).
157  * Furthermore, if 'GNUNET_PEERINFO_add_peer' operations are still
158  * pending, they will be cancelled silently on disconnect.
159  *
160  * @param h handle to disconnect
161  */
162 void
163 GNUNET_PEERINFO_disconnect (struct GNUNET_PEERINFO_Handle *h)
164 {
165   struct TransmissionQueueEntry *tqe;
166
167   while (NULL != (tqe = h->tq_head))
168     {     
169       GNUNET_CONTAINER_DLL_remove (h->tq_head,
170                                    h->tq_tail,
171                                    tqe);
172       if (tqe->cont != NULL)
173         tqe->cont (tqe->cont_cls, GNUNET_SYSERR);
174       GNUNET_free (tqe);
175     }
176   GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
177   GNUNET_free (h);
178 }
179
180
181 /**
182  * Check if we have a request pending in the transmission queue and are
183  * able to transmit it right now.  If so, schedule transmission.
184  *
185  * @param h handle to the service
186  */
187 static void
188 trigger_transmit (struct GNUNET_PEERINFO_Handle *h);
189
190
191 /**
192  * Close the existing connection to PEERINFO and reconnect.
193  *
194  * @param h handle to the service
195  */
196 static void
197 reconnect (struct GNUNET_PEERINFO_Handle *h)
198 {
199   GNUNET_CLIENT_disconnect (h->client, GNUNET_SYSERR);
200   h->client = GNUNET_CLIENT_connect (h->sched, "client", h->cfg);
201   GNUNET_assert (h->client != NULL);
202 }
203
204
205 /**
206  * Transmit the request at the head of the transmission queue
207  * and trigger continuation (if any).
208  *
209  * @param cls the 'struct GNUNET_PEERINFO_Handle' (with the queue)
210  * @param size size of the buffer (0 on error)
211  * @param buf where to copy the message
212  * @return number of bytes copied to buf
213  */
214 static size_t
215 do_transmit (void *cls, size_t size, void *buf)
216 {
217   struct GNUNET_PEERINFO_Handle *h = cls;
218   struct TransmissionQueueEntry *tqe = h->tq_head;
219   size_t ret;
220
221   h->th = NULL;
222   if (buf == NULL)
223     {
224 #if DEBUG_PEERINFO
225       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226                   _
227                   ("Failed to transmit message of type %u to `%s' service.\n"),
228                   ntohs (msg->type), "peerinfo");
229 #endif
230       GNUNET_CONTAINER_DLL_remove (h->tq_head,
231                                    h->tq_tail,
232                                    tqe);
233       reconnect (h);
234       trigger_transmit (h);
235       if (tqe->cont != NULL)
236         tqe->cont (tqe->cont_cls, GNUNET_SYSERR);
237       GNUNET_free (tqe);
238       return 0;
239     }
240   ret = tqe->size;
241   GNUNET_assert (size >= ret);
242   memcpy (buf, &tqe[1], ret);
243   GNUNET_CONTAINER_DLL_remove (h->tq_head,
244                                h->tq_tail,
245                                tqe);
246   if (tqe->cont != NULL)
247     tqe->cont (tqe->cont_cls, GNUNET_OK);
248   else
249     trigger_transmit (h);
250   GNUNET_free (tqe);
251   return ret;
252 }
253
254
255 /**
256  * Check if we have a request pending in the transmission queue and are
257  * able to transmit it right now.  If so, schedule transmission.
258  *
259  * @param h handle to the service
260  */
261 static void
262 trigger_transmit (struct GNUNET_PEERINFO_Handle *h)
263 {
264   struct TransmissionQueueEntry *tqe;
265
266   /* FIXME: need to handle case where we are still *receiving* (and then
267      do nothing here as well!) */
268   if (NULL == (tqe = h->tq_head))
269     return;
270   if (h->th != NULL)
271     return;
272   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
273                                                tqe->size,
274                                                GNUNET_TIME_absolute_get_remaining (tqe->timeout),
275                                                GNUNET_YES,
276                                                &do_transmit, h);  
277 }
278
279
280 /**
281  * Add a host to the persistent list.  This method operates in 
282  * semi-reliable mode: if the transmission is not completed by
283  * the time 'GNUNET_PEERINFO_disconnect' is called, it will be
284  * aborted.  Furthermore, if a second HELLO is added for the
285  * same peer before the first one was transmitted, PEERINFO may
286  * merge the two HELLOs prior to transmission to the service.
287  *
288  * @param h handle to the peerinfo service
289  * @param hello the verified (!) HELLO message
290  */
291 void
292 GNUNET_PEERINFO_add_peer_new (struct GNUNET_PEERINFO_Handle *h,
293                               const struct GNUNET_HELLO_Message *hello)
294 {
295   uint16_t hs = GNUNET_HELLO_size (hello);
296   struct TransmissionQueueEntry *tqe;
297   
298 #if DEBUG_PEERINFO
299   struct GNUNET_PeerIdentity peer;
300   GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer));
301   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
302               "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n",
303               GNUNET_i2s(&peer),
304               hs,
305               "HELLO");
306 #endif
307   tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) + hs);
308   tqe->size = hs;
309   tqe->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
310   memcpy (&tqe[1], hello, hs);
311   GNUNET_CONTAINER_DLL_insert_after (h->tq_head,
312                                      h->tq_tail,
313                                      h->tq_tail,
314                                      tqe);
315   trigger_transmit (h);
316 }
317
318
319 /**
320  * Context for an iteration request.
321  */
322 struct GNUNET_PEERINFO_NewIteratorContext
323 {
324   /**
325    * Handle to the PEERINFO service.
326    */
327   struct GNUNET_PEERINFO_Handle *h;
328
329   /**
330    * Function to call with the results.
331    */
332   GNUNET_PEERINFO_Processor callback;
333
334   /**
335    * Closure for 'callback'.
336    */
337   void *callback_cls;
338
339   /**
340    * Timeout for the operation.
341    */
342   struct GNUNET_TIME_Absolute timeout;
343 };
344
345
346 /**
347  * Type of a function to call when we receive a message
348  * from the service.
349  *
350  * @param cls closure
351  * @param msg message received, NULL on timeout or fatal error
352  */
353 static void
354 peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
355 {
356   struct GNUNET_PEERINFO_NewIteratorContext *ic = cls;
357   const struct InfoMessage *im;
358   const struct GNUNET_HELLO_Message *hello;
359   uint16_t ms;
360
361   if (msg == NULL)
362     {
363       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
364                   _("Failed to receive response from `%s' service.\n"),
365                   "peerinfo");
366       reconnect (ic->h);
367       trigger_transmit (ic->h);
368       ic->callback (ic->callback_cls, NULL, NULL, 1);
369       GNUNET_free (ic);
370       return;
371     }
372   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
373     {
374 #if DEBUG_PEERINFO
375       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376                   "Received end of list of peers from peerinfo database\n");
377 #endif
378       trigger_transmit (ic->h);
379       ic->callback (ic->callback_cls, NULL, NULL, 0);
380       GNUNET_free (ic);
381       return;
382     }
383   ms = ntohs (msg->size);
384   if ((ms < sizeof (struct InfoMessage)) ||
385       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
386     {
387       GNUNET_break (0);
388       reconnect (ic->h);
389       trigger_transmit (ic->h);
390       ic->callback (ic->callback_cls, NULL, NULL, 2);
391       GNUNET_free (ic);
392       return;
393     }
394   im = (const struct InfoMessage *) msg;
395   hello = NULL;
396   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
397     {
398       hello = (const struct GNUNET_HELLO_Message *) &im[1];
399       if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
400         {
401           GNUNET_break (0);
402           reconnect (ic->h);
403           trigger_transmit (ic->h);
404           ic->callback (ic->callback_cls, NULL, NULL, 2);
405           GNUNET_free (ic);
406           return;
407         }
408     }
409 #if DEBUG_PEERINFO
410   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411               "Received %u bytes of `%s' information about peer `%s' from PEERINFO database\n",
412               (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello),
413               "HELLO",
414               GNUNET_i2s (&im->peer));
415 #endif
416   ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
417   GNUNET_CLIENT_receive (ic->h->client,
418                          &peerinfo_handler,
419                          ic,
420                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
421 }
422
423
424 /**
425  * We've transmitted the iteration request.  Now get ready to process
426  * the results (or handle transmission error).
427  *
428  * @param cls the 'struct GNUNET_PEERINFO_NewIteratorContext'
429  * @param transmit_success GNUNET_OK if transmission worked
430  */
431 static void
432 iterator_start_receive (void *cls,
433                         int transmit_success)
434 {
435   struct GNUNET_PEERINFO_NewIteratorContext *ic = cls;
436
437   if (GNUNET_OK != transmit_success)
438     {
439       ic->callback (ic->callback_cls, NULL, NULL, 2);
440       reconnect (ic->h);
441       trigger_transmit (ic->h);
442       GNUNET_free (ic);
443       return;
444     }  
445   GNUNET_CLIENT_receive (ic->h->client,
446                          &peerinfo_handler,
447                          ic,
448                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
449 }
450
451
452 /**
453  * Call a method for each known matching host and change its trust
454  * value.  The callback method will be invoked once for each matching
455  * host and then finally once with a NULL pointer.  After that final
456  * invocation, the iterator context must no longer be used.
457  *
458  * Note that the last call can be triggered by timeout or by simply
459  * being done; however, the trust argument will be set to zero if we
460  * are done, 1 if we timed out and 2 for fatal error.
461  *
462  * Instead of calling this function with 'peer == NULL' and 'trust ==
463  * 0', it is often better to use 'GNUNET_PEERINFO_notify'.
464  * 
465  * @param h handle to the peerinfo service
466  * @param peer restrict iteration to this peer only (can be NULL)
467  * @param trust_delta how much to change the trust in all matching peers
468  * @param timeout how long to wait until timing out
469  * @param callback the method to call for each peer
470  * @param callback_cls closure for callback
471  * @return NULL on error (in this case, 'callback' is never called!), 
472  *         otherwise an iterator context
473  */
474 struct GNUNET_PEERINFO_NewIteratorContext *
475 GNUNET_PEERINFO_iterate_new (struct GNUNET_PEERINFO_Handle *h,
476                              const struct GNUNET_PeerIdentity *peer,
477                              int trust_delta,
478                              struct GNUNET_TIME_Relative timeout,
479                              GNUNET_PEERINFO_Processor callback,
480                              void *callback_cls)
481 {
482   struct ListAllPeersMessage *lapm;
483   struct ListPeerMessage *lpm;
484   struct GNUNET_PEERINFO_NewIteratorContext *ic;
485   struct TransmissionQueueEntry *tqe;
486
487 #if DEBUG_PEERINFO
488   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489               "Requesting list of peers from peerinfo database\n");
490 #endif
491   if (peer == NULL)
492     {
493       tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
494                            sizeof (struct ListAllPeersMessage));
495       tqe->size = sizeof (struct ListAllPeersMessage);
496       lapm = (struct ListAllPeersMessage *) &tqe[1];
497       lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
498       lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
499       lapm->trust_change = htonl (trust_delta);
500     }
501   else
502     {
503       tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
504                            sizeof (struct ListPeerMessage));
505       tqe->size = sizeof (struct ListPeerMessage);
506       lpm = (struct ListPeerMessage *) &tqe[1];
507       lpm->header.size = htons (sizeof (struct ListPeerMessage));
508       lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
509       lpm->trust_change = htonl (trust_delta);
510       memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
511     }
512   ic = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_NewIteratorContext));
513   ic->callback = callback;
514   ic->callback_cls = callback_cls;
515   ic->timeout = GNUNET_TIME_relative_to_absolute (timeout);
516   tqe->timeout = ic->timeout;
517   tqe->cont = &iterator_start_receive;
518   tqe->cont_cls = ic;
519   /* FIXME: sort DLL by timeout? */
520   /* FIXME: add timeout task!? */
521   GNUNET_CONTAINER_DLL_insert_after (h->tq_head,
522                                      h->tq_tail,
523                                      h->tq_tail,
524                                      tqe);
525   trigger_transmit (h);
526   return ic;
527 }
528
529
530
531 /**
532  * Cancel an iteration over peer information.
533  *
534  * @param ic context of the iterator to cancel
535  */
536 void
537 GNUNET_PEERINFO_iterate_cancel_new (struct GNUNET_PEERINFO_NewIteratorContext *ic)
538 {
539   GNUNET_assert (0); 
540   // FIXME: not implemented
541 }
542
543
544
545
546
547 /* ***************************** OLD API ****************************** */
548
549
550
551
552 #define ADD_PEER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
553
554
555 struct CAFContext
556 {
557   struct GNUNET_CLIENT_Connection *client;
558   struct GNUNET_MessageHeader *msg;
559 };
560
561
562 static size_t
563 copy_and_free (void *cls, size_t size, void *buf)
564 {
565   struct CAFContext *cc = cls;
566   struct GNUNET_MessageHeader *msg = cc->msg;
567   uint16_t msize;
568
569   if (buf == NULL)
570     {
571 #if DEBUG_PEERINFO
572       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
573                   _
574                   ("Failed to transmit message of type %u to `%s' service.\n"),
575                   ntohs (msg->type), "peerinfo");
576 #endif
577       GNUNET_free (msg);
578       GNUNET_CLIENT_disconnect (cc->client, GNUNET_NO);
579       GNUNET_free (cc);
580       return 0;
581     }
582   msize = ntohs (msg->size);
583   GNUNET_assert (size >= msize);
584   memcpy (buf, msg, msize);
585   GNUNET_free (msg);
586   GNUNET_CLIENT_disconnect (cc->client, GNUNET_YES);
587   GNUNET_free (cc);
588   return msize;
589 }
590
591
592
593 /**
594  * Add a host to the persistent list.
595  *
596  * @param cfg configuration to use
597  * @param sched scheduler to use
598  * @param peer identity of the peer
599  * @param hello the verified (!) HELLO message
600  */
601 void
602 GNUNET_PEERINFO_add_peer (const struct GNUNET_CONFIGURATION_Handle *cfg,
603                           struct GNUNET_SCHEDULER_Handle *sched,
604                           const struct GNUNET_PeerIdentity *peer,
605                           const struct GNUNET_HELLO_Message *hello)
606 {
607   struct GNUNET_CLIENT_Connection *client;
608   struct PeerAddMessage *pam;
609   uint16_t hs;
610   struct CAFContext *cc;
611
612 #if DEBUG_PEERINFO
613   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614               "Adding peer `%s' to peerinfo database\n",
615               GNUNET_i2s(peer));
616 #endif
617   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
618   if (client == NULL)
619     {
620       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
621                   _("Could not connect to `%s' service.\n"), "peerinfo");
622       return;
623     }
624   hs = GNUNET_HELLO_size (hello);
625 #if DEBUG_PEERINFO
626   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627               "Size of `%s' is %u bytes\n",
628               "HELLO",
629               (unsigned int) GNUNET_HELLO_size (hello));
630 #endif  
631   pam = GNUNET_malloc (sizeof (struct PeerAddMessage) + hs);
632   pam->header.size = htons (hs + sizeof (struct PeerAddMessage));
633   pam->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_ADD);
634   memcpy (&pam->peer, peer, sizeof (struct GNUNET_PeerIdentity));
635   memcpy (&pam[1], hello, hs);
636   cc = GNUNET_malloc (sizeof (struct CAFContext));
637   cc->client = client;
638   cc->msg = &pam->header;
639   GNUNET_CLIENT_notify_transmit_ready (client,
640                                        ntohs (pam->header.size),
641                                        ADD_PEER_TIMEOUT, 
642                                        GNUNET_NO,
643                                        &copy_and_free, cc);
644 }
645
646
647 /**
648  * Context for the info handler.
649  */
650 struct GNUNET_PEERINFO_IteratorContext
651 {
652
653   /**
654    * Our connection to the PEERINFO service.
655    */
656   struct GNUNET_CLIENT_Connection *client;
657
658   /**
659    * Function to call with information.
660    */
661   GNUNET_PEERINFO_Processor callback;
662
663   /**
664    * Closure for callback.
665    */
666   void *callback_cls;
667
668   /**
669    * When should we time out?
670    */
671   struct GNUNET_TIME_Absolute timeout;
672
673 };
674
675
676 /**
677  * Type of a function to call when we receive a message
678  * from the service.
679  *
680  * @param cls closure
681  * @param msg message received, NULL on timeout or fatal error
682  */
683 static void
684 info_handler (void *cls, const struct GNUNET_MessageHeader *msg)
685 {
686   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
687   const struct InfoMessage *im;
688   const struct GNUNET_HELLO_Message *hello;
689   uint16_t ms;
690
691   if (msg == NULL)
692     {
693       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
694                   _("Failed to receive response from `%s' service.\n"),
695                   "peerinfo");
696       ic->callback (ic->callback_cls, NULL, NULL, 1);
697       GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
698       GNUNET_free (ic);
699       return;
700     }
701   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
702     {
703 #if DEBUG_PEERINFO
704       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
705                   "Received end of list of peers from peerinfo database\n");
706 #endif
707       ic->callback (ic->callback_cls, NULL, NULL, 0);
708       GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
709       GNUNET_free (ic);
710       return;
711     }
712   ms = ntohs (msg->size);
713   if ((ms < sizeof (struct InfoMessage)) ||
714       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
715     {
716       GNUNET_break (0);
717       ic->callback (ic->callback_cls, NULL, NULL, 2);
718       GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
719       GNUNET_free (ic);
720       return;
721     }
722   im = (const struct InfoMessage *) msg;
723   hello = NULL;
724   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
725     {
726       hello = (const struct GNUNET_HELLO_Message *) &im[1];
727       if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
728         {
729           GNUNET_break (0);
730           ic->callback (ic->callback_cls, NULL, NULL, 2);
731           GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
732           GNUNET_free (ic);
733           return;
734         }
735     }
736 #if DEBUG_PEERINFO
737   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
738               "Received %u bytes of `%s' information about peer `%s' from PEERINFO database\n",
739               (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello),
740               "HELLO",
741               GNUNET_i2s (&im->peer));
742 #endif
743   ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
744   GNUNET_CLIENT_receive (ic->client,
745                          &info_handler,
746                          ic,
747                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
748 }
749
750
751 /**
752  * Call a method for each known matching host and change
753  * its trust value.  The method will be invoked once for
754  * each host and then finally once with a NULL pointer.
755  * Note that the last call can be triggered by timeout or
756  * by simply being done; however, the trust argument will
757  * be set to zero if we are done and to 1 if we timed out.
758  *
759  * @param cfg configuration to use
760  * @param sched scheduler to use
761  * @param peer restrict iteration to this peer only (can be NULL)
762  * @param trust_delta how much to change the trust in all matching peers
763  * @param timeout how long to wait until timing out
764  * @param callback the method to call for each peer
765  * @param callback_cls closure for callback
766  * @return NULL on error, otherwise an iterator context
767  */
768 struct GNUNET_PEERINFO_IteratorContext *
769 GNUNET_PEERINFO_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
770                          struct GNUNET_SCHEDULER_Handle *sched,
771                          const struct GNUNET_PeerIdentity *peer,
772                          int trust_delta,
773                          struct GNUNET_TIME_Relative timeout,
774                          GNUNET_PEERINFO_Processor callback,
775                          void *callback_cls)
776 {
777   struct GNUNET_CLIENT_Connection *client;
778   struct ListAllPeersMessage *lapm;
779   struct ListPeerMessage *lpm;
780   struct GNUNET_PEERINFO_IteratorContext *ihc;
781
782   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
783   if (client == NULL)
784     {
785       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
786                   _("Could not connect to `%s' service.\n"), "peerinfo");
787       return NULL;
788     }
789 #if DEBUG_PEERINFO
790   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
791               "Requesting list of peers from peerinfo database\n");
792 #endif
793   if (peer == NULL)
794     {
795       ihc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext) +
796                            sizeof (struct ListAllPeersMessage));
797       lapm = (struct ListAllPeersMessage *) &ihc[1];
798       lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
799       lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
800       lapm->trust_change = htonl (trust_delta);
801     }
802   else
803     {
804       ihc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext) +
805                            sizeof (struct ListPeerMessage));
806       lpm = (struct ListPeerMessage *) &ihc[1];
807       lpm->header.size = htons (sizeof (struct ListPeerMessage));
808       lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
809       lpm->trust_change = htonl (trust_delta);
810       memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
811     }
812   ihc->client = client;
813   ihc->callback = callback;
814   ihc->callback_cls = callback_cls;
815   ihc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
816   if (GNUNET_OK != 
817       GNUNET_CLIENT_transmit_and_get_response (client,
818                                                (const struct GNUNET_MessageHeader*) &ihc[1],
819                                                timeout,
820                                                GNUNET_YES,
821                                                &info_handler,
822                                                ihc))
823     {
824       GNUNET_break (0);
825       GNUNET_CLIENT_disconnect (ihc->client, GNUNET_NO);
826       GNUNET_free (ihc);
827       return NULL;
828     }
829   return ihc;
830 }
831
832
833 /**
834  * Cancel an iteration over peer information.
835  *
836  * @param ic context of the iterator to cancel
837  */
838 void
839 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
840 {
841   GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
842   GNUNET_free (ic);
843 }
844
845
846 /* end of peerinfo_api.c */