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