452 lines
13 KiB
C++
452 lines
13 KiB
C++
// Copyright (c) 2013, IOnU Security, Inc.
|
|
// Copyright (c) 2016, Sequence Logic, Inc. All rights reserved.
|
|
|
|
#include <iostream>
|
|
#include "time.h"
|
|
#include "pubsubmessage.h"
|
|
#include "../cppcoreobjects/cloudguardurn.h"
|
|
#include "../cppcoreobjects/permissions.h"
|
|
|
|
#include "../../cppjson/jsonobject.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace sequencelogic;
|
|
|
|
const PubSubMessage::ActionResponseTTLData PubSubMessage::ActionResponseTTL[] = {
|
|
{Action::PING, Action::PONG, (MS_MINUTE*5), false, CloudGuardURN::URN_TYPE::INVALID},
|
|
{Action::PONG, Action::NONE, (MS_MINUTE*5), false, CloudGuardURN::URN_TYPE::INVALID},
|
|
{Action::REMOVE_DEVICE, Action::PONG, (MS_DAY*365), false, CloudGuardURN::URN_TYPE::INVALID},
|
|
{Action::LOGOUT_DEVICE, Action::PONG, (MS_HOUR), false, CloudGuardURN::URN_TYPE::INVALID},
|
|
|
|
{Action::SHARE, Action::SHARE, (MS_DAY*30), true, CloudGuardURN::URN_TYPE::PMO_DEVICE},
|
|
{Action::SHARE_RECEIVED, Action::NONE, (MS_DAY), false, CloudGuardURN::URN_TYPE::PMO_DEVICE},
|
|
{Action::MODIFY_PROFILE, Action::NONE, (MS_DAY*7), false, CloudGuardURN::URN_TYPE::PMO_DEVICE},
|
|
|
|
{Action::FLUSH, Action::NONE, (MS_DAY*7), false, CloudGuardURN::URN_TYPE::PMO_DEVICE},
|
|
|
|
{ Action::STATISTICS_REQUEST, Action::STATISTICS_REPLY, (MS_HOUR), false, CloudGuardURN::URN_TYPE::PMO_DEVICE },
|
|
{ Action::STATISTICS_REPLY, Action::NONE, (MS_HOUR), false, CloudGuardURN::URN_TYPE::INVALID },
|
|
{ Action::WORKFLOW_READY, Action::NONE, (2 * MS_HOUR), false, CloudGuardURN::URN_TYPE::INVALID },
|
|
{ Action::WORKFLOW_TRANSITION, Action::NONE, (2 * MS_HOUR), false, CloudGuardURN::URN_TYPE::INVALID }
|
|
};
|
|
const int PubSubMessage::NUM_ActionResponseTTL = sizeof(ActionResponseTTL) / sizeof(ActionResponseTTL[0]);
|
|
|
|
const char* PubSubMessage::ACTION_NAMES[] = {
|
|
"PING", // Client A says hello to client B and expects a PONG in reply
|
|
"PONG", // Reply to PING/CHAT/REMOVE_DEVICE
|
|
"REMOVE_DEVICE", //
|
|
"LOGOUT_DEVICE", //
|
|
"SHARE", // User A shares a Permissions digest (folder) with a destination
|
|
// no response required, but the receiver can dispose of this to
|
|
// remove any trace that A and B are sharing data
|
|
"MODIFY_PROFILE", // sent from device to all offices to indicate office updated
|
|
"SHARE_RECEIVED", // Sent by client as "delivery receipt"
|
|
"MESSAGE_READ", // Sent by client to indicate user has eyeballed a message
|
|
"FLUSH", // Flush TGI cache
|
|
// SL specific
|
|
"STATISTICS_REQUEST", // please give me statistics
|
|
"STATISTICS_REPLY", // responseData is a JSONObject with stat keys
|
|
"WORKFLOW_READY", // actionData has urn (package) and purpose (wstate)
|
|
"WORKFLOW_TRANSITION", // @see WorkflowBot.transitionNode for actionData
|
|
"NONE"
|
|
};
|
|
const int PubSubMessage::NUM_ACTION_NAMES = sizeof(ACTION_NAMES) / sizeof(ACTION_NAMES[0]);
|
|
std::string PubSubMessage::getActionName(Action act)
|
|
{
|
|
return ACTION_NAMES[act];
|
|
}
|
|
PubSubMessage::Action PubSubMessage::getActionFromName(const std::string &actName)
|
|
{
|
|
Action nRetVal = NONE;
|
|
for (int i = 0; i < NUM_ACTION_NAMES; ++i)
|
|
{
|
|
if (actName == ACTION_NAMES[i])
|
|
{
|
|
nRetVal = static_cast<Action>(i);
|
|
break;
|
|
}
|
|
}
|
|
return nRetVal;
|
|
}
|
|
|
|
const char* PubSubMessage::REPLY_TYPE_NAMES[] = {
|
|
"ANY",
|
|
"P2P",
|
|
"PUBSUB_MESSAGE"
|
|
};
|
|
const int PubSubMessage::NUM_REPLY_TYPE_NAMES = sizeof(REPLY_TYPE_NAMES) / sizeof(REPLY_TYPE_NAMES[0]);
|
|
std::string PubSubMessage::getReplyTypeName(ReplyType rt)
|
|
{
|
|
return REPLY_TYPE_NAMES[rt];
|
|
}
|
|
PubSubMessage::ReplyType PubSubMessage::getReplyFromName(const std::string &rtName)
|
|
{
|
|
ReplyType nRetVal = ANY;
|
|
for (int i = 0; i < NUM_REPLY_TYPE_NAMES; ++i)
|
|
{
|
|
if (rtName == REPLY_TYPE_NAMES[i])
|
|
{
|
|
nRetVal = static_cast<ReplyType>(i);
|
|
break;
|
|
}
|
|
}
|
|
return nRetVal;
|
|
}
|
|
|
|
const int PubSubMessage::VERSION_122 = 122;
|
|
const int PubSubMessage::VERSION_CURRENT = PubSubMessage::VERSION_122;
|
|
const char PubSubMessage::VERSION_TAG[] = "v";
|
|
|
|
PubSubMessage::PubSubMessage() : _pData(new JSONObject()), _bDispose(false)
|
|
{
|
|
setPermittedReplyType(ANY);
|
|
setAttempts(1);
|
|
// EyeTime curTime;
|
|
// setIssueDate(curTime);
|
|
_pData->setJSONValue(VERSION_TAG, VERSION_CURRENT);
|
|
}
|
|
|
|
PubSubMessage::PubSubMessage(const CloudGuardURN &src, const CloudGuardURN &dest,
|
|
Action act /*= NONE*/, const EyeTime &issueDate /*= Time()*/) : _pData(new JSONObject()), _bDispose(false)
|
|
{
|
|
setPermittedReplyType(ReplyType::ANY);
|
|
setSource(src);
|
|
setDestination(dest);
|
|
setAttempts(1);
|
|
// setIssueDate(issueDate);
|
|
|
|
if (act != NONE)
|
|
setAction(act);
|
|
_pData->setJSONValue(VERSION_TAG, VERSION_CURRENT);
|
|
}
|
|
PubSubMessage::PubSubMessage(const std::string &msg) : _pData(new JSONObject(msg)), _bDispose(false)
|
|
{
|
|
_pData->setJSONValue(VERSION_TAG, VERSION_CURRENT);
|
|
}
|
|
|
|
PubSubMessage::PubSubMessage(const JSONObject &msg) : _pData(new JSONObject (msg)), _bDispose(false)
|
|
{
|
|
_pData->setJSONValue(VERSION_TAG, VERSION_CURRENT);
|
|
}
|
|
|
|
std::string PubSubMessage::toDebugString() const
|
|
{
|
|
std::stringstream retValStr;
|
|
|
|
retValStr << getMessageIdentifier();
|
|
retValStr << " action: " << getActionName(getAction());
|
|
retValStr << " source: " << getSource();
|
|
retValStr << " dest: " << getDestination();
|
|
retValStr << " issue: " << _pData->getJSONString("issueDate");
|
|
retValStr << " expires: " << _pData->getJSONString("expires");
|
|
retValStr << " attempts: " << getAttempts();
|
|
retValStr << " version: " << getVersion();
|
|
retValStr << " inReplyTo: " << getInReplyToMessageIdentifier();
|
|
if (_pData->get("processed") != NULL)
|
|
retValStr << " processed: " << _pData->getJSONString("processed");
|
|
|
|
return retValStr.str();
|
|
}
|
|
|
|
/**
|
|
* Messages are equivalent if they have the same source, destination, action and actionData values.
|
|
* @param other
|
|
* @return
|
|
*/
|
|
bool PubSubMessage::isEquivalent(const PubSubMessage &other) const
|
|
{
|
|
bool bRetVal = false;
|
|
if ( (getSource() == other.getSource()) &&
|
|
(getDestination() == other.getDestination()) &&
|
|
(getAction() == other.getAction()) )
|
|
{
|
|
// Check action data...
|
|
JSONObjectPtr actData = getActionData();
|
|
JSONObjectPtr otherActData = other.getActionData();
|
|
bRetVal = (actData == otherActData);
|
|
}
|
|
return bRetVal;
|
|
}
|
|
|
|
void PubSubMessage::setSource(const CloudGuardURN &urn)
|
|
{
|
|
_pData->setJSONValue("source", urn.getUrn().c_str());
|
|
}
|
|
|
|
CloudGuardURN PubSubMessage::getSource() const
|
|
{
|
|
return _pData->getJSONString("source");
|
|
}
|
|
|
|
void PubSubMessage::setDestination(const CloudGuardURN &urn)
|
|
{
|
|
_pData->setJSONValue("destination", urn.getUrn().c_str());
|
|
}
|
|
|
|
CloudGuardURN PubSubMessage::getDestination() const
|
|
{
|
|
return _pData->getJSONString("destination");
|
|
}
|
|
|
|
/**
|
|
* When publishing a new message this is NOT set by calling client, but rather the server. It will
|
|
* be returned (set) when message is published.
|
|
* @param id
|
|
*/
|
|
void PubSubMessage::setMessageIdentifier(const std::string &id)
|
|
{
|
|
_pData->setJSONValue("msgId", id.c_str());
|
|
}
|
|
|
|
std::string PubSubMessage::getMessageIdentifier() const
|
|
{
|
|
return _pData->getJSONString("msgId");
|
|
}
|
|
|
|
/**
|
|
* For duplicate messages, it is useful to know how many times this message was attempted.
|
|
* Caller typically doesn't set this, the server does, ala messageIdentifier.
|
|
* @param attempts
|
|
*/
|
|
void PubSubMessage::setAttempts(int attempts)
|
|
{
|
|
_pData->setJSONValue("attempts", attempts);
|
|
}
|
|
|
|
int PubSubMessage::getAttempts() const
|
|
{
|
|
return _pData->getJSONInt("attempts");
|
|
}
|
|
|
|
//void PubSubMessage::setIssueDate(const EyeTime &date)
|
|
//{
|
|
// _pData->setJSONValue("issueDate", const_cast<EyeTime &>(date).GetISO8601TimeMs());
|
|
//}
|
|
|
|
sequencelogic::EyeTime PubSubMessage::getIssueDate() const
|
|
{
|
|
return EyeTime(_pData->getJSONString("issueDate").c_str());
|
|
}
|
|
|
|
int PubSubMessage::getVersion() const
|
|
{
|
|
return _pData->getJSONInt(VERSION_TAG, 0);
|
|
}
|
|
|
|
/**
|
|
* As a side effect, this will set the Expires TTL. If you want a specific TTL set AFTER calling this
|
|
* method.
|
|
* @param a
|
|
*/
|
|
void PubSubMessage::setAction(Action a)
|
|
{
|
|
_pData->setJSONValue("action", getActionName(a).c_str());
|
|
for (int i=0; i < NUM_ActionResponseTTL; ++i)
|
|
{
|
|
if (ActionResponseTTL[i]._action == a)
|
|
{
|
|
EyeTime curTime;
|
|
curTime.SetTimeMs(curTime.GetTimeMs() + ActionResponseTTL[i]._nTTL);
|
|
setExpires(curTime);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PubSubMessage::Action PubSubMessage::getAction() const
|
|
{
|
|
return getActionFromName(_pData->getJSONString("action", "PING"));
|
|
}
|
|
|
|
void PubSubMessage::setActionData(const JSONObject &data)
|
|
{
|
|
_pData->setJSONValue("actionData", data);
|
|
}
|
|
|
|
JSONObjectPtr PubSubMessage::getActionData() const
|
|
{
|
|
return JSONObjectPtr(new JSONObject(_pData->getJSONObject("actionData")));
|
|
}
|
|
|
|
bool PubSubMessage::isManualUserReplyRequired() const
|
|
{
|
|
bool bRetVal = false;
|
|
Action act = getAction();
|
|
for (int i = 0; i < NUM_ActionResponseTTL; ++i)
|
|
{
|
|
if (ActionResponseTTL[i]._action == act)
|
|
{
|
|
bRetVal = ActionResponseTTL[i]._bManualReply;
|
|
break;
|
|
}
|
|
}
|
|
return bRetVal;
|
|
}
|
|
|
|
void PubSubMessage::setPermittedReplyType(ReplyType rt)
|
|
{
|
|
_pData->setJSONValue("replyWith", getReplyTypeName(rt).c_str());
|
|
}
|
|
|
|
PubSubMessage::ReplyType PubSubMessage::getPermittedReplyType() const
|
|
{
|
|
return getReplyFromName(_pData->getJSONString("replyWith"));
|
|
}
|
|
|
|
void PubSubMessage::setActualReplyType(ReplyType rt)
|
|
{
|
|
_pData->setJSONValue("replyActual", getReplyTypeName(rt).c_str());
|
|
}
|
|
|
|
PubSubMessage::ReplyType PubSubMessage::getActualReplyType() const
|
|
{
|
|
return getReplyFromName(_pData->getJSONString("replyActual"));
|
|
}
|
|
|
|
void PubSubMessage::setInReplyToMessageIdentifier(const std::string &id)
|
|
{
|
|
_pData->setJSONValue("inReplyTo", id.c_str());
|
|
}
|
|
|
|
std::string PubSubMessage::getInReplyToMessageIdentifier() const
|
|
{
|
|
return _pData->getJSONString("inReplyTo");
|
|
}
|
|
|
|
PubSubMessage::Action PubSubMessage::getReplyToAction() const
|
|
{
|
|
Action a = getAction();
|
|
for (int i = 0; i < NUM_ActionResponseTTL; ++i)
|
|
{
|
|
if (ActionResponseTTL[i]._action == a)
|
|
return ActionResponseTTL[i]._reply;
|
|
}
|
|
return NONE;
|
|
}
|
|
|
|
void PubSubMessage::setExpires(const EyeTime &date)
|
|
{
|
|
_pData->setJSONValue("expires", const_cast<EyeTime &>(date).GetISO8601TimeMs());
|
|
}
|
|
|
|
sequencelogic::EyeTime PubSubMessage::getExpires() const
|
|
{
|
|
return EyeTime(_pData->getJSONString("expires").c_str());
|
|
}
|
|
|
|
void PubSubMessage::setResponseData(const JSONObject &data)
|
|
{
|
|
_pData->setJSONValue("responseData", data);
|
|
}
|
|
|
|
/**
|
|
* This is only valid if NOT replyType P2P and inReplyToMessageIdentifier is set, e.g. this is an inline
|
|
* response. Note the contents can be very large, e.g. a Base64 encoded .eye file, etc.
|
|
* @return
|
|
* @throws JSONException
|
|
*/
|
|
JSONObjectPtr PubSubMessage::getResponseData() const
|
|
{
|
|
return JSONObjectPtr(new JSONObject(_pData->getJSONObject("responseData")));
|
|
}
|
|
|
|
/**
|
|
* Check if this message is fully configured.
|
|
* @return
|
|
*/
|
|
bool PubSubMessage::isValid(std::string *pOptError /*= NULL*/) const
|
|
{
|
|
bool bRetVal = true;
|
|
|
|
if (pOptError != NULL)
|
|
*pOptError = "";
|
|
|
|
// Hmmm... Check if we have the stuff?
|
|
bRetVal = ((getSource().getType() != CloudGuardURN::URN_TYPE::INVALID) &&
|
|
(getDestination().getType() != CloudGuardURN::URN_TYPE::INVALID) &&
|
|
(getAction() != NONE) &&
|
|
(_pData->getJSONString("expires") != "") );
|
|
|
|
bool bMarkedInvalid = _pData->getJSONBool("invalid");
|
|
if (bRetVal && bMarkedInvalid)
|
|
{
|
|
if (pOptError != NULL)
|
|
*pOptError = "Message is marked invalid.\n";
|
|
bRetVal = false;
|
|
}
|
|
|
|
if (bRetVal && (getMessageIdentifier() == ""))
|
|
{
|
|
if (pOptError != NULL)
|
|
*pOptError = "Message identifier is bad.\n";
|
|
bRetVal = false;
|
|
}
|
|
if (!bRetVal && (pOptError != NULL))
|
|
{
|
|
*pOptError += "Invalid message: ";
|
|
*pOptError += toDebugString();
|
|
}
|
|
return bRetVal;
|
|
}
|
|
|
|
void PubSubMessage::invalidate()
|
|
{
|
|
_pData->setJSONValue("invalid", true);
|
|
}
|
|
|
|
bool PubSubMessage::isExpired() const
|
|
{
|
|
EyeTime curTime;
|
|
|
|
return (getExpires().Compare(&curTime) < 0);
|
|
}
|
|
|
|
void PubSubMessage::setProcessed(const EyeTime &date)
|
|
{
|
|
if (const_cast<EyeTime &>(date).GetTimeMs() == 0)
|
|
_pData->erase("processed");
|
|
else
|
|
_pData->setJSONValue("processed", const_cast<EyeTime &>(date).GetISO8601TimeMs());
|
|
}
|
|
|
|
bool PubSubMessage::isProcessed() const
|
|
{
|
|
return ((_pData->get("processed") != NULL) && (_pData->getJSONString("processed") != ""));
|
|
}
|
|
|
|
std::string PubSubMessage::toString() const
|
|
{
|
|
return _pData->toString();
|
|
}
|
|
|
|
const JSONObject &PubSubMessage::toJSONObj() const
|
|
{
|
|
return *_pData;
|
|
}
|
|
|
|
CloudGuardURN::URN_TYPE PubSubMessage::getPublishType() const
|
|
{
|
|
Action act = getAction();
|
|
for (int i = 0; i < NUM_ActionResponseTTL; ++i)
|
|
{
|
|
if (ActionResponseTTL[i]._action == act)
|
|
return ActionResponseTTL[i]._nUrnType;
|
|
}
|
|
return CloudGuardURN::URN_TYPE::INVALID;
|
|
}
|
|
|
|
bool PubSubMessage::isEmpty() const
|
|
{
|
|
return _pData->empty();
|
|
}
|
|
|
|
namespace sequencelogic
|
|
{
|
|
std::ostream& operator<<(std::ostream &out, const PubSubMessage &msg)
|
|
{
|
|
out << msg.toString();
|
|
return out;
|
|
}
|
|
}
|