+ if (was_head)
+ {
+ if ( (pr->prev != NULL) ||
+ (pr->next != NULL) ||
+ (pr == h->ready_peer_head) )
+ {
+ /* the request that was 'approved' by core was
+ canceled before it could be transmitted; remove
+ us from the 'ready' list */
+ GNUNET_CONTAINER_DLL_remove (h->ready_peer_head,
+ h->ready_peer_tail,
+ pr);
+ }
+ request_next_transmission (pr);
+ }
+}
+
+
+/* ****************** GNUNET_CORE_peer_request_connect ******************** */
+
+/**
+ * Handle for a request to the core to connect to
+ * a particular peer. Can be used to cancel the request
+ * (before the 'cont'inuation is called).
+ */
+struct GNUNET_CORE_PeerRequestHandle
+{
+
+ /**
+ * Link to control message.
+ */
+ struct ControlMessage *cm;
+
+ /**
+ * Core handle used.
+ */
+ struct GNUNET_CORE_Handle *h;
+
+ /**
+ * Continuation to run when done.
+ */
+ GNUNET_CORE_ControlContinuation cont;
+
+ /**
+ * Closure for 'cont'.
+ */
+ void *cont_cls;
+
+};
+
+
+/**
+ * Continuation called when the control message was transmitted.
+ * Calls the original continuation and frees the remaining
+ * resources.
+ *
+ * @param cls the 'struct GNUNET_CORE_PeerRequestHandle'
+ * @param success was the request transmitted?
+ */
+static void
+peer_request_connect_cont (void *cls,
+ int success)
+{
+ struct GNUNET_CORE_PeerRequestHandle *ret = cls;
+
+ if (ret->cont != NULL)
+ ret->cont (ret->cont_cls, success);
+ GNUNET_free (ret);
+}
+
+
+/**
+ * Request that the core should try to connect to a particular peer.
+ * Once the request has been transmitted to the core, the continuation
+ * function will be called. Note that this does NOT mean that a
+ * connection was successfully established -- it only means that the
+ * core will now try. Successful establishment of the connection
+ * will be signalled to the 'connects' callback argument of
+ * 'GNUNET_CORE_connect' only. If the core service does not respond
+ * to our connection attempt within the given time frame, 'cont' will
+ * be called with the TIMEOUT reason code.
+ *
+ * @param h core handle
+ * @param timeout how long to try to talk to core
+ * @param peer who should we connect to
+ * @param cont function to call once the request has been completed (or timed out)
+ * @param cont_cls closure for cont
+ *
+ * @return NULL on error or already connected,
+ * otherwise handle for cancellation
+ */
+struct GNUNET_CORE_PeerRequestHandle *
+GNUNET_CORE_peer_request_connect (struct GNUNET_CORE_Handle *h,
+ struct GNUNET_TIME_Relative timeout,
+ const struct GNUNET_PeerIdentity * peer,
+ GNUNET_CORE_ControlContinuation cont,
+ void *cont_cls)
+{
+ struct GNUNET_CORE_PeerRequestHandle *ret;
+ struct ControlMessage *cm;
+ struct ConnectMessage *msg;
+
+ if (NULL != GNUNET_CONTAINER_multihashmap_get (h->peers,
+ &peer->hashPubKey))
+ return NULL; /* Already connected, means callback should have happened already! */
+
+
+ cm = GNUNET_malloc (sizeof (struct ControlMessage) +
+ sizeof (struct ConnectMessage));
+ msg = (struct ConnectMessage*) &cm[1];
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONNECT);
+ msg->header.size = htons (sizeof (struct ConnectMessage));
+ msg->reserved = htonl (0);
+ msg->timeout = GNUNET_TIME_relative_hton (timeout);
+ msg->peer = *peer;
+ GNUNET_CONTAINER_DLL_insert (h->control_pending_head,
+ h->control_pending_tail,
+ cm);
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CORE_PeerRequestHandle));
+ ret->h = h;
+ ret->cm = cm;
+ ret->cont = cont;
+ ret->cont_cls = cont_cls;
+ cm->cont = &peer_request_connect_cont;
+ cm->cont_cls = ret;
+#if DEBUG_CORE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing REQUEST_CONNECT request\n");
+#endif
+ if (h->control_pending_head == cm)
+ trigger_next_request (h, GNUNET_NO);
+ return ret;
+}
+
+
+/**
+ * Cancel a pending request to connect to a particular peer. Must not
+ * be called after the 'cont' function was invoked.
+ *
+ * @param req request handle that was returned for the original request
+ */
+void
+GNUNET_CORE_peer_request_connect_cancel (struct GNUNET_CORE_PeerRequestHandle *req)
+{
+ struct GNUNET_CORE_Handle *h = req->h;
+ struct ControlMessage *cm = req->cm;
+
+ GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
+ h->control_pending_tail,
+ cm);
+ GNUNET_free (cm);
+ GNUNET_free (req);
+}
+
+
+/* ****************** GNUNET_CORE_peer_change_preference ******************** */
+
+
+struct GNUNET_CORE_InformationRequestContext
+{
+
+ /**
+ * Our connection to the service.
+ */
+ struct GNUNET_CORE_Handle *h;
+
+ /**
+ * Function to call with the information.
+ */
+ GNUNET_CORE_PeerConfigurationInfoCallback info;
+
+ /**
+ * Closure for info.
+ */
+ void *info_cls;
+
+ /**
+ * Link to control message, NULL if CM was sent.
+ */
+ struct ControlMessage *cm;
+
+ /**
+ * Link to peer record.
+ */
+ struct PeerRecord *pr;
+};
+
+
+/**
+ * CM was sent, remove link so we don't double-free.
+ *
+ * @param cls the 'struct GNUNET_CORE_InformationRequestContext'
+ * @param success were we successful?
+ */
+static void
+change_preference_send_continuation (void *cls,
+ int success)
+{
+ struct GNUNET_CORE_InformationRequestContext *irc = cls;
+
+ irc->cm = NULL;
+}
+
+
+/**
+ * Obtain statistics and/or change preferences for the given peer.
+ *
+ * @param h core handle
+ * @param peer identifies the peer
+ * @param timeout after how long should we give up (and call "info" with NULL
+ * for "peer" to signal an error)?
+ * @param bw_out set to the current bandwidth limit (sending) for this peer,
+ * caller should set "bw_out" to "-1" to avoid changing
+ * the current value; otherwise "bw_out" will be lowered to
+ * the specified value; passing a pointer to "0" can be used to force
+ * us to disconnect from the peer; "bw_out" might not increase
+ * as specified since the upper bound is generally
+ * determined by the other peer!
+ * @param amount reserve N bytes for receiving, negative
+ * amounts can be used to undo a (recent) reservation;
+ * @param preference increase incoming traffic share preference by this amount;
+ * in the absence of "amount" reservations, we use this
+ * preference value to assign proportional bandwidth shares
+ * to all connected peers
+ * @param info function to call with the resulting configuration information
+ * @param info_cls closure for info
+ * @return NULL on error
+ */
+struct GNUNET_CORE_InformationRequestContext *
+GNUNET_CORE_peer_change_preference (struct GNUNET_CORE_Handle *h,
+ const struct GNUNET_PeerIdentity *peer,
+ struct GNUNET_TIME_Relative timeout,
+ struct GNUNET_BANDWIDTH_Value32NBO bw_out,
+ int32_t amount,
+ uint64_t preference,
+ GNUNET_CORE_PeerConfigurationInfoCallback info,
+ void *info_cls)
+{
+ struct GNUNET_CORE_InformationRequestContext *irc;
+ struct PeerRecord *pr;
+ struct RequestInfoMessage *rim;
+ struct ControlMessage *cm;
+
+ pr = GNUNET_CONTAINER_multihashmap_get (h->peers,
+ &peer->hashPubKey);
+ if (NULL == pr)
+ {
+ /* attempt to change preference on peer that is not connected */
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (pr->pcic != NULL)
+ {
+ /* second change before first one is done */
+ GNUNET_break (0);
+ return NULL;
+ }
+ irc = GNUNET_malloc (sizeof (struct GNUNET_CORE_InformationRequestContext));
+ irc->h = h;
+ irc->pr = pr;
+ irc->info = info;
+ irc->info_cls = info_cls;
+ cm = GNUNET_malloc (sizeof (struct ControlMessage) +
+ sizeof (struct RequestInfoMessage));
+ cm->cont = &change_preference_send_continuation;
+ cm->cont_cls = irc;
+ irc->cm = cm;
+ rim = (struct RequestInfoMessage*) &cm[1];
+ rim->header.size = htons (sizeof (struct RequestInfoMessage));
+ rim->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO);
+ rim->rim_id = htonl (pr->rim_id = h->rim_id_gen++);
+ rim->limit_outbound = bw_out;
+ rim->reserve_inbound = htonl (amount);
+ rim->preference_change = GNUNET_htonll(preference);
+ rim->peer = *peer;
+#if DEBUG_CORE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing CHANGE PREFERENCE request\n");
+#endif
+ GNUNET_CONTAINER_DLL_insert (h->control_pending_head,
+ h->control_pending_tail,
+ cm);
+ pr->pcic = info;
+ pr->pcic_cls = info_cls;
+ if (h->control_pending_head == cm)
+ trigger_next_request (h, GNUNET_NO);
+ return irc;
+}
+
+
+/**
+ * Cancel request for getting information about a peer.
+ * Note that an eventual change in preference, trust or bandwidth
+ * assignment MAY have already been committed at the time,
+ * so cancelling a request is NOT sure to undo the original
+ * request. The original request may or may not still commit.
+ * The only thing cancellation ensures is that the callback
+ * from the original request will no longer be called.
+ *
+ * @param irc context returned by the original GNUNET_CORE_peer_get_info call
+ */
+void
+GNUNET_CORE_peer_change_preference_cancel (struct GNUNET_CORE_InformationRequestContext *irc)
+{
+ struct GNUNET_CORE_Handle *h = irc->h;
+ struct PeerRecord *pr = irc->pr;
+
+ if (irc->cm != NULL)
+ {
+ GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
+ h->control_pending_tail,
+ irc->cm);
+ GNUNET_free (irc->cm);
+ }
+ pr->pcic = NULL;
+ pr->pcic_cls = NULL;
+ GNUNET_free (irc);