/* * libretroshare/src/services msgservice.cc * * Services for RetroShare. * * Copyright 2004-2008 by Robert Fernie. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License Version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. * * Please report all bugs and problems to "retroshare@lunamutt.com". * */ #include "retroshare/rsiface.h" #include "retroshare/rspeers.h" #include "retroshare/rsidentity.h" #include "pqi/pqibin.h" #include "pqi/pqiarchive.h" #include "pqi/p3linkmgr.h" #include "pqi/authgpg.h" #include "pqi/p3cfgmgr.h" #include "gxs/gxssecurity.h" #include "services/p3idservice.h" #include "services/p3msgservice.h" #include "pgp/pgpkeyutil.h" #include "rsserver/p3face.h" #include "serialiser/rsconfigitems.h" #include "grouter/p3grouter.h" #include "grouter/groutertypes.h" #include "util/rsdebug.h" #include "util/rsdir.h" #include "util/rsstring.h" #include "util/radix64.h" #include "util/rsrandom.h" #include "util/rsprint.h" #include #include #include #include //#define MSG_DEBUG 1 //#define DEBUG_DISTANT_MSG //#define DISABLE_DISTANT_MESSAGES //#define DEBUG_DISTANT_MSG using namespace Rs::Msgs; const int msgservicezone = 54319; /* Another little hack ..... unique message Ids * will be handled in this class..... * These are unique within this run of the server, * and are not stored long term.... * * Only 3 entry points: * (1) from network.... * (2) from local send * (3) from storage... */ p3MsgService::p3MsgService(p3ServiceControl *sc, p3IdService *id_serv) :p3Service(), p3Config(), mIdService(id_serv), mServiceCtrl(sc), mMsgMtx("p3MsgService"), mMsgUniqueId(0) { _serialiser = new RsMsgSerialiser(); // this serialiser is used for services. It's not the same than the one returned by setupSerialiser(). We need both!! addSerialType(_serialiser); mMsgUniqueId = RSRandom::random_u32() ; // better than time(NULL). We don't need crypto-safe random here. Just something much likely // different from what friends use. mShouldEnableDistantMessaging = true ; mDistantMessagingEnabled = false ; mDistantMessagePermissions = RS_DISTANT_MESSAGING_CONTACT_PERMISSION_FLAG_FILTER_NONE ; /* Initialize standard tag types */ if(sc) initStandardTagTypes(); } const std::string MSG_APP_NAME = "msg"; const uint16_t MSG_APP_MAJOR_VERSION = 1; const uint16_t MSG_APP_MINOR_VERSION = 0; const uint16_t MSG_MIN_MAJOR_VERSION = 1; const uint16_t MSG_MIN_MINOR_VERSION = 0; RsServiceInfo p3MsgService::getServiceInfo() { return RsServiceInfo(RS_SERVICE_TYPE_MSG, MSG_APP_NAME, MSG_APP_MAJOR_VERSION, MSG_APP_MINOR_VERSION, MSG_MIN_MAJOR_VERSION, MSG_MIN_MINOR_VERSION); } uint32_t p3MsgService::getNewUniqueMsgId() { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ return mMsgUniqueId++; } int p3MsgService::tick() { pqioutput(PQL_DEBUG_BASIC, msgservicezone, "p3MsgService::tick()"); /* don't worry about increasing tick rate! * (handled by p3service) */ incomingMsgs(); static time_t last_management_time = 0 ; time_t now = time(NULL) ; if(now > last_management_time + 5) { manageDistantPeers() ; checkOutgoingMessages(); last_management_time = now ; } return 0; } int p3MsgService::status() { pqioutput(PQL_DEBUG_BASIC, msgservicezone, "p3MsgService::status()"); return 1; } void p3MsgService::processIncomingMsg(RsMsgItem *mi) { mi -> recvTime = time(NULL); mi -> msgId = getNewUniqueMsgId(); { RsStackMutex stack(mMsgMtx); /*** STACK LOCKED MTX ***/ /* from a peer */ mi->msgFlags &= (RS_MSG_FLAGS_DISTANT | RS_MSG_FLAGS_SYSTEM); // remove flags except those mi->msgFlags |= RS_MSG_FLAGS_NEW; p3Notify *notify = RsServer::notify(); if (notify) { notify->AddPopupMessage(RS_POPUP_MSG, mi->PeerId().toStdString(), mi->subject, mi->message); std::string out; rs_sprintf(out, "%lu", mi->msgId); notify->AddFeedItem(RS_FEED_ITEM_MESSAGE, out, "", ""); } imsg[mi->msgId] = mi; RsMsgSrcId* msi = new RsMsgSrcId(); msi->msgId = mi->msgId; msi->srcId = mi->PeerId(); mSrcIds.insert(std::pair(msi->msgId, msi)); IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ /**** STACK UNLOCKED ***/ } // If the peer is allowed to push files, then auto-download the recommended files. if(rsPeers->servicePermissionFlags(mi->PeerId()) & RS_NODE_PERM_ALLOW_PUSH) { std::list srcIds; srcIds.push_back(mi->PeerId()); for(std::list::const_iterator it(mi->attachment.items.begin());it!=mi->attachment.items.end();++it) rsFiles->FileRequest((*it).name,(*it).hash,(*it).filesize,std::string(),RS_FILE_REQ_ANONYMOUS_ROUTING,srcIds) ; } RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_ADD); } bool p3MsgService::checkAndRebuildPartialMessage(RsMsgItem *ci) { // Check is the item is ending an incomplete item. // std::map::iterator it = _pendingPartialMessages.find(ci->PeerId()) ; bool ci_is_partial = ci->msgFlags & RS_MSG_FLAGS_PARTIAL ; if(it != _pendingPartialMessages.end()) { #ifdef MSG_DEBUG std::cerr << "Pending message found. Appending it." << std::endl; #endif // Yes, there is. Append the item to ci. ci->message = it->second->message + ci->message ; ci->msgFlags |= it->second->msgFlags ; delete it->second ; if(!ci_is_partial) _pendingPartialMessages.erase(it) ; } if(ci_is_partial) { #ifdef MSG_DEBUG std::cerr << "Message is partial, storing for later." << std::endl; #endif // The item is a partial message. Push it, and wait for the rest. // _pendingPartialMessages[ci->PeerId()] = ci ; return false ; } else { #ifdef MSG_DEBUG std::cerr << "Message is complete, using it now." << std::endl; #endif return true ; } } int p3MsgService::incomingMsgs() { RsMsgItem *mi; int i = 0; while((mi = (RsMsgItem *) recvItem()) != NULL) { handleIncomingItem(mi) ; ++i ; } return i; } void p3MsgService::handleIncomingItem(RsMsgItem *mi) { bool changed = false ; if(checkAndRebuildPartialMessage(mi)) // only returns true when a msg is complete. { processIncomingMsg(mi); changed = true ; } if(changed) RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } void p3MsgService::statusChange(const std::list &plist) { /* should do it properly! */ /* only do this when a new peer is connected */ bool newPeers = false; std::list::const_iterator it; for(it = plist.begin(); it != plist.end(); ++it) { if (it->actions & RS_SERVICE_PEER_CONNECTED) { newPeers = true; } } if (newPeers) { checkOutgoingMessages(); } } void p3MsgService::checkSizeAndSendMessage(RsMsgItem *msg) { // We check the message item, and possibly split it into multiple messages, if the message is too big. static const uint32_t MAX_STRING_SIZE = 15000 ; std::cerr << "Msg is size " << msg->message.size() << std::endl; while(msg->message.size() > MAX_STRING_SIZE) { // chop off the first 15000 wchars RsMsgItem *item = new RsMsgItem(*msg) ; item->message = item->message.substr(0,MAX_STRING_SIZE) ; msg->message = msg->message.substr(MAX_STRING_SIZE,msg->message.size()-MAX_STRING_SIZE) ; #ifdef DEBUG_DISTANT_MSG std::cerr << " Chopped off msg of size " << item->message.size() << std::endl; #endif // Indicate that the message is to be continued. // item->msgFlags |= RS_MSG_FLAGS_PARTIAL ; sendItem(item) ; } #ifdef DEBUG_DISTANT_MSG std::cerr << " Chopped off msg of size " << msg->message.size() << std::endl; #endif sendItem(msg) ; } int p3MsgService::checkOutgoingMessages() { /* iterate through the outgoing queue * * if online, send */ static const uint32_t OLD_MESSAGE_FLUSHING_DELAY = 86400*7 ; // re-send old messages every week. This mainly ensures that // messages that where never sent get sent at some point. time_t now = time(NULL); bool changed = false ; std::list output_queue ; { RS_STACK_MUTEX(mMsgMtx); /********** STACK LOCKED MTX ******/ const RsPeerId& ownId = mServiceCtrl->getOwnId(); std::list::iterator it; std::list toErase; std::map::iterator mit; for(mit = msgOutgoing.begin(); mit != msgOutgoing.end(); ++mit) { if (mit->second->msgFlags & RS_MSG_FLAGS_TRASH) continue; /* find the certificate */ RsPeerId pid = mit->second->PeerId(); bool should_send = false ; if( pid == ownId) should_send = true ; if( mServiceCtrl->isPeerConnected(getServiceInfo().mServiceType, pid) ) /* FEEDBACK Msg to Ourselves */ should_send = true ; if (mit->second->msgFlags & RS_MSG_FLAGS_DISTANT) { if(!(mit->second->msgFlags & RS_MSG_FLAGS_ROUTED)) should_send = true ; // if(mit->second->sendTime + OLD_MESSAGE_FLUSHING_DELAY < now) //{ // should_send = true ; // mit->second->sendTime = now; // } } if(should_send) { /* send msg */ pqioutput(PQL_DEBUG_BASIC, msgservicezone, "p3MsgService::checkOutGoingMessages() Sending out message"); /* remove the pending flag */ output_queue.push_back(mit->second) ; // When the message is a distant msg, dont remove it yet from the list. Only mark it as being sent, so that we don't send it again. // if(!(mit->second->msgFlags & RS_MSG_FLAGS_DISTANT)) { (mit->second)->msgFlags &= ~RS_MSG_FLAGS_PENDING; toErase.push_back(mit->first); changed = true ; } else { #ifdef DEBUG_DISTANT_MSG std::cerr << "Message id " << mit->first << " is distant: kept in outgoing, and marked as ROUTED" << std::endl; #endif mit->second->msgFlags |= RS_MSG_FLAGS_ROUTED ; } } else { pqioutput(PQL_DEBUG_BASIC, msgservicezone, "p3MsgService::checkOutGoingMessages() Delaying until available..."); } } /* clean up */ for(it = toErase.begin(); it != toErase.end(); ++it) { mit = msgOutgoing.find(*it); if (mit != msgOutgoing.end()) { msgOutgoing.erase(mit); } std::map::iterator srcIt = mSrcIds.find(*it); if (srcIt != mSrcIds.end()) { delete (srcIt->second); mSrcIds.erase(srcIt); } } if (toErase.size() > 0) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ } } for(std::list::const_iterator it(output_queue.begin());it!=output_queue.end();++it) if((*it)->msgFlags & RS_MSG_FLAGS_DISTANT) // don't split distant messages. The global router takes care of it. sendDistantMsgItem(*it) ; else checkSizeAndSendMessage(*it) ; if(changed) RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); return 0; } bool p3MsgService::saveList(bool& cleanup, std::list& itemList) { std::map::iterator mit; std::map::iterator mit2; std::map::iterator mit3; std::map::iterator lit; std::map::iterator mit4; MsgTagType stdTags; cleanup = true; mMsgMtx.lock(); for(mit = imsg.begin(); mit != imsg.end(); ++mit) itemList.push_back(new RsMsgItem(*mit->second)); for(lit = mSrcIds.begin(); lit != mSrcIds.end(); ++lit) itemList.push_back(new RsMsgSrcId(*lit->second)); for(mit = msgOutgoing.begin(); mit != msgOutgoing.end(); ++mit) itemList.push_back(new RsMsgItem(*mit->second)) ; for(mit2 = mTags.begin(); mit2 != mTags.end(); ++mit2) itemList.push_back(new RsMsgTagType(*mit2->second)); for(mit3 = mMsgTags.begin(); mit3 != mMsgTags.end(); ++mit3) itemList.push_back(new RsMsgTags(*mit3->second)); for(mit4 = mParentId.begin(); mit4 != mParentId.end(); ++mit4) itemList.push_back(new RsMsgParentId(*mit4->second)); RsMsgGRouterMap *grmap = new RsMsgGRouterMap ; grmap->ongoing_msgs = _ongoing_messages ; itemList.push_back(grmap) ; RsMsgDistantMessagesHashMap *ghm = new RsMsgDistantMessagesHashMap ; ghm->hash_map = mRecentlyReceivedDistantMessageHashes ; itemList.push_back(ghm) ; RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; RsTlvKeyValue kv; kv.key = "DISTANT_MESSAGES_ENABLED" ; kv.value = mShouldEnableDistantMessaging?"YES":"NO" ; vitem->tlvkvs.pairs.push_back(kv) ; kv.key = "DISTANT_MESSAGE_PERMISSION_FLAGS" ; kv.value = RsUtil::NumberToString(mDistantMessagePermissions) ; vitem->tlvkvs.pairs.push_back(kv) ; itemList.push_back(vitem) ; return true; } void p3MsgService::saveDone() { // unlocks mutex which has been locked by savelist mMsgMtx.unlock(); } RsSerialiser* p3MsgService::setupSerialiser() // this serialiser is used for config. So it adds somemore info in the serialised items { RsSerialiser *rss = new RsSerialiser ; rss->addSerialType(new RsMsgSerialiser(true)); rss->addSerialType(new RsGeneralConfigSerialiser()); return rss; } // build list of standard tag types static void getStandardTagTypes(MsgTagType &tags) { /* create standard tag types, the text must be translated in the GUI */ tags.types [RS_MSGTAGTYPE_IMPORTANT] = std::pair ("Important", 0xFF0000); tags.types [RS_MSGTAGTYPE_WORK] = std::pair ("Work", 0xFF9900); tags.types [RS_MSGTAGTYPE_PERSONAL] = std::pair ("Personal", 0x009900); tags.types [RS_MSGTAGTYPE_TODO] = std::pair ("Todo", 0x3333FF); tags.types [RS_MSGTAGTYPE_LATER] = std::pair ("Later", 0x993399); } // Initialize the standard tag types after load void p3MsgService::initStandardTagTypes() { bool bChanged = false; const RsPeerId& ownId = mServiceCtrl->getOwnId(); MsgTagType tags; getStandardTagTypes(tags); std::map >::iterator tit; for (tit = tags.types.begin(); tit != tags.types.end(); ++tit) { std::map::iterator mit = mTags.find(tit->first); if (mit == mTags.end()) { RsMsgTagType* tagType = new RsMsgTagType(); tagType->PeerId (ownId); tagType->tagId = tit->first; tagType->text = tit->second.first; tagType->rgb_color = tit->second.second; mTags.insert(std::pair(tit->first, tagType)); bChanged = true; } } if (bChanged) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ } } bool p3MsgService::loadList(std::list& load) { RsMsgItem *mitem; RsMsgTagType* mtt; RsMsgTags* mti; RsMsgSrcId* msi; RsMsgParentId* msp; RsMsgGRouterMap* grm; RsMsgDistantMessagesHashMap *ghm; std::list items; std::list::iterator it; std::map::iterator tagIt; std::map srcIdMsgMap; std::map::iterator srcIt; uint32_t max_msg_id = 0 ; // load items and calculate next unique msgId for(it = load.begin(); it != load.end(); ++it) { if (NULL != (mitem = dynamic_cast(*it))) { /* STORE MsgID */ if (mitem->msgId > max_msg_id) max_msg_id = mitem->msgId ; items.push_back(mitem); } else if (NULL != (grm = dynamic_cast(*it))) { // merge. for(std::map::const_iterator it(grm->ongoing_msgs.begin());it!=grm->ongoing_msgs.end();++it) _ongoing_messages.insert(*it) ; } else if(NULL != (ghm = dynamic_cast(*it))) { mRecentlyReceivedDistantMessageHashes = ghm->hash_map ; } else if(NULL != (mtt = dynamic_cast(*it))) { // delete standard tags as they are now save in config if(mTags.end() == (tagIt = mTags.find(mtt->tagId))) { mTags.insert(std::pair(mtt->tagId, mtt)); } else { delete mTags[mtt->tagId]; mTags.erase(tagIt); mTags.insert(std::pair(mtt->tagId, mtt)); } } else if(NULL != (mti = dynamic_cast(*it))) { mMsgTags.insert(std::pair(mti->msgId, mti)); } else if(NULL != (msi = dynamic_cast(*it))) { srcIdMsgMap.insert(std::pair(msi->msgId, msi->srcId)); mSrcIds.insert(std::pair(msi->msgId, msi)); // does not need to be kept } else if(NULL != (msp = dynamic_cast(*it))) { mParentId.insert(std::pair(msp->msgId, msp)); } RsConfigKeyValueSet *vitem = NULL ; if(NULL != (vitem = dynamic_cast(*it))) { for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { if(kit->key == "DISTANT_MESSAGES_ENABLED") { #ifdef MSG_DEBUG std::cerr << "Loaded config default nick name for distant chat: " << kit->value << std::endl ; #endif mShouldEnableDistantMessaging = (kit->value == "YES") ; } if(kit->key == "DISTANT_MESSAGE_PERMISSION_FLAGS") { #ifdef MSG_DEBUG std::cerr << "Loaded distant message permission flags: " << kit->value << std::endl ; #endif if (!kit->value.empty()) { std::istringstream is(kit->value) ; uint32_t tmp ; is >> tmp ; if(tmp < 3) mDistantMessagePermissions = tmp ; else std::cerr << "(EE) Invalid value read for DistantMessagePermission flags in config: " << tmp << std::endl; } } } delete *it ; continue ; } } mMsgUniqueId = max_msg_id + 1; // make it unique with respect to what was loaded. Not totally safe, but works 99.9999% of the cases. load.clear() ; // sort items into lists std::list::iterator msgIt; for (msgIt = items.begin(); msgIt != items.end(); ++msgIt) { mitem = *msgIt; /* STORE MsgID */ if (mitem->msgId == 0) { mitem->msgId = getNewUniqueMsgId(); } RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ srcIt = srcIdMsgMap.find(mitem->msgId); if(srcIt != srcIdMsgMap.end()) { mitem->PeerId(srcIt->second); srcIdMsgMap.erase(srcIt); } /* switch depending on the PENDING * flags */ if (mitem -> msgFlags & RS_MSG_FLAGS_PENDING) { //std::cerr << "MSG_PENDING"; //std::cerr << std::endl; //mitem->print(std::cerr); msgOutgoing[mitem->msgId] = mitem; } else { imsg[mitem->msgId] = mitem; } } RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ /* remove missing msgId in mSrcIds */ for (srcIt = srcIdMsgMap.begin(); srcIt != srcIdMsgMap.end(); ++srcIt) { std::map::iterator it = mSrcIds.find(srcIt->first); if (it != mSrcIds.end()) { delete(it->second); mSrcIds.erase(it); } } /* remove missing msgId in mParentId */ std::map::iterator mit = mParentId.begin(); while (mit != mParentId.end()) { if (imsg.find(mit->first) == imsg.end()) { if (msgOutgoing.find(mit->first) == msgOutgoing.end()) { /* not found */ mParentId.erase(mit++); continue; } } ++mit; } return true; } void p3MsgService::loadWelcomeMsg() { /* Load Welcome Message */ RsMsgItem *msg = new RsMsgItem(); //msg -> PeerId(mServiceCtrl->getOwnId()); msg -> sendTime = time(NULL); msg -> recvTime = time(NULL); msg -> msgFlags = RS_MSG_FLAGS_NEW; msg -> subject = "Welcome to Retroshare"; msg -> message = "Send and receive messages with your friends...\n"; msg -> message += "These can hold recommendations from your local shared files.\n\n"; msg -> message += "Add recommendations through the Local Files Dialog.\n\n"; msg -> message += "Enjoy."; msg -> msgId = getNewUniqueMsgId(); RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ imsg[msg->msgId] = msg; IndicateConfigChanged(); } /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ /****************************************/ /****************************************/ bool p3MsgService::getMessageSummaries(std::list &msgList) { /* do stuff */ msgList.clear(); RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ std::map::iterator mit; for(mit = imsg.begin(); mit != imsg.end(); ++mit) { MsgInfoSummary mis; initRsMIS(mit->second, mis); msgList.push_back(mis); } for(mit = msgOutgoing.begin(); mit != msgOutgoing.end(); ++mit) { MsgInfoSummary mis; initRsMIS(mit->second, mis); msgList.push_back(mis); } return true; } bool p3MsgService::getMessage(const std::string &mId, MessageInfo &msg) { std::map::iterator mit; uint32_t msgId = atoi(mId.c_str()); RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ mit = imsg.find(msgId); if (mit == imsg.end()) { mit = msgOutgoing.find(msgId); if (mit == msgOutgoing.end()) { return false; } } /* mit valid */ initRsMI(mit->second, msg); std::map::const_iterator it = mSrcIds.find(msgId) ; if(it != mSrcIds.end()) msg.rsgxsid_srcId = RsGxsId(it->second->srcId) ; return true; } void p3MsgService::getMessageCount(unsigned int *pnInbox, unsigned int *pnInboxNew, unsigned int *pnOutbox, unsigned int *pnDraftbox, unsigned int *pnSentbox, unsigned int *pnTrashbox) { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ if (pnInbox) *pnInbox = 0; if (pnInboxNew) *pnInboxNew = 0; if (pnOutbox) *pnOutbox = 0; if (pnDraftbox) *pnDraftbox = 0; if (pnSentbox) *pnSentbox = 0; if (pnTrashbox) *pnTrashbox = 0; std::map::iterator mit; std::map *apMsg [2] = { &imsg, &msgOutgoing }; for (int i = 0; i < 2; i++) { for (mit = apMsg [i]->begin(); mit != apMsg [i]->end(); ++mit) { MsgInfoSummary mis; initRsMIS(mit->second, mis); if (mis.msgflags & RS_MSG_TRASH) { if (pnTrashbox) ++(*pnTrashbox); continue; } switch (mis.msgflags & RS_MSG_BOXMASK) { case RS_MSG_INBOX: if (pnInbox) ++(*pnInbox); if ((mis.msgflags & RS_MSG_NEW) == RS_MSG_NEW) { if (pnInboxNew) ++(*pnInboxNew); } break; case RS_MSG_OUTBOX: if (pnOutbox) ++(*pnOutbox); break; case RS_MSG_DRAFTBOX: if (pnDraftbox) ++(*pnDraftbox); break; case RS_MSG_SENTBOX: if (pnSentbox) ++(*pnSentbox); break; } } } } /* remove based on the unique mid (stored in sid) */ bool p3MsgService::removeMsgId(const std::string &mid) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); if (msgId == 0) { std::cerr << "p3MsgService::removeMsgId: Unknown msgId " << msgId << std::endl; return false; } bool changed = false; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ mit = imsg.find(msgId); if (mit != imsg.end()) { changed = true; RsMsgItem *mi = mit->second; imsg.erase(mit); delete mi; } mit = msgOutgoing.find(msgId); if (mit != msgOutgoing.end()) { changed = true ; RsMsgItem *mi = mit->second; msgOutgoing.erase(mit); delete mi; } std::map::iterator srcIt = mSrcIds.find(msgId); if (srcIt != mSrcIds.end()) { changed = true; delete (srcIt->second); mSrcIds.erase(srcIt); } } if(changed) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ setMessageTag(mid, 0, false); setMsgParentId(msgId, 0); RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } return changed; } bool p3MsgService::markMsgIdRead(const std::string &mid, bool unreadByUser) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); bool changed = false; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ mit = imsg.find(msgId); if (mit != imsg.end()) { RsMsgItem *mi = mit->second; uint32_t msgFlags = mi->msgFlags; /* remove new state */ mi->msgFlags &= ~(RS_MSG_FLAGS_NEW); /* set state from user */ if (unreadByUser) { mi->msgFlags |= RS_MSG_FLAGS_UNREAD_BY_USER; } else { mi->msgFlags &= ~RS_MSG_FLAGS_UNREAD_BY_USER; } if (mi->msgFlags != msgFlags) { changed = true; IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ } } else { return false; } } /* UNLOCKED */ if (changed) { RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } return true; } bool p3MsgService::setMsgFlag(const std::string &mid, uint32_t flag, uint32_t mask) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); bool changed = false; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ mit = imsg.find(msgId); if (mit == imsg.end()) { mit = msgOutgoing.find(msgId); if (mit == msgOutgoing.end()) { return false; } } uint32_t oldFlag = mit->second->msgFlags; mit->second->msgFlags &= ~mask; mit->second->msgFlags |= flag; if (mit->second->msgFlags != oldFlag) { changed = true; IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ } } /* UNLOCKED */ if (changed) { RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } return true; } bool p3MsgService::getMsgParentId(const std::string &msgId, std::string &msgParentId) { msgParentId.clear(); RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ std::map::iterator mit = mParentId.find(atoi(msgId.c_str())); if (mit == mParentId.end()) { return false; } rs_sprintf(msgParentId, "%lu", mit->second->msgParentId); return true; } bool p3MsgService::setMsgParentId(uint32_t msgId, uint32_t msgParentId) { std::map::iterator mit; bool changed = false; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ mit = mParentId.find(msgId); if (mit == mParentId.end()) { if (msgParentId) { RsMsgParentId* msp = new RsMsgParentId(); msp->PeerId (mServiceCtrl->getOwnId()); msp->msgId = msgId; msp->msgParentId = msgParentId; mParentId.insert(std::pair(msgId, msp)); changed = true; } } else { if (msgParentId) { if (mit->second->msgParentId != msgParentId) { mit->second->msgParentId = msgParentId; changed = true; } } else { delete mit->second; mParentId.erase(mit); changed = true; } } } /* UNLOCKED */ if (changed) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ } return true; } /****************************************/ /****************************************/ /* Message Items */ uint32_t p3MsgService::sendMessage(RsMsgItem *item) // no from field because it's implicitly our own PeerId { if(!item) return 0 ; pqioutput(PQL_DEBUG_BASIC, msgservicezone, "p3MsgService::sendMessage()"); item->msgId = getNewUniqueMsgId(); /* grabs Mtx as well */ item->msgFlags |= (RS_MSG_FLAGS_OUTGOING | RS_MSG_FLAGS_PENDING); /* add pending flag */ { RS_STACK_MUTEX(mMsgMtx) ; /* STORE MsgID */ msgOutgoing[item->msgId] = item; if (item->PeerId() != mServiceCtrl->getOwnId()) { /* not to the loopback device */ RsMsgSrcId* msi = new RsMsgSrcId(); msi->msgId = item->msgId; msi->srcId = mServiceCtrl->getOwnId(); mSrcIds.insert(std::pair(msi->msgId, msi)); } } IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST, NOTIFY_TYPE_ADD); return item->msgId; } uint32_t p3MsgService::sendDistantMessage(RsMsgItem *item,const RsGxsId& from) { if(!item) return 0 ; item->msgId = getNewUniqueMsgId(); /* grabs Mtx as well */ item->msgFlags |= (RS_MSG_FLAGS_DISTANT | RS_MSG_FLAGS_OUTGOING | RS_MSG_FLAGS_PENDING); /* add pending flag */ { RS_STACK_MUTEX(mMsgMtx) ; /* STORE MsgID */ msgOutgoing[item->msgId] = item; mDistantOutgoingMsgSigners[item->msgId] = from ; if (item->PeerId() != mServiceCtrl->getOwnId()) { /* not to the loopback device */ RsMsgSrcId* msi = new RsMsgSrcId(); msi->msgId = item->msgId; msi->srcId = RsPeerId(from) ; mSrcIds.insert(std::pair(msi->msgId, msi)); } } IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST, NOTIFY_TYPE_ADD); return item->msgId; } bool p3MsgService::MessageSend(MessageInfo &info) { for(std::set::const_iterator pit = info.rspeerid_msgto.begin(); pit != info.rspeerid_msgto.end(); ++pit) sendMessage(initMIRsMsg(info, *pit)); for(std::set::const_iterator pit = info.rspeerid_msgcc.begin(); pit != info.rspeerid_msgcc.end(); ++pit) sendMessage(initMIRsMsg(info, *pit)); for(std::set::const_iterator pit = info.rspeerid_msgbcc.begin(); pit != info.rspeerid_msgbcc.end(); ++pit) sendMessage(initMIRsMsg(info, *pit)); for(std::set::const_iterator pit = info.rsgxsid_msgto.begin(); pit != info.rsgxsid_msgto.end(); ++pit) sendDistantMessage(initMIRsMsg(info, *pit),info.rsgxsid_srcId); for(std::set::const_iterator pit = info.rsgxsid_msgcc.begin(); pit != info.rsgxsid_msgcc.end(); ++pit) sendDistantMessage(initMIRsMsg(info, *pit),info.rsgxsid_srcId); for(std::set::const_iterator pit = info.rsgxsid_msgbcc.begin(); pit != info.rsgxsid_msgbcc.end(); ++pit) sendDistantMessage(initMIRsMsg(info, *pit),info.rsgxsid_srcId); // store message in outgoing list. In order to appear as sent the message needs to have the OUTGOING flg, but no pending flag on. RsMsgItem *msg = initMIRsMsg(info, mServiceCtrl->getOwnId()); if (msg) { std::list::iterator it ; if (msg->msgFlags & RS_MSG_FLAGS_SIGNED) msg->msgFlags |= RS_MSG_FLAGS_SIGNATURE_CHECKS; // this is always true, since we are sending the message /* use processMsg to get the new msgId */ msg->recvTime = time(NULL); msg->msgId = getNewUniqueMsgId(); msg->msgFlags |= RS_MSG_OUTGOING; imsg[msg->msgId] = msg; RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_ADD); // // // return new message id // rs_sprintf(info.msgId, "%lu", msg->msgId); } return true; } bool p3MsgService::SystemMessage(const std::string &title, const std::string &message, uint32_t systemFlag) { if ((systemFlag & RS_MSG_SYSTEM) == 0) { /* no flag specified */ return false; } const RsPeerId& ownId = mServiceCtrl->getOwnId(); RsMsgItem *msg = new RsMsgItem(); msg->PeerId(ownId); msg->msgFlags = 0; if (systemFlag & RS_MSG_USER_REQUEST) { msg->msgFlags |= RS_MSG_FLAGS_USER_REQUEST; } if (systemFlag & RS_MSG_FRIEND_RECOMMENDATION) { msg->msgFlags |= RS_MSG_FLAGS_FRIEND_RECOMMENDATION; } if (systemFlag & RS_MSG_PUBLISH_KEY) { msg->msgFlags |= RS_MSG_FLAGS_PUBLISH_KEY; } msg->msgId = 0; msg->sendTime = time(NULL); msg->recvTime = 0; msg->subject = title; msg->message = message; msg->rspeerid_msgto.ids.insert(ownId); processIncomingMsg(msg); return true; } bool p3MsgService::MessageToDraft(MessageInfo &info, const std::string &msgParentId) { RsMsgItem *msg = initMIRsMsg(info, mServiceCtrl->getOwnId()); if (msg) { uint32_t msgId = 0; if (info.msgId.empty() == false) { msgId = atoi(info.msgId.c_str()); } if (msgId) { msg->msgId = msgId; } else { msg->msgId = getNewUniqueMsgId(); /* grabs Mtx as well */ } { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ /* add pending flag */ msg->msgFlags |= (RS_MSG_OUTGOING | RS_MSG_FLAGS_DRAFT); if (msgId) { // remove existing message std::map::iterator mit; mit = imsg.find(msgId); if (mit != imsg.end()) { RsMsgItem *mi = mit->second; imsg.erase(mit); delete mi; } } /* STORE MsgID */ imsg[msg->msgId] = msg; // return new message id rs_sprintf(info.msgId, "%lu", msg->msgId); } setMsgParentId(msg->msgId, atoi(msgParentId.c_str())); IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); return true; } return false; } bool p3MsgService::getMessageTagTypes(MsgTagType& tags) { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ std::map::iterator mit; for(mit = mTags.begin(); mit != mTags.end(); ++mit) { std::pair p(mit->second->text, mit->second->rgb_color); tags.types.insert(std::pair >(mit->first, p)); } return true; } bool p3MsgService::setMessageTagType(uint32_t tagId, std::string& text, uint32_t rgb_color) { int nNotifyType = 0; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ std::map::iterator mit; mit = mTags.find(tagId); if (mit == mTags.end()) { if (tagId < RS_MSGTAGTYPE_USER) { std::cerr << "p3MsgService::MessageSetTagType: Standard tag type " << tagId << " cannot be inserted" << std::endl; return false; } /* new tag */ RsMsgTagType* tagType = new RsMsgTagType(); tagType->PeerId (mServiceCtrl->getOwnId()); tagType->rgb_color = rgb_color; tagType->tagId = tagId; tagType->text = text; mTags.insert(std::pair(tagId, tagType)); nNotifyType = NOTIFY_TYPE_ADD; } else { if (mit->second->text != text || mit->second->rgb_color != rgb_color) { /* modify existing tag */ if (tagId >= RS_MSGTAGTYPE_USER) { mit->second->text = text; } else { /* don't change text for standard tag types */ if (mit->second->text != text) { std::cerr << "p3MsgService::MessageSetTagType: Text " << text << " for standard tag type " << tagId << " cannot be changed" << std::endl; } } mit->second->rgb_color = rgb_color; nNotifyType = NOTIFY_TYPE_MOD; } } } /* UNLOCKED */ if (nNotifyType) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGE_TAGS, nNotifyType); return true; } return false; } bool p3MsgService::removeMessageTagType(uint32_t tagId) { if (tagId < RS_MSGTAGTYPE_USER) { std::cerr << "p3MsgService::MessageRemoveTagType: Can't delete standard tag type " << tagId << std::endl; return false; } { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ std::map::iterator mit; mit = mTags.find(tagId); if (mit == mTags.end()) { /* tag id not found */ std::cerr << "p3MsgService::MessageRemoveTagType: Tag Id not found " << tagId << std::endl; return false; } /* search for messages with this tag type */ std::map::iterator mit1; for (mit1 = mMsgTags.begin(); mit1 != mMsgTags.end(); ) { RsMsgTags* tag = mit1->second; std::list::iterator lit; lit = std::find(tag->tagIds.begin(), tag->tagIds.end(), tagId); if (lit != tag->tagIds.end()) { tag->tagIds.erase(lit); if (tag->tagIds.empty()) { /* remove empty tag */ delete(tag); mMsgTags.erase(mit1++); continue; } } ++mit1; } /* remove tag type */ delete(mit->second); mTags.erase(mit); } /* UNLOCKED */ IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGE_TAGS, NOTIFY_TYPE_DEL); return true; } bool p3MsgService::getMessageTag(const std::string &msgId, MsgTagInfo& info) { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ uint32_t mid = atoi(msgId.c_str()); if (mid == 0) { std::cerr << "p3MsgService::MessageGetMsgTag: Unknown msgId " << msgId << std::endl; return false; } std::map::iterator mit; if(mMsgTags.end() != (mit = mMsgTags.find(mid))) { rs_sprintf(info.msgId, "%lu", mit->second->msgId); info.tagIds = mit->second->tagIds; return true; } return false; } /* set == false && tagId == 0 --> remove all */ bool p3MsgService::setMessageTag(const std::string &msgId, uint32_t tagId, bool set) { uint32_t mid = atoi(msgId.c_str()); if (mid == 0) { std::cerr << "p3MsgService::MessageSetMsgTag: Unknown msgId " << msgId << std::endl; return false; } if (tagId == 0) { if (set == true) { std::cerr << "p3MsgService::MessageSetMsgTag: No valid tagId given " << tagId << std::endl; return false; } } int nNotifyType = 0; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ std::map::iterator mit; mit = mMsgTags.find(mid); if (mit == mMsgTags.end()) { if (set) { /* new msg */ RsMsgTags* tag = new RsMsgTags(); tag->PeerId (mServiceCtrl->getOwnId()); tag->msgId = mid; tag->tagIds.push_back(tagId); mMsgTags.insert(std::pair(tag->msgId, tag)); nNotifyType = NOTIFY_TYPE_ADD; } } else { RsMsgTags* tag = mit->second; /* search existing tagId */ std::list::iterator lit; if (tagId) { lit = std::find(tag->tagIds.begin(), tag->tagIds.end(), tagId); } else { lit = tag->tagIds.end(); } if (set) { if (lit == tag->tagIds.end()) { tag->tagIds.push_back(tagId); /* keep the list sorted */ tag->tagIds.sort(); nNotifyType = NOTIFY_TYPE_ADD; } } else { if (tagId == 0) { /* remove all */ delete(tag); mMsgTags.erase(mit); nNotifyType = NOTIFY_TYPE_DEL; } else { if (lit != tag->tagIds.end()) { tag->tagIds.erase(lit); nNotifyType = NOTIFY_TYPE_DEL; if (tag->tagIds.empty()) { /* remove empty tag */ delete(tag); mMsgTags.erase(mit); } } } } } } /* UNLOCKED */ if (nNotifyType) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGE_TAGS, nNotifyType); return true; } return false; } bool p3MsgService::resetMessageStandardTagTypes(MsgTagType& tags) { MsgTagType standardTags; getStandardTagTypes(standardTags); std::map >::iterator mit; for (mit = standardTags.types.begin(); mit != standardTags.types.end(); ++mit) { tags.types[mit->first] = mit->second; } return true; } /* move message to trash based on the unique mid */ bool p3MsgService::MessageToTrash(const std::string &mid, bool bTrash) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); bool bChanged = false; bool bFound = false; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ RsMsgItem *mi = NULL; mit = imsg.find(msgId); if (mit != imsg.end()) { mi = mit->second; } else { mit = msgOutgoing.find(msgId); if (mit != msgOutgoing.end()) { mi = mit->second; } } if (mi) { bFound = true; if (bTrash) { if ((mi->msgFlags & RS_MSG_FLAGS_TRASH) == 0) { mi->msgFlags |= RS_MSG_FLAGS_TRASH; bChanged = true; } } else { if (mi->msgFlags & RS_MSG_FLAGS_TRASH) { mi->msgFlags &= ~RS_MSG_FLAGS_TRASH; bChanged = true; } } } } if (bChanged) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ checkOutgoingMessages(); RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } return bFound; } /****************************************/ /****************************************/ /****************************************/ /**** HELPER FNS For Chat/Msg/Channel Lists ************ * These aren't required to be locked, unless * the data used is from internal stores -> then they should be. */ void p3MsgService::initRsMI(RsMsgItem *msg, MessageInfo &mi) { mi.msgflags = 0; /* translate flags, if we sent it... outgoing */ if (msg->msgFlags & RS_MSG_FLAGS_OUTGOING) mi.msgflags |= RS_MSG_OUTGOING; if (msg->msgFlags & RS_MSG_FLAGS_PENDING) mi.msgflags |= RS_MSG_PENDING; /* if it has a pending flag, then its in the outbox */ if (msg->msgFlags & RS_MSG_FLAGS_DRAFT) mi.msgflags |= RS_MSG_DRAFT; if (msg->msgFlags & RS_MSG_FLAGS_NEW) mi.msgflags |= RS_MSG_NEW; if (msg->msgFlags & RS_MSG_FLAGS_SIGNED) mi.msgflags |= RS_MSG_SIGNED ; if (msg->msgFlags & RS_MSG_FLAGS_SIGNATURE_CHECKS) mi.msgflags |= RS_MSG_SIGNATURE_CHECKS ; if (msg->msgFlags & RS_MSG_FLAGS_DISTANT) mi.msgflags |= RS_MSG_DISTANT ; if (msg->msgFlags & RS_MSG_FLAGS_TRASH) mi.msgflags |= RS_MSG_TRASH; if (msg->msgFlags & RS_MSG_FLAGS_UNREAD_BY_USER) mi.msgflags |= RS_MSG_UNREAD_BY_USER; if (msg->msgFlags & RS_MSG_FLAGS_REPLIED) mi.msgflags |= RS_MSG_REPLIED; if (msg->msgFlags & RS_MSG_FLAGS_FORWARDED) mi.msgflags |= RS_MSG_FORWARDED; if (msg->msgFlags & RS_MSG_FLAGS_STAR) mi.msgflags |= RS_MSG_STAR; if (msg->msgFlags & RS_MSG_FLAGS_USER_REQUEST) mi.msgflags |= RS_MSG_USER_REQUEST; if (msg->msgFlags & RS_MSG_FLAGS_FRIEND_RECOMMENDATION) mi.msgflags |= RS_MSG_FRIEND_RECOMMENDATION; if (msg->msgFlags & RS_MSG_FLAGS_PUBLISH_KEY) mi.msgflags |= RS_MSG_PUBLISH_KEY; if (msg->msgFlags & RS_MSG_FLAGS_LOAD_EMBEDDED_IMAGES) mi.msgflags |= RS_MSG_LOAD_EMBEDDED_IMAGES; mi.ts = msg->sendTime; mi.rspeerid_srcId = msg->PeerId(); mi.rspeerid_msgto = msg->rspeerid_msgto.ids ; mi.rspeerid_msgcc = msg->rspeerid_msgcc.ids ; mi.rspeerid_msgbcc = msg->rspeerid_msgbcc.ids ; mi.rsgxsid_msgto = msg->rsgxsid_msgto.ids ; mi.rsgxsid_msgcc = msg->rsgxsid_msgcc.ids ; mi.rsgxsid_msgbcc = msg->rsgxsid_msgbcc.ids ; mi.title = msg->subject; mi.msg = msg->message; { //msg->msgId; rs_sprintf(mi.msgId, "%lu", msg->msgId); } mi.attach_title = msg->attachment.title; mi.attach_comment = msg->attachment.comment; mi.count = 0; mi.size = 0; for(std::list::iterator it = msg->attachment.items.begin(); it != msg->attachment.items.end(); ++it) { FileInfo fi; fi.fname = RsDirUtil::getTopDir(it->name); fi.size = it->filesize; fi.hash = it->hash; fi.path = it->path; mi.files.push_back(fi); mi.count++; mi.size += fi.size; } } void p3MsgService::initRsMIS(RsMsgItem *msg, MsgInfoSummary &mis) { mis.msgflags = 0; if (msg->msgFlags & RS_MSG_FLAGS_DISTANT) mis.msgflags |= RS_MSG_DISTANT; if (msg->msgFlags & RS_MSG_FLAGS_SIGNED) mis.msgflags |= RS_MSG_SIGNED ; if (msg->msgFlags & RS_MSG_FLAGS_SIGNATURE_CHECKS) mis.msgflags |= RS_MSG_SIGNATURE_CHECKS ; /* translate flags, if we sent it... outgoing */ if ((msg->msgFlags & RS_MSG_FLAGS_OUTGOING) /*|| (msg->PeerId() == mServiceCtrl->getOwnId())*/) { mis.msgflags |= RS_MSG_OUTGOING; } /* if it has a pending flag, then its in the outbox */ if (msg->msgFlags & RS_MSG_FLAGS_PENDING) { mis.msgflags |= RS_MSG_PENDING; } if (msg->msgFlags & RS_MSG_FLAGS_DRAFT) { mis.msgflags |= RS_MSG_DRAFT; } if (msg->msgFlags & RS_MSG_FLAGS_NEW) { mis.msgflags |= RS_MSG_NEW; } if (msg->msgFlags & RS_MSG_FLAGS_TRASH) { mis.msgflags |= RS_MSG_TRASH; } if (msg->msgFlags & RS_MSG_FLAGS_UNREAD_BY_USER) { mis.msgflags |= RS_MSG_UNREAD_BY_USER; } if (msg->msgFlags & RS_MSG_FLAGS_REPLIED) { mis.msgflags |= RS_MSG_REPLIED; } if (msg->msgFlags & RS_MSG_FLAGS_FORWARDED) { mis.msgflags |= RS_MSG_FORWARDED; } if (msg->msgFlags & RS_MSG_FLAGS_STAR) { mis.msgflags |= RS_MSG_STAR; } if (msg->msgFlags & RS_MSG_FLAGS_USER_REQUEST) { mis.msgflags |= RS_MSG_USER_REQUEST; } if (msg->msgFlags & RS_MSG_FLAGS_FRIEND_RECOMMENDATION) { mis.msgflags |= RS_MSG_FRIEND_RECOMMENDATION; } if (msg->msgFlags & RS_MSG_FLAGS_PUBLISH_KEY) { mis.msgflags |= RS_MSG_PUBLISH_KEY; } if (msg->msgFlags & RS_MSG_FLAGS_LOAD_EMBEDDED_IMAGES) { mis.msgflags |= RS_MSG_LOAD_EMBEDDED_IMAGES; } mis.srcId = msg->PeerId(); { //msg->msgId; rs_sprintf(mis.msgId, "%lu", msg->msgId); } mis.title = msg->subject; mis.count = msg->attachment.items.size(); mis.ts = msg->sendTime; } void p3MsgService::initMIRsMsg(RsMsgItem *msg,const MessageInfo& info) { msg -> msgFlags = 0; msg -> msgId = 0; msg -> sendTime = time(NULL); msg -> recvTime = 0; msg -> subject = info.title; msg -> message = info.msg; msg->rspeerid_msgto.ids = info.rspeerid_msgto ; msg->rspeerid_msgcc.ids = info.rspeerid_msgcc ; msg->rsgxsid_msgto.ids = info.rsgxsid_msgto ; msg->rsgxsid_msgcc.ids = info.rsgxsid_msgcc ; /* We don't fill in bcc (unless to ourselves) */ if (msg->PeerId() == mServiceCtrl->getOwnId()) { msg->rsgxsid_msgbcc.ids = info.rsgxsid_msgbcc ; msg->rspeerid_msgbcc.ids = info.rspeerid_msgbcc ; } msg -> attachment.title = info.attach_title; msg -> attachment.comment = info.attach_comment; for(std::list::const_iterator it = info.files.begin(); it != info.files.end(); ++it) { RsTlvFileItem mfi; mfi.hash = it -> hash; mfi.name = it -> fname; mfi.filesize = it -> size; msg -> attachment.items.push_back(mfi); } /* translate flags from outside */ if (info.msgflags & RS_MSG_USER_REQUEST) msg->msgFlags |= RS_MSG_FLAGS_USER_REQUEST; if (info.msgflags & RS_MSG_FRIEND_RECOMMENDATION) msg->msgFlags |= RS_MSG_FLAGS_FRIEND_RECOMMENDATION; } RsMsgItem *p3MsgService::initMIRsMsg(const MessageInfo& info, const RsGxsId& to) { RsMsgItem *msg = new RsMsgItem(); initMIRsMsg(msg,info) ; msg->PeerId(RsPeerId(to)); msg->msgFlags |= RS_MSG_FLAGS_DISTANT; if (info.msgflags & RS_MSG_SIGNED) msg->msgFlags |= RS_MSG_FLAGS_SIGNED; // // We replace the msg text by the whole message serialized possibly signed, // // and binary encrypted, so as to obfuscate all its content. // // // if(!createDistantMessage(to,info.rsgxsid_srcId,msg)) // { // std::cerr << "Cannot encrypt distant message. Something went wrong." << std::endl; // delete msg ; // return NULL ; // } return msg ; } RsMsgItem *p3MsgService::initMIRsMsg(const MessageInfo &info, const RsPeerId& to) { RsMsgItem *msg = new RsMsgItem(); initMIRsMsg(msg,info) ; msg->PeerId(to) ; /* load embedded images from own messages */ msg->msgFlags |= RS_MSG_FLAGS_LOAD_EMBEDDED_IMAGES; return msg; } void p3MsgService::connectToGlobalRouter(p3GRouter *gr) { mGRouter = gr ; gr->registerClientService(GROUTER_CLIENT_ID_MESSAGES,this) ; } void p3MsgService::enableDistantMessaging(bool b) { // We use a temporary variable because the call to OwnIds() might fail. mShouldEnableDistantMessaging = b ; IndicateConfigChanged() ; } bool p3MsgService::distantMessagingEnabled() { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ return mShouldEnableDistantMessaging ; } void p3MsgService::manageDistantPeers() { // now possibly flush pending messages if(mShouldEnableDistantMessaging == mDistantMessagingEnabled) return ; #ifdef DEBUG_DISTANT_MSG std::cerr << "p3MsgService::manageDistantPeers()" << std::endl; #endif std::list own_id_list ; if(mIdService->getOwnIds(own_id_list)) { #ifdef DEBUG_DISTANT_MSG for(std::list::const_iterator it(own_id_list.begin());it!=own_id_list.end();++it) std::cerr << (b?"Enabling":"Disabling") << " distant messaging, with peer id = " << *it << std::endl; #endif for(std::list::const_iterator it(own_id_list.begin());it!=own_id_list.end();++it) { if(mShouldEnableDistantMessaging) mGRouter->registerKey(*it,GROUTER_CLIENT_ID_MESSAGES,"Messaging contact") ; else mGRouter->unregisterKey(*it,GROUTER_CLIENT_ID_MESSAGES) ; } RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ mDistantMessagingEnabled = mShouldEnableDistantMessaging ; } } void p3MsgService::notifyDataStatus(const GRouterMsgPropagationId& id,uint32_t data_status) { if(data_status == GROUTER_CLIENT_SERVICE_DATA_STATUS_FAILED) { RS_STACK_MUTEX(mMsgMtx); /********** STACK LOCKED MTX ******/ std::cerr << "(WW) p3MsgService::notifyDataStatus: Global router tells us that item ID " << id << " could not be delivered on time." ; std::map::iterator it = _ongoing_messages.find(id) ; if(it == _ongoing_messages.end()) { std::cerr << " (EE) cannot find pending message to acknowledge. Weird. grouter id = " << id << std::endl; return ; } uint32_t msg_id = it->second ; std::cerr << " message id = " << msg_id << std::endl; std::map::iterator mit = msgOutgoing.find(msg_id) ; if(mit == msgOutgoing.end()) { std::cerr << " (EE) message has been notified as not delivered, but it not on outgoing list. Something's wrong!!" << std::endl; return ; } std::cerr << " reseting the ROUTED flag so that the message is requested again" << std::endl; mit->second->msgFlags &= ~RS_MSG_FLAGS_ROUTED ; // clear the routed flag so that the message is requested again return ; } if(data_status == GROUTER_CLIENT_SERVICE_DATA_STATUS_RECEIVED) { RS_STACK_MUTEX(mMsgMtx); /********** STACK LOCKED MTX ******/ #ifdef DEBUG_DISTANT_MSG std::cerr << "p3MsgService::acknowledgeDataReceived(): acknowledging data received for msg propagation id " << id << std::endl; #endif std::map::iterator it = _ongoing_messages.find(id) ; if(it == _ongoing_messages.end()) { std::cerr << " (EE) cannot find pending message to acknowledge. Weird. grouter id = " << id << std::endl; return ; } uint32_t msg_id = it->second ; // we should now remove the item from the msgOutgoing list. std::map::iterator it2 = msgOutgoing.find(msg_id) ; if(it2 == msgOutgoing.end()) { std::cerr << "(EE) message has been ACKed, but is not in outgoing list. Something's wrong!!" << std::endl; return ; } delete it2->second ; msgOutgoing.erase(it2) ; RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_ADD); IndicateConfigChanged() ; return ; } std::cerr << "p3MsgService: unhandled data status info from global router for msg ID " << id << ": this is a bug." << std::endl; } bool p3MsgService::acceptDataFromPeer(const RsGxsId& to_gxs_id) { if(mDistantMessagePermissions & RS_DISTANT_MESSAGING_CONTACT_PERMISSION_FLAG_FILTER_NON_CONTACTS) return (rsIdentity!=NULL) && rsIdentity->isARegularContact(to_gxs_id) ; if(mDistantMessagePermissions & RS_DISTANT_MESSAGING_CONTACT_PERMISSION_FLAG_FILTER_EVERYBODY) return false ; return true ; } void p3MsgService::setDistantMessagingPermissionFlags(uint32_t flags) { if(flags != mDistantMessagePermissions) { mDistantMessagePermissions = flags ; IndicateConfigChanged() ; } } uint32_t p3MsgService::getDistantMessagingPermissionFlags() { return mDistantMessagePermissions ; } void p3MsgService::receiveGRouterData(const RsGxsId& destination_key, const RsGxsId& signing_key,GRouterServiceId& client_id,uint8_t *data,uint32_t data_size) { std::cerr << "p3MsgService::receiveGRouterData(): received message item of size " << data_size << ", for key " << destination_key << std::endl; // first make sure that we havn't already received the data. Since we allow to re-send messages, it's necessary to check. Sha1CheckSum hash = RsDirUtil::sha1sum(data,data_size) ; if(mRecentlyReceivedDistantMessageHashes.find(hash) != mRecentlyReceivedDistantMessageHashes.end()) { std::cerr << "(WW) receiving distant message of hash " << hash << " more than once. This is not a bug, unless it happens very often." << std::endl; free(data) ; return ; } mRecentlyReceivedDistantMessageHashes[hash] = time(NULL) ; RsItem *item = _serialiser->deserialise(data,&data_size) ; free(data) ; RsMsgItem *msg_item = dynamic_cast(item) ; if(msg_item != NULL) { std::cerr << " Encrypted item correctly deserialised. Passing on to incoming list." << std::endl; msg_item->msgFlags |= RS_MSG_FLAGS_DISTANT ; /* we expect complete msgs - remove partial flag just in case someone has funny ideas */ msg_item->msgFlags &= ~RS_MSG_FLAGS_PARTIAL; msg_item->PeerId(RsPeerId(signing_key)) ; // hack to pass on GXS id. handleIncomingItem(msg_item) ; } else std::cerr << " Item could not be deserialised. Format error??" << std::endl; } void p3MsgService::sendDistantMsgItem(RsMsgItem *msgitem) { RsGxsId destination_key_id(msgitem->PeerId()) ; RsGxsId signing_key_id ; msgitem->msgFlags |= RS_MSG_FLAGS_DISTANT ; { RS_STACK_MUTEX(mMsgMtx) ; signing_key_id = mDistantOutgoingMsgSigners[msgitem->msgId] ; if(signing_key_id.isNull()) { std::cerr << "ERROR: cannot find signing key id for msg id " << msgitem->msgId << std::endl; std::cerr << " available keys are:" << std::endl; for(std::map::const_iterator it(mDistantOutgoingMsgSigners.begin());it!=mDistantOutgoingMsgSigners.end();++it) std::cerr << " " << it->first << " " << it->second << std::endl; return ; } } #ifdef DEBUG_DISTANT_MSG std::cerr << "p3MsgService::sendDistanteMsgItem(): sending distant msg item" << std::endl; std::cerr << " msg ID : " << msgitem->msgId << std::endl; std::cerr << " to peer : " << destination_key_id << std::endl; std::cerr << " signing : " << signing_key_id << std::endl; #endif // The item is serialized and turned into a generic turtle item. Use use the explicit serialiser to make sure that the msgId is not included uint32_t msg_serialized_rssize = msgitem->serial_size(false) ; unsigned char *msg_serialized_data = new unsigned char[msg_serialized_rssize] ; if(!msgitem->serialise(msg_serialized_data,msg_serialized_rssize,false)) { std::cerr << "(EE) p3MsgService::sendTurtleData(): Serialization error." << std::endl; delete[] msg_serialized_data ; return ; } #ifdef DEBUG_DISTANT_MSG std::cerr << " serialised size : " << msg_serialized_rssize << std::endl; #endif GRouterMsgPropagationId grouter_message_id ; mGRouter->sendData(destination_key_id,GROUTER_CLIENT_ID_MESSAGES,msg_serialized_data,msg_serialized_rssize,signing_key_id,grouter_message_id) ; delete[] msg_serialized_data ; // now store the grouter id along with the message id, so that we can keep track of received messages { RS_STACK_MUTEX(mMsgMtx) ; _ongoing_messages[grouter_message_id] = msgitem->msgId ; } }