/**
 * Handle request connect message
 *
 * @param cls closure (always NULL)
 * @param client identification of the client
 * @param message the actual message
 */
static void
clients_handle_request_connect (void *cls, struct GNUNET_SERVER_Client *client,
                                const struct GNUNET_MessageHeader *message)
{
  const struct TransportRequestConnectMessage *trcm =
      (const struct TransportRequestConnectMessage *) message;

  GNUNET_STATISTICS_update (GST_stats,
                            gettext_noop
                            ("# REQUEST CONNECT messages received"), 1,
                            GNUNET_NO);

  if (0 == memcmp (&trcm->peer, &GST_my_identity,
  		sizeof (struct GNUNET_PeerIdentity)))
  {
    GNUNET_break_op (0);
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Received a request connect message myself `%s'\n",
                GNUNET_i2s (&trcm->peer));
  }
  else
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Received a request connect message for peer `%s'\n",
                GNUNET_i2s (&trcm->peer));

    (void) GST_blacklist_test_allowed (&trcm->peer, NULL, &try_connect_if_allowed,
                                     NULL);
  }
  GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
/**
 * Do address validation again to keep address valid.
 *
 * @param cls the 'struct ValidationEntry'
 * @param tc scheduler context (unused)
 */
static void
revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct ValidationEntry *ve = cls;
  struct GNUNET_TIME_Relative canonical_delay;
  struct GNUNET_TIME_Relative delay;
  struct GST_BlacklistCheck *bc;
  uint32_t rdelay;

  ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK;
  delay = GNUNET_TIME_absolute_get_remaining (ve->revalidation_block);
  /* How long until we can possibly permit the next PING? */
  canonical_delay =
      (ve->in_use ==
       GNUNET_YES) ? CONNECTED_PING_FREQUENCY
      : ((GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value >
          0) ? VALIDATED_PING_FREQUENCY : UNVALIDATED_PING_KEEPALIVE);
  if (delay.rel_value > canonical_delay.rel_value * 2)
  {
    /* situation changed, recalculate delay */
    delay = canonical_delay;
    ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay);
  }
  if (delay.rel_value > 0)
  {
    /* should wait a bit longer */
    ve->revalidation_task =
        GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve);
    return;
  }
  ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay);

  /* schedule next PINGing with some extra random delay to avoid synchronous re-validations */
  rdelay =
      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
                                canonical_delay.rel_value);
  delay =
      GNUNET_TIME_relative_add (canonical_delay,
                                GNUNET_TIME_relative_multiply
                                (GNUNET_TIME_UNIT_MILLISECONDS, rdelay));
  ve->revalidation_task =
      GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve);

  /* start PINGing by checking blacklist */
  GNUNET_STATISTICS_update (GST_stats,
                            gettext_noop ("# address revalidations started"), 1,
                            GNUNET_NO);
  bc = GST_blacklist_test_allowed (&ve->pid, ve->address->transport_name,
                                   &transmit_ping_if_allowed, ve);
  if (NULL != bc)
    ve->bc = bc;                /* only set 'bc' if 'transmit_ping_if_allowed' was not already
                                 * called... */
}
/**
 * Plugin tells transport service about a new inbound session
 *
 * @param cls unused
 * @param address the address
 * @param session the new session
 * @param scope network scope information
 */
static void
plugin_env_session_start (void *cls,
                          const struct GNUNET_HELLO_Address *address,
                          struct GNUNET_ATS_Session *session,
                          enum GNUNET_ATS_Network_Type scope)
{
  struct GNUNET_ATS_Properties prop;

  if (NULL == address)
  {
    GNUNET_break(0);
    return;
  }
  if (NULL == session)
  {
    GNUNET_break(0);
    return;
  }
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Notification from plugin `%s' about new session from peer `%s' address `%s'\n",
              address->transport_name,
              GNUNET_i2s (&address->peer),
              GST_plugins_a2s (address));
  if (GNUNET_YES ==
      GNUNET_HELLO_address_check_option (address,
                                         GNUNET_HELLO_ADDRESS_INFO_INBOUND))
  {
    /* inbound is always new, but outbound MAY already be known, but
       for example for UNIX, we have symmetric connections and thus we
       may not know the address yet; add if necessary! */
    /* FIXME: maybe change API here so we just pass scope? */
    memset (&prop, 0, sizeof (prop));
    GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != scope);
    prop.scope = scope;
    GST_ats_add_inbound_address (address,
                                 session,
                                 &prop);
  }
  /* Do blacklist check if communication with this peer is allowed */
  (void) GST_blacklist_test_allowed (&address->peer,
				     address->transport_name,
				     &plugin_env_session_start_bl_check_cont,
				     NULL,
				     address,
				     session);
}
/**
 * Function called by the transport for each received message.
 *
 * @param cls closure, const char* with the name of the plugin we received the message from
 * @param address address and (claimed) identity of the other peer
 * @param message the message, NULL if we only care about
 *                learning about the delay until we should receive again
 * @param session identifier used for this session (NULL for plugins
 *                that do not offer bi-directional communication to the sender
 *                using the same "connection")
 * @return how long the plugin should wait until receiving more data
 *         (plugins that do not support this, can ignore the return value)
 */
struct GNUNET_TIME_Relative
GST_receive_callback (void *cls,
                      const struct GNUNET_HELLO_Address *address,
                      struct GNUNET_ATS_Session *session,
                      const struct GNUNET_MessageHeader *message)
{
  const char *plugin_name = cls;
  struct GNUNET_TIME_Relative ret;
  uint16_t type;

  ret = GNUNET_TIME_UNIT_ZERO;
  if (NULL == message)
    goto end;
  type = ntohs (message->type);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received message with type %u from peer `%s'\n",
              type,
              GNUNET_i2s (&address->peer));

  GNUNET_STATISTICS_update (GST_stats,
                            gettext_noop ("# bytes total received"),
                            ntohs (message->size),
                            GNUNET_NO);
  GST_neighbours_notify_data_recv (address,
                                   message);
  switch (type)
  {
  case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
    /* Legacy HELLO message, discard  */
    return ret;
  case GNUNET_MESSAGE_TYPE_HELLO:
    if (GNUNET_OK != GST_validation_handle_hello (message))
    {
      GNUNET_break_op (0);
      GST_blacklist_abort_matching (address,
				    session);
    }
    return ret;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Processing PING from `%s'\n",
                GST_plugins_a2s (address));
    if (GNUNET_OK !=
        GST_validation_handle_ping (&address->peer,
                                    message,
                                    address,
                                    session))
    {
      GST_blacklist_abort_matching (address,
				    session);
      kill_session (plugin_name,
                    session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
               "Processing PONG from `%s'\n",
               GST_plugins_a2s (address));
    if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
    {
      GNUNET_break_op (0);
      GST_blacklist_abort_matching (address,
				    session);
      kill_session (plugin_name, session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
    /* Do blacklist check if communication with this peer is allowed */
    (void) GST_blacklist_test_allowed (&address->peer,
				       NULL,
				       &connect_bl_check_cont,
				       GNUNET_copy_message (message),
				       address,
				       session);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
    if (GNUNET_OK !=
        GST_neighbours_handle_session_syn_ack (message,
                                               address,
                                               session))
    {
      GST_blacklist_abort_matching (address, session);
      kill_session (plugin_name, session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
    if (GNUNET_OK !=
        GST_neighbours_handle_session_ack (message,
                                           address,
                                           session))
    {
      GNUNET_break_op(0);
      GST_blacklist_abort_matching (address, session);
      kill_session (plugin_name, session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
    GST_neighbours_handle_disconnect_message (&address->peer,
                                              message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA:
    GST_neighbours_handle_quota_message (&address->peer,
                                         message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
    GST_neighbours_keepalive (&address->peer,
                              message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
    GST_neighbours_keepalive_response (&address->peer,
                                       message);
    break;
  default:
    /* should be payload */
    GNUNET_STATISTICS_update (GST_stats,
                              gettext_noop ("# bytes payload received"),
                              ntohs (message->size),
                              GNUNET_NO);
    ret = process_payload (address,
                           session,
                           message);
    break;
  }
 end:
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Allowing receive from peer %s to continue in %s\n",
              GNUNET_i2s (&address->peer),
              GNUNET_STRINGS_relative_time_to_string (ret,
                                                      GNUNET_YES));
  return ret;
}