/******************************************************************************* * libretroshare/src/gxstrans: p3gxstrans.h * * * * libretroshare: retroshare core library * * * * Copyright (C) 2016-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * *******************************************************************************/ #pragma once #include #include #include #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" #include "retroshare/rsgxstrans.h" class 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; }; struct MsgSizeCount { MsgSizeCount() : size(0),count(0) {} uint32_t size ; uint32_t count ; }; /** * @brief p3GxsTrans asyncronous redundant small mail trasport on top of 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(...). */ class p3GxsTrans : public RsGenExchange, public GxsTokenQueue, public p3Config, public RsGxsTrans { public: p3GxsTrans( RsGeneralDataService* gds, RsNetworkExchangeService* nes, p3IdService& identities ) : RsGenExchange( gds, nes, new RsGxsTransSerializer(), RS_SERVICE_TYPE_GXS_TRANS, &identities, AuthenPolicy()), GxsTokenQueue(this), RsGxsTrans(static_cast(*this)), // always check 30 secs after start) mLastMsgCleanup(time(NULL) - MAX_DELAY_BETWEEN_CLEANUPS + 30), mIdService(identities), mServClientsMutex("p3GxsTrans client services map mutex"), mOutgoingMutex("p3GxsTrans outgoing queue map mutex"), mIngoingMutex("p3GxsTrans ingoing queue map mutex"), mCleanupThread(nullptr), mPerUserStatsMutex("p3GxsTrans user stats mutex"), mDataMutex("p3GxsTrans data mutex") {} virtual ~p3GxsTrans(); /*! * \brief getStatistics * Gathers all sorts of statistics about the data transported by p3GxsTrans, in order to display info about the running status, * message transport, etc. This is a blocking call. Use it in a thread. * \param stats This structure contains all statistics information. * \return true is the call succeeds. */ virtual bool getDataStatistics(GxsTransStatistics& stats) override; /*! * \brief getGroupStatistics * Gathers statistics about GXS groups and messages used by GxsTrans to transport data. This is a blocking call. Use it in a thread. * \param stats * \return true if the data collection succeeds. */ virtual bool getGroupStatistics(std::map& stats) override; /** * 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 sendData( 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() override { return RsServiceInfo( RS_SERVICE_TYPE_GXS_TRANS, "GXS Mails", 0, 1, 0, 1 ); } static const uint32_t GXS_STORAGE_PERIOD = 15*86400; // 15 days. static const uint32_t GXS_SYNC_PERIOD = 15*86400; 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 = 16*86400; // 16 days /** * 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. */ static const uint32_t MAX_DELAY_BETWEEN_CLEANUPS ; // every 20 mins. Could be less. rstime_t mLastMsgCleanup ; /// 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 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 prMap; prMap mOutgoingQueue; RsMutex mOutgoingMutex; void locked_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 inMap; inMap mIncomingQueue; RsMutex mIngoingMutex; /// @see GxsTokenQueue::handleResponse(uint32_t token, uint32_t req_type /// , RsTokenService::GxsRequestStatus status) virtual void handleResponse(uint32_t token, uint32_t req_type , RsTokenService::GxsRequestStatus status) override; /// @see RsGenExchange::service_tick() virtual void service_tick() override; /// @see RsGenExchange::service_CreateGroup(...) RsGenExchange::ServiceCreate_Return service_CreateGroup( RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& ) override; /// @see RsGenExchange::notifyChanges(std::vector &changes) void notifyChanges(std::vector &changes) override; /// @see p3Config::setupSerialiser() virtual RsSerialiser* setupSerialiser() override; /// @see p3Config::saveList(bool &cleanup, std::list&) virtual bool saveList(bool &cleanup, std::list&saveList) override; /// @see p3Config::saveDone() void saveDone() override; /// @see p3Config::loadList(std::list&) virtual bool loadList(std::list& loadList) override; /// Request groups list to GXS backend. Async method. bool requestGroupsData(const std::list* 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 locked_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(rstime_t timeStamp, int32_t interval, rstime_t ref = time(NULL)) { return (timeStamp + interval) < ref; } /// Decrypt email content and pass it to dispatchDecryptedMail(...) bool handleEncryptedMail(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); /// Checks the integrity message and groups class GxsTransIntegrityCleanupThread : public RsThread { enum CheckState { CheckStart, CheckChecking }; public: explicit GxsTransIntegrityCleanupThread( RsGeneralDataService* const dataService ): mDs(dataService), mMtx("GxsTransIntegrityCheck"), mDone(false) {} bool isDone(); void run(); void getDeletedIds(std::list& grpIds, std::map >& msgIds); void getMessagesToDelete(GxsMsgReq& req) ; void getPerUserStatistics(std::map& m) ; private: RsGeneralDataService* const mDs; RsMutex mMtx ; GxsMsgReq mMsgToDel ; std::map total_message_size_and_count; bool mDone; }; // Overloaded from RsGenExchange. bool acceptNewMessage(const RsGxsMsgMetaData *msgMeta, uint32_t size) override; GxsTransIntegrityCleanupThread *mCleanupThread ; // statistics of the load across all groups, per user. RsMutex mPerUserStatsMutex; std::map per_user_statistics ; // Mutex to protect local data RsMutex mDataMutex; };