c8a9daabec0bbd728b132fc5c05598cda393694c
[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 peer identity of the peer
290  * @param hello the verified (!) HELLO message
291  */
292 void
293 GNUNET_PEERINFO_add_peer_new (struct GNUNET_PEERINFO_Handle *h,
294                               const struct GNUNET_HELLO_Message *hello)
295 {
296   uint16_t hs = GNUNET_HELLO_size (hello);
297   struct TransmissionQueueEntry *tqe;
298   
299 #if DEBUG_PEERINFO
300   struct GNUNET_PeerIdentity peer;
301   GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer));
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303               "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n",
304               GNUNET_i2s(&peer),
305               hs,
306               "HELLO");
307 #endif
308   tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) + hs);
309   tqe->size = hs;
310   tqe->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
311   memcpy (&tqe[1], hello, hs);
312   GNUNET_CONTAINER_DLL_insert_after (h->tq_head,
313                                      h->tq_tail,
314                                      h->tq_tail,
315                                      tqe);
316   trigger_transmit (h);
317 }
318
319
320 /**
321  * Context for an iteration request.
322  */
323 struct GNUNET_PEERINFO_NewIteratorContext
324 {
325   /**
326    * Handle to the PEERINFO service.
327    */
328   struct GNUNET_PEERINFO_Handle *h;
329
330   /**
331    * Function to call with the results.
332    */
333   GNUNET_PEERINFO_Processor callback;
334
335   /**
336    * Closure for 'callback'.
337    */
338   void *callback_cls;
339
340   /**
341    * Timeout for the operation.
342    */
343   struct GNUNET_TIME_Absolute timeout;
344 };
345
346
347 /**
348  * Type of a function to call when we receive a message
349  * from the service.
350  *
351  * @param cls closure
352  * @param msg message received, NULL on timeout or fatal error
353  */
354 static void
355 peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
356 {
357   struct GNUNET_PEERINFO_NewIteratorContext *ic = cls;
358   const struct InfoMessage *im;
359   const struct GNUNET_HELLO_Message *hello;
360   uint16_t ms;
361
362   if (msg == NULL)
363     {
364       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
365                   _("Failed to receive response from `%s' service.\n"),
366                   "peerinfo");
367       reconnect (ic->h);
368       trigger_transmit (ic->h);
369       ic->callback (ic->callback_cls, NULL, NULL, 1);
370       GNUNET_free (ic);
371       return;
372     }
373   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
374     {
375 #if DEBUG_PEERINFO
376       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377                   "Received end of list of peers from peerinfo database\n");
378 #endif
379       trigger_transmit (ic->h);
380       ic->callback (ic->callback_cls, NULL, NULL, 0);
381       GNUNET_free (ic);
382       return;
383     }
384   ms = ntohs (msg->size);
385   if ((ms < sizeof (struct InfoMessage)) ||
386       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
387     {
388       GNUNET_break (0);
389       reconnect (ic->h);
390       trigger_transmit (ic->h);
391       ic->callback (ic->callback_cls, NULL, NULL, 2);
392       GNUNET_free (ic);
393       return;
394     }
395   im = (const struct InfoMessage *) msg;
396   hello = NULL;
397   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
398     {
399       hello = (const struct GNUNET_HELLO_Message *) &im[1];
400       if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
401         {
402           GNUNET_break (0);
403           reconnect (ic->h);
404           trigger_transmit (ic->h);
405           ic->callback (ic->callback_cls, NULL, NULL, 2);
406           GNUNET_free (ic);
407           return;
408         }
409     }
410 #if DEBUG_PEERINFO
411   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412               "Received %u bytes of `%s' information about peer `%s' from PEERINFO database\n",
413               (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello),
414               "HELLO",
415               GNUNET_i2s (&im->peer));
416 #endif
417   ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
418   GNUNET_CLIENT_receive (ic->h->client,
419                          &peerinfo_handler,
420                          ic,
421                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
422 }
423
424
425 /**
426  * We've transmitted the iteration request.  Now get ready to process
427  * the results (or handle transmission error).
428  *
429  * @param cls the 'struct GNUNET_PEERINFO_NewIteratorContext'
430  * @param transmit_success GNUNET_OK if transmission worked
431  */
432 static void
433 iterator_start_receive (void *cls,
434                         int transmit_success)
435 {
436   struct GNUNET_PEERINFO_NewIteratorContext *ic = cls;
437
438   if (GNUNET_OK != transmit_success)
439     {
440       ic->callback (ic->callback_cls, NULL, NULL, 2);
441       reconnect (ic->h);
442       trigger_transmit (ic->h);
443       GNUNET_free (ic);
444       return;
445     }  
446   GNUNET_CLIENT_receive (ic->h->client,
447                          &peerinfo_handler,
448                          ic,
449                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
450 }
451
452
453 /**
454  * Call a method for each known matching host and change its trust
455  * value.  The callback method will be invoked once for each matching
456  * host and then finally once with a NULL pointer.  After that final
457  * invocation, the iterator context must no longer be used.
458  *
459  * Note that the last call can be triggered by timeout or by simply
460  * being done; however, the trust argument will be set to zero if we
461  * are done, 1 if we timed out and 2 for fatal error.
462  *
463  * Instead of calling this function with 'peer == NULL' and 'trust ==
464  * 0', it is often better to use 'GNUNET_PEERINFO_notify'.
465  * 
466  * @param h handle to the peerinfo service
467  * @param peer restrict iteration to this peer only (can be NULL)
468  * @param trust_delta how much to change the trust in all matching peers
469  * @param timeout how long to wait until timing out
470  * @param callback the method to call for each peer
471  * @param callback_cls closure for callback
472  * @return NULL on error (in this case, 'callback' is never called!), 
473  *         otherwise an iterator context
474  */
475 struct GNUNET_PEERINFO_NewIteratorContext *
476 GNUNET_PEERINFO_iterate_new (struct GNUNET_PEERINFO_Handle *h,
477                              const struct GNUNET_PeerIdentity *peer,
478                              int trust_delta,
479                              struct GNUNET_TIME_Relative timeout,
480                              GNUNET_PEERINFO_Processor callback,
481                              void *callback_cls)
482 {
483   struct ListAllPeersMessage *lapm;
484   struct ListPeerMessage *lpm;
485   struct GNUNET_PEERINFO_NewIteratorContext *ic;
486   struct TransmissionQueueEntry *tqe;
487
488 #if DEBUG_PEERINFO
489   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490               "Requesting list of peers from peerinfo database\n");
491 #endif
492   if (peer == NULL)
493     {
494       tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
495                            sizeof (struct ListAllPeersMessage));
496       tqe->size = sizeof (struct ListAllPeersMessage);
497       lapm = (struct ListAllPeersMessage *) &tqe[1];
498       lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
499       lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
500       lapm->trust_change = htonl (trust_delta);
501     }
502   else
503     {
504       tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
505                            sizeof (struct ListPeerMessage));
506       tqe->size = sizeof (struct ListPeerMessage);
507       lpm = (struct ListPeerMessage *) &tqe[1];
508       lpm->header.size = htons (sizeof (struct ListPeerMessage));
509       lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
510       lpm->trust_change = htonl (trust_delta);
511       memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
512     }
513   ic = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_NewIteratorContext));
514   ic->callback = callback;
515   ic->callback_cls = callback_cls;
516   ic->timeout = GNUNET_TIME_relative_to_absolute (timeout);
517   tqe->timeout = ic->timeout;
518   tqe->cont = &iterator_start_receive;
519   tqe->cont_cls = ic;
520   /* FIXME: sort DLL by timeout? */
521   /* FIXME: add timeout task!? */
522   GNUNET_CONTAINER_DLL_insert_after (h->tq_head,
523                                      h->tq_tail,
524                                      h->tq_tail,
525                                      tqe);
526   trigger_transmit (h);
527   return ic;
528 }
529
530
531
532 /**
533  * Cancel an iteration over peer information.
534  *
535  * @param ic context of the iterator to cancel
536  */
537 void
538 GNUNET_PEERINFO_iterate_cancel_new (struct GNUNET_PEERINFO_NewIteratorContext *ic)
539 {
540   GNUNET_assert (0); 
541   // FIXME: not implemented
542 }
543
544
545
546
547
548 /* ***************************** OLD API ****************************** */
549
550
551
552
553 #define ADD_PEER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
554
555
556 struct CAFContext
557 {
558   struct GNUNET_CLIENT_Connection *client;
559   struct GNUNET_MessageHeader *msg;
560 };
561
562
563 static size_t
564 copy_and_free (void *cls, size_t size, void *buf)
565 {
566   struct CAFContext *cc = cls;
567   struct GNUNET_MessageHeader *msg = cc->msg;
568   uint16_t msize;
569
570   if (buf == NULL)
571     {
572 #if DEBUG_PEERINFO
573       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574                   _
575                   ("Failed to transmit message of type %u to `%s' service.\n"),
576                   ntohs (msg->type), "peerinfo");
577 #endif
578       GNUNET_free (msg);
579       GNUNET_CLIENT_disconnect (cc->client, GNUNET_NO);
580       GNUNET_free (cc);
581       return 0;
582     }
583   msize = ntohs (msg->size);
584   GNUNET_assert (size >= msize);
585   memcpy (buf, msg, msize);
586   GNUNET_free (msg);
587   GNUNET_CLIENT_disconnect (cc->client, GNUNET_YES);
588   GNUNET_free (cc);
589   return msize;
590 }
591
592
593
594 /**
595  * Add a host to the persistent list.
596  *
597  * @param cfg configuration to use
598  * @param sched scheduler to use
599  * @param peer identity of the peer
600  * @param hello the verified (!) HELLO message
601  */
602 void
603 GNUNET_PEERINFO_add_peer (const struct GNUNET_CONFIGURATION_Handle *cfg,
604                           struct GNUNET_SCHEDULER_Handle *sched,
605                           const struct GNUNET_PeerIdentity *peer,
606                           const struct GNUNET_HELLO_Message *hello)
607 {
608   struct GNUNET_CLIENT_Connection *client;
609   struct PeerAddMessage *pam;
610   uint16_t hs;
611   struct CAFContext *cc;
612
613 #if DEBUG_PEERINFO
614   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
615               "Adding peer `%s' to peerinfo database\n",
616               GNUNET_i2s(peer));
617 #endif
618   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
619   if (client == NULL)
620     {
621       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
622                   _("Could not connect to `%s' service.\n"), "peerinfo");
623       return;
624     }
625   hs = GNUNET_HELLO_size (hello);
626 #if DEBUG_PEERINFO
627   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628               "Size of `%s' is %u bytes\n",
629               "HELLO",
630               (unsigned int) GNUNET_HELLO_size (hello));
631 #endif  
632   pam = GNUNET_malloc (sizeof (struct PeerAddMessage) + hs);
633   pam->header.size = htons (hs + sizeof (struct PeerAddMessage));
634   pam->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_ADD);
635   memcpy (&pam->peer, peer, sizeof (struct GNUNET_PeerIdentity));
636   memcpy (&pam[1], hello, hs);
637   cc = GNUNET_malloc (sizeof (struct CAFContext));
638   cc->client = client;
639   cc->msg = &pam->header;
640   GNUNET_CLIENT_notify_transmit_ready (client,
641                                        ntohs (pam->header.size),
642                                        ADD_PEER_TIMEOUT, 
643                                        GNUNET_NO,
644                                        &copy_and_free, cc);
645 }
646
647
648 /**
649  * Context for the info handler.
650  */
651 struct GNUNET_PEERINFO_IteratorContext
652 {
653
654   /**
655    * Our connection to the PEERINFO service.
656    */
657   struct GNUNET_CLIENT_Connection *client;
658
659   /**
660    * Function to call with information.
661    */
662   GNUNET_PEERINFO_Processor callback;
663
664   /**
665    * Closure for callback.
666    */
667   void *callback_cls;
668
669   /**
670    * When should we time out?
671    */
672   struct GNUNET_TIME_Absolute timeout;
673
674 };
675
676
677 /**
678  * Type of a function to call when we receive a message
679  * from the service.
680  *
681  * @param cls closure
682  * @param msg message received, NULL on timeout or fatal error
683  */
684 static void
685 info_handler (void *cls, const struct GNUNET_MessageHeader *msg)
686 {
687   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
688   const struct InfoMessage *im;
689   const struct GNUNET_HELLO_Message *hello;
690   uint16_t ms;
691
692   if (msg == NULL)
693     {
694       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
695                   _("Failed to receive response from `%s' service.\n"),
696                   "peerinfo");
697       ic->callback (ic->callback_cls, NULL, NULL, 1);
698       GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
699       GNUNET_free (ic);
700       return;
701     }
702   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
703     {
704 #if DEBUG_PEERINFO
705       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
706                   "Received end of list of peers from peerinfo database\n");
707 #endif
708       ic->callback (ic->callback_cls, NULL, NULL, 0);
709       GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
710       GNUNET_free (ic);
711       return;
712     }
713   ms = ntohs (msg->size);
714   if ((ms < sizeof (struct InfoMessage)) ||
715       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
716     {
717       GNUNET_break (0);
718       ic->callback (ic->callback_cls, NULL, NULL, 2);
719       GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
720       GNUNET_free (ic);
721       return;
722     }
723   im = (const struct InfoMessage *) msg;
724   hello = NULL;
725   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
726     {
727       hello = (const struct GNUNET_HELLO_Message *) &im[1];
728       if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
729         {
730           GNUNET_break (0);
731           ic->callback (ic->callback_cls, NULL, NULL, 2);
732           GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
733           GNUNET_free (ic);
734           return;
735         }
736     }
737 #if DEBUG_PEERINFO
738   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
739               "Received %u bytes of `%s' information about peer `%s' from PEERINFO database\n",
740               (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello),
741               "HELLO",
742               GNUNET_i2s (&im->peer));
743 #endif
744   ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
745   GNUNET_CLIENT_receive (ic->client,
746                          &info_handler,
747                          ic,
748                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
749 }
750
751
752 /**
753  * Call a method for each known matching host and change
754  * its trust value.  The method will be invoked once for
755  * each host and then finally once with a NULL pointer.
756  * Note that the last call can be triggered by timeout or
757  * by simply being done; however, the trust argument will
758  * be set to zero if we are done and to 1 if we timed out.
759  *
760  * @param cfg configuration to use
761  * @param sched scheduler to use
762  * @param peer restrict iteration to this peer only (can be NULL)
763  * @param trust_delta how much to change the trust in all matching peers
764  * @param timeout how long to wait until timing out
765  * @param callback the method to call for each peer
766  * @param callback_cls closure for callback
767  * @return NULL on error, otherwise an iterator context
768  */
769 struct GNUNET_PEERINFO_IteratorContext *
770 GNUNET_PEERINFO_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
771                          struct GNUNET_SCHEDULER_Handle *sched,
772                          const struct GNUNET_PeerIdentity *peer,
773                          int trust_delta,
774                          struct GNUNET_TIME_Relative timeout,
775                          GNUNET_PEERINFO_Processor callback,
776                          void *callback_cls)
777 {
778   struct GNUNET_CLIENT_Connection *client;
779   struct ListAllPeersMessage *lapm;
780   struct ListPeerMessage *lpm;
781   struct GNUNET_PEERINFO_IteratorContext *ihc;
782
783   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
784   if (client == NULL)
785     {
786       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
787                   _("Could not connect to `%s' service.\n"), "peerinfo");
788       return NULL;
789     }
790 #if DEBUG_PEERINFO
791   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
792               "Requesting list of peers from peerinfo database\n");
793 #endif
794   if (peer == NULL)
795     {
796       ihc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext) +
797                            sizeof (struct ListAllPeersMessage));
798       lapm = (struct ListAllPeersMessage *) &ihc[1];
799       lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
800       lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
801       lapm->trust_change = htonl (trust_delta);
802     }
803   else
804     {
805       ihc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext) +
806                            sizeof (struct ListPeerMessage));
807       lpm = (struct ListPeerMessage *) &ihc[1];
808       lpm->header.size = htons (sizeof (struct ListPeerMessage));
809       lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
810       lpm->trust_change = htonl (trust_delta);
811       memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
812     }
813   ihc->client = client;
814   ihc->callback = callback;
815   ihc->callback_cls = callback_cls;
816   ihc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
817   if (GNUNET_OK != 
818       GNUNET_CLIENT_transmit_and_get_response (client,
819                                                (const struct GNUNET_MessageHeader*) &ihc[1],
820                                                timeout,
821                                                GNUNET_YES,
822                                                &info_handler,
823                                                ihc))
824     {
825       GNUNET_break (0);
826       GNUNET_CLIENT_disconnect (ihc->client, GNUNET_NO);
827       GNUNET_free (ihc);
828       return NULL;
829     }
830   return ihc;
831 }
832
833
834 /**
835  * Cancel an iteration over peer information.
836  *
837  * @param ic context of the iterator to cancel
838  */
839 void
840 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
841 {
842   GNUNET_CLIENT_disconnect (ic->client, GNUNET_NO);
843   GNUNET_free (ic);
844 }
845
846
847 /* end of peerinfo_api.c */