Renamed GxsMails to GxsTrans

GxsTrans is a generic transport service, use more generic name trans
 instead of mail to avoid confusion
Renamed size(...) to seria_size(...) for costistence whit the codebase
Moved GxsTrans and related things to gxstrans directory
Removed outdated and now uncompatible gxsmail test service
Avoid expose internal items in public interface methods
This commit is contained in:
Gioacchino Mazzurco 2017-03-02 02:37:53 +01:00
parent 08161db43b
commit da459c884e
14 changed files with 427 additions and 500 deletions

View file

@ -0,0 +1,782 @@
/*
* GXS Mailing Service
* Copyright (C) 2016-2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gxstrans/p3gxstrans.h"
#include "util/stacktrace.h"
typedef unsigned int uint;
p3GxsTrans::~p3GxsTrans()
{
p3Config::saveConfiguration();
{
RS_STACK_MUTEX(mIngoingMutex);
for ( auto& kv : mIngoingQueue ) delete kv.second;
}
}
bool p3GxsTrans::sendMail( RsGxsTransId& mailId,
GxsTransSubServices service,
const RsGxsId& own_gxsid, const RsGxsId& recipient,
const uint8_t* data, uint32_t size,
RsGxsTransEncryptionMode cm )
{
std::cout << "p3GxsTrans::sendEmail(...)" << std::endl;
if(!mIdService.isOwnId(own_gxsid))
{
std::cerr << "p3GxsTrans::sendEmail(...) isOwnId(own_gxsid) false!"
<< std::endl;
return false;
}
if(recipient.isNull())
{
std::cerr << "p3GxsTrans::sendEmail(...) got invalid recipient"
<< std::endl;
print_stacktrace();
return false;
}
OutgoingRecord pr( recipient, service, data, size );
pr.mailItem.meta.mAuthorId = own_gxsid;
pr.mailItem.cryptoType = cm;
pr.mailItem.mailId = RSRandom::random_u64();
{
RS_STACK_MUTEX(mOutgoingMutex);
mOutgoingQueue.insert(prMap::value_type(pr.mailItem.mailId, pr));
}
mailId = pr.mailItem.mailId;
return true;
}
bool p3GxsTrans::querySendStatus(RsGxsTransId mailId, GxsTransSendStatus& st)
{
RS_STACK_MUTEX(mOutgoingMutex);
auto it = mOutgoingQueue.find(mailId);
if( it != mOutgoingQueue.end() )
{
st = it->second.status;
return true;
}
return false;
}
void p3GxsTrans::registerGxsTransClient(
GxsTransSubServices serviceType, GxsTransClient* service)
{
RS_STACK_MUTEX(mServClientsMutex);
mServClients[serviceType] = service;
}
void p3GxsTrans::handleResponse(uint32_t token, uint32_t req_type)
{
std::cout << "p3GxsTrans::handleResponse(" << token << ", " << req_type
<< ")" << std::endl;
switch (req_type)
{
case GROUPS_LIST:
{
std::vector<RsGxsGrpItem*> groups;
getGroupData(token, groups);
for( auto grp : groups )
{
/* For each group check if it is better candidate then
* preferredGroupId, if it is supplant it and subscribe if it is not
* subscribed yet.
* Otherwise if it has recent messages subscribe.
* If the group was already subscribed has no recent messages
* unsubscribe.
*/
const RsGroupMetaData& meta = grp->meta;
bool subscribed = IS_GROUP_SUBSCRIBED(meta.mSubscribeFlags);
bool old = olderThen( meta.mLastPost,
UNUSED_GROUP_UNSUBSCRIBE_INTERVAL );
bool supersede = supersedePreferredGroup(meta.mGroupId);
uint32_t token;
bool shoudlSubscribe = !subscribed && ( !old || supersede );
bool shoudlUnSubscribe = subscribed && old
&& meta.mGroupId != mPreferredGroupId;
if(shoudlSubscribe)
subscribeToGroup(token, meta.mGroupId, true);
else if(shoudlUnSubscribe)
subscribeToGroup(token, meta.mGroupId, false);
#ifdef GXS_MAIL_GRP_DEBUG
char buff[30];
struct tm* timeinfo;
timeinfo = localtime(&meta.mLastPost);
strftime(buff, sizeof(buff), "%Y %b %d %H:%M", timeinfo);
std::cout << "p3GxsTrans::handleResponse(...) GROUPS_LIST "
<< "meta.mGroupId: " << meta.mGroupId
<< " meta.mLastPost: " << buff
<< " subscribed: " << subscribed
<< " old: " << old
<< " shoudlSubscribe: " << shoudlSubscribe
<< " shoudlUnSubscribe: " << shoudlUnSubscribe
<< std::endl;
#endif // GXS_MAIL_GRP_DEBUG
delete grp;
}
if(mPreferredGroupId.isNull())
{
/* This is true only at first run when we haven't received mail
* distribuition groups from friends
* TODO: We should check if we have some connected friend too, to
* avoid to create yet another never used mail distribution group.
*/
std::cerr << "p3GxsTrans::handleResponse(...) preferredGroupId.isNu"
<< "ll() let's create a new group." << std::endl;
uint32_t token;
publishGroup(token, new RsGxsTransGroupItem());
queueRequest(token, GROUP_CREATE);
}
break;
}
case GROUP_CREATE:
{
std::cerr << "p3GxsTrans::handleResponse(...) GROUP_CREATE" << std::endl;
RsGxsGroupId grpId;
acknowledgeTokenGrp(token, grpId);
supersedePreferredGroup(grpId);
break;
}
case MAILS_UPDATE:
{
std::cout << "p3GxsTrans::handleResponse(...) MAILS_UPDATE" << std::endl;
typedef std::map<RsGxsGroupId, std::vector<RsGxsMsgItem*> > GxsMsgDataMap;
GxsMsgDataMap gpMsgMap;
getMsgData(token, gpMsgMap);
for ( GxsMsgDataMap::iterator gIt = gpMsgMap.begin();
gIt != gpMsgMap.end(); ++gIt )
{
typedef std::vector<RsGxsMsgItem*> vT;
vT& mv(gIt->second);
for( vT::const_iterator mIt = mv.begin(); mIt != mv.end(); ++mIt )
{
RsGxsMsgItem* gIt = *mIt;
switch(static_cast<GxsTransItemsSubtypes>(gIt->PacketSubType()))
{
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL:
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT:
{
RsGxsTransBaseItem* mb =
dynamic_cast<RsGxsTransBaseItem*>(*mIt);
if(mb)
{
RS_STACK_MUTEX(mIngoingMutex);
mIngoingQueue.insert(inMap::value_type(mb->mailId, mb));
}
else
std::cerr << "p3GxsTrans::handleResponse(...) "
<< "GXS_MAIL_SUBTYPE_MAIL cast error, "
<< "something really wrong is happening"
<< std::endl;
break;
}
default:
std::cerr << "p3GxsTrans::handleResponse(...) MAILS_UPDATE "
<< "Unknown mail subtype : "
<< static_cast<uint>(gIt->PacketSubType())
<< std::endl;
delete gIt;
break;
}
}
}
break;
}
default:
std::cerr << "p3GxsTrans::handleResponse(...) Unknown req_type: "
<< req_type << std::endl;
break;
}
}
void p3GxsTrans::service_tick()
{
GxsTokenQueue::checkRequests();
{
RS_STACK_MUTEX(mOutgoingMutex);
for ( auto it = mOutgoingQueue.begin(); it != mOutgoingQueue.end(); )
{
OutgoingRecord& pr(it->second);
GxsTransSendStatus oldStatus = pr.status;
processOutgoingRecord(pr);
if (oldStatus != pr.status) notifyClientService(pr);
if( pr.status >= GxsTransSendStatus::RECEIPT_RECEIVED )
it = mOutgoingQueue.erase(it);
else ++it;
}
}
{
RS_STACK_MUTEX(mIngoingMutex);
for( auto it = mIngoingQueue.begin(); it != mIngoingQueue.end(); )
{
switch(static_cast<GxsTransItemsSubtypes>(
it->second->PacketSubType()))
{
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL:
{
RsGxsTransMailItem* msg = dynamic_cast<RsGxsTransMailItem*>(it->second);
if(!msg)
{
std::cerr << "p3GxsTrans::service_tick() (EE) "
<< "GXS_MAIL_SUBTYPE_MAIL dynamic_cast failed, "
<< "something really wrong is happening!"
<< std::endl;
}
else
{
std::cout << "p3GxsTrans::service_tick() "
<< "GXS_MAIL_SUBTYPE_MAIL handling: "
<< msg->meta.mMsgId
<< " with cryptoType: "
<< static_cast<uint32_t>(msg->cryptoType)
<< " recipientHint: " << msg->recipientHint
<< " mailId: "<< msg->mailId
<< " payload.size(): " << msg->payload.size()
<< std::endl;
handleEcryptedMail(msg);
}
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT:
{
RsGxsTransPresignedReceipt* rcpt =
dynamic_cast<RsGxsTransPresignedReceipt*>(it->second);
if(!rcpt)
{
std::cerr << "p3GxsTrans::service_tick() (EE) "
<< "GXS_MAIL_SUBTYPE_RECEIPT dynamic_cast failed,"
<< " something really wrong is happening!"
<< std::endl;
}
else if(mIdService.isOwnId(rcpt->meta.mAuthorId))
{
/* It is a receipt for a mail sent by this node live it in
* ingoingQueue so processOutgoingRecord(...) will take care
* of it at next tick */
++it;
continue;
}
else
{
/* TODO: It is a receipt for a message sent by someone else
* we can delete original mail from our GXS DB without
* waiting for GXS_STORAGE_PERIOD */
}
break;
}
default:
std::cerr << "p3GxsTrans::service_tick() (EE) got something "
<< "really unknown into ingoingQueue!!" << std::endl;
break;
}
delete it->second; it = mIngoingQueue.erase(it);
}
}
}
RsGenExchange::ServiceCreate_Return p3GxsTrans::service_CreateGroup(
RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& /*keySet*/ )
{
std::cout << "p3GxsTrans::service_CreateGroup(...) "
<< grpItem->meta.mGroupId << std::endl;
return SERVICE_CREATE_SUCCESS;
}
void p3GxsTrans::notifyChanges(std::vector<RsGxsNotify*>& changes)
{
std::cout << "p3GxsTrans::notifyChanges(...)" << std::endl;
for( std::vector<RsGxsNotify*>::const_iterator it = changes.begin();
it != changes.end(); ++it )
{
RsGxsGroupChange* grpChange = dynamic_cast<RsGxsGroupChange *>(*it);
RsGxsMsgChange* msgChange = dynamic_cast<RsGxsMsgChange *>(*it);
if (grpChange)
{
std::cout << "p3GxsTrans::notifyChanges(...) grpChange" << std::endl;
requestGroupsData(&(grpChange->mGrpIdList));
}
else if(msgChange)
{
std::cout << "p3GxsTrans::notifyChanges(...) msgChange" << std::endl;
uint32_t token;
RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA;
getTokenService()->requestMsgInfo( token, 0xcaca,
opts, msgChange->msgChangeMap );
GxsTokenQueue::queueRequest(token, MAILS_UPDATE);
for( GxsMsgReq::const_iterator it = msgChange->msgChangeMap.begin();
it != msgChange->msgChangeMap.end(); ++it )
{
const RsGxsGroupId& grpId = it->first;
const std::vector<RsGxsMessageId>& msgsIds = it->second;
typedef std::vector<RsGxsMessageId>::const_iterator itT;
for(itT vit = msgsIds.begin(); vit != msgsIds.end(); ++vit)
{
const RsGxsMessageId& msgId = *vit;
std::cout << "p3GxsTrans::notifyChanges(...) got "
<< "notification for message " << msgId
<< " in group " << grpId << std::endl;
}
}
}
}
}
uint32_t p3GxsTrans::AuthenPolicy()
{
uint32_t policy = 0;
uint32_t flag = 0;
// This ensure propagated message have valid author signature
flag = GXS_SERV::MSG_AUTHEN_ROOT_AUTHOR_SIGN |
GXS_SERV::MSG_AUTHEN_CHILD_AUTHOR_SIGN;
RsGenExchange::setAuthenPolicyFlag( flag, policy,
RsGenExchange::PUBLIC_GRP_BITS );
/* This ensure that in for restricted and private groups only authorized
* authors get the messages. Really not used ATM but don't hurts. */
flag |= GXS_SERV::MSG_AUTHEN_ROOT_PUBLISH_SIGN |
GXS_SERV::MSG_AUTHEN_CHILD_PUBLISH_SIGN;
RsGenExchange::setAuthenPolicyFlag( flag, policy,
RsGenExchange::RESTRICTED_GRP_BITS );
RsGenExchange::setAuthenPolicyFlag( flag, policy,
RsGenExchange::PRIVATE_GRP_BITS );
/* This seems never used RetroShare wide but we should investigate it
* more before considering this conclusive */
flag = 0;
RsGenExchange::setAuthenPolicyFlag( flag, policy,
RsGenExchange::GRP_OPTION_BITS );
return policy;
}
bool p3GxsTrans::requestGroupsData(const std::list<RsGxsGroupId>* groupIds)
{
// std::cout << "p3GxsTrans::requestGroupsList()" << std::endl;
uint32_t token;
RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
if(!groupIds) getTokenService()->requestGroupInfo(token, 0xcaca, opts);
else getTokenService()->requestGroupInfo(token, 0xcaca, opts, *groupIds);
GxsTokenQueue::queueRequest(token, GROUPS_LIST);
return true;
}
bool p3GxsTrans::handleEcryptedMail(const RsGxsTransMailItem* mail)
{
std::cout << "p3GxsTrans::handleEcryptedMail(...)" << std::endl;
std::set<RsGxsId> decryptIds;
std::list<RsGxsId> ownIds;
mIdService.getOwnIds(ownIds);
for(auto it = ownIds.begin(); it != ownIds.end(); ++it)
if(mail->maybeRecipient(*it)) decryptIds.insert(*it);
// Hint match none of our own ids
if(decryptIds.empty())
{
std::cout << "p3GxsTrans::handleEcryptedMail(...) hint doesn't match" << std::endl;
return true;
}
switch (mail->cryptoType)
{
case RsGxsTransEncryptionMode::CLEAR_TEXT:
{
uint16_t csri = 0;
uint32_t off = 0;
getRawUInt16(&mail->payload[0], mail->payload.size(), &off, &csri);
std::cerr << "service: " << csri << " got CLEAR_TEXT mail!"
<< std::endl;
/* As we cannot verify recipient without encryption, just pass the hint
* as recipient */
return dispatchDecryptedMail( mail->meta.mAuthorId, mail->recipientHint,
&mail->payload[0], mail->payload.size() );
}
case RsGxsTransEncryptionMode::RSA:
{
bool ok = true;
for( std::set<RsGxsId>::const_iterator it = decryptIds.begin();
it != decryptIds.end(); ++it )
{
const RsGxsId& decryptId(*it);
uint8_t* decrypted_data = NULL;
uint32_t decrypted_data_size = 0;
uint32_t decryption_error;
if( mIdService.decryptData( &mail->payload[0],
mail->payload.size(), decrypted_data,
decrypted_data_size, decryptId,
decryption_error ) )
ok = ok && dispatchDecryptedMail( mail->meta.mAuthorId,
decryptId, decrypted_data,
decrypted_data_size );
free(decrypted_data);
}
return ok;
}
default:
std::cout << "Unknown encryption type:"
<< static_cast<uint32_t>(mail->cryptoType) << std::endl;
return false;
}
}
bool p3GxsTrans::dispatchDecryptedMail( const RsGxsId& authorId,
const RsGxsId& decryptId,
const uint8_t* decrypted_data,
uint32_t decrypted_data_size )
{
std::cout << "p3GxsTrans::dispatchDecryptedMail(, , " << decrypted_data_size
<< ")" << std::endl;
uint16_t csri = 0;
uint32_t offset = 0;
if(!getRawUInt16( decrypted_data, decrypted_data_size, &offset, &csri))
{
std::cerr << "p3GxsTrans::dispatchDecryptedMail(...) (EE) fatal error "
<< "deserializing service type, something really wrong is "
<< "happening!" << std::endl;
return false;
}
GxsTransSubServices rsrvc = static_cast<GxsTransSubServices>(csri);
RsNxsTransPresignedReceipt* receipt = new RsNxsTransPresignedReceipt();
uint32_t rcptsize = decrypted_data_size;
if(!receipt->deserialize(decrypted_data, rcptsize, offset))
{
std::cerr << "p3GxsTrans::dispatchDecryptedMail(...) (EE) fatal error "
<< "deserializing presigned return receipt , something really"
<< " wrong is happening!" << std::endl;
delete receipt;
return false;
}
std::cout << "p3GxsTrans::dispatchDecryptedMail(...) dispatching receipt "
<< "with: msgId: " << receipt->msgId << std::endl;
std::vector<RsNxsMsg*> rcct; rcct.push_back(receipt);
RsGenExchange::notifyNewMessages(rcct);
GxsTransClient* recipientService = NULL;
{
RS_STACK_MUTEX(mServClientsMutex);
recipientService = mServClients[rsrvc];
}
if(recipientService)
return recipientService->receiveGxsTransMail(
authorId, decryptId, &decrypted_data[offset],
decrypted_data_size-offset );
else
{
std::cerr << "p3GxsTrans::dispatchReceivedMail(...) "
<< "got message for unknown service: "
<< csri << std::endl;
return false;
}
}
void p3GxsTrans::processOutgoingRecord(OutgoingRecord& pr)
{
//std::cout << "p3GxsTrans::processRecord(...)" << std::endl;
switch (pr.status)
{
case GxsTransSendStatus::PENDING_PROCESSING:
{
pr.mailItem.saltRecipientHint(pr.recipient);
pr.mailItem.saltRecipientHint(RsGxsId::random());
}
case GxsTransSendStatus::PENDING_PREFERRED_GROUP:
{
if(mPreferredGroupId.isNull())
{
requestGroupsData();
pr.status = GxsTransSendStatus::PENDING_PREFERRED_GROUP;
break;
}
pr.mailItem.meta.mGroupId = mPreferredGroupId;
}
case GxsTransSendStatus::PENDING_RECEIPT_CREATE:
{
RsGxsTransPresignedReceipt grcpt;
grcpt.meta = pr.mailItem.meta;
grcpt.meta.mPublishTs = time(NULL);
grcpt.mailId = pr.mailItem.mailId;
uint32_t groff = 0, grsz = grcpt.serial_size();
std::vector<uint8_t> grsrz;
grsrz.resize(grsz);
grcpt.serialize(&grsrz[0], grsz, groff);
pr.presignedReceipt.grpId = mPreferredGroupId;
pr.presignedReceipt.metaData = new RsGxsMsgMetaData();
*pr.presignedReceipt.metaData = grcpt.meta;
pr.presignedReceipt.msg.setBinData(&grsrz[0], grsz);
}
case GxsTransSendStatus::PENDING_RECEIPT_SIGNATURE:
{
switch (RsGenExchange::createMessage(&pr.presignedReceipt))
{
case CREATE_SUCCESS: break;
case CREATE_FAIL_TRY_LATER:
pr.status = GxsTransSendStatus::PENDING_RECEIPT_CREATE;
return;
default:
pr.status = GxsTransSendStatus::FAILED_RECEIPT_SIGNATURE;
goto processingFailed;
}
uint32_t metaSize = pr.presignedReceipt.metaData->serial_size();
std::vector<uint8_t> srx; srx.resize(metaSize);
pr.presignedReceipt.metaData->serialise(&srx[0], &metaSize);
pr.presignedReceipt.meta.setBinData(&srx[0], metaSize);
}
case GxsTransSendStatus::PENDING_PAYLOAD_CREATE:
{
uint16_t serv = static_cast<uint16_t>(pr.clientService);
uint32_t rcptsize = pr.presignedReceipt.serial_size();
uint32_t datasize = pr.mailData.size();
pr.mailItem.payload.resize(2 + rcptsize + datasize);
uint32_t offset = 0;
setRawUInt16(&pr.mailItem.payload[0], 2, &offset, serv);
pr.presignedReceipt.serialise( &pr.mailItem.payload[offset],
rcptsize );
offset += rcptsize;
memcpy(&pr.mailItem.payload[offset], &pr.mailData[0], datasize);
}
case GxsTransSendStatus::PENDING_PAYLOAD_ENCRYPT:
{
switch (pr.mailItem.cryptoType)
{
case RsGxsTransEncryptionMode::CLEAR_TEXT:
{
std::cerr << "p3GxsTrans::sendMail(...) you are sending a mail "
<< "without encryption, everyone can read it!"
<< std::endl;
break;
}
case RsGxsTransEncryptionMode::RSA:
{
uint8_t* encryptedData = NULL;
uint32_t encryptedSize = 0;
uint32_t encryptError = 0;
if( mIdService.encryptData( &pr.mailItem.payload[0],
pr.mailItem.payload.size(),
encryptedData, encryptedSize,
pr.recipient, encryptError, true ) )
{
pr.mailItem.payload.resize(encryptedSize);
memcpy( &pr.mailItem.payload[0], encryptedData,
encryptedSize );
free(encryptedData);
break;
}
else
{
std::cerr << "p3GxsTrans::sendMail(...) RSA encryption failed! "
<< "error_status: " << encryptError << std::endl;
pr.status = GxsTransSendStatus::FAILED_ENCRYPTION;
goto processingFailed;
}
}
case RsGxsTransEncryptionMode::UNDEFINED_ENCRYPTION:
default:
std::cerr << "p3GxsTrans::sendMail(...) attempt to send mail with "
<< "wrong EncryptionMode: "
<< static_cast<uint>(pr.mailItem.cryptoType)
<< " dropping mail!" << std::endl;
pr.status = GxsTransSendStatus::FAILED_ENCRYPTION;
goto processingFailed;
}
}
case GxsTransSendStatus::PENDING_PUBLISH:
{
std::cout << "p3GxsTrans::sendEmail(...) sending mail to: "
<< pr.recipient
<< " with cryptoType: "
<< static_cast<uint>(pr.mailItem.cryptoType)
<< " recipientHint: " << pr.mailItem.recipientHint
<< " receiptId: " << pr.mailItem.mailId
<< " payload size: " << pr.mailItem.payload.size()
<< std::endl;
uint32_t token;
publishMsg(token, new RsGxsTransMailItem(pr.mailItem));
pr.status = GxsTransSendStatus::PENDING_RECEIPT_RECEIVE;
break;
}
//case GxsTransSendStatus::PENDING_TRANSFER:
case GxsTransSendStatus::PENDING_RECEIPT_RECEIVE:
{
RS_STACK_MUTEX(mIngoingMutex);
auto range = mIngoingQueue.equal_range(pr.mailItem.mailId);
for( auto it = range.first; it != range.second; ++it)
{
RsGxsTransPresignedReceipt* rt =
dynamic_cast<RsGxsTransPresignedReceipt*>(it->second);
if(rt && mIdService.isOwnId(rt->meta.mAuthorId))
{
mIngoingQueue.erase(it); delete rt;
pr.status = GxsTransSendStatus::RECEIPT_RECEIVED;
break;
}
}
// TODO: Resend message if older then treshold
break;
}
case GxsTransSendStatus::RECEIPT_RECEIVED:
break;
processingFailed:
case GxsTransSendStatus::FAILED_RECEIPT_SIGNATURE:
case GxsTransSendStatus::FAILED_ENCRYPTION:
default:
{
std::cout << "p3GxsTrans::processRecord(" << pr.mailItem.mailId
<< ") failed with: " << static_cast<uint>(pr.status)
<< std::endl;
break;
}
}
}
void p3GxsTrans::notifyClientService(const OutgoingRecord& pr)
{
RS_STACK_MUTEX(mServClientsMutex);
auto it = mServClients.find(pr.clientService);
if( it != mServClients.end())
{
GxsTransClient* serv(it->second);
if(serv)
{
serv->notifyGxsTransSendStatus(pr.mailItem.mailId, pr.status);
return;
}
}
std::cerr << "p3GxsTrans::processRecord(...) (EE) processed"
<< " mail for unkown service: "
<< static_cast<uint32_t>(pr.clientService)
<< " fatally failed with: "
<< static_cast<uint32_t>(pr.status) << std::endl;
print_stacktrace();
}
RsSerialiser* p3GxsTrans::setupSerialiser()
{
RsSerialiser* rss = new RsSerialiser;
rss->addSerialType(new RsGxsTransSerializer);
return rss;
}
bool p3GxsTrans::saveList(bool &cleanup, std::list<RsItem *>& saveList)
{
std::cout << "p3GxsTrans::saveList(...)" << saveList.size() << " "
<< mIngoingQueue.size() << " " << mOutgoingQueue.size()
<< std::endl;
mOutgoingMutex.lock();
mIngoingMutex.lock();
for ( auto& kv : mOutgoingQueue ) saveList.push_back(&kv.second);
for ( auto& kv : mIngoingQueue ) saveList.push_back(kv.second);
std::cout << "p3GxsTrans::saveList(...)" << saveList.size() << " "
<< mIngoingQueue.size() << " " << mOutgoingQueue.size()
<< std::endl;
cleanup = false;
return true;
}
void p3GxsTrans::saveDone()
{
mOutgoingMutex.unlock();
mIngoingMutex.unlock();
}
bool p3GxsTrans::loadList(std::list<RsItem *>&loadList)
{
std::cout << "p3GxsTrans::loadList(...) " << loadList.size() << " "
<< mIngoingQueue.size() << " " << mOutgoingQueue.size()
<< std::endl;
for(auto& v : loadList)
switch(static_cast<GxsTransItemsSubtypes>(v->PacketSubType()))
{
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL:
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT:
{
RsGxsTransBaseItem* mi = dynamic_cast<RsGxsTransBaseItem*>(v);
if(mi)
{
RS_STACK_MUTEX(mIngoingMutex);
mIngoingQueue.insert(inMap::value_type(mi->mailId, mi));
}
break;
}
case GxsTransItemsSubtypes::OUTGOING_RECORD_ITEM:
{
OutgoingRecord* ot = dynamic_cast<OutgoingRecord*>(v);
if(ot)
{
RS_STACK_MUTEX(mOutgoingMutex);
mOutgoingQueue.insert(
prMap::value_type(ot->mailItem.mailId, *ot));
}
delete v;
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_GROUP:
default:
std::cerr << "p3GxsTrans::loadList(...) (EE) got item with "
<< "unhandled type: "
<< static_cast<uint>(v->PacketSubType())
<< std::endl;
delete v;
break;
}
std::cout << "p3GxsTrans::loadList(...) " << loadList.size() << " "
<< mIngoingQueue.size() << " " << mOutgoingQueue.size()
<< std::endl;
return true;
}

View file

@ -0,0 +1,255 @@
#pragma once
/*
* GXS Mailing Service
* Copyright (C) 2016-2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <unordered_map>
#include <map>
#include "retroshare/rsgxsifacetypes.h" // For RsGxsId, RsGxsCircleId
#include "gxs/gxstokenqueue.h" // For GxsTokenQueue
#include "gxstrans/p3gxstransitems.h"
#include "services/p3idservice.h" // For p3IdService
#include "util/rsthreads.h"
struct p3GxsTrans;
/// Services who want to make use of p3GxsTrans should inherit this struct
struct GxsTransClient
{
/**
* This will be called by p3GxsTrans to dispatch mails to the subservice
* @param authorId message sender
* @param decryptId recipient id
* @param data buffer containing the decrypted data
* @param dataSize size of the buffer
* @return true if dispatching goes fine, false otherwise
*/
virtual bool receiveGxsTransMail( const RsGxsId& authorId,
const RsGxsId& recipientId,
const uint8_t* data, uint32_t dataSize
) = 0;
/**
* This will be called by p3GxsTrans to notify the subservice about the
* status of a sent email.
* @param originalMessage message for with the notification is made
* @param status the new status of the message
* @return true if notification goes fine, false otherwise (ignored ATM)
*/
virtual bool notifyGxsTransSendStatus( RsGxsTransId mailId,
GxsTransSendStatus status ) = 0;
};
/**
* @brief p3GxsTrans is a mail delivery service based on GXS.
* p3GxsTrans is capable of asynchronous mail delivery and acknowledgement.
* p3GxsTrans is meant to be capable of multiple encryption options,
* @see RsGxsTransEncryptionMode at moment messages are encrypted using RSA
* unless the user ask for them being sent in clear text ( this is not supposed
* to happen in non testing environment so warnings and stack traces are printed
* in the log if an attempt to send something in clear text is made ).
* p3GxsTrans try to hide metadata so the travelling message signed by the author
* but the recipient is not disclosed, instead to avoid everyone trying to
* decrypt every message a hint has been introduced, the hint is calculated in a
* way that one can easily prove that a message is not destined to someone, but
* cannot prove the message is destined to someone
* @see RsGxsTransMailItem::recipientHint for more details.
* p3GxsTrans expose a simple API to send and receive mails, the API also
* provide notification for the sending mail status @see sendMail(...),
* @see querySendStatus(...), @see registerGxsTransClient(...),
* @see GxsTransClient::receiveGxsTransMail(...),
* @see GxsTransClient::notifyGxsTransSendStatus(...).
*/
struct p3GxsTrans : RsGenExchange, GxsTokenQueue, p3Config
{
p3GxsTrans( RsGeneralDataService* gds, RsNetworkExchangeService* nes,
p3IdService& identities ) :
RsGenExchange( gds, nes, new RsGxsTransSerializer(),
RS_SERVICE_TYPE_GXS_TRANS, &identities,
AuthenPolicy(), GXS_STORAGE_PERIOD ),
GxsTokenQueue(this), mIdService(identities),
mServClientsMutex("p3GxsTrans client services map mutex"),
mOutgoingMutex("p3GxsTrans outgoing queue map mutex"),
mIngoingMutex("p3GxsTrans ingoing queue map mutex") {}
~p3GxsTrans();
/**
* Send an email to recipient, in the process author of the email is
* disclosed to the network (because the sent GXS item is signed), while
* recipient is not @see RsGxsTransMailItem::recipientHint for details on
* recipient protection.
* This method is part of the public interface of this service.
* @return true if the mail will be sent, false if not
*/
bool sendMail( RsGxsTransId& mailId,
GxsTransSubServices service,
const RsGxsId& own_gxsid, const RsGxsId& recipient,
const uint8_t* data, uint32_t size,
RsGxsTransEncryptionMode cm = RsGxsTransEncryptionMode::RSA
);
/**
* This method is part of the public interface of this service.
* @return false if mail is not found in outgoing queue, true otherwise
*/
bool querySendStatus( RsGxsTransId mailId, GxsTransSendStatus& st );
/**
* Register a client service to p3GxsTrans to receive mails via
* GxsTransClient::receiveGxsTransMail(...) callback
* This method is part of the public interface of this service.
*/
void registerGxsTransClient( GxsTransSubServices serviceType,
GxsTransClient* service );
/// @see RsGenExchange::getServiceInfo()
virtual RsServiceInfo getServiceInfo() { return RsServiceInfo( RS_SERVICE_TYPE_GXS_TRANS, "GXS Mails", 0, 1, 0, 1 ); }
private:
/** Time interval of inactivity before a distribution group is unsubscribed.
* Approximatively 3 months seems ok ATM. */
const static int32_t UNUSED_GROUP_UNSUBSCRIBE_INTERVAL = 0x76A700;
/**
* This should be as little as possible as the size of the database can grow
* very fast taking in account we are handling mails for the whole network.
* We do prefer to resend a not acknowledged yet mail after
* GXS_STORAGE_PERIOD has passed and keep it little.
* Tought it can't be too little as this may cause signed receipts to
* get lost thus causing resend and fastly grow perceived async latency, in
* case two sporadically connected users sends mails each other.
* While it is ok for signed acknowledged to stays in the DB for a
* full GXS_STORAGE_PERIOD, mails should be removed as soon as a valid
* signed acknowledged is received for each of them.
* Two weeks seems fair ATM.
*/
const static uint32_t GXS_STORAGE_PERIOD = 0x127500;
/// Define how the backend should handle authentication based on signatures
static uint32_t AuthenPolicy();
/// Types to mark queries in tokens queue
enum GxsReqResTypes
{
GROUPS_LIST = 1,
GROUP_CREATE = 2,
MAILS_UPDATE = 3
};
/// Store the id of the preferred GXS group to send emails
RsGxsGroupId mPreferredGroupId;
/// Used for items {de,en}cryption
p3IdService& mIdService;
/// Stores pointers to client services to notify them about new mails
std::map<GxsTransSubServices, GxsTransClient*> mServClients;
RsMutex mServClientsMutex;
/**
* @brief Keep track of outgoing mails.
* Records enter the queue when a mail is sent, and are removed when a
* receipt has been received or sending is considered definetly failed.
* Items are saved in config for consistence accross RetroShare shutdowns.
*/
typedef std::map<RsGxsTransId, OutgoingRecord> prMap;
prMap mOutgoingQueue;
RsMutex mOutgoingMutex;
void processOutgoingRecord(OutgoingRecord& r);
/**
* @brief Ingoing mail and receipt processing queue.
* At shutdown remaining items are saved in config and then deleted in
* destructor for consistence accross RetroShare instances.
* In order to avoid malicious messages ( non malicious collision has 1/2^64
* probablity ) to smash items in the queue thus causing previous incoming
* item to not being processed and memleaked multimap is used instead of map
* for incoming queue.
*/
typedef std::unordered_multimap<RsGxsTransId, RsGxsTransBaseItem*> inMap;
inMap mIngoingQueue;
RsMutex mIngoingMutex;
/// @see GxsTokenQueue::handleResponse(uint32_t token, uint32_t req_type)
virtual void handleResponse(uint32_t token, uint32_t req_type);
/// @see RsGenExchange::service_tick()
virtual void service_tick();
/// @see RsGenExchange::service_CreateGroup(...)
RsGenExchange::ServiceCreate_Return service_CreateGroup(
RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& );
/// @see RsGenExchange::notifyChanges(std::vector<RsGxsNotify *> &changes)
void notifyChanges(std::vector<RsGxsNotify *> &changes);
/// @see p3Config::setupSerialiser()
virtual RsSerialiser* setupSerialiser();
/// @see p3Config::saveList(bool &cleanup, std::list<RsItem *>&)
virtual bool saveList(bool &cleanup, std::list<RsItem *>&saveList);
/// @see p3Config::saveDone()
void saveDone();
/// @see p3Config::loadList(std::list<RsItem *>&)
virtual bool loadList(std::list<RsItem *>& loadList);
/// Request groups list to GXS backend. Async method.
bool requestGroupsData(const std::list<RsGxsGroupId>* groupIds = NULL);
/**
* Check if current preferredGroupId is the best against potentialGrId, if
* the passed one is better update it.
* Useful when GXS backend notifies groups changes, or when a reponse to an
* async grop request (@see GXS_REQUEST_TYPE_GROUP_*) is received.
* @return true if preferredGroupId has been supeseded by potentialGrId
* false otherwise.
*/
bool inline supersedePreferredGroup(const RsGxsGroupId& potentialGrId)
{
if(mPreferredGroupId < potentialGrId)
{
std::cerr << "supersedePreferredGroup(...) " << potentialGrId
<< " supersed " << mPreferredGroupId << std::endl;
mPreferredGroupId = potentialGrId;
return true;
}
return false;
}
/** @return true if has passed more then interval seconds between timeStamp
* and ref. @param ref by default now is taked as reference. */
bool static inline olderThen(time_t timeStamp, int32_t interval,
time_t ref = time(NULL))
{ return (timeStamp + interval) < ref; }
/// Decrypt email content and pass it to dispatchDecryptedMail(...)
bool handleEcryptedMail(const RsGxsTransMailItem* mail);
/// Dispatch the message to the recipient service
bool dispatchDecryptedMail( const RsGxsId& authorId,
const RsGxsId& decryptId,
const uint8_t* decrypted_data,
uint32_t decrypted_data_size );
void notifyClientService(const OutgoingRecord& pr);
};

View file

@ -0,0 +1,212 @@
/*
* GXS Mailing Service
* Copyright (C) 2016-2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gxstrans/p3gxstransitems.h"
const RsGxsId RsGxsTransMailItem::allRecipientsHint("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
bool RsGxsTransBaseItem::serialize(uint8_t* data, uint32_t size,
uint32_t& offset) const
{
bool ok = setRsItemHeader(data+offset, size, PacketId(), size);
ok = ok && (offset += 8); // Take header in account
ok = ok && setRawUInt64(data, size, &offset, mailId);
return ok;
}
bool RsGxsTransBaseItem::deserialize( const uint8_t* data, uint32_t& size,
uint32_t& offset)
{
void* hdrPtr = const_cast<uint8_t*>(data+offset);
uint32_t rssize = getRsItemSize(hdrPtr);
uint32_t roffset = offset + 8; // Take header in account
void* dataPtr = const_cast<uint8_t*>(data);
bool ok = rssize <= size;
ok = ok && getRawUInt64(dataPtr, rssize, &roffset, &mailId);
if(ok) { size = rssize; offset = roffset; }
else size = 0;
return ok;
}
std::ostream& RsGxsTransBaseItem::print(std::ostream &out, uint16_t)
{ return out << __PRETTY_FUNCTION__ << " mailId: " << mailId; }
bool RsGxsTransSerializer::serialise(RsItem* item, void* data, uint32_t* size)
{
uint32_t itemSize = RsGxsTransSerializer::size(item);
if(*size < itemSize)
{
std::cout << __PRETTY_FUNCTION__ << " failed due to wrong size: "
<< size << " < " << itemSize << std::endl;
return false;
}
uint8_t* dataPtr = reinterpret_cast<uint8_t*>(data);
bool ok = false;
switch(static_cast<GxsTransItemsSubtypes>(item->PacketSubType()))
{
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL:
{
uint32_t offset = 0;
RsGxsTransMailItem* i = dynamic_cast<RsGxsTransMailItem*>(item);
ok = i && i->serialize(dataPtr, itemSize, offset);
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT:
{
RsGxsTransPresignedReceipt* i =
dynamic_cast<RsGxsTransPresignedReceipt*>(item);
uint32_t offset = 0;
ok = i && i->serialize(dataPtr, itemSize, offset);
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_GROUP:
ok = setRsItemHeader(data, itemSize, item->PacketId(), itemSize);
break;
case GxsTransItemsSubtypes::OUTGOING_RECORD_ITEM:
{
uint32_t offset = 0;
OutgoingRecord* i = dynamic_cast<OutgoingRecord*>(item);
ok = i && i->serialize(dataPtr, itemSize, offset);
break;
}
default: ok = false; break;
}
if(ok)
{
*size = itemSize;
return true;
}
std::cout << __PRETTY_FUNCTION__ << " failed!" << std::endl;
return false;
}
OutgoingRecord::OutgoingRecord( RsGxsId rec, GxsTransSubServices cs,
const uint8_t* data, uint32_t size ) :
RsItem( RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_GXS_TRANS,
static_cast<uint8_t>(GxsTransItemsSubtypes::OUTGOING_RECORD_ITEM) ),
status(GxsTransSendStatus::PENDING_PROCESSING), recipient(rec),
clientService(cs)
{
mailData.resize(size);
memcpy(&mailData[0], data, size);
}
void OutgoingRecord::clear()
{
status = GxsTransSendStatus::UNKNOWN;
recipient.clear();
mailItem.clear();
mailData.clear();
clientService = GxsTransSubServices::UNKNOWN;
presignedReceipt.clear();
}
std::ostream& OutgoingRecord::print(std::ostream& out, uint16_t)
{ return out << "TODO: OutgoingRecordItem::print(...)"; }
OutgoingRecord::OutgoingRecord() :
RsItem( RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_GXS_TRANS,
static_cast<uint8_t>(GxsTransItemsSubtypes::OUTGOING_RECORD_ITEM) )
{ clear();}
uint32_t OutgoingRecord::size() const
{
return 8 + // Header
1 + // status
recipient.serial_size() +
mailItem.serial_size() +
4 + // sizeof(mailData.size())
mailData.size() +
2 + // clientService
presignedReceipt.serial_size();
}
bool OutgoingRecord::serialize( uint8_t* data, uint32_t size,
uint32_t& offset) const
{
bool ok = true;
ok = ok && setRsItemHeader(data+offset, size, PacketId(), size)
&& (offset += 8); // Take header in account
ok = ok && setRawUInt8(data, size, &offset, static_cast<uint8_t>(status));
ok = ok && recipient.serialise(data, size, offset);
uint32_t tmpOffset = 0;
uint32_t tmpSize = mailItem.serial_size();
ok = ok && mailItem.serialize(data+offset, tmpSize, tmpOffset)
&& (offset += tmpOffset);
uint32_t dSize = mailData.size();
ok = ok && setRawUInt32(data, size, &offset, dSize)
&& memcpy(data+offset, &mailData[0], dSize) && (offset += dSize);
ok = ok && setRawUInt16( data, size, &offset,
static_cast<uint16_t>(clientService) );
dSize = presignedReceipt.serial_size();
ok = ok && presignedReceipt.serialise(data+offset, dSize)
&& (offset += dSize);
return ok;
}
bool OutgoingRecord::deserialize(
const uint8_t* data, uint32_t& size, uint32_t& offset)
{
bool ok = true;
void* dataPtr = const_cast<uint8_t*>(data);
offset += 8; // Header
uint8_t tmpStatus = 0;
ok = ok && getRawUInt8(dataPtr, size, &offset, &tmpStatus);
status = static_cast<GxsTransSendStatus>(tmpStatus);
uint32_t tmpSize = size;
ok = ok && recipient.deserialise(dataPtr, tmpSize, offset);
void* hdrPtr = const_cast<uint8_t*>(data+offset);
tmpSize = getRsItemSize(hdrPtr);
uint32_t tmpOffset = 0;
ok = ok && mailItem.deserialize(static_cast<uint8_t*>(hdrPtr), tmpSize, tmpOffset);
ok = ok && (offset += tmpOffset);
tmpSize = size;
ok = getRawUInt32(dataPtr, tmpSize, &offset, &tmpSize);
ok = ok && (tmpSize+offset < size);
ok = ok && (mailData.resize(tmpSize), memcpy(&mailData[0], data, tmpSize));
ok = ok && (offset += tmpSize);
uint16_t cs = 0;
ok = ok && getRawUInt16(dataPtr, offset+2, &offset, &cs);
clientService = static_cast<GxsTransSubServices>(cs);
tmpSize = size;
ok = ok && presignedReceipt.deserialize(data, tmpSize, offset);
return ok;
}

View file

@ -0,0 +1,365 @@
#pragma once
/*
* GXS Mailing Service
* Copyright (C) 2016-2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include "serialiser/rsgxsitems.h"
#include "serialiser/rsbaseserial.h"
#include "serialiser/rstlvidset.h"
#include "retroshare/rsgxsflags.h"
#include "retroshare/rsgxscircles.h" // For: GXS_CIRCLE_TYPE_PUBLIC
#include "services/p3idservice.h"
/// Subservices identifiers (like port for TCP)
enum class GxsTransSubServices : uint16_t
{
UNKNOWN = 0,
TEST_SERVICE = 1,
P3_MSG_SERVICE = 2,
P3_CHAT_SERVICE = 3
};
/// Values must fit into uint8_t
enum class GxsTransItemsSubtypes : uint8_t
{
GXS_TRANS_SUBTYPE_MAIL = 1,
GXS_TRANS_SUBTYPE_RECEIPT = 2,
GXS_TRANS_SUBTYPE_GROUP = 3,
OUTGOING_RECORD_ITEM = 4
};
typedef uint64_t RsGxsTransId;
struct RsNxsTransPresignedReceipt : RsNxsMsg
{
RsNxsTransPresignedReceipt() : RsNxsMsg(RS_SERVICE_TYPE_GXS_TRANS) {}
};
struct RsGxsTransBaseItem : RsGxsMsgItem
{
RsGxsTransBaseItem(GxsTransItemsSubtypes subtype) :
RsGxsMsgItem( RS_SERVICE_TYPE_GXS_TRANS,
static_cast<uint8_t>(subtype) ), mailId(0) {}
RsGxsTransId mailId;
void inline clear()
{
mailId = 0;
meta = RsMsgMetaData();
}
static uint32_t inline serial_size()
{
return 8 + // Header
8; // mailId
}
bool serialize(uint8_t* data, uint32_t size, uint32_t& offset) const;
bool deserialize(const uint8_t* data, uint32_t& size, uint32_t& offset);
std::ostream &print(std::ostream &out, uint16_t /*indent = 0*/);
};
struct RsGxsTransPresignedReceipt : RsGxsTransBaseItem
{
RsGxsTransPresignedReceipt() :
RsGxsTransBaseItem(GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT) {}
};
enum class RsGxsTransEncryptionMode : uint8_t
{
CLEAR_TEXT = 1,
RSA = 2,
UNDEFINED_ENCRYPTION = 250
};
struct RsGxsTransMailItem : RsGxsTransBaseItem
{
RsGxsTransMailItem() :
RsGxsTransBaseItem(GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL),
cryptoType(RsGxsTransEncryptionMode::UNDEFINED_ENCRYPTION) {}
RsGxsTransEncryptionMode cryptoType;
/**
* @brief recipientHint used instead of plain recipient id, so sender can
* decide the equilibrium between exposing the recipient and the cost of
* completely anonymize it. So a bunch of luky non recipient can conclude
* rapidly that they are not the recipient without trying to decrypt the
* message.
*
* To be able to decide how much metadata we disclose sending a message we
* send an hint instead of the recipient id in clear, the hint cannot be
* false (the recipient would discard the mail) but may be arbitrarly
* obscure like 0xFF...FF so potentially everyone could be the recipient, or
* may expose the complete recipient id or be a middle ground.
* To calculate arbitrary precise hint one do a bitwise OR of the recipients
* keys and an arbitrary salt, the more recipients has the mail and the more
* 1 bits has the salt the less accurate is the hint.
* This way the sender is able to adjust the metadata privacy needed for the
* message, in the more private case (recipientHint == 0xFFF...FFF) no one
* has a clue about who is the actual recipient, while this imply the cost
* that every potencial recipient has to try to decrypt it to know if it is
* for herself. This way a bunch of non recipients can rapidly discover that
* the message is not directed to them without attempting it's decryption.
*
* To check if one id may be the recipient of the mail or not one need to
* bitwise compare the hint with the id, if at least one bit of the hint is
* 0 while the corrisponding bit in the id is 1 then the id cannot be the
* recipient of the mail.
*
* Note that by design one can prove that an id is not recipient of the mail
* but cannot prove it is.
* Also in the extreme case of using 0x00...00 as salt that is equivalent
* to not salting at all (aka the plain recipient id is used as hint) a
* malicious observer could not demostrate in a conclusive manner that the
* mail is directed to the actual recipient as the "apparently"
* corresponding hint may be fruit of a "luky" salting of another id.
*/
RsGxsId recipientHint;
void inline saltRecipientHint(const RsGxsId& salt)
{ recipientHint = recipientHint | salt; }
/**
* @brief maybeRecipient given an id and an hint check if they match
* @see recipientHint
* @return true if the id may be recipient of the hint, false otherwise
*/
bool inline maybeRecipient(const RsGxsId& id) const
{ return (~id|recipientHint) == allRecipientsHint; }
const static RsGxsId allRecipientsHint;
/** This should travel encrypted, unless EncryptionMode::CLEAR_TEXT
* is specified */
std::vector<uint8_t> payload;
uint32_t serial_size() const
{
return RsGxsTransBaseItem::serial_size() +
1 + // cryptoType
recipientHint.serial_size() +
payload.size();
}
bool serialize(uint8_t* data, uint32_t size, uint32_t& offset) const
{
bool ok = size < MAX_SIZE;
ok = ok && RsGxsTransBaseItem::serialize(data, size, offset);
ok = ok && setRawUInt8( data, size, &offset,
static_cast<uint8_t>(cryptoType) );
ok = ok && recipientHint.serialise(data, size, offset);
uint32_t psz = payload.size();
ok = ok && memcpy(data+offset, &payload[0], psz);
offset += psz;
return ok;
}
bool deserialize(const uint8_t* data, uint32_t& size, uint32_t& offset)
{
void* sizePtr = const_cast<uint8_t*>(data+offset);
uint32_t rssize = getRsItemSize(sizePtr);
uint32_t roffset = offset;
bool ok = rssize <= size && size < MAX_SIZE;
ok = ok && RsGxsTransBaseItem::deserialize(data, rssize, roffset);
void* dataPtr = const_cast<uint8_t*>(data);
uint8_t crType;
ok = ok && getRawUInt8(dataPtr, rssize, &roffset, &crType);
cryptoType = static_cast<RsGxsTransEncryptionMode>(crType);
ok = ok && recipientHint.deserialise(dataPtr, rssize, roffset);
uint32_t psz = rssize - roffset;
ok = ok && (payload.resize(psz), memcpy(&payload[0], data+roffset, psz));
ok = ok && (roffset += psz);
if(ok) { size = rssize; offset = roffset; }
else size = 0;
return ok;
}
void clear()
{
RsGxsTransBaseItem::clear();
cryptoType = RsGxsTransEncryptionMode::UNDEFINED_ENCRYPTION;
recipientHint.clear();
payload.clear();
}
/// Maximum mail size in bytes 10 MiB is more than anything sane can need
const static uint32_t MAX_SIZE = 10*8*1024*1024;
};
struct RsGxsTransGroupItem : RsGxsGrpItem
{
RsGxsTransGroupItem() :
RsGxsGrpItem( RS_SERVICE_TYPE_GXS_TRANS,
static_cast<uint8_t>(
GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_GROUP) )
{
meta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC;
meta.mGroupName = "Mail";
meta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC;
}
void clear() {}
std::ostream &print(std::ostream &out, uint16_t /*indent = 0*/)
{ return out; }
};
enum class GxsTransSendStatus : uint8_t
{
UNKNOWN = 0,
PENDING_PROCESSING,
PENDING_PREFERRED_GROUP,
PENDING_RECEIPT_CREATE,
PENDING_RECEIPT_SIGNATURE,
PENDING_SERIALIZATION,
PENDING_PAYLOAD_CREATE,
PENDING_PAYLOAD_ENCRYPT,
PENDING_PUBLISH,
/** This will be useful so the user can know if the mail reached at least
* some friend node, in case of internet connection interruption */
//PENDING_TRANSFER,
PENDING_RECEIPT_RECEIVE,
/// Records with status >= RECEIPT_RECEIVED get deleted
RECEIPT_RECEIVED,
FAILED_RECEIPT_SIGNATURE = 240,
FAILED_ENCRYPTION
};
class RsGxsTransSerializer;
struct OutgoingRecord : RsItem
{
OutgoingRecord( RsGxsId rec, GxsTransSubServices cs,
const uint8_t* data, uint32_t size );
GxsTransSendStatus status;
RsGxsId recipient;
/// Don't use a pointer would be invalid after publish
RsGxsTransMailItem mailItem;
std::vector<uint8_t> mailData;
GxsTransSubServices clientService;
RsNxsTransPresignedReceipt presignedReceipt;
uint32_t size() const;
bool serialize(uint8_t* data, uint32_t size, uint32_t& offset) const;
bool deserialize(const uint8_t* data, uint32_t& size, uint32_t& offset);
virtual void clear();
virtual std::ostream &print(std::ostream &out, uint16_t indent = 0);
private:
friend class RsGxsTransSerializer;
OutgoingRecord();
};
struct RsGxsTransSerializer : RsSerialType
{
RsGxsTransSerializer() : RsSerialType( RS_PKT_VERSION_SERVICE,
RS_SERVICE_TYPE_GXS_TRANS ) {}
~RsGxsTransSerializer() {}
uint32_t size(RsItem* item)
{
uint32_t sz = 0;
switch(static_cast<GxsTransItemsSubtypes>(item->PacketSubType()))
{
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL:
{
RsGxsTransMailItem* i = dynamic_cast<RsGxsTransMailItem*>(item);
if(i) sz = i->serial_size();
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT:
sz = RsGxsTransPresignedReceipt::serial_size(); break;
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_GROUP: sz = 8; break;
case GxsTransItemsSubtypes::OUTGOING_RECORD_ITEM:
{
OutgoingRecord* ci = dynamic_cast<OutgoingRecord*>(item);
if(ci) sz = ci->size();
break;
}
default: break;
}
return sz;
}
bool serialise(RsItem* item, void* data, uint32_t* size);
RsItem* deserialise(void* data, uint32_t* size)
{
uint32_t rstype = getRsItemId(data);
uint32_t rssize = getRsItemSize(data);
uint8_t pktv = getRsItemVersion(rstype);
uint16_t srvc = getRsItemService(rstype);
const uint8_t* dataPtr = reinterpret_cast<uint8_t*>(data);
if ( (RS_PKT_VERSION_SERVICE != pktv) || // 0x02
(RS_SERVICE_TYPE_GXS_TRANS != srvc) || // 0x0230 = 560
(*size < rssize) )
{
print_stacktrace();
return NULL;
}
*size = rssize;
bool ok = true;
RsItem* ret = NULL;
switch (static_cast<GxsTransItemsSubtypes>(getRsItemSubType(rstype)))
{
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_MAIL:
{
RsGxsTransMailItem* i = new RsGxsTransMailItem();
uint32_t offset = 0;
ok = ok && i->deserialize(dataPtr, *size, offset);
ret = i;
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_RECEIPT:
{
RsGxsTransPresignedReceipt* i = new RsGxsTransPresignedReceipt();
uint32_t offset = 0;
ok &= i->deserialize(dataPtr, *size, offset);
ret = i;
break;
}
case GxsTransItemsSubtypes::GXS_TRANS_SUBTYPE_GROUP:
{
ret = new RsGxsTransGroupItem();
break;
}
case GxsTransItemsSubtypes::OUTGOING_RECORD_ITEM:
{
OutgoingRecord* i = new OutgoingRecord();
uint32_t offset = 0;
ok = ok && i->deserialize(dataPtr, *size, offset);
ret = i;
break;
}
default:
ok = false;
break;
}
if(ok) return ret;
delete ret;
return NULL;
}
};