-fix test FTBFS
[oweals/gnunet.git] / src / peerinfo / peerinfo_api.c
index 5def415e979748df16e3fff259a63aba4c922b3d..fad77a194de544e3991dac6ac5b672d009ecb1c9 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2001, 2002, 2004, 2005, 2007, 2009 Christian Grothoff (and other contributing authors)
+     (C) 2001, 2002, 2004, 2005, 2007, 2009, 2010, 2012 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @author Christian Grothoff
  */
 #include "platform.h"
-#include "gnunet_client_lib.h"
-#include "gnunet_container_lib.h"
-#include "gnunet_peerinfo_service.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_protocols.h"
-#include "gnunet_time_lib.h"
 #include "peerinfo.h"
 
 #include "peerinfo.h"
 
-/**
- * Function to call after transmission has succeeded.
- * 
- * @param cls closure
- * @param success GNUNET_OK if transmission worked, GNUNET_SYSERR on error
- */
-typedef void (*TransmissionContinuation)(void *cls,
-                                        int success);
+#define LOG(kind,...) GNUNET_log_from (kind, "peerinfo-api",__VA_ARGS__)
 
 
 /**
 
 
 /**
- * Entry in the transmission queue to PEERINFO service.
+ * Entry in the transmission queue to PEERINFO service.  We use
+ * the same structure for queueing 'iteration' requests and
+ * actual 'add' messages.
  */
  */
-struct TransmissionQueueEntry
+struct GNUNET_PEERINFO_AddContext
 {
   /**
    * This is a linked list.
    */
 {
   /**
    * This is a linked list.
    */
-  struct TransmissionQueueEntry *next;
-  
+  struct GNUNET_PEERINFO_AddContext *next;
+
   /**
    * This is a linked list.
    */
   /**
    * This is a linked list.
    */
-  struct TransmissionQueueEntry *prev;
+  struct GNUNET_PEERINFO_AddContext *prev;
 
   /**
 
   /**
-   * Function to call after request has been transmitted, or NULL (in which
-   * case we must consider sending the next entry immediately).
+   * Handle to the PEERINFO service.
    */
    */
-  TransmissionContinuation cont;
-  
+  struct GNUNET_PEERINFO_Handle *h;
+
+  /**
+   * Function to call after request has been transmitted, or NULL.
+   */
+  GNUNET_PEERINFO_Continuation cont;
+
   /**
    * Closure for 'cont'.
    */
   void *cont_cls;
 
   /**
    * Closure for 'cont'.
    */
   void *cont_cls;
 
+  /**
+   * Number of bytes of the request message (follows after this struct).
+   */
+  size_t size;
+
+};
+
+
+/**
+ * Context for an iteration request.
+ */
+struct GNUNET_PEERINFO_IteratorContext
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct GNUNET_PEERINFO_IteratorContext *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct GNUNET_PEERINFO_IteratorContext *prev;
+
+  /**
+   * Handle to the PEERINFO service.
+   */
+  struct GNUNET_PEERINFO_Handle *h;
+
+  /**
+   * Function to call with the results.
+   */
+  GNUNET_PEERINFO_Processor callback;
+
+  /**
+   * Closure for 'callback'.
+   */
+  void *callback_cls;
+
+  /**
+   * Our entry in the transmission queue.
+   */
+  struct GNUNET_PEERINFO_AddContext *ac;
+
+  /**
+   * Task responsible for timeout.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+
   /**
    * Timeout for the operation.
    */
   struct GNUNET_TIME_Absolute timeout;
 
   /**
   /**
    * Timeout for the operation.
    */
   struct GNUNET_TIME_Absolute timeout;
 
   /**
-   * Number of bytes of the request message (follows after this struct).
+   * Peer we are interested in (only valid if iteration was restricted to one peer).
    */
    */
-  size_t size;
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * Is 'peer' set?
+   */
+  int have_peer;
+
+  /**
+   * Set to GNUNET_YES if we are currently receiving replies from the
+   * service.
+   */
+  int request_transmitted;
 
 };
 
 
 };
 
@@ -90,11 +146,6 @@ struct GNUNET_PEERINFO_Handle
    */
   const struct GNUNET_CONFIGURATION_Handle *cfg;
 
    */
   const struct GNUNET_CONFIGURATION_Handle *cfg;
 
-  /**
-   * Our scheduler.
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
-
   /**
    * Connection to the service.
    */
   /**
    * Connection to the service.
    */
@@ -103,12 +154,12 @@ struct GNUNET_PEERINFO_Handle
   /**
    * Head of transmission queue.
    */
   /**
    * Head of transmission queue.
    */
-  struct TransmissionQueueEntry *tq_head;
+  struct GNUNET_PEERINFO_AddContext *ac_head;
 
   /**
    * Tail of transmission queue.
    */
 
   /**
    * Tail of transmission queue.
    */
-  struct TransmissionQueueEntry *tq_tail;
+  struct GNUNET_PEERINFO_AddContext *ac_tail;
 
   /**
    * Handle for the current transmission request, or NULL if none is pending.
 
   /**
    * Handle for the current transmission request, or NULL if none is pending.
@@ -116,8 +167,22 @@ struct GNUNET_PEERINFO_Handle
   struct GNUNET_CLIENT_TransmitHandle *th;
 
   /**
   struct GNUNET_CLIENT_TransmitHandle *th;
 
   /**
-   * Set to GNUNET_YES if we are currently receiving replies from the
-   * service.
+   * Head of iterator DLL.
+   */
+  struct GNUNET_PEERINFO_IteratorContext *ic_head;
+
+  /**
+   * Tail of iterator DLL.
+   */
+  struct GNUNET_PEERINFO_IteratorContext *ic_tail;
+
+  /**
+   * ID for a reconnect task.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier r_task;
+
+  /**
+   * Are we now receiving?
    */
   int in_receive;
 
    */
   int in_receive;
 
@@ -127,26 +192,19 @@ struct GNUNET_PEERINFO_Handle
 /**
  * Connect to the peerinfo service.
  *
 /**
  * Connect to the peerinfo service.
  *
- * @param sched scheduler to use
  * @param cfg configuration to use
  * @return NULL on error (configuration related, actual connection
  * @param cfg configuration to use
  * @return NULL on error (configuration related, actual connection
- *         etablishment may happen asynchronously).
+ *         establishment may happen asynchronously).
  */
 struct GNUNET_PEERINFO_Handle *
  */
 struct GNUNET_PEERINFO_Handle *
-GNUNET_PEERINFO_connect (struct GNUNET_SCHEDULER_Handle *sched,
-                        const struct GNUNET_CONFIGURATION_Handle *cfg)                  
+GNUNET_PEERINFO_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
 {
-  struct GNUNET_CLIENT_Connection *client;
-  struct GNUNET_PEERINFO_Handle *ret;
-
-  client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
-  if (client == NULL)
-    return NULL;
-  ret = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_Handle));
-  ret->client = client;
-  ret->cfg = cfg;
-  ret->sched = sched;
-  return ret;
+  struct GNUNET_PEERINFO_Handle *h;
+
+  h = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_Handle));
+  h->client = GNUNET_CLIENT_connect ("peerinfo", cfg);
+  h->cfg = cfg;
+  return h;
 }
 
 
 }
 
 
@@ -162,23 +220,37 @@ GNUNET_PEERINFO_connect (struct GNUNET_SCHEDULER_Handle *sched,
 void
 GNUNET_PEERINFO_disconnect (struct GNUNET_PEERINFO_Handle *h)
 {
 void
 GNUNET_PEERINFO_disconnect (struct GNUNET_PEERINFO_Handle *h)
 {
-  struct TransmissionQueueEntry *tqe;
-
-  while (NULL != (tqe = h->tq_head))
-    {     
-      GNUNET_CONTAINER_DLL_remove (h->tq_head,
-                                  h->tq_tail,
-                                  tqe);
-      if (tqe->cont != NULL)
-       tqe->cont (tqe->cont_cls, GNUNET_SYSERR);
-      GNUNET_free (tqe);
-    }
-  if (h->th != NULL)
-    {
-      GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
-      h->th = NULL;
-    }
-  GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
+  struct GNUNET_PEERINFO_AddContext *ac;
+  struct GNUNET_PEERINFO_IteratorContext *ic;
+
+  while (NULL != (ic = h->ic_head))
+  {
+    GNUNET_break (GNUNET_YES == ic->request_transmitted);
+    ic->request_transmitted = GNUNET_NO;
+    GNUNET_PEERINFO_iterate_cancel (ic);
+  }
+  while (NULL != (ac = h->ac_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
+    if (NULL != ac->cont)
+      ac->cont (ac->cont_cls, _("aborted due to explicit disconnect request"));
+    GNUNET_free (ac);
+  }
+  if (NULL != h->th)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
+    h->th = NULL;
+  }
+  if (NULL != h->client)
+  {
+    GNUNET_CLIENT_disconnect (h->client);
+    h->client = NULL;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
+  {
+    GNUNET_SCHEDULER_cancel (h->r_task);
+    h->r_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   GNUNET_free (h);
 }
 
   GNUNET_free (h);
 }
 
@@ -193,6 +265,31 @@ static void
 trigger_transmit (struct GNUNET_PEERINFO_Handle *h);
 
 
 trigger_transmit (struct GNUNET_PEERINFO_Handle *h);
 
 
+/**
+ * Close the existing connection to PEERINFO and reconnect.
+ *
+ * @param h handle to the service
+ */
+static void
+reconnect (struct GNUNET_PEERINFO_Handle *h);
+
+
+/**
+ * Task scheduled to re-try connecting to the peerinfo service.
+ *
+ * @param cls the 'struct GNUNET_PEERINFO_Handle'
+ * @param tc scheduler context
+ */
+static void
+reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_PEERINFO_Handle *h = cls;
+
+  h->r_task = GNUNET_SCHEDULER_NO_TASK;
+  reconnect (h);
+}
+
+
 /**
  * Close the existing connection to PEERINFO and reconnect.
  *
 /**
  * Close the existing connection to PEERINFO and reconnect.
  *
@@ -201,9 +298,31 @@ trigger_transmit (struct GNUNET_PEERINFO_Handle *h);
 static void
 reconnect (struct GNUNET_PEERINFO_Handle *h)
 {
 static void
 reconnect (struct GNUNET_PEERINFO_Handle *h)
 {
-  GNUNET_CLIENT_disconnect (h->client, GNUNET_SYSERR);
-  h->client = GNUNET_CLIENT_connect (h->sched, "peerinfo", h->cfg);
-  GNUNET_assert (h->client != NULL);
+  if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
+  {
+    GNUNET_SCHEDULER_cancel (h->r_task);
+    h->r_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (NULL != h->th)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
+    h->th = NULL;
+  }
+  if (NULL != h->client)
+  {
+    GNUNET_CLIENT_disconnect (h->client);
+    h->client = NULL;
+  }
+  h->in_receive = GNUNET_NO;
+  h->client = GNUNET_CLIENT_connect ("peerinfo", h->cfg);
+  if (NULL == h->client)
+  {
+    h->r_task =
+        GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
+                                      h);
+    return;
+  }
+  trigger_transmit (h);
 }
 
 
 }
 
 
@@ -220,43 +339,39 @@ static size_t
 do_transmit (void *cls, size_t size, void *buf)
 {
   struct GNUNET_PEERINFO_Handle *h = cls;
 do_transmit (void *cls, size_t size, void *buf)
 {
   struct GNUNET_PEERINFO_Handle *h = cls;
-  struct TransmissionQueueEntry *tqe = h->tq_head;
+  struct GNUNET_PEERINFO_AddContext *ac = h->ac_head;
   size_t ret;
 
   h->th = NULL;
   size_t ret;
 
   h->th = NULL;
-  if (buf == NULL)
-    {
-#if DEBUG_PEERINFO
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _
-                  ("Failed to transmit message of type %u to `%s' service.\n"),
-                  ntohs (msg->type), "peerinfo");
-#endif
-      if (tqe != NULL)
-       GNUNET_CONTAINER_DLL_remove (h->tq_head,
-                                    h->tq_tail,
-                                    tqe);
-      reconnect (h);
-      trigger_transmit (h);
-      if (tqe != NULL)
-       {
-         if (tqe->cont != NULL)
-           tqe->cont (tqe->cont_cls, GNUNET_SYSERR);
-         GNUNET_free (tqe);
-       }
-      return 0;
-    }
-  ret = tqe->size;
-  GNUNET_assert (size >= ret);
-  memcpy (buf, &tqe[1], ret);
-  GNUNET_CONTAINER_DLL_remove (h->tq_head,
-                              h->tq_tail,
-                              tqe);
-  if (tqe->cont != NULL)
-    tqe->cont (tqe->cont_cls, GNUNET_OK);
-  else
+  if (NULL == ac)
+    return 0; /* request was cancelled in the meantime */
+  if (NULL == buf)
+  {
+    /* peerinfo service died */
+    LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
+         "Failed to transmit message to `%s' service.\n", "PEERINFO");
+    GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
+    reconnect (h);
+    if (NULL != ac->cont)
+      ac->cont (ac->cont_cls, _("failed to transmit request (service down?)"));
+    GNUNET_free (ac);
+    return 0;
+  }
+  ret = ac->size;
+  if (size < ret)
+  {
+    /* change in head of queue (i.e. cancel + add), try again */
     trigger_transmit (h);
     trigger_transmit (h);
-  GNUNET_free (tqe);
+    return 0;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Transmitting request of size %u to `%s' service.\n", ret, "PEERINFO");
+  memcpy (buf, &ac[1], ret);
+  GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
+  trigger_transmit (h);
+  if (NULL != ac->cont)
+    ac->cont (ac->cont_cls, NULL);
+  GNUNET_free (ac);
   return ret;
 }
 
   return ret;
 }
 
@@ -270,24 +385,28 @@ do_transmit (void *cls, size_t size, void *buf)
 static void
 trigger_transmit (struct GNUNET_PEERINFO_Handle *h)
 {
 static void
 trigger_transmit (struct GNUNET_PEERINFO_Handle *h)
 {
-  struct TransmissionQueueEntry *tqe;
-
-  if (NULL == (tqe = h->tq_head))
-    return;
-  if (h->th != NULL)
+  struct GNUNET_PEERINFO_AddContext *ac;
+
+  if (NULL == (ac = h->ac_head))
+    return; /* no requests queued */
+  if (NULL != h->th)
+    return; /* request already pending */
+  if (NULL == h->client)
+  {
+    /* disconnected, try to reconnect */
+    reconnect (h);
     return;
     return;
-  if (h->in_receive == GNUNET_YES)
-    return;
-  h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
-                                              tqe->size,
-                                              GNUNET_TIME_absolute_get_remaining (tqe->timeout),
-                                              GNUNET_YES,
-                                              &do_transmit, h);  
+  }
+  h->th =
+    GNUNET_CLIENT_notify_transmit_ready (h->client, ac->size,
+                                        GNUNET_TIME_UNIT_FOREVER_REL,
+                                        GNUNET_YES,
+                                        &do_transmit, h);
 }
 
 
 /**
 }
 
 
 /**
- * Add a host to the persistent list.  This method operates in 
+ * Add a host to the persistent list.  This method operates in
  * semi-reliable mode: if the transmission is not completed by
  * the time 'GNUNET_PEERINFO_disconnect' is called, it will be
  * aborted.  Furthermore, if a second HELLO is added for the
  * semi-reliable mode: if the transmission is not completed by
  * the time 'GNUNET_PEERINFO_disconnect' is called, it will be
  * aborted.  Furthermore, if a second HELLO is added for the
@@ -296,80 +415,61 @@ trigger_transmit (struct GNUNET_PEERINFO_Handle *h)
  *
  * @param h handle to the peerinfo service
  * @param hello the verified (!) HELLO message
  *
  * @param h handle to the peerinfo service
  * @param hello the verified (!) HELLO message
+ * @param cont continuation to call when done, NULL is allowed
+ * @param cont_cls closure for 'cont'
+ * @return handle to cancel add operation; all pending
+ *         'add' operations will be cancelled automatically
+ *        on disconnect, so it is not necessary to keep this
+ *        handle (unless 'cont' is NULL and at some point
+ *        calling 'cont' must be prevented)
  */
  */
-void
+struct GNUNET_PEERINFO_AddContext *
 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
-                         const struct GNUNET_HELLO_Message *hello)
+                          const struct GNUNET_HELLO_Message *hello,
+                         GNUNET_PEERINFO_Continuation cont,
+                         void *cont_cls)
 {
   uint16_t hs = GNUNET_HELLO_size (hello);
 {
   uint16_t hs = GNUNET_HELLO_size (hello);
-  struct TransmissionQueueEntry *tqe;
-  
-#if DEBUG_PEERINFO
+  struct GNUNET_PEERINFO_AddContext *ac;
   struct GNUNET_PeerIdentity peer;
   struct GNUNET_PeerIdentity peer;
+
   GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer));
   GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer));
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n",
-             GNUNET_i2s(&peer),
-             hs,
-             "HELLO");
-#endif
-  tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) + hs);
-  tqe->size = hs;
-  tqe->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
-  memcpy (&tqe[1], hello, hs);
-  GNUNET_CONTAINER_DLL_insert_after (h->tq_head,
-                                    h->tq_tail,
-                                    h->tq_tail,
-                                    tqe);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n",
+       GNUNET_i2s (&peer), hs, "HELLO");
+  ac = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) + hs);
+  ac->h = h;
+  ac->size = hs;
+  ac->cont = cont;
+  ac->cont_cls = cont_cls;
+  memcpy (&ac[1], hello, hs);
+  GNUNET_CONTAINER_DLL_insert_tail (h->ac_head, h->ac_tail, ac);
   trigger_transmit (h);
   trigger_transmit (h);
+  return ac;
 }
 
 
 /**
 }
 
 
 /**
- * Context for an iteration request.
+ * Cancel pending 'add' operation.  Must only be called before
+ * either 'cont' or 'GNUNET_PEERINFO_disconnect' are invoked.
+ *
+ * @param ac handle for the add operation to cancel
  */
  */
-struct GNUNET_PEERINFO_IteratorContext
+void 
+GNUNET_PEERINFO_add_peer_cancel (struct GNUNET_PEERINFO_AddContext *ac)
 {
 {
-  /**
-   * Handle to the PEERINFO service.
-   */
-  struct GNUNET_PEERINFO_Handle *h;
+  struct GNUNET_PEERINFO_Handle *h = ac->h;
 
 
-  /**
-   * Function to call with the results.
-   */
-  GNUNET_PEERINFO_Processor callback;
-
-  /**
-   * Closure for 'callback'.
-   */
-  void *callback_cls;
-
-  /**
-   * Our entry in the transmission queue.
-   */
-  struct TransmissionQueueEntry *tqe;
-
-  /**
-   * Task responsible for timeout.
-   */
-  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
-
-  /**
-   * Timeout for the operation.
-   */
-  struct GNUNET_TIME_Absolute timeout;
-
-  /**
-   * Are we now receiving?
-   */
-  int in_receive;
-};
+  GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
+  GNUNET_free (ac);
+}
 
 
 /**
 
 
 /**
- * Type of a function to call when we receive a message
- * from the service.
+ * Type of a function to call when we receive a message from the
+ * service.  Call the iterator with the result and (if applicable)
+ * continue to receive more messages or trigger processing the next
+ * event (if applicable).
  *
  * @param cls closure
  * @param msg message received, NULL on timeout or fatal error
  *
  * @param cls closure
  * @param msg message received, NULL on timeout or fatal error
@@ -377,90 +477,144 @@ struct GNUNET_PEERINFO_IteratorContext
 static void
 peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
 {
 static void
 peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
 {
-  struct GNUNET_PEERINFO_IteratorContext *ic = cls;
+  struct GNUNET_PEERINFO_Handle *h = cls;
+  struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
   const struct InfoMessage *im;
   const struct GNUNET_HELLO_Message *hello;
   const struct InfoMessage *im;
   const struct GNUNET_HELLO_Message *hello;
+  GNUNET_PEERINFO_Processor cb;
+  struct GNUNET_PeerIdentity id;
+  void *cb_cls;
   uint16_t ms;
 
   uint16_t ms;
 
-  ic->h->in_receive = GNUNET_NO;
-  if (msg == NULL)
+  h->in_receive = GNUNET_NO;
+  if (NULL == msg)
+  {
+    /* peerinfo service died, signal error */
+    if (NULL != ic)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Failed to receive response from `%s' service.\n"),
-                  "peerinfo");
-      reconnect (ic->h);
-      trigger_transmit (ic->h);
-      if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-       GNUNET_SCHEDULER_cancel (ic->h->sched, 
-                                ic->timeout_task);
-      if (ic->callback != NULL)
-       ic->callback (ic->callback_cls, NULL, NULL, 1);
-      GNUNET_free (ic);
-      return;
+      cb = ic->callback;
+      cb_cls = ic->callback_cls;
+      GNUNET_PEERINFO_iterate_cancel (ic);
     }
     }
-  if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
+    else
     {
     {
-#if DEBUG_PEERINFO
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Received end of list of peers from peerinfo database\n");
-#endif
-      trigger_transmit (ic->h);
-      if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-       GNUNET_SCHEDULER_cancel (ic->h->sched, 
-                                ic->timeout_task);
-      if (ic->callback != NULL)
-       ic->callback (ic->callback_cls, NULL, NULL, 0);
-      GNUNET_free (ic);
-      return;
+      cb = NULL;
     }
     }
+    reconnect (h);
+    if (NULL != cb)
+      cb (cb_cls, NULL, NULL,
+         _("Failed to receive response from `PEERINFO' service."));
+    return;
+  }
+  if (NULL == ic)
+  {
+    /* didn't expect a response, reconnect */
+    reconnect (h);
+    return;
+  }
+  ic->request_transmitted = GNUNET_NO;
+  cb = ic->callback;
+  cb_cls = ic->callback_cls;
+  if (GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END == ntohs (msg->type))
+  {
+    /* normal end of list of peers, signal end, process next pending request */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received end of list of peers from `%s' service\n", "PEERINFO");
+    GNUNET_PEERINFO_iterate_cancel (ic);
+    trigger_transmit (h);
+    if ( (GNUNET_NO == h->in_receive) &&
+        (NULL != h->ic_head) )
+    {
+      h->in_receive = GNUNET_YES;
+      GNUNET_CLIENT_receive (h->client, &peerinfo_handler, h,
+                            GNUNET_TIME_absolute_get_remaining (h->ic_head->timeout));
+    }    
+    if (NULL != cb)
+      cb (cb_cls, NULL, NULL, NULL);
+    return;
+  }
+
   ms = ntohs (msg->size);
   if ((ms < sizeof (struct InfoMessage)) ||
       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
   ms = ntohs (msg->size);
   if ((ms < sizeof (struct InfoMessage)) ||
       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
+  {
+    /* malformed message */
+    GNUNET_break (0);
+    GNUNET_PEERINFO_iterate_cancel (ic);
+    reconnect (h);
+    if (NULL != cb)
+      cb (cb_cls, NULL, NULL,
+         _("Received invalid message from `PEERINFO' service."));
+    return;
+  }
+  im = (const struct InfoMessage *) msg;
+  GNUNET_break (0 == ntohl (im->reserved));
+  if ( (GNUNET_YES == ic->have_peer) &&
+       (0 != memcmp (&ic->peer, &im->peer, sizeof (struct GNUNET_PeerIdentity))) )
+  {
+    /* bogus message (from a different iteration call?); out of sequence! */
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Received HELLO for peer `%s', expected peer `%s'\n",
+        GNUNET_i2s (&im->peer),
+        GNUNET_i2s (&ic->peer));
+    
+    GNUNET_break (0);
+    GNUNET_PEERINFO_iterate_cancel (ic);
+    reconnect (h);
+    if (NULL != cb)      
+      cb (cb_cls, NULL, NULL,
+         _("Received invalid message from `PEERINFO' service."));
+    return;
+  }
+  hello = NULL;
+  if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
+  {
+    hello = (const struct GNUNET_HELLO_Message *) &im[1];
+    if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
     {
     {
+      /* malformed message */
       GNUNET_break (0);
       GNUNET_break (0);
-      reconnect (ic->h);
-      trigger_transmit (ic->h);
-      if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-       GNUNET_SCHEDULER_cancel (ic->h->sched, 
-                                ic->timeout_task);
-      if (ic->callback != NULL)
-       ic->callback (ic->callback_cls, NULL, NULL, 2);
-      GNUNET_free (ic);
+      GNUNET_PEERINFO_iterate_cancel (ic);
+      reconnect (h);
+      if (NULL != cb)      
+        cb (cb_cls, NULL, NULL,
+           _("Received invalid message from `PEERINFO' service."));
       return;
     }
       return;
     }
-  im = (const struct InfoMessage *) msg;
-  hello = NULL;
-  if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
+    if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &id))
+    {
+      /* malformed message */
+      GNUNET_break (0);
+      GNUNET_PEERINFO_iterate_cancel (ic);
+      reconnect (h);
+      if (NULL != cb)      
+        cb (cb_cls, NULL, NULL,
+           _("Received invalid message from `PEERINFO' service."));
+      return;
+    }
+    if (0 != memcmp (&im->peer, &id, sizeof (struct GNUNET_PeerIdentity)))
     {
     {
-      hello = (const struct GNUNET_HELLO_Message *) &im[1];
-      if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
-        {
-         GNUNET_break (0);
-         reconnect (ic->h);
-         trigger_transmit (ic->h);
-         if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-           GNUNET_SCHEDULER_cancel (ic->h->sched, 
-                                    ic->timeout_task);
-         if (ic->callback != NULL)
-           ic->callback (ic->callback_cls, NULL, NULL, 2);
-         GNUNET_free (ic);
-          return;
-        }
+      /* malformed message */
+      GNUNET_break (0);
+      GNUNET_PEERINFO_iterate_cancel (ic);
+      reconnect (h);
+      if (NULL != cb)      
+        cb (cb_cls, NULL, NULL,
+           _("Received invalid message from `PEERINFO' service."));
+      return;
     }
     }
-#if DEBUG_PEERINFO
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received %u bytes of `%s' information about peer `%s' from PEERINFO database\n",
-             (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello),
-             "HELLO",
-             GNUNET_i2s (&im->peer));
-#endif
-  ic->h->in_receive = GNUNET_YES;
-  if (ic->callback != NULL)
-    ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
-  GNUNET_CLIENT_receive (ic->h->client,
-                         &peerinfo_handler,
-                         ic,
+  }
+
+  /* normal data message */
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received %u bytes of `%s' information about peer `%s' from `%s' service\n",
+       (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello), "HELLO",
+       GNUNET_i2s (&im->peer), "PEERINFO");
+  h->in_receive = GNUNET_YES;
+  GNUNET_CLIENT_receive (h->client, &peerinfo_handler, h,
                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
+  if (NULL != cb)
+    cb (cb_cls, &im->peer, hello, NULL);
 }
 
 
 }
 
 
@@ -469,148 +623,140 @@ peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
  * the results (or handle transmission error).
  *
  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext'
  * the results (or handle transmission error).
  *
  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext'
- * @param transmit_success GNUNET_OK if transmission worked
+ * @param emsg error message, NULL if transmission worked
  */
 static void
  */
 static void
-iterator_start_receive (void *cls,
-                       int transmit_success)
+iterator_start_receive (void *cls, const char *emsg)
 {
   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
 {
   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
-
-  if (GNUNET_OK != transmit_success)
-    {
-      if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-       {
-         GNUNET_SCHEDULER_cancel (ic->h->sched,
-                                  ic->timeout_task);
-         ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-       }
-      reconnect (ic->h);
-      trigger_transmit (ic->h);
-      ic->callback (ic->callback_cls, NULL, NULL, 1);
-      GNUNET_free (ic);
-      return;
-    }  
-  ic->h->in_receive = GNUNET_YES;
-  ic->in_receive = GNUNET_YES;
-  ic->tqe = NULL;
-  GNUNET_CLIENT_receive (ic->h->client,
-                         &peerinfo_handler,
-                         ic,
-                         GNUNET_TIME_absolute_get_remaining (ic->timeout));
+  struct GNUNET_PEERINFO_Handle *h = ic->h;
+  GNUNET_PEERINFO_Processor cb;
+  void *cb_cls;
+
+  ic->ac = NULL;
+  if (NULL != emsg)
+  {
+    cb = ic->callback;
+    cb_cls = ic->callback_cls;
+    GNUNET_PEERINFO_iterate_cancel (ic);
+    reconnect (h);
+    if (NULL != cb)
+      cb (cb_cls, NULL, NULL, emsg);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for response from `%s' service.\n",
+       "PEERINFO");
+  ic->request_transmitted = GNUNET_YES;
+  if (GNUNET_NO == h->in_receive)
+  {
+    h->in_receive = GNUNET_YES;
+    GNUNET_CLIENT_receive (h->client, &peerinfo_handler, h,
+                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
+  }
 }
 
 
 /**
 }
 
 
 /**
- * Peerinfo iteration request has timed out.  
+ * Peerinfo iteration request has timed out.
  *
  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext*'
  * @param tc scheduler context
  */
 static void
  *
  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext*'
  * @param tc scheduler context
  */
 static void
-signal_timeout (void *cls,
-               const struct GNUNET_SCHEDULER_TaskContext *tc)
+signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
 {
   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
+  GNUNET_PEERINFO_Processor cb;
+  void *cb_cls;
 
   ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
 
   ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-  if (! ic->in_receive)
-    GNUNET_CONTAINER_DLL_remove (ic->h->tq_head,
-                                ic->h->tq_tail,
-                                ic->tqe);
-  ic->callback (ic->callback_cls, NULL, NULL, 1);
-  ic->callback = NULL;
-  if (ic->in_receive)
-    return;
-  GNUNET_free (ic->tqe);
-  GNUNET_free (ic);
+  cb = ic->callback;
+  cb_cls = ic->callback_cls;
+  GNUNET_PEERINFO_iterate_cancel (ic);
+  if (NULL != cb)
+    cb (cb_cls, NULL, NULL,
+       _("Timeout transmitting iteration request to `PEERINFO' service."));
 }
 
 
 /**
 }
 
 
 /**
- * Call a method for each known matching host and change its trust
- * value.  The callback method will be invoked once for each matching
- * host and then finally once with a NULL pointer.  After that final
- * invocation, the iterator context must no longer be used.
+ * Call a method for each known matching host.  The callback method
+ * will be invoked once for each matching host and then finally once
+ * with a NULL pointer.  After that final invocation, the iterator
+ * context must no longer be used.
  *
  *
- * Note that the last call can be triggered by timeout or by simply
- * being done; however, the trust argument will be set to zero if we
- * are done, 1 if we timed out and 2 for fatal error.
+ * Instead of calling this function with 'peer == NULL' it is often
+ * better to use 'GNUNET_PEERINFO_notify'.
  *
  *
- * Instead of calling this function with 'peer == NULL' and 'trust ==
- * 0', it is often better to use 'GNUNET_PEERINFO_notify'.
- * 
  * @param h handle to the peerinfo service
  * @param h handle to the peerinfo service
+ * @param include_friend_only include HELLO messages for friends only
  * @param peer restrict iteration to this peer only (can be NULL)
  * @param peer restrict iteration to this peer only (can be NULL)
- * @param trust_delta how much to change the trust in all matching peers
  * @param timeout how long to wait until timing out
  * @param callback the method to call for each peer
  * @param callback_cls closure for callback
  * @param timeout how long to wait until timing out
  * @param callback the method to call for each peer
  * @param callback_cls closure for callback
- * @return NULL on error (in this case, 'callback' is never called!), 
- *         otherwise an iterator context
+ * @return iterator context
  */
 struct GNUNET_PEERINFO_IteratorContext *
 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
  */
 struct GNUNET_PEERINFO_IteratorContext *
 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
-                        const struct GNUNET_PeerIdentity *peer,
-                        int trust_delta,
-                        struct GNUNET_TIME_Relative timeout,
-                        GNUNET_PEERINFO_Processor callback,
-                        void *callback_cls)
+                                                                                                int include_friend_only,
+                         const struct GNUNET_PeerIdentity *peer,
+                         struct GNUNET_TIME_Relative timeout,
+                         GNUNET_PEERINFO_Processor callback, void *callback_cls)
 {
   struct ListAllPeersMessage *lapm;
   struct ListPeerMessage *lpm;
   struct GNUNET_PEERINFO_IteratorContext *ic;
 {
   struct ListAllPeersMessage *lapm;
   struct ListPeerMessage *lpm;
   struct GNUNET_PEERINFO_IteratorContext *ic;
-  struct TransmissionQueueEntry *tqe;
+  struct GNUNET_PEERINFO_AddContext *ac;
 
 
-#if DEBUG_PEERINFO
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Requesting list of peers from peerinfo database\n");
-#endif
-  if (peer == NULL)
-    {
-      tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
-                          sizeof (struct ListAllPeersMessage));
-      tqe->size = sizeof (struct ListAllPeersMessage);
-      lapm = (struct ListAllPeersMessage *) &tqe[1];
-      lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
-      lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
-      lapm->trust_change = htonl (trust_delta);
-    }
-  else
-    {
-      tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
-                          sizeof (struct ListPeerMessage));
-      tqe->size = sizeof (struct ListPeerMessage);
-      lpm = (struct ListPeerMessage *) &tqe[1];
-      lpm->header.size = htons (sizeof (struct ListPeerMessage));
-      lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
-      lpm->trust_change = htonl (trust_delta);
-      memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
-    }
   ic = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext));
   ic = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext));
+  if (NULL == peer)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Requesting list of peers from PEERINFO service\n");
+    ac =
+        GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) +
+                       sizeof (struct ListAllPeersMessage));
+    ac->size = sizeof (struct ListAllPeersMessage);
+    lapm = (struct ListAllPeersMessage *) &ac[1];
+    lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
+    lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
+    lapm->include_friend_only = htonl (include_friend_only);
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Requesting information on peer `%4s' from PEERINFO service\n",
+         GNUNET_i2s (peer));
+    ac =
+        GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) +
+                       sizeof (struct ListPeerMessage));
+    ac->size = sizeof (struct ListPeerMessage);
+    lpm = (struct ListPeerMessage *) &ac[1];
+    lpm->header.size = htons (sizeof (struct ListPeerMessage));
+    lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
+    lpm->include_friend_only = htonl (include_friend_only);
+    memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
+    ic->have_peer = GNUNET_YES;
+    ic->peer = *peer;
+  }
   ic->h = h;
   ic->h = h;
-  ic->tqe = tqe;
+  ic->ac = ac;
   ic->callback = callback;
   ic->callback_cls = callback_cls;
   ic->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   ic->callback = callback;
   ic->callback_cls = callback_cls;
   ic->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  ic->timeout_task = GNUNET_SCHEDULER_add_delayed (h->sched, 
-                                                  timeout,
-                                                  &signal_timeout,
-                                                  ic);
-  tqe->timeout = ic->timeout;
-  tqe->cont = &iterator_start_receive;
-  tqe->cont_cls = ic;
-  tqe->timeout = ic->timeout;
-  GNUNET_CONTAINER_DLL_insert_after (h->tq_head,
-                                    h->tq_tail,
-                                    h->tq_tail,
-                                    tqe);
+  ic->timeout_task =
+      GNUNET_SCHEDULER_add_delayed (timeout, &signal_timeout, ic);
+  ac->cont = &iterator_start_receive;
+  ac->cont_cls = ic;
+  GNUNET_CONTAINER_DLL_insert_tail (h->ac_head, h->ac_tail, ac);
+  GNUNET_CONTAINER_DLL_insert_tail (h->ic_head,
+                                   h->ic_tail,
+                                   ic);
   trigger_transmit (h);
   return ic;
 }
 
 
   trigger_transmit (h);
   return ic;
 }
 
 
-
 /**
  * Cancel an iteration over peer information.
  *
 /**
  * Cancel an iteration over peer information.
  *
@@ -619,19 +765,25 @@ GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
 void
 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
 {
 void
 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
 {
-  if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (ic->h->sched,
-                              ic->timeout_task);
-      ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-    }
+  struct GNUNET_PEERINFO_Handle *h;
+
+  h = ic->h;
+  if (GNUNET_SCHEDULER_NO_TASK != ic->timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (ic->timeout_task);
+    ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   ic->callback = NULL;
   ic->callback = NULL;
-  if (ic->in_receive)
-    return; /* need to finish processing */
-  GNUNET_CONTAINER_DLL_remove (ic->h->tq_head,
-                              ic->h->tq_tail,
-                              ic->tqe);
-  GNUNET_free (ic->tqe);
+  if (GNUNET_YES == ic->request_transmitted)
+    return;                     /* need to finish processing */
+  GNUNET_CONTAINER_DLL_remove (h->ic_head,
+                              h->ic_tail,
+                              ic);
+  if (NULL != ic->ac)
+  {
+    GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ic->ac);
+    GNUNET_free (ic->ac);
+  }
   GNUNET_free (ic);
 }
 
   GNUNET_free (ic);
 }