/*
 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL). See the LICENSE file in the root
 * directory for license terms. You may choose either license, or both.
 */

#include <string.h>
#include "iotcs/iotcs_private.h"
#include "protocol/http/http_wrapper.h"
#include "protocol/mqtt/mqtt_wrapper.h"
#include "iotcs_port_ssl.h"
#include "external/mqtt/include/MQTTClient.h"
#include "trusted_assets_manager/iotcs_tam.h"
#include "util/util.h"
#include "util/util_buffer.h"

#include "log/log.h"
#define IOTCSP_MODULE_LOG_CHANNEL LOG_CHANNEL_PROTO
#include "log/log_template.h"

#define MQTT_SSL_ON  1
#define MQTT_SSL_OFF 0

#ifndef IOTCS_MQTT_SUB_TOPIC_BUFFER_SIZE
#define IOTCS_MQTT_SUB_TOPIC_BUFFER_SIZE (32 + IOTCS_CLIENT_ID_BUFFER_LENGTH)
#endif

#define MQTT_KEEP_ALIVE_INTERVAL_SEC 1000
#ifndef IOTCS_MQTT_CMD_TIMEOUT_MS
#define IOTCS_MQTT_CMD_TIMEOUT_MS 10000
#endif
#define MQTT_REQUEST_TMP_PAYLOAD_LENGTH 128
#define MQTT_ACCEPTED_BYTES_LENGTH 10

#define TOKEN_URL "/iot/api/v2/oauth2/token"
#define POLICY_URL "/iot/api/v2/activation/policy"
#define DIRECT_ACTIVATION_URL "/iot/api/v2/activation/direct"
#define INDIRECT_ACTIVATION_URL "/iot/api/v2/activation/indirect/device"
#define MESSAGES_URL "/iot/api/v2/messages"
#define DEVICE_MODELS_URL "/iot/api/v2/deviceModels/"

static Client g_client;
static unsigned char* g_client_send_buf = NULL;
static unsigned char* g_client_recv_buf = NULL;
static char g_recived_accepted_bytes[MQTT_ACCEPTED_BYTES_LENGTH];

static char g_sub_topic_buf_activation[IOTCS_MQTT_SUB_TOPIC_BUFFER_SIZE];
static char g_sub_topic_buf_messages[IOTCS_MQTT_SUB_TOPIC_BUFFER_SIZE];
static char g_sub_topic_buf_device_model[IOTCS_MQTT_SUB_TOPIC_BUFFER_SIZE];

#define MQTT_PUB_TOPIC_BUF_SZ (32 + IOTCS_CLIENT_ID_BUFFER_LENGTH)
static char g_req_topic_buf[MQTT_PUB_TOPIC_BUF_SZ];
static char g_resp_topic_buf[MQTT_PUB_TOPIC_BUF_SZ];
static char g_payload[MQTT_REQUEST_TMP_PAYLOAD_LENGTH];

static const char *g_resp_topic;
static char **g_resp_payload;
static iotcs_result g_resp_rc;

#ifndef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES
#define IOTCS_MQTT_FIXED_HEADER_SIZE  5
#define IOTCS_MQTT_VARIABLE_HEADER_SIZE  2
#define IOTCS_MQTT_MAX_HEADER_SIZE (IOTCS_MQTT_FIXED_HEADER_SIZE + IOTCS_MQTT_VARIABLE_HEADER_SIZE)
#define IOTCS_INTEGER_BYTES (sizeof(int32_t))
#endif

#ifndef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES

static int32_t get_message_buffer_available_bytes() {
    return (int32_t) (g_client.readbuf_size - IOTCS_MQTT_MAX_HEADER_SIZE);
}

static void mqtt_messages_accept_bytes_handler(MessageData* md) {
    MQTTMessage* message = md->message;
    int32_t requested_bytes = 0, available_bytes = get_message_buffer_available_bytes();
    int i;
    if (message->payloadlen > IOTCS_INTEGER_BYTES) {
        LOG_ERR("%s message ignored. Expected <= %d bytes payload, received %d",
                md->topicName->lenstring.data, IOTCS_INTEGER_BYTES, message->payloadlen);
        return;
    }
    // convert char array to integer
    for (i = 0; i < (int) message->payloadlen; i++) {
        requested_bytes <<= 8;
        requested_bytes |= (((char*) message->payload)[i] & 0xff);
    }

    if (0 > util_safe_snprintf(g_recived_accepted_bytes, MQTT_ACCEPTED_BYTES_LENGTH, "%d", requested_bytes)) {
        LOG_ERRS("Couldn't convert accepted_bytes from integer to string");
    }

    if (requested_bytes > available_bytes) {
        LOG_ERR("The server request of %d bytes exceeds the client buffer of %d bytes",
                requested_bytes, available_bytes);
    }
}
#endif

static void default_mqtt_message_handler(MessageData* md) {
    MQTTMessage* message = md->message;

    if (g_resp_rc == IOTCS_RESULT_OK) {
        LOG_CRITS("unexpected invocation of default MQTT message handler");
        return;
    }

#ifndef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES
    if (md->topicName->lenstring.data &&
            strstr(md->topicName->lenstring.data, "messages/acceptBytes/error") == NULL &&
            strstr(md->topicName->lenstring.data, "messages/acceptBytes") != NULL) {
        mqtt_messages_accept_bytes_handler(md);
        return;
    }
#endif

    LOG_INFOS("---------------------------------------------------");
    LOG_INFOS("Message is received:");
    LOG_INFO("Topic: %s", md->topicName->lenstring.data);
    LOG_INFO("Len:  %d", (int) message->payloadlen);
    LOG_INFOSN((const char*) message->payload, (int) message->payloadlen);

    if (g_resp_topic && strcmp(md->topicName->lenstring.data, g_resp_topic) == 0) {
        *g_resp_payload = (char*) message->payload;
        (*g_resp_payload)[message->payloadlen] = '\0';
        g_resp_rc = IOTCS_RESULT_OK;
    } else {
        LOG_ERR("ERROR: Wrong message topic!!! got %s expect %s", md->topicName->lenstring.data, g_resp_topic);
        g_resp_rc = IOTCS_RESULT_FAIL;
    }
    LOG_INFOS("---------------------------------------------------");
}

static void mqtt_messages_handler(MessageData* md) {
    MQTTMessage* message = md->message;

    LOG_INFOS("---------------------------------------------------");
    LOG_INFOS("Message is received:");
    LOG_INFO("Topic: %s", md->topicName->lenstring.data);
    LOG_INFO("Len:  %d", (int) message->payloadlen);
    LOG_INFOSN((const char*) message->payload, (int) message->payloadlen);
    LOG_INFOS("---------------------------------------------------");

    ((char*) message->payload)[message->payloadlen] = '\0';
    cl_process_message_from_json_unsafe((char*) message->payload);
}

void mqtt_check_min_accept_bytes(protocol_response* response) {
    if (response->min_accept_bytes && response->min_accept_bytes[0] != '\0') {
#ifndef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES
        LOG_ERR("The server request of %s bytes exceeds the client buffer of %d bytes",
                response->min_accept_bytes, get_message_buffer_available_bytes());
#endif
    }
}

static void proceed_mqtt_disconnect(void) {
    if (g_client.isconnected) {
        MQTTDisconnect(&g_client);
        iotcs_port_mqtt_network_disconnect();
        cl_reset_token();
    }
}

static iotcs_result proceed_mqtt_connect(char* password_str, int ssl_support) {

    const char *id;
    Network *network;

    if (g_client.isconnected) {
        LOG_WARNS("MQTT Client is already connected");
        return IOTCS_RESULT_OK;
    }

    if (ssl_support != MQTT_SSL_ON) {
        LOG_ERRS("Only MQTTs is supported");
        return IOTCS_RESULT_FAIL;
    }

    int rc = 0;
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    char* client_id = (char*) (tam_is_activated() ?
            tam_get_endpoint_id() : tam_get_client_id());

    if ((network = iotcs_port_mqtt_network_connect((char*) tam_get_server_host(), tam_get_server_port(), 1 /* use ssl */)) == NULL) {
        LOG_ERRS("server connection failed");
        return IOTCS_RESULT_FAIL;
    }

    g_client_send_buf = (unsigned char*) util_get_mqtt_send_buffer();
    g_client_recv_buf = (unsigned char*) util_get_mqtt_receive_buffer();

    MQTTClient(&g_client, network, IOTCS_MQTT_CMD_TIMEOUT_MS,
            g_client_send_buf, IOTCS_MQTT_SEND_BUFFER_SIZE,
            g_client_recv_buf, UTIL_HTTP_HEADER_BUFFER_SIZE + UTIL_PAYLOAD_BUFFER_SIZE - 1 /* for Null char */);

    data.willFlag = 0;
    data.MQTTVersion = 3;
    data.clientID.cstring = client_id;
    data.username.cstring = client_id;
    data.password.cstring = password_str;
    data.keepAliveInterval = MQTT_KEEP_ALIVE_INTERVAL_SEC;
    data.cleansession = 1;

    if ((rc = MQTTConnect(&g_client, &data)) != 0) {
        if (rc == MQTTPACKET_BUFFER_TOO_SHORT) {
            LOG_CRITS("MQTTConnect: some buffers size aren't enough. Try to increase the value of IOTCS_MQTT_SEND_BUFFER_SIZE variable.");
        } else {
            LOG_ERR("MQTTConnect method returns %d", rc);
        }
        return IOTCS_RESULT_FAIL;
    }

    id = tam_is_activated() ? tam_get_endpoint_id() : tam_get_client_id();

    /* g_sub_topic_buf_activation = "devices/<id>/activation/#"
     * Contains:
     * 1) devices/%s/activation/direct
     * 2) devices/%s/activation/direct/error
     * 3) devices/%s/activation/policy
     * 4) devices/%s/activation/policy/error
     * 5) devices/%s/activation/indirect/# (include all error handles)
     */
    if (0 > util_safe_snprintf(g_sub_topic_buf_activation,
            sizeof (g_sub_topic_buf_activation),
            "devices/%s/activation/#", id)) {
        return IOTCS_RESULT_FAIL;
    }
    if (SUCCESS != MQTTSubscribe(&g_client, g_sub_topic_buf_activation, QOS1, default_mqtt_message_handler)) {
        proceed_mqtt_disconnect();
        return IOTCS_RESULT_FAIL;
    }

    /* g_sub_topic_buf_messages = "devices/<id>/messages/#"
     * Contains:
     * 1) devices/%s/messages
     * 2) devices/%s/messages/error
     * 3) devices/%s/messages/acceptBytes
     * 4) devices/%s/messages/acceptBytes/error
     */
    if (0 > util_safe_snprintf(g_sub_topic_buf_messages,
            sizeof (g_sub_topic_buf_messages),
            "devices/%s/messages/#", id)) {
        return IOTCS_RESULT_FAIL;
    }
    if (SUCCESS != MQTTSubscribe(&g_client, g_sub_topic_buf_messages, QOS1, mqtt_messages_handler)) {
        proceed_mqtt_disconnect();
        return IOTCS_RESULT_FAIL;
    }

    /* g_sub_topic_buf_device_model = "devices/<id>/deviceModels/#"
     * Contains:
     * 1) devices/%s/deviceModels
     * 2) devices/%s/deviceModels/error
     */
    if (0 > util_safe_snprintf(g_sub_topic_buf_device_model,
            sizeof (g_sub_topic_buf_device_model),
            "devices/%s/deviceModels/#", id)) {
        return IOTCS_RESULT_FAIL;
    }
    if (SUCCESS != MQTTSubscribe(&g_client, g_sub_topic_buf_device_model, QOS1, default_mqtt_message_handler)) {
        proceed_mqtt_disconnect();
        return IOTCS_RESULT_FAIL;
    }

    return IOTCS_RESULT_OK;
}

iotcs_result mqtt_request(const char *req_topic, const char *req_payload, int payload_len,
        const char *resp_topic, char **resp_payload) {
    MQTTMessage req_message;
    int rc = FAILURE;

    if (!g_client.isconnected) {
        LOG_ERRS("MQTT Client isn't connected");
        return IOTCS_RESULT_FAIL;
    }

    LOG_INFO("mqtt_request: to %s resp %s", req_topic, resp_topic ? resp_topic : "NONE");
    LOG_INFOSN(req_payload, payload_len);

    g_resp_rc = IOTCS_RESULT_OK;
    if (resp_topic) {
        g_resp_topic = resp_topic;
        g_resp_payload = resp_payload;
        /* Use IOTCS_RESULT_INVALID_ARGUMENT as a marker that message response isn't received yet.
         * default_mqtt_message_handler() never set g_resp_rc to IOTCS_RESULT_INVALID_ARGUMENT. */
        g_resp_rc = IOTCS_RESULT_INVALID_ARGUMENT;
    }

    req_message.payload = (void*) req_payload;
    req_message.payloadlen = payload_len;
    req_message.qos = QOS1;
    req_message.dup = 0;
    req_message.retained = 0;

    if (SUCCESS != (rc = MQTTPublish(&g_client, req_topic, &req_message))) {
        LOG_ERR("Publish failed %d", rc);
        return IOTCS_RESULT_FAIL;
    }

    /* If we are waiting for response and the response isn't received yet */
    if (resp_topic && IOTCS_RESULT_INVALID_ARGUMENT == g_resp_rc) {
        MQTTWaitForPublication(&g_client, IOTCS_MQTT_CMD_TIMEOUT_MS);
    }
    g_resp_payload = NULL;

    return g_resp_rc;
}

static iotcs_result proceed_mqtt_activation_policy(const char *name, const char *ver, char **response) {
    const char *id = tam_get_client_id();

    /* g_req_topic_buf = "iotcs/<id>/activation/policy" */
    if (0 > util_safe_snprintf(g_req_topic_buf,
            sizeof (g_req_topic_buf),
            "iotcs/%s/activation/policy", id)) {
        return IOTCS_RESULT_FAIL;
    }
    /* g_resp_topic_buf = "devices/<id>/activation/policy" */
    if (0 > util_safe_snprintf(g_resp_topic_buf,
            sizeof (g_resp_topic_buf),
            "devices/%s/activation/policy", id)) {
        return IOTCS_RESULT_FAIL;
    }

    if (0 > util_safe_snprintf(g_payload, MQTT_REQUEST_TMP_PAYLOAD_LENGTH, "{\"OSName\":\"%s\",\"OSVersion\":\"%s\"}", name, ver)) {
        LOG_ERRS("util_safe_snprintf method failed");
        return IOTCS_RESULT_OUT_OF_MEMORY;
    }

    if (IOTCS_RESULT_OK != mqtt_request(g_req_topic_buf, g_payload, strlen(g_payload),
            g_resp_topic_buf, response)) {
        LOG_ERRS("proceed_mqtt_activation_policy failed");
        return IOTCS_RESULT_FAIL;
    }

    LOG_INFO("proceed_mqtt_activation_policy OK: %s", *response);

    return IOTCS_RESULT_OK;
}

static iotcs_result proceed_mqtt_device_model(const char *urn, char **response) {
    const char *id = tam_get_endpoint_id();

    /* g_req_topic_buf = "iotcs/<id>/deviceModels" */
    if (0 > util_safe_snprintf(g_req_topic_buf,
            sizeof (g_req_topic_buf),
            "iotcs/%s/deviceModels", id)) {
        return IOTCS_RESULT_FAIL;
    }
    /* g_resp_topic_buf = "devices/<id>/deviceModels" */
    if (0 > util_safe_snprintf(g_resp_topic_buf,
            sizeof (g_resp_topic_buf),
            "devices/%s/deviceModels", id)) {
        return IOTCS_RESULT_FAIL;
    }

    if (0 > util_safe_snprintf(g_payload, MQTT_REQUEST_TMP_PAYLOAD_LENGTH, "{\"urn\":\"%s\"}", urn)) {
        LOG_ERRS("util_safe_snprintf method failed");
        return IOTCS_RESULT_OUT_OF_MEMORY;
    }

    if (IOTCS_RESULT_OK != mqtt_request(g_req_topic_buf, g_payload, strlen(g_payload),
            g_resp_topic_buf, response)) {
        LOG_ERRS("proceed_mqtt_device_model failed");
        return IOTCS_RESULT_FAIL;
    }

    LOG_INFOS("proceed_mqtt_device_model OK: ");
    LOG_INFOS(*response);

    return IOTCS_RESULT_OK;
}

static iotcs_result proceed_mqtt_direct_activation(const char *payload, char **response) {
    const char *id = tam_get_client_id();

    /* g_req_topic_buf = "iotcs/<id>/activation/direct" */
    if (0 > util_safe_snprintf(g_req_topic_buf,
            sizeof (g_req_topic_buf),
            "iotcs/%s/activation/direct", id)) {
        return IOTCS_RESULT_FAIL;
    }
    /* g_resp_topic_buf = "devices/<id>/activation/direct" */
    if (0 > util_safe_snprintf(g_resp_topic_buf,
            sizeof (g_resp_topic_buf),
            "devices/%s/activation/direct", id)) {
        return IOTCS_RESULT_FAIL;
    }

    if (IOTCS_RESULT_OK != mqtt_request(g_req_topic_buf, payload, strlen(payload),
            g_resp_topic_buf, response)) {
        LOG_ERRS("proceed_mqtt_direct_activation failed");
        return IOTCS_RESULT_FAIL;
    }

    LOG_INFO("proceed_mqtt_direct_activation OK: %s", response);

    return IOTCS_RESULT_OK;
}

#ifdef IOTCS_GATEWAY

static iotcs_result proceed_mqtt_indirect_activation(const char *payload, char **response) {
    const char *id = tam_get_endpoint_id();

    /* g_req_topic_buf = "iotcs/<id>/activation/indirect/device" */
    if (0 > util_safe_snprintf(g_req_topic_buf,
            sizeof (g_req_topic_buf),
            "iotcs/%s/activation/indirect/device", id)) {
        return IOTCS_RESULT_FAIL;
    }
    /* g_resp_topic_buf = "devices/<id>/activation/indirect/device" */
    if (0 > util_safe_snprintf(g_resp_topic_buf,
            sizeof (g_resp_topic_buf),
            "devices/%s/activation/indirect/device", id)) {
        return IOTCS_RESULT_FAIL;
    }

    if (IOTCS_RESULT_OK != mqtt_request(g_req_topic_buf, payload, strlen(payload),
            g_resp_topic_buf, response)) {
        LOG_ERRS("proceed_mqtt_indirect_activation failed");
        return IOTCS_RESULT_FAIL;
    }

    LOG_INFO("proceed_mqtt_indirect_activation OK: %s", *response);

    return IOTCS_RESULT_OK;
}
#endif

#ifndef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES

static iotcs_result proceed_mqtt_messages_accept_bytes(const char* client_id) {
    static int32_t last_accepted_bytes = -1;
    int32_t accepted_bytes = get_message_buffer_available_bytes();
    if (last_accepted_bytes != accepted_bytes) {
        // convert integer to char array
        char payload[IOTCS_INTEGER_BYTES];
        int i;
        int32_t val = accepted_bytes;
        for (i = IOTCS_INTEGER_BYTES - 1; 0 <= i; i--) {
            payload[i] = (char) (val & 0xff);
            val >>= 8;
        }

        /* g_req_topic_buf = "iotcs/<id>/messages/acceptBytes" */
        if (0 > util_safe_snprintf(g_req_topic_buf, sizeof (g_req_topic_buf), "iotcs/%s/messages/acceptBytes", client_id)) {
            return IOTCS_RESULT_FAIL;
        }

        if (IOTCS_RESULT_OK != mqtt_request(g_req_topic_buf, payload, IOTCS_INTEGER_BYTES,
                NULL, NULL)) {
            LOG_ERRS("proceed_mqtt_messages_accept_bytes failed");
            return IOTCS_RESULT_FAIL;
        }
        LOG_INFO("proceed_mqtt_messages_accept_bytes OK, acceptBytes=%d", (int) accepted_bytes);
        last_accepted_bytes = accepted_bytes;
    }
    return IOTCS_RESULT_OK;
}
#endif

static iotcs_result proceed_mqtt_messages(const char *payload) {
    const char *id = tam_get_endpoint_id();
#ifndef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES
    if (IOTCS_RESULT_OK != proceed_mqtt_messages_accept_bytes(id)) {
        return IOTCS_RESULT_FAIL;
    }
#endif

    /* g_req_topic_buf = "iotcs/<id>/messages" */
    if (0 > util_safe_snprintf(g_req_topic_buf,
            sizeof (g_req_topic_buf),
            "iotcs/%s/messages", id)) {
        return IOTCS_RESULT_FAIL;
    }

    if (IOTCS_RESULT_OK != mqtt_request(g_req_topic_buf, payload, strlen(payload),
            NULL, NULL)) {
        LOG_ERRS("proceed_mqtt_messages failed");
        return IOTCS_RESULT_FAIL;
    }

    LOG_INFOS("proceed_mqtt_messages OK");

    return IOTCS_RESULT_OK;
}

iotcs_result mqtt_proceed_request(
        protocol_request request,
        protocol_response* response) {

    iotcs_result result = IOTCS_RESULT_FAIL;
    response->status_code = PROTOCOL_RESPONSE_CODE_BAD_REQUEST;
    response->body = NULL;
    response->min_accept_bytes = NULL;

    if (strcmp(request.url, TOKEN_URL) == 0) {
        char *begin = strstr(request.body, "&client_assertion=");
        char *end = strstr(begin, "&scope=");
        if (!end || !begin) {
            return IOTCS_RESULT_FAIL;
        }
        *end = '\0';
        begin += strlen("&client_assertion=");
        LOG_INFOS("HIT /iot/api/v2/oauth2/token: ");
        LOG_INFOS(begin);

        /* Disconnect first */
        proceed_mqtt_disconnect();

        if (IOTCS_RESULT_OK == (result = proceed_mqtt_connect(begin, MQTT_SSL_ON))) {
            response->status_code = PROTOCOL_RESPONSE_CODE_OK;
            response->body = util_safe_strcpy("{\"expires_in\":3215408703000,\"access_token\":\"FAKE\"}");
        }
        return result;
    }

    if (!g_client.isconnected) {
        LOG_ERRS("MQTT Client is NOT connected");
        return IOTCS_RESULT_OK;
    }

    if (strncmp(request.url, POLICY_URL, strlen(POLICY_URL)) == 0) {
        LOG_INFOS("HIT /iot/api/v2/activation/policy");
        char *name = strstr(request.url, "OSName=");
        char *ver = strstr(name, "&OSVersion=");
        if (!name || !ver) {
            return IOTCS_RESULT_FAIL;
        }
        *ver = '\0';
        name += strlen("OSName=");
        ver += strlen("&OSVersion=");

        if (IOTCS_RESULT_OK == (result = proceed_mqtt_activation_policy(name, ver, &response->body))) {
            response->status_code = PROTOCOL_RESPONSE_CODE_OK;
        }
    } else if (strncmp(request.url, DIRECT_ACTIVATION_URL, strlen(DIRECT_ACTIVATION_URL)) == 0) {
        if (IOTCS_RESULT_OK == (result = proceed_mqtt_direct_activation(request.body, &response->body))) {
            response->status_code = PROTOCOL_RESPONSE_CODE_OK;
            proceed_mqtt_disconnect();
        }
    } else if (strncmp(request.url, MESSAGES_URL, strlen(MESSAGES_URL)) == 0) {
        LOG_INFOS("HIT /iot/api/v2/messages");
        if (IOTCS_RESULT_OK == (result = proceed_mqtt_messages(request.body))) {
            response->status_code = PROTOCOL_RESPONSE_CODE_ACCEPTED;
            response->body = strdup("[]");
            response->min_accept_bytes = g_recived_accepted_bytes;
        }
    } else if (strncmp(request.url, DEVICE_MODELS_URL, strlen(DEVICE_MODELS_URL)) == 0) {
        LOG_INFO("HIT /iot/api/v2/deviceModels %s", request.url);
        char *urn = (char *) request.url;
        urn += strlen(DEVICE_MODELS_URL);
        if (IOTCS_RESULT_OK == (result = proceed_mqtt_device_model(urn, &response->body))) {
            response->status_code = PROTOCOL_RESPONSE_CODE_OK;
        }
    }
#ifdef IOTCS_GATEWAY
    else if (strncmp(request.url, INDIRECT_ACTIVATION_URL, strlen(INDIRECT_ACTIVATION_URL)) == 0) {
        LOG_INFO("HIT /iot/api/v2/activation/indirect/device %s", request.url);
        if (IOTCS_RESULT_OK == (result = proceed_mqtt_indirect_activation(request.body, &response->body))) {
            response->status_code = PROTOCOL_RESPONSE_CODE_OK;
        }
    }
#endif
    else {
        LOG_ERR("UNKNOWN URL %s", request.url);
        return IOTCS_RESULT_FAIL;
    }

    return result;
}
