fix
[oweals/gnunet.git] / src / core / core_api.c
index f8c7c0aa6ef79a231e1d5454bb897117db764cc8..c21be318a4ffac0fd5fcee4480fd523b36340e1d 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
-     (C) 2009 Christian Grothoff (and other contributing authors)
+     (C) 2009, 2010 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
-     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
@@ -25,6 +25,7 @@
  * @author Christian Grothoff
  */
 #include "platform.h"
+#include "gnunet_constants.h"
 #include "gnunet_core_service.h"
 #include "core.h"
 
@@ -65,6 +66,11 @@ struct GNUNET_CORE_Handle
    */
   GNUNET_CORE_DisconnectEventHandler disconnects;
 
+  /**
+   * Function to call whenever we're notified about a peer changing status.
+   */  
+  GNUNET_CORE_PeerStatusEventHandler status_events;
+  
   /**
    * Function to call whenever we receive an inbound message.
    */
@@ -88,7 +94,7 @@ struct GNUNET_CORE_Handle
   /**
    * Handle for our current transmission request.
    */
-  struct GNUNET_CLIENT_TransmitHandle *th;
+  struct GNUNET_CLIENT_TransmitHandle *cth;
 
   /**
    * Head of doubly-linked list of pending requests.
@@ -250,6 +256,10 @@ static size_t transmit_start (void *cls, size_t size, void *buf);
 static void
 reconnect (struct GNUNET_CORE_Handle *h)
 {
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Reconnecting to CORE service\n");
+#endif
   if (h->client_notifications != NULL)
     GNUNET_CLIENT_disconnect (h->client_notifications, GNUNET_NO);
   h->currently_down = GNUNET_YES;
@@ -260,12 +270,12 @@ reconnect (struct GNUNET_CORE_Handle *h)
                                                      &reconnect_task,
                                                      h);
   else
-    h->th = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
-                                                sizeof (struct InitMessage) +
-                                                sizeof (uint16_t) * h->hcnt,
-                                                GNUNET_TIME_UNIT_SECONDS,
-                                                GNUNET_NO,
-                                                &transmit_start, h);
+    h->cth = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
+                                                 sizeof (struct InitMessage) +
+                                                 sizeof (uint16_t) * h->hcnt,
+                                                 GNUNET_TIME_UNIT_SECONDS,
+                                                 GNUNET_NO,
+                                                 &transmit_start, h);
 }
 
 
@@ -277,11 +287,16 @@ reconnect (struct GNUNET_CORE_Handle *h)
  * @param tc context, can be NULL (!)
  */
 static void
-timeout_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+timeout_request (void *cls, 
+                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_CORE_TransmitHandle *th = cls;
 
   th->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Signalling timeout of request for transmission to CORE service\n");
+#endif
   GNUNET_assert (0 == th->get_message (th->get_message_cls, 0, NULL));
 }
 
@@ -302,30 +317,28 @@ request_start (void *cls, size_t size, void *buf)
   struct GNUNET_CORE_TransmitHandle *th;
   size_t ret;
 
-  h->th = NULL;
+  h->cth = NULL;  
   th = h->pending_head;
+  if (th == NULL)
+    return 0;
   if (buf == NULL)
     {
       timeout_request (th, NULL);
       return 0;
     }
-  /* create new timeout task (in case core takes too long to respond!) */
-  th->timeout_task = GNUNET_SCHEDULER_add_delayed (h->sched,
-                                                   GNUNET_TIME_absolute_get_remaining
-                                                   (th->timeout),
-                                                   &timeout_request, th);
-  /* remove th from doubly-linked pending list, move to submitted */
-  GNUNET_assert (th->prev == NULL);
-  h->pending_head = th->next;
-  if (th->next == NULL)
-    h->pending_tail = NULL;
-  else
-    th->next->prev = NULL;
+  GNUNET_CONTAINER_DLL_remove (h->pending_head,
+                              h->pending_tail,
+                              th);
   GNUNET_assert (h->submitted == NULL);
   h->submitted = th;
   GNUNET_assert (size >= th->msize);
   ret = th->get_message (th->get_message_cls, size, buf);
   GNUNET_assert (ret <= size);
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Transmitting %u bytes to core\n",
+             ret);
+#endif
   return ret;
 }
 
@@ -349,19 +362,14 @@ trigger_next_request (struct GNUNET_CORE_Handle *h)
     }
   if (NULL == (th = h->pending_head))
     return;                     /* no requests pending */
-  GNUNET_assert (NULL == h->th);
-  if (GNUNET_SCHEDULER_NO_TASK != th->timeout_task)
-    {
-      GNUNET_SCHEDULER_cancel (h->sched, th->timeout_task);
-      th->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-    }
-  h->th = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
-                                               th->msize,
-                                               GNUNET_TIME_absolute_get_remaining
-                                               (th->timeout), 
-                                              GNUNET_NO,
-                                              &request_start,
-                                               h);
+  GNUNET_assert (NULL == h->cth);
+  h->cth = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
+                                               th->msize,
+                                               GNUNET_TIME_absolute_get_remaining
+                                               (th->timeout), 
+                                               GNUNET_NO,
+                                               &request_start,
+                                               h);
 }
 
 
@@ -380,6 +388,7 @@ main_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
   const struct DisconnectNotifyMessage *dnm;
   const struct NotifyTrafficMessage *ntm;
   const struct GNUNET_MessageHeader *em;
+  const struct PeerStatusNotifyMessage *psnm;
   uint16_t msize;
   uint16_t et;
   const struct GNUNET_CORE_MessageHandler *mh;
@@ -432,6 +441,26 @@ main_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
       h->disconnects (h->cls,
                      &dnm->peer);
       break;
+    case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_STATUS_CHANGE:
+      if (NULL == h->status_events)
+        {
+          GNUNET_break (0);
+          break;
+        }
+      if (msize != sizeof (struct PeerStatusNotifyMessage))
+        {
+          GNUNET_break (0);
+          break;
+        }
+      psnm = (const struct PeerStatusNotifyMessage *) msg;
+      h->status_events (h->cls,
+                       &psnm->peer,
+                       GNUNET_TIME_relative_ntoh (psnm->latency),
+                       ntohl (psnm->distance),
+                       psnm->bandwidth_in,
+                       psnm->bandwidth_out,
+                       GNUNET_TIME_absolute_ntoh (psnm->timeout));
+      break;
     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND:
       if (msize <
           sizeof (struct NotifyTrafficMessage) +
@@ -444,8 +473,10 @@ main_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
       em = (const struct GNUNET_MessageHeader *) &ntm[1];
 #if DEBUG_CORE
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Received message of type %u from peer `%4s'\n",
-                  ntohs (em->type), GNUNET_i2s (&ntm->peer));
+                  "Received message of type %u and size %u from peer `%4s'\n",
+                  ntohs (em->type), 
+                 ntohs (em->size),
+                 GNUNET_i2s (&ntm->peer));
 #endif
       if ((GNUNET_NO == h->inbound_hdr_only) &&
           (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
@@ -547,13 +578,23 @@ init_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg)
       (ntohs (msg->size) != sizeof (struct InitReplyMessage)) ||
       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  _
-                  ("Error connecting to core service (failed to receive `%s' message, got message of type %u and size %u).\n"),
-                  "INIT_REPLY",
-                 (msg == NULL) ? -1 : ntohs (msg->type),
-                 (msg == NULL) ? -1 : ntohs (msg->size));
-      GNUNET_break (msg == NULL);
+      if (msg != NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     _
+                     ("Error connecting to core service (failed to receive `%s' message, got message of type %u and size %u).\n"),
+                     "INIT_REPLY",
+                     ntohs (msg->type),
+                     ntohs (msg->size));
+         GNUNET_break (0);
+       }
+      else
+       {
+#if DEBUG_CORE
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     _("Failed to connect to core service, will retry.\n"));
+#endif
+       }
       transmit_start (h, 0, NULL);
       return;
     }
@@ -609,7 +650,7 @@ transmit_start (void *cls, size_t size, void *buf)
   unsigned int hpos;
   struct GNUNET_TIME_Relative delay;
 
-  h->th = NULL;
+  h->cth = NULL;
   if (size == 0)
     {
       if ((h->init == NULL) ||
@@ -645,6 +686,8 @@ transmit_start (void *cls, size_t size, void *buf)
     opt |= GNUNET_CORE_OPTION_SEND_CONNECT;
   if (h->disconnects != NULL)
     opt |= GNUNET_CORE_OPTION_SEND_DISCONNECT;
+  if (h->status_events != NULL)
+    opt |= GNUNET_CORE_OPTION_SEND_STATUS_CHANGE;
   if (h->inbound_notify != NULL)
     {
       if (h->inbound_hdr_only)
@@ -684,6 +727,7 @@ transmit_start (void *cls, size_t size, void *buf)
  *        connected to the core service; note that timeout is only meaningful if init is not NULL
  * @param connects function to call on peer connect, can be NULL
  * @param disconnects function to call on peer disconnect / timeout, can be NULL
+ * @param status_events function to call on changes to peer connection status, can be NULL
  * @param inbound_notify function to call for all inbound messages, can be NULL
  * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the
  *                GNUNET_MessageHeader and hence we do not need to give it the full message;
@@ -704,6 +748,7 @@ GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
                      GNUNET_CORE_StartupCallback init,
                      GNUNET_CORE_ConnectEventHandler connects,
                      GNUNET_CORE_DisconnectEventHandler disconnects,
+                    GNUNET_CORE_PeerStatusEventHandler status_events,
                      GNUNET_CORE_MessageCallback inbound_notify,
                      int inbound_hdr_only,
                      GNUNET_CORE_MessageCallback outbound_notify,
@@ -719,6 +764,7 @@ GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
   h->init = init;
   h->connects = connects;
   h->disconnects = disconnects;
+  h->status_events = status_events;
   h->inbound_notify = inbound_notify;
   h->outbound_notify = outbound_notify;
   h->inbound_hdr_only = inbound_hdr_only;
@@ -742,7 +788,7 @@ GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
               "Trying to connect to core service in next %llu ms.\n",
               timeout.value);
 #endif
-  h->th =
+  h->cth =
     GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
                                          sizeof (struct InitMessage) +
                                          sizeof (uint16_t) * h->hcnt, timeout,
@@ -760,14 +806,15 @@ GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
 void
 GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
 {
-  if (handle->th != NULL)
-    GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
+  if (handle->cth != NULL)
+    GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth);
   if (handle->solicit_transmit_req != NULL)
     GNUNET_CORE_notify_transmit_ready_cancel (handle->solicit_transmit_req);
   if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task);
   if (handle->client_notifications != NULL)
     GNUNET_CLIENT_disconnect (handle->client_notifications, GNUNET_NO);
+  GNUNET_break (handle->pending_head == NULL);
   GNUNET_free_non_null (handle->solicit_buffer);
   GNUNET_free (handle);
 }
@@ -800,12 +847,6 @@ produce_send (void *cls, size_t size, void *buf)
       trigger_next_request (h);
       return 0;
     }
-#if DEBUG_CORE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Preparing for P2P transmission to `%4s'.\n",
-             GNUNET_i2s(&th->peer));
-#endif
-  GNUNET_assert (th->timeout_task != GNUNET_SCHEDULER_NO_TASK);
   sm = (struct SendMessage *) buf;
   sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND);
   sm->priority = htonl (th->priority);
@@ -815,6 +856,8 @@ produce_send (void *cls, size_t size, void *buf)
   notify_cls = th->notify_cls;
   GNUNET_CORE_notify_transmit_ready_cancel (th);
   trigger_next_request (h);
+  size = GNUNET_MIN (size,
+                    GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
   GNUNET_assert (size >= sizeof (struct SendMessage));
   dt = notify (notify_cls, size - sizeof (struct SendMessage), &sm[1]);
   if (0 == dt)
@@ -827,12 +870,23 @@ produce_send (void *cls, size_t size, void *buf)
       /* client decided to send nothing! */
       return 0;
     }
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Produced SEND message to core with %u bytes payload\n",
+             dt);
+#endif
   GNUNET_assert (dt >= sizeof (struct GNUNET_MessageHeader));
   if (dt + sizeof (struct SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
     {
       GNUNET_break (0);
       return 0;
     }
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Preparing for P2P transmission of %u bytes to `%4s'.\n",
+             dt,
+             GNUNET_i2s(&th->peer));
+#endif
   sm->header.size = htons (dt + sizeof (struct SendMessage));
   GNUNET_assert (dt + sizeof (struct SendMessage) <= size);
   return dt + sizeof (struct SendMessage);
@@ -872,12 +926,10 @@ GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
   th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle));
   th->ch = handle;
-  /* append to list */
-  th->prev = handle->pending_tail;
-  if (handle->pending_tail == NULL)
-    handle->pending_head = th;
-  else
-    handle->pending_tail->next = th;
+  GNUNET_CONTAINER_DLL_insert_after (handle->pending_head,
+                                    handle->pending_tail,
+                                    handle->pending_tail,
+                                    th);
   th->get_message = &produce_send;
   th->get_message_cls = th;
   th->notify = notify;
@@ -891,7 +943,7 @@ GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
   th->msize = sizeof (struct SendMessage) + notify_size;
   /* was the request queue previously empty? */
   if ( (handle->pending_head == th) &&
-       (handle->th == NULL) )
+       (handle->cth == NULL) )
     trigger_next_request (handle);
   return th;
 }
@@ -900,32 +952,23 @@ GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
 /**
  * Cancel the specified transmission-ready notification.
  *
- * @param h handle that was returned by "notify_transmit_ready".
+ * @param th handle that was returned by "notify_transmit_ready".
  */
 void
 GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle
-                                          *h)
+                                          *th)
 {
-  struct GNUNET_CORE_Handle *handle = h->ch;
-
-  if (handle->submitted == h)
-    {
-      handle->submitted = NULL;
-    }
-  else
-    {
-      if (h->prev == NULL)
-        handle->pending_head = h->next;
-      else
-        h->prev->next = h->next;
-      if (h->next == NULL)
-        handle->pending_tail = h->prev;
-      else
-        h->next->prev = h->prev;
-    }
-  if (h->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-    GNUNET_SCHEDULER_cancel (handle->sched, h->timeout_task);
-  GNUNET_free (h);
+  struct GNUNET_CORE_Handle *h = th->ch;
+  
+  if (h->submitted == th)
+    h->submitted = NULL;
+  else    
+    GNUNET_CONTAINER_DLL_remove (h->pending_head,
+                                h->pending_tail,
+                                th);    
+  if (th->timeout_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (h->sched, th->timeout_task);
+  GNUNET_free (th);
 }