/*
* Copyright 2009 Funambol, Inc.
*
* 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.
*/
/* $Id$ */
#include "NotificationListener/MessageHandler.h"
#include "NotificationListener/NotificationMessage.h"
#include "NotificationListener/SessionInfo.h"
#include "NotificationListener/DMSessionRequestCommand.h"
#include "NotificationListener/BootstrapMessage.h"
#include "NotificationListener/WSPPushDecoder.h"
#include "NotificationListener/ProcessBootstrapCommand.h"
#include "lock.h"
#include "Event.h"
#include "daemon/ProfileComponentsHolder.h"
#include "daemon/ProfileTypes.h"
#include "executionqueue/IExecutionQueue.h"
#include "DeviceAdapter/IDeviceAdapter.h"
#include "common/Utils.h"
#include "Logger/LoggerMacroses.h"
#include <cmath>
using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_NotificationListener;
MessageHandler::MessageHandler(ProfileComponentsHolder& pch, bool checkNotificationDiggest, bool plainBootstrap,
bool cipherHeaderPresent, NS_Logging::Logger& logger)
: m_pch(pch),
m_logger(logger),
m_checkNotificationDiggest(checkNotificationDiggest),
m_plainBootstrap(plainBootstrap),
m_cipherHeaderPresent(cipherHeaderPresent)
{
}
MessageHandler::~MessageHandler()
{
}
void MessageHandler::Write(const void* buf, size_t size)
{
message_t msg;
msg.resize(size, '\0');
memcpy(&msg[0], buf, msg.size());
add(msg);
}
void MessageHandler::add(message_t& msg)
{
NS_Common::Lock lock(m_csMsgHandler);
m_messages.resize(m_messages.size() + 1);
m_messages.back().swap(msg);
LOG_DEBUG_(m_logger, "Fire NEW_MSG signal. ");
m_newMsg.signal(e_NewMsg);
}
bool MessageHandler::get(message_t& msg)
{
NS_Common::Lock lock(m_csMsgHandler);
if (m_messages.empty())
{
return false;
}
m_messages.back().swap(msg);
m_messages.resize(m_messages.size() - 1);
return true;
}
void MessageHandler::Start()
{
while(true)
{
Signals signal = static_cast<Signals>(m_newMsg.wait());
LOG_DEBUG_(m_logger, "MessageHandler:: received signal=%d. ", signal);
if (signal == e_Stop)
{
LOG_DEBUG_(m_logger, "MessageHandler:: STOP signal received. ");
break;
}
if (signal == e_NewMsg)
{
LOG_DEBUG_(m_logger, "MessageHandler:: NEW_MSG signal received. ");
message_t msg;
if (!get(msg))
{
LOG_ERROR_(m_logger, "Failed get message. ");
continue;
}
if (msg.empty())
{
LWARN("Empty message");
continue;
}
String dump;
const size_t dumpSize = __MIN(msg.size(), 32);
LDUMP("MSG (%d bytes): %s %s", msg.size(), Utils::ToAsciiHex(dump, &msg[0], dumpSize), (dumpSize < msg.size()) ? "..." : "");
WSPPushDecoder pd;
if (pd.SetPDU(&msg[0], msg.size(), true))
{
NS_WSP::EnumContentTypes ct = pd.GetWNContentType_MIMEType();
if (ct == NS_WSP::e_CT_NotWellKnown)
{
String ctRef = pd.GetNotWNContentTypeRef();
const String wmfBootstrapCT = "application/vnd.wmf.bootstrap";
char c = 0;
// Sample content type:
// Content-Type: application/vnd.wmf.bootstrap;SEC=0;MAC=0A85FF86325D34556EC9E1292AF71BDB5F146F42
if (ctRef.length() >= wmfBootstrapCT.length()
&& strncmp(ctRef.c_str(), wmfBootstrapCT.c_str(), wmfBootstrapCT.length()) == 0
&& (c = ctRef.c_str()[wmfBootstrapCT.length()], c == ';' || c == '\0')
)
{
ct = NS_WSP::e_application_vnd_wmf_bootstrap;
}
LOG_DUMP_(m_logger, "Content-Type value '%s' ", ctRef.c_str());
}
LOG_DUMP_(m_logger, "Content-Type (hex code) %Xh. ", ct);
int dataOffset = 0;
int dataSize = 0;
if (!pd.GetDataInfo(dataOffset, dataSize))
{
LOG_ERROR_(m_logger, "Failed get data info from the message. ");
}
else
switch (ct)
{
case NS_WSP::e_application_vnd_syncml_notification:
{
SessionInfo sessInfo;
memset(&sessInfo, 0, sizeof(SessionInfo));
if (checkNotificationMessage(&msg[dataOffset], dataSize, sessInfo))
{
LOG_DUMP_(m_logger, "NotificationMsg: Server='%s', Session='%s', server initiated=%d, UI mode=%d. ",
sessInfo.serverID.c_str(), sessInfo.sessionID.c_str(), sessInfo.serverInitiated, sessInfo.uiMode);
DMSessionRequestCommand* cmd = new(std::nothrow) DMSessionRequestCommand(sessInfo, m_checkNotificationDiggest, m_pch, m_logger);
if (cmd != (DMSessionRequestCommand*)NULL)
{
LOG_ERROR_(m_logger, "Failed to allocate DMSessionRequestCommand. ");
continue;
}
IExecutionQueue* exq = m_pch.GetExecutionQueue();
if (!exq->Add(*cmd))
{
LOG_ERROR_(m_logger, "Failed to add DMSessionRequestCommand command to Q. ");
}
}
else
{
LOG_ERROR_(m_logger, "Failed to process Notification message. ");
}
break;
}
case NS_WSP::e_application_vnd_syncml_dm_wbxml:
case NS_WSP::e_application_vnd_wmf_bootstrap:
{
if (m_pch.GetProfileType().compare(NS_Daemon::c_ProfileTypeWiMAX) == 0)
{
buffer_t bsdata;
if (checkBootstrapMessage(&msg[dataOffset], dataSize, bsdata))
{
ProcessBootstrapCommand* cmd = new(std::nothrow) ProcessBootstrapCommand(bsdata, *m_pch.GetServerExchangeManager());
if (cmd != (ProcessBootstrapCommand*)NULL)
{
LOG_ERROR_(m_logger, "Failed to allocate ProcessBootstrapCommand. ");
continue;
}
IExecutionQueue* exq = m_pch.GetExecutionQueue();
if (!exq->Add(*cmd))
{
LOG_ERROR_(m_logger, "Failed to add ProcessBootstrapCommand command to Q. ");
}
}
else
{
LOG_ERROR_(m_logger, "Failed to pre-process Bootstrap message. ");
}
}
else
{
LOG_DEBUG_(m_logger, "Not WiMAX. SKIP BOOTSTRAP");
}
break;
}
default:
LOG_WARNING_(m_logger, "Unsupported message Content-Type: %d. ", ct);
break;
}
}
else
{
LOG_ERROR_(m_logger, "Not valid Push message. ");
}
}
}
}
void MessageHandler::Stop()
{
LOG_DEBUG_(m_logger, "Fire STOP signal. ");
m_newMsg.signal(e_Stop);
}
bool MessageHandler::checkNotificationMessage(const char* buf, size_t size, SessionInfo& sessInfo) const
{
NotificationMessage notifMsg;
if (!notifMsg.SetMessage(buf, size))
{
LOG_ERROR_(m_logger, "NotificationMessage::SetMessage failed. ");
return false;
}
const int msgVersion = notifMsg.GetVersion();
if (NotificationMessage::VERSION != msgVersion)
{
LOG_ERROR_(m_logger, "Unsupported version %d. ", msgVersion);
return false;
}
if (!notifMsg.GetSessionID(sessInfo.sessionID))
{
LOG_ERROR_(m_logger, "failed to get session ID. ");
return false;
}
sessInfo.serverID = notifMsg.GetServerID();
sessInfo.serverInitiated = notifMsg.GetInitiator() == NotificationMessage::e_InitiatorServer;
sessInfo.uiMode = notifMsg.GetUIMode();
LOG_DEBUG_(m_logger, "bRes=%d. ", true);
return true;
}
bool MessageHandler::checkBootstrapMessage(const char* buf, size_t size, buffer_t& bsdata) const
{
BootstrapMessage bm(m_logger);
if (!m_plainBootstrap)
{
buffer_t emsk;
size_t emskSize = 256;
emsk.resize(emskSize,'\0');
if (!m_pch.GetDeviceAdapter()->GetEMSK(&emsk[0], emskSize))
{
LOG_ERROR_(m_logger, "failed to get EMSK");
return false;
}
emsk.resize(emskSize,'\0');
bm.SetEMSK(&emsk[0], emsk.size());
LOG_DEBUG_(m_logger, "EMSK size=%d. ", emsk.size());
}
buffer_t decoded;
bool brc = bm.Decode(buf, size, m_plainBootstrap, m_cipherHeaderPresent, bsdata);
LOG_DEBUG_(m_logger, "Decoding bRes=%d", brc);
return brc;
}