/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sms_wap_push_handler.h"

#include <memory>

#include "common_event.h"
#include "common_event_manager.h"
#include "common_event_support.h"
#include "mms_msg.h"
#include "securec.h"
#include "sms_broadcast_subscriber_receiver.h"
#include "sms_hisysevent.h"
#include "sms_persist_helper.h"
#include "string_utils.h"
#include "telephony_log_wrapper.h"
#include "telephony_permission.h"
#include "want.h"

namespace OHOS {
namespace Telephony {
using namespace OHOS::EventFwk;
static constexpr uint8_t PDU_TYPE_PUSH = 0x06;
static constexpr uint8_t PDU_TYPE_CONFIRMED_PUSH = 0x07;
static constexpr uint32_t PARAMETER_X_WAP_APPLICATION_ID = 0x2F;
static constexpr const char *CONTENT_MIME_TYPE_B_PUSH_CO = "application/vnd.wap.coc";

SmsWapPushHandler::SmsWapPushHandler(int32_t slotId) : slotId_(slotId) {}

SmsWapPushHandler::~SmsWapPushHandler() {}

bool SmsWapPushHandler::DecodeWapPushPduData(SmsWapPushBuffer &decodeBuffer, uint32_t startPos, uint32_t len)
{
    uint32_t headerLength = len;
    uint32_t startHeader = startPos;

    std::unique_ptr<char[]> headerBuffer = nullptr;
    headerBuffer = decodeBuffer.ReadDataBuffer(startHeader, headerLength);
    if (headerBuffer == nullptr) {
        TELEPHONY_LOGE("Read Header Buffer nullptr error");
        return false;
    }
    hexHeaderData_ = StringUtils::StringToHex(static_cast<char *>(headerBuffer.get()), headerLength);

    uint32_t posData = 0;
    uint32_t dataLength = 0;
    if (contentType_.GetContentType() == std::string(CONTENT_MIME_TYPE_B_PUSH_CO)) {
        dataLength = decodeBuffer.GetSize();
    } else {
        dataLength = decodeBuffer.GetSize() - startHeader - headerLength;
        posData = startHeader + headerLength;
    }
    std::unique_ptr<char[]> pduBuffer = nullptr;
    pduBuffer = decodeBuffer.ReadDataBuffer(posData, dataLength);
    if (pduBuffer == nullptr) {
        TELEPHONY_LOGE("Read Pdu Buffer nullptr error");
        return false;
    }
    hexWbXmlData_ = StringUtils::StringToHex(static_cast<char *>(pduBuffer.get()), dataLength);
    return true;
}
/*
 * wap-230-wsp-20010705-a 8.2.4.1 Push and ConfirmedPush
 */
bool SmsWapPushHandler::DecodeWapPushPdu(std::shared_ptr<SmsReceiveIndexer> indexer, std::string &wapPdu)
{
    if (indexer == nullptr) {
        TELEPHONY_LOGE("indexer is nullptr");
        return false;
    }
    SmsWapPushBuffer decodeBuffer;
    if (!decodeBuffer.WriteRawStringBuffer(wapPdu)) {
        TELEPHONY_LOGE("Wap push WriteRawStringBuffer fail.");
        return false;
    }
    if (!DecodePushType(decodeBuffer)) {
        TELEPHONY_LOGE("Wap push DecodePushType fail.");
        return false;
    }
    uint32_t count = 0;
    uint32_t headerLength = 0;
    if (!decodeBuffer.DecodeUintvar(headerLength, count)) {
        TELEPHONY_LOGE("Wap push DecodeUintvar fail.");
        return false;
    }
    int32_t contentTypeLength = 0;
    uint32_t startHeader = decodeBuffer.GetCurPosition();
    if (!contentType_.DecodeContentType(decodeBuffer, contentTypeLength)) {
        TELEPHONY_LOGE("Wap push DecodeContentType fail.");
        return false;
    }
    uint32_t headersLen = 0;
    uint32_t curentPosition = decodeBuffer.GetCurPosition();
    if (headerLength + startHeader <= curentPosition) {
        TELEPHONY_LOGE("Wap push headersLen fail.");
        return false;
    }
    headersLen = headerLength - curentPosition + startHeader;
    DecodeXWapApplication(decodeBuffer, headersLen);

    if (!DecodeWapPushPduData(decodeBuffer, startHeader, headerLength)) {
        TELEPHONY_LOGE("Wap push DecodeWapPushPduData fail.");
        return false;
    }
    if (DeocdeCheckIsBlock(hexWbXmlData_)) {
        TELEPHONY_LOGI("Wap Push Mms-message Is Blocked Dispatcher.");
        DeleteWapPush(indexer);
        SmsHiSysEvent::WriteSmsReceiveFaultEvent(slotId_, SmsMmsMessageType::SMS_SHORT_MESSAGE,
            SmsMmsErrorCode::SMS_ERROR_ADDRESS_BLOCKED, "The Wap Push address is blocked");
        return true;
    }
    SendWapPushMessageBroadcast(indexer);
    return true;
}

void SmsWapPushHandler::DeleteWapPush(std::shared_ptr<SmsReceiveIndexer> indexer)
{
    auto handler = std::make_shared<SmsReceiveReliabilityHandler>(slotId_);
    if (handler == nullptr) {
        TELEPHONY_LOGE("handler is nullptr.");
        return;
    }
    handler->DeleteMessageFormDb(indexer->GetMsgRefId(), indexer->GetDataBaseId());
}

/*
 * wap-230-wsp-20010705-a 8.2.4.1 Push and ConfirmedPush
 * 8.2.4 Push and Confirmed Push Facilities
 */
bool SmsWapPushHandler::DecodePushType(SmsWapPushBuffer &decodeBuffer)
{
    transactionId_ = 0;
    if (!decodeBuffer.GetOneByte(transactionId_)) {
        TELEPHONY_LOGE("Decode Transaction Id error.");
        return false;
    }
    pushType_ = 0;
    if (!decodeBuffer.GetOneByte(pushType_)) {
        TELEPHONY_LOGE("Decode PushType Error.");
        return false;
    }
    /** 8.2.4 Push and Confirmed Push Facilities **/
    if (pushType_ != PDU_TYPE_PUSH && pushType_ != PDU_TYPE_CONFIRMED_PUSH) {
        TELEPHONY_LOGE("unSupported this pushType:%{public}d", pushType_);
        return false;
    }
    return true;
}

/**
 * @brief DeocdeCheckIsBlock
 * Check Block From Address From Contact BataBase
 * @param pdus [in]
 * @param len [in]
 * @return true
 * @return false
 */
bool SmsWapPushHandler::DeocdeCheckIsBlock(std::string &hexData)
{
    const uint8_t mmsNotificationInd = 130;

    std::string pdustr = StringUtils::HexToString(hexData);
    uint32_t pduLen = pdustr.length();

    std::unique_ptr<char[]> pdus = std::make_unique<char[]>(pduLen);
    if (pdus == nullptr || pduLen == 0) {
        TELEPHONY_LOGE("pdu buffer data param error");
        return false;
    }
    if (memcpy_s(pdus.get(), pduLen, pdustr.data(), pduLen) != EOK) {
        TELEPHONY_LOGE("Memcpy_s DeocdeCheckIsBlock Error.");
        return false;
    }

    MmsMsg mmsMsg;
    bool result = mmsMsg.DecodeMsg(std::move(pdus), pduLen);
    if (result && (mmsMsg.GetMmsMessageType() == mmsNotificationInd)) {
        mmsMsg.DumpMms();
        MmsAddress fromAddress = mmsMsg.GetMmsFrom();
        auto helper = DelayedSingleton<SmsPersistHelper>::GetInstance();
        if (helper == nullptr) {
            TELEPHONY_LOGE("SmsPersist Helper nullptr error");
            return false;
        }
        std::string address = fromAddress.GetAddressString();
        std::size_t pos = address.find('/');
        if (pos == 0) {
            TELEPHONY_LOGE("pos invalid.");
            return false;
        } else if (pos == std::string::npos) {
            return helper->QueryBlockPhoneNumber(address);
        } else {
            return helper->QueryBlockPhoneNumber(address.substr(0, pos));
        }
    }
    TELEPHONY_LOGI("wap push is not blocked.");
    return false;
}

/**
 * @brief DecodeXwapApplication
 * WAP-251-PushMessage-20010322-a    5.2.1 5.2.2. WAP Headers
 * @param decodeBuffer [in]
 * @param headersLen [in]
 * @return true
 * @return false
 */
bool SmsWapPushHandler::DecodeXWapApplication(SmsWapPushBuffer &decodeBuffer, uint32_t headersLen)
{
    std::unique_ptr<char[]> tempHeadersBuffer = nullptr;
    tempHeadersBuffer = decodeBuffer.ReadDataBuffer(headersLen);
    if (headersLen > 0 && tempHeadersBuffer != nullptr) {
        SmsWapPushBuffer tempXWapDataBuffer;
        if (!tempXWapDataBuffer.WriteDataBuffer(std::move(tempHeadersBuffer), headersLen)) {
            TELEPHONY_LOGE("Wap push WriteDataBuffer fail.");
            return false;
        }
        decodeBuffer.IncreasePointer(headersLen);
        return DecodeXWapApplicationField(tempXWapDataBuffer, strAppId_);
    }
    return false;
}

/**
 * @brief DecodeXWapApplicationField
 * WAP-251-PushMessage-20010322-a    5.2.1 5.2.2. WAP Headers
 * @param decodeBuffer [in]
 * @param strWapAppId [out]
 * @return true
 * @return false
 */
bool SmsWapPushHandler::DecodeXWapApplicationField(SmsWapPushBuffer &decodeBuffer, std::string &strWapAppId)
{
    while (decodeBuffer.GetCurPosition() < decodeBuffer.GetSize()) {
        uint64_t fieldValue = 0;
        if (!decodeBuffer.DecodeInteger(fieldValue)) {
            TELEPHONY_LOGE("Wap push DecodeInteger fail.");
            return false;
        }
        if (fieldValue == PARAMETER_X_WAP_APPLICATION_ID) {
            return DecodeXWapApplicationValue(decodeBuffer, strWapAppId);
        } else {
            DecodeXWapAbandonHeaderValue(decodeBuffer);
        }
    }
    return false;
}

/*
 * wap-230-wsp-20010705-a
 * 8.4.2.54 X-Wap-Application-Id field
 * The following rule is used to encode the X-Wap-Application-Id field.
 * Application-id-value = Uri-value | App-assigned-code
 * App-assigned-code = Integer-value
 */
bool SmsWapPushHandler::DecodeXWapApplicationValue(SmsWapPushBuffer &decodeBuffer, std::string &strWapAppId)
{
    uint64_t appIdValue = 0;
    if (decodeBuffer.DecodeInteger(appIdValue)) {
        return true;
    }
    uint32_t len = 0;
    if (!decodeBuffer.DecodeText(strWapAppId, len)) {
        TELEPHONY_LOGE("Wap push DecodeText fail.");
        return false;
    }
    return true;
}

/*
 * wap-230-wsp-20010705-a
 * 8.4.1.2 Field values, We abancon beyond that X-Wap-Application-Id
 * Value Interpretation of First Octet
 * 0 - 30 This octet is followed by the indicated number (0 –30) of data octets
 * 31 This octet is followed by a uintvar, which indicates the number of data octets after it
 * 32 - 127 The value is a text string, terminated by a zero octet (NUL character)
 * 128 - 255 It is an encoded 7-bit value; this header has no more data
 */
bool SmsWapPushHandler::DecodeXWapAbandonHeaderValue(SmsWapPushBuffer &decodeBuffer)
{
    const uint8_t wapShortLengthMax = 30;
    const uint8_t wapLengthQuote = 31;
    const uint8_t textLengthMax = 127;

    uint8_t oneByte = 0;
    if (!decodeBuffer.GetOneByte(oneByte)) {
        TELEPHONY_LOGE("Wap push GetOneByte fail.");
        return false;
    }

    if (oneByte <= wapShortLengthMax) {
        if (!decodeBuffer.IncreasePointer(oneByte)) {
            TELEPHONY_LOGE("Wap push IncreasePointer fail.");
            return false;
        }
    } else if (oneByte == wapLengthQuote) {
        uint32_t length = 0;
        uint32_t count = 0;
        if (!decodeBuffer.DecodeUintvar(length, count)) {
            TELEPHONY_LOGE("Wap push DecodeUintvar fail.");
            return false;
        }
        if (!decodeBuffer.IncreasePointer(length)) {
            TELEPHONY_LOGE("Wap push IncreasePointer fail.");
            return false;
        }
    } else if (oneByte <= textLengthMax) {
        std::string strTemp = "";
        uint32_t length = 0;
        if (!decodeBuffer.DecodeText(strTemp, length)) {
            TELEPHONY_LOGE("Wap push DecodeText fail.");
            return false;
        }
    }
    return true;
}

bool SmsWapPushHandler::SendWapPushMessageBroadcast(std::shared_ptr<SmsReceiveIndexer> indexer)
{
    if (indexer == nullptr) {
        TELEPHONY_LOGE("indexer is nullptr");
        return false;
    }
    TELEPHONY_LOGI("wap push broadcast slotId:%{public}d", slotId_);
    EventFwk::Want want;
    want.SetAction(EventFwk::CommonEventSupport::COMMON_EVENT_SMS_WAPPUSH_RECEIVE_COMPLETED);
    want.SetParam("slotId", static_cast<int>(slotId_));
    want.SetParam("pushType", static_cast<int>(pushType_));
    want.SetParam("applicationId", strAppId_);
    want.SetParam("transactionId", static_cast<int>(transactionId_));
    want.SetParam("contentType", contentType_.GetContentType());
    want.SetParam("headerData", hexHeaderData_);
    want.SetParam("rawData", hexWbXmlData_);

    EventFwk::CommonEventData data;
    data.SetWant(want);
    data.SetData("Sms WapPush Message");
    data.SetCode(0);
    EventFwk::CommonEventPublishInfo publishInfo;
    publishInfo.SetOrdered(true);
    std::vector<std::string> wappushPermissions;
    wappushPermissions.emplace_back(Permission::RECEIVE_MMS);
    publishInfo.SetSubscriberPermissions(wappushPermissions);

    MatchingSkills smsSkills;
    smsSkills.AddEvent(CommonEventSupport::COMMON_EVENT_SMS_WAPPUSH_RECEIVE_COMPLETED);
    CommonEventSubscribeInfo smsSubscriberInfo(smsSkills);
    smsSubscriberInfo.SetThreadMode(EventFwk::CommonEventSubscribeInfo::COMMON);
    auto handler = std::make_shared<SmsReceiveReliabilityHandler>(slotId_);
    auto wapPushReceiver = std::make_shared<SmsBroadcastSubscriberReceiver>(
        smsSubscriberInfo, handler, indexer->GetMsgRefId(), indexer->GetDataBaseId(), indexer->GetOriginatingAddress());
    bool publishResult = EventFwk::CommonEventManager::PublishCommonEvent(data, publishInfo, wapPushReceiver);
    sptrQueue.push(wapPushReceiver);
    HiSysEventWapPushResult(publishResult);
    return true;
}

void SmsWapPushHandler::HiSysEventWapPushResult(bool publishResult)
{
    if (!publishResult) {
        TELEPHONY_LOGE("SendBroadcast PublishBroadcastEvent result fail");
        SmsHiSysEvent::WriteSmsReceiveFaultEvent(slotId_, SmsMmsMessageType::WAP_PUSH,
            SmsMmsErrorCode::SMS_ERROR_PUBLISH_COMMON_EVENT_FAIL, "publish wpa push broadcast event fail");
    }
    DelayedSingleton<SmsHiSysEvent>::GetInstance()->SetWapPushBroadcastStartTime();
}
} // namespace Telephony
} // namespace OHOS