diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index 2ac0736e5..657bf0f85 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -21,6 +21,7 @@ * * *******************************************************************************/ #include +#include #include "pqi/pqihash.h" #include "rsgenexchange.h" @@ -38,8 +39,7 @@ #include "rsgxsutil.h" #include "rsserver/p3face.h" #include "retroshare/rsevents.h" - -#include +#include "util/radix64.h" #define PUB_GRP_MASK 0x000f #define RESTR_GRP_MASK 0x00f0 @@ -3445,6 +3445,71 @@ bool RsGenExchange::localSearch( const std::string& matchString, return mNetService->search(matchString, results); } +bool RsGenExchange::exportGroupBase64( + std::string& radix, const RsGxsGroupId& groupId, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(groupId.isNull()) return failure("groupId cannot be null"); + + const std::list groupIds({groupId}); + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + uint32_t token; + mDataAccess->requestGroupInfo( + token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds); + RsTokenService::GxsRequestStatus wtStatus = mDataAccess->waitToken(token); + if(wtStatus != RsTokenService::COMPLETE) + return failure( "waitToken(...) failed with: " + + std::to_string(wtStatus) ); + + uint8_t* buf = nullptr; + uint32_t size; + RsGxsGroupId grpId; + if(!getSerializedGroupData(token, grpId, buf, size)) + return failure("failed retrieving GXS data"); + + Radix64::encode(buf, static_cast(size), radix); + free(buf); + + return true; +} + +bool RsGenExchange::importGroupBase64( + const std::string& radix, RsGxsGroupId& groupId, + std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(radix.empty()) return failure("radix is empty"); + + std::vector mem = Radix64::decode(radix); + if(mem.empty()) return failure("radix seems corrupted"); + + // On success this also import the group as pending validation + if(!deserializeGroupData( + mem.data(), static_cast(mem.size()), + reinterpret_cast(&groupId) )) + return failure("failed deserializing group"); + + return true; +} + RsGxsChanges::RsGxsChanges() : RsEvent(RsEventType::GXS_CHANGES), mServiceType(RsServiceType::NONE), mService(nullptr) {} + +RsGxsIface::~RsGxsIface() = default; +RsGxsGroupSummary::~RsGxsGroupSummary() = default; diff --git a/libretroshare/src/gxs/rsgenexchange.h b/libretroshare/src/gxs/rsgenexchange.h index f2efd9b66..f8da21177 100644 --- a/libretroshare/src/gxs/rsgenexchange.h +++ b/libretroshare/src/gxs/rsgenexchange.h @@ -95,7 +95,8 @@ typedef std::map > GxsMsgRelatedDa class RsGixs; -class RsGenExchange : public RsNxsObserver, public RsTickingThread, public RsGxsIface +class RsGenExchange : public RsNxsObserver, public RsTickingThread, + public RsGxsIface { public: @@ -325,6 +326,19 @@ public: bool localSearch( const std::string& matchString, std::list& results ); + /// @see RsGxsIface + bool exportGroupBase64( + std::string& radix, const RsGxsGroupId& groupId, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @see RsGxsIface + bool importGroupBase64( + const std::string& radix, + RsGxsGroupId& groupId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + protected: bool messagePublicationTest(const RsGxsMsgMetaData&) ; diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 4b82792f2..3e0d95219 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -404,6 +404,61 @@ public: const std::function& multiCallback, rstime_t maxWait = 30 ) = 0; + /// default base URL used for channels links @see exportChannelLink + static const std::string DEFAULT_CHANNEL_BASE_URL; + + /// Link query field used to store channel name @see exportChannelLink + static const std::string CHANNEL_URL_NAME_FIELD; + + /// Link query field used to store channel id @see exportChannelLink + static const std::string CHANNEL_URL_ID_FIELD; + + /// Link query field used to store channel data @see exportChannelLink + static const std::string CHANNEL_URL_DATA_FIELD; + + /** Link query field used to store channel message title + * @see exportChannelLink */ + static const std::string CHANNEL_URL_MSG_TITLE_FIELD; + + /// Link query field used to store channel message id @see exportChannelLink + static const std::string CHANNEL_URL_MSG_ID_FIELD; + + /** + * @brief Get link to a channel + * @jsonapi{development} + * @param[out] link storage for the generated link + * @param[in] chanId Id of the channel of which we want to generate a link + * @param[in] includeGxsData if true include the channel GXS group data so + * the receiver can subscribe to the channel even if she hasn't received it + * through GXS yet + * @param[in] baseUrl URL into which to sneak in the RetroShare link + * radix, this is primarly useful to induce applications into making the + * link clickable, or to disguise the RetroShare link into a + * "normal" looking web link. If empty the GXS data link will be outputted + * in plain base64 format. + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if something failed, true otherwhise + */ + virtual bool exportChannelLink( + std::string& link, const RsGxsGroupId& chanId, + bool includeGxsData = true, + const std::string& baseUrl = RsGxsChannels::DEFAULT_CHANNEL_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + /** + * @brief Import channel from full link + * @param[in] link channel link either in radix or link format + * @param[out] chanId optional storage for parsed channel id + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if some error occurred, true otherwise + */ + virtual bool importChannelLink( + const std::string& link, + RsGxsGroupId& chanId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + /* Following functions are deprecated as they expose internal functioning * semantic, instead of a safe to use API */ diff --git a/libretroshare/src/retroshare/rsgxscircles.h b/libretroshare/src/retroshare/rsgxscircles.h index 407871d80..80e5862cf 100644 --- a/libretroshare/src/retroshare/rsgxscircles.h +++ b/libretroshare/src/retroshare/rsgxscircles.h @@ -274,6 +274,54 @@ public: virtual bool cancelCircleMembership( const RsGxsId& ownGxsId, const RsGxsCircleId& circleId ) = 0; + /// default base URL used for circle links @see exportCircleLink + static const std::string DEFAULT_CIRCLE_BASE_URL; + + /// Circle link query field used to store circle name @see exportCircleLink + static const std::string CIRCLE_URL_NAME_FIELD; + + /// Circle link query field used to store circle id @see exportCircleLink + static const std::string CIRCLE_URL_ID_FIELD; + + /// Circle link query field used to store circle data @see exportCircleLink + static const std::string CIRCLE_URL_DATA_FIELD; + + /** + * @brief Get link to a circle + * @jsonapi{development} + * @param[out] link storage for the generated link + * @param[in] circleId Id of the circle of which we want to generate a link + * @param[in] includeGxsData if true include the circle GXS group data so + * the receiver can request circle membership even if the circle hasn't + * propagated through GXS to her yet + * @param[in] baseUrl URL into which to sneak in the RetroShare circle link + * radix, this is primarly useful to induce applications into making the + * link clickable, or to disguise the RetroShare circle link into a + * "normal" looking web link. If empty the circle data link will be + * outputted in plain base64 format. + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if something failed, true otherwhise + */ + virtual bool exportCircleLink( + std::string& link, const RsGxsCircleId& circleId, + bool includeGxsData = true, + const std::string& baseUrl = RsGxsCircles::DEFAULT_CIRCLE_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + /** + * @brief Import circle from full link + * @param[in] link circle link either in radix or link format + * @param[out] circleId optional storage for parsed circle id + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if some error occurred, true otherwise + */ + virtual bool importCircleLink( + const std::string& link, + RsGxsCircleId& circleId = RS_DEFAULT_STORAGE_PARAM(RsGxsCircleId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + RS_DEPRECATED_FOR("getCirclesSummaries getCirclesInfo") virtual bool getGroupData( const uint32_t& token, std::vector& groups ) = 0; diff --git a/libretroshare/src/retroshare/rsgxsforums.h b/libretroshare/src/retroshare/rsgxsforums.h index 3be38691d..92f2d11d5 100644 --- a/libretroshare/src/retroshare/rsgxsforums.h +++ b/libretroshare/src/retroshare/rsgxsforums.h @@ -241,6 +241,62 @@ public: virtual bool subscribeToForum( const RsGxsGroupId& forumId, bool subscribe ) = 0; + /// default base URL used for forums links @see exportForumLink + static const std::string DEFAULT_FORUM_BASE_URL; + + /// Link query field used to store forum name @see exportForumLink + static const std::string FORUM_URL_NAME_FIELD; + + /// Link query field used to store forum id @see exportForumLink + static const std::string FORUM_URL_ID_FIELD; + + /// Link query field used to store forum data @see exportForumLink + static const std::string FORUM_URL_DATA_FIELD; + + /** Link query field used to store forum message title + * @see exportChannelLink */ + static const std::string FORUM_URL_MSG_TITLE_FIELD; + + /// Link query field used to store forum message id @see exportChannelLink + static const std::string FORUM_URL_MSG_ID_FIELD; + + /** + * @brief Get link to a forum + * @jsonapi{development} + * @param[out] link storage for the generated link + * @param[in] forumId Id of the forum of which we want to generate a link + * @param[in] includeGxsData if true include the forum GXS group data so + * the receiver can subscribe to the forum even if she hasn't received it + * through GXS yet + * @param[in] baseUrl URL into which to sneak in the RetroShare link + * radix, this is primarly useful to induce applications into making the + * link clickable, or to disguise the RetroShare link into a + * "normal" looking web link. If empty the GXS data link will be outputted + * in plain base64 format. + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if something failed, true otherwhise + */ + virtual bool exportForumLink( + std::string& link, const RsGxsGroupId& forumId, + bool includeGxsData = true, + const std::string& baseUrl = RsGxsForums::DEFAULT_FORUM_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + /** + * @brief Import forum from full link + * @param[in] link forum link either in radix or URL format + * @param[out] forumId optional storage for parsed forum id + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if some error occurred, true otherwise + */ + virtual bool importForumLink( + const std::string& link, + RsGxsGroupId& forumId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + /** * @brief Create forum. Blocking API. * @jsonapi{development} diff --git a/libretroshare/src/retroshare/rsgxsiface.h b/libretroshare/src/retroshare/rsgxsiface.h index c230c06fa..54a27656d 100644 --- a/libretroshare/src/retroshare/rsgxsiface.h +++ b/libretroshare/src/retroshare/rsgxsiface.h @@ -68,6 +68,8 @@ struct RsGxsGroupSummary : RsSerializable RS_SERIAL_PROCESS(mPopularity); RS_SERIAL_PROCESS(mSearchContext); } + + ~RsGxsGroupSummary(); }; @@ -108,8 +110,6 @@ struct RsGxsChanges : RsEvent */ struct RsGxsIface { - virtual ~RsGxsIface() {} - /*! * Gxs services should call this for automatic handling of * changes, send @@ -238,4 +238,32 @@ struct RsGxsIface virtual RsReputationLevel minReputationForForwardingMessages( uint32_t group_sign_flags,uint32_t identity_flags ) = 0; + + /** + * @brief Export group public data in base64 format + * @jsonapi{development} + * @param[out] radix storage for the generated base64 data + * @param[in] groupId Id of the group of which to output the data + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if something failed, true otherwhise + */ + virtual bool exportGroupBase64( + std::string& radix, const RsGxsGroupId& groupId, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + /** + * @brief Import group public data from base64 string + * @param[in] radix group invite in radix format + * @param[out] groupId optional storage for imported group id + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if some error occurred, true otherwise + */ + virtual bool importGroupBase64( + const std::string& radix, + RsGxsGroupId& groupId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + virtual ~RsGxsIface(); }; diff --git a/libretroshare/src/retroshare/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index fce16d8f7..de9925135 100644 --- a/libretroshare/src/retroshare/rsgxsifacehelper.h +++ b/libretroshare/src/retroshare/rsgxsifacehelper.h @@ -296,47 +296,7 @@ protected: uint32_t token, std::chrono::milliseconds maxWait = std::chrono::milliseconds(500), std::chrono::milliseconds checkEvery = std::chrono::milliseconds(2)) - { -#if defined(__ANDROID__) && (__ANDROID_API__ < 24) - auto wkStartime = std::chrono::steady_clock::now(); - int maxWorkAroundCnt = 10; -LLwaitTokenBeginLabel: -#endif - auto timeout = std::chrono::steady_clock::now() + maxWait; - auto st = requestStatus(token); - while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) - && std::chrono::steady_clock::now() < timeout ) - { - std::this_thread::sleep_for(checkEvery); - st = requestStatus(token); - } - -#if defined(__ANDROID__) && (__ANDROID_API__ < 24) - /* Work around for very slow/old android devices, we don't expect this - * to be necessary on newer devices. If it take unreasonably long - * something worser is already happening elsewere and we return anyway. - */ - if( st > RsTokenService::FAILED && st < RsTokenService::COMPLETE - && maxWorkAroundCnt-- > 0 ) - { - maxWait *= 10; - checkEvery *= 3; - std::cerr << __PRETTY_FUNCTION__ << " Slow Android device " - << " workaround st: " << st - << " maxWorkAroundCnt: " << maxWorkAroundCnt - << " maxWait: " << maxWait.count() - << " checkEvery: " << checkEvery.count() << std::endl; - goto LLwaitTokenBeginLabel; - } - std::cerr << __PRETTY_FUNCTION__ << " lasted: " - << std::chrono::duration_cast( - std::chrono::steady_clock::now() - wkStartime ).count() - << "ms" << std::endl; - -#endif - - return st; - } + { return mTokenService.waitToken(token, maxWait, checkEvery); } private: RsGxsIface& mGxs; diff --git a/libretroshare/src/retroshare/rsidentity.h b/libretroshare/src/retroshare/rsidentity.h index cc5772e83..ddda6e65a 100644 --- a/libretroshare/src/retroshare/rsidentity.h +++ b/libretroshare/src/retroshare/rsidentity.h @@ -95,14 +95,17 @@ struct GxsReputation : RsSerializable int32_t mPeerOpinion; /// @see RsSerializable - void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx ) + void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override { RS_SERIAL_PROCESS(mOverallScore); RS_SERIAL_PROCESS(mIdScore); RS_SERIAL_PROCESS(mOwnOpinion); RS_SERIAL_PROCESS(mPeerOpinion); } + + ~GxsReputation() override; }; @@ -110,7 +113,6 @@ struct RsGxsIdGroup : RsSerializable { RsGxsIdGroup() : mLastUsageTS(0), mPgpKnown(false), mIsAContact(false) {} - virtual ~RsGxsIdGroup() {} RsGroupMetaData mMeta; @@ -149,6 +151,8 @@ struct RsGxsIdGroup : RsSerializable /// @see RsSerializable void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override; + + ~RsGxsIdGroup() override; }; // DATA TYPE FOR EXTERNAL INTERFACE. @@ -331,8 +335,9 @@ struct RsIdentityDetails : RsSerializable std::map mUseCases; /// @see RsSerializable - virtual void serial_process(RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx) + virtual void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override { RS_SERIAL_PROCESS(mId); RS_SERIAL_PROCESS(mNickname); @@ -344,6 +349,8 @@ struct RsIdentityDetails : RsSerializable RS_SERIAL_PROCESS(mLastUsageTS); RS_SERIAL_PROCESS(mUseCases); } + + ~RsIdentityDetails() override; }; @@ -434,26 +441,6 @@ struct RsIdentity : RsGxsIfaceHelper */ virtual bool isOwnId(const RsGxsId& id) = 0; - /** - * @brief Get base64 representation of an identity - * @jsonapi{development} - * @param[in] id Id of the identity - * @param[out] base64String storage for the identity base64 - * @return false on error, true otherwise - */ - virtual bool identityToBase64( const RsGxsId& id, - std::string& base64String ) = 0; - - /** - * @brief Import identity from base64 representation - * @jsonapi{development} - * @param[in] base64String base64 representation of the identity to import - * @param[out] id storage for the identity id - * @return false on error, true otherwise - */ - virtual bool identityFromBase64( const std::string& base64String, - RsGxsId& id ) = 0; - /** * @brief Get identities summaries list. * @jsonapi{development} @@ -528,8 +515,55 @@ struct RsIdentity : RsGxsIfaceHelper */ virtual bool requestIdentity(const RsGxsId& id) = 0; + /// default base URL used for indentity links @see exportIdentityLink + static const std::string DEFAULT_IDENTITY_BASE_URL; - RS_DEPRECATED + /// Link query field used to store indentity name @see exportIdentityLink + static const std::string IDENTITY_URL_NAME_FIELD; + + /// Link query field used to store indentity id @see exportIdentityLink + static const std::string IDENTITY_URL_ID_FIELD; + + /// Link query field used to store indentity data @see exportIdentityLink + static const std::string IDENTITY_URL_DATA_FIELD; + + /** + * @brief Get link to a identity + * @jsonapi{development} + * @param[out] link storage for the generated link + * @param[in] id Id of the identity of which you want to generate a link + * @param[in] includeGxsData if true include the identity GXS group data so + * the receiver can make use of the identity even if she hasn't received it + * through GXS yet + * @param[in] baseUrl URL into which to sneak in the RetroShare link + * radix, this is primarly useful to induce applications into making the + * link clickable, or to disguise the RetroShare link into a + * "normal" looking web link. If empty the GXS data link will be outputted + * in plain base64 format. + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if something failed, true otherwhise + */ + virtual bool exportIdentityLink( + std::string& link, const RsGxsId& id, + bool includeGxsData = true, + const std::string& baseUrl = RsIdentity::DEFAULT_IDENTITY_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + /** + * @brief Import identity from full link + * @param[in] link identity link either in radix or link format + * @param[out] id optional storage for parsed identity + * @param[out] errMsg optional storage for error message, meaningful only in + * case of failure + * @return false if some error occurred, true otherwise + */ + virtual bool importIdentityLink( + const std::string& link, + RsGxsId& id = RS_DEFAULT_STORAGE_PARAM(RsGxsId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + + RS_DEPRECATED_FOR(exportIdentityLink) virtual bool getGroupSerializedData( const uint32_t& token, std::map& serialized_groups ) = 0; @@ -547,10 +581,10 @@ struct RsIdentity : RsGxsIfaceHelper RS_DEPRECATED virtual uint32_t nbRegularContacts() =0; - RS_DEPRECATED_FOR(identityToBase64) + RS_DEPRECATED_FOR(exportIdentityLink) virtual bool serialiseIdentityToMemory( const RsGxsId& id, std::string& radix_string ) = 0; - RS_DEPRECATED_FOR(identityFromBase64) + RS_DEPRECATED_FOR(importIdentityLink) virtual bool deserialiseIdentityFromMemory( const std::string& radix_string, RsGxsId* id = nullptr ) = 0; diff --git a/libretroshare/src/retroshare/rstokenservice.h b/libretroshare/src/retroshare/rstokenservice.h index bea291e25..a7da6ee32 100644 --- a/libretroshare/src/retroshare/rstokenservice.h +++ b/libretroshare/src/retroshare/rstokenservice.h @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2012 by Robert Fernie, Chris Evi-Parker * + * Copyright (C) 2012 Chris Evi-Parker * + * Copyright (C) 2012 Robert Fernie * + * Copyright (C) 2018-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 * @@ -19,8 +21,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef RSTOKENSERVICE_H -#define RSTOKENSERVICE_H +#pragma once #include #include @@ -28,6 +29,7 @@ #include "retroshare/rsgxsifacetypes.h" #include "util/rsdeprecate.h" +#include "util/rsdebug.h" // TODO CLEANUP: GXS_REQUEST_TYPE_* should be an inner enum of RsTokReqOptions #define GXS_REQUEST_TYPE_GROUP_DATA 0x00010000 @@ -113,7 +115,6 @@ struct RsTokReqOptions */ class RsTokenService { - public: enum GxsRequestStatus : uint8_t @@ -220,6 +221,59 @@ public: * @return false if unusuccessful in cancelling request, true if successful */ virtual bool cancelRequest(const uint32_t &token) = 0; -}; -#endif // RSTOKENSERVICE_H + /** + * Block caller while request is being processed. + * Useful for blocking API implementation. + * @param[in] token token associated to the request caller is waiting for + * @param[in] maxWait maximum waiting time in milliseconds + * @param[in] checkEvery time in millisecond between status checks + */ + RsTokenService::GxsRequestStatus waitToken( + uint32_t token, + std::chrono::milliseconds maxWait = std::chrono::milliseconds(500), + std::chrono::milliseconds checkEvery = std::chrono::milliseconds(2)) + { +#if defined(__ANDROID__) && (__ANDROID_API__ < 24) + auto wkStartime = std::chrono::steady_clock::now(); + int maxWorkAroundCnt = 10; +LLwaitTokenBeginLabel: +#endif + auto timeout = std::chrono::steady_clock::now() + maxWait; + auto st = requestStatus(token); + while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) + && std::chrono::steady_clock::now() < timeout ) + { + std::this_thread::sleep_for(checkEvery); + st = requestStatus(token); + } + +#if defined(__ANDROID__) && (__ANDROID_API__ < 24) + /* Work around for very slow/old android devices, we don't expect this + * to be necessary on newer devices. If it take unreasonably long + * something worser is already happening elsewere and we return anyway. + */ + if( st > RsTokenService::FAILED && st < RsTokenService::COMPLETE + && maxWorkAroundCnt-- > 0 ) + { + maxWait *= 10; + checkEvery *= 3; + Dbg3() << __PRETTY_FUNCTION__ << " Slow Android device " + << " workaround st: " << st + << " maxWorkAroundCnt: " << maxWorkAroundCnt + << " maxWait: " << maxWait.count() + << " checkEvery: " << checkEvery.count() << std::endl; + goto LLwaitTokenBeginLabel; + } + Dbg3() << __PRETTY_FUNCTION__ << " lasted: " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - wkStartime ).count() + << "ms" << std::endl; + +#endif + + return st; + } + + RS_SET_CONTEXT_DEBUG_LEVEL(2) +}; diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 70e0763bf..fd40a5783 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -2466,6 +2466,85 @@ void p3GxsChannels::cleanTimedOutCallbacks() } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex) } +bool p3GxsChannels::exportChannelLink( + std::string& link, const RsGxsGroupId& chanId, bool includeGxsData, + const std::string& baseUrl, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(chanId.isNull()) return failure("chanId cannot be null"); + + const bool outputRadix = baseUrl.empty(); + if(outputRadix && !includeGxsData) return + failure("includeGxsData must be true if format requested is base64"); + + if( includeGxsData && + !RsGenExchange::exportGroupBase64(link, chanId, errMsg) ) + return failure(errMsg); + + if(outputRadix) return true; + + std::vector chansInfo; + if( !getChannelsInfo(std::list({chanId}), chansInfo) + || chansInfo.empty() ) + return failure("failure retrieving channel information"); + + RsUrl inviteUrl(baseUrl); + inviteUrl.setQueryKV(CHANNEL_URL_ID_FIELD, chanId.toStdString()); + inviteUrl.setQueryKV(CHANNEL_URL_NAME_FIELD, chansInfo[0].mMeta.mGroupName); + if(includeGxsData) inviteUrl.setQueryKV(CHANNEL_URL_DATA_FIELD, link); + + link = inviteUrl.toString(); + return true; +} + +bool p3GxsChannels::importChannelLink( + const std::string& link, RsGxsGroupId& chanId, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(link.empty()) return failure("link is empty"); + + const std::string* radixPtr(&link); + + RsUrl url(link); + const auto& query = url.query(); + const auto qIt = query.find(CHANNEL_URL_DATA_FIELD); + if(qIt != query.end()) radixPtr = &qIt->second; + + if(radixPtr->empty()) return failure(CHANNEL_URL_DATA_FIELD + " is empty"); + + if(!RsGenExchange::importGroupBase64(*radixPtr, chanId, errMsg)) + return failure(errMsg); + + return true; +} + +/*static*/ const std::string RsGxsChannels::DEFAULT_CHANNEL_BASE_URL = + "retroshare:///channels"; +/*static*/ const std::string RsGxsChannels::CHANNEL_URL_NAME_FIELD = + "chanName"; +/*static*/ const std::string RsGxsChannels::CHANNEL_URL_ID_FIELD = + "chanId"; +/*static*/ const std::string RsGxsChannels::CHANNEL_URL_DATA_FIELD = + "chanData"; +/*static*/ const std::string RsGxsChannels::CHANNEL_URL_MSG_TITLE_FIELD = + "chanMsgTitle"; +/*static*/ const std::string RsGxsChannels::CHANNEL_URL_MSG_ID_FIELD = + "chanMsgId"; + RsGxsChannelGroup::~RsGxsChannelGroup() = default; RsGxsChannelPost::~RsGxsChannelPost() = default; RsGxsChannels::~RsGxsChannels() = default; diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 2978dcd0e..40ca193e6 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -246,9 +246,24 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); bool subscribeToChannel( const RsGxsGroupId &groupId, bool subscribe ) override; - /// Implementation of @see RsGxsChannels::setPostRead + /// @see RsGxsChannels virtual bool markRead(const RsGxsGrpMsgIdPair& msgId, bool read); + /// @see RsGxsChannels + bool exportChannelLink( + std::string& link, const RsGxsGroupId& chanId, + bool includeGxsData = true, + const std::string& baseUrl = DEFAULT_CHANNEL_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @see RsGxsChannels + bool importChannelLink( + const std::string& link, + RsGxsGroupId& chanId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + virtual bool shareChannelKeys( const RsGxsGroupId& channelId, const std::set& peers ); diff --git a/libretroshare/src/services/p3gxscircles.cc b/libretroshare/src/services/p3gxscircles.cc index eed23313d..a7d201292 100644 --- a/libretroshare/src/services/p3gxscircles.cc +++ b/libretroshare/src/services/p3gxscircles.cc @@ -339,6 +339,76 @@ bool p3GxsCircles::inviteIdsToCircle( const std::set& identities, return editCircle(circleGrp); } +bool p3GxsCircles::exportCircleLink( + std::string& link, const RsGxsCircleId& circleId, + bool includeGxsData, const std::string& baseUrl, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(circleId.isNull()) return failure("circleId cannot be null"); + + const bool outputRadix = baseUrl.empty(); + if(outputRadix && !includeGxsData) return + failure("includeGxsData must be true if format requested is base64"); + + RsGxsGroupId&& groupId = static_cast(circleId); + if( includeGxsData && + !RsGenExchange::exportGroupBase64(link, groupId, errMsg) ) + return failure(errMsg); + + if(outputRadix) return true; + + std::vector circlesInfo; + if( !getCirclesInfo( + std::list({groupId}), circlesInfo ) + || circlesInfo.empty() ) + return failure("failure retrieving circle information"); + + RsUrl inviteUrl(baseUrl); + inviteUrl.setQueryKV(CIRCLE_URL_ID_FIELD, circleId.toStdString()); + inviteUrl.setQueryKV(CIRCLE_URL_NAME_FIELD, circlesInfo[0].mMeta.mGroupName); + if(includeGxsData) inviteUrl.setQueryKV(CIRCLE_URL_DATA_FIELD, link); + + link = inviteUrl.toString(); + return true; +} + +bool p3GxsCircles::importCircleLink( + const std::string& link, RsGxsCircleId& circleId, + std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(link.empty()) return failure("link is empty"); + + const std::string* radixPtr(&link); + + RsUrl url(link); + const auto& query = url.query(); + const auto qIt = query.find(CIRCLE_URL_DATA_FIELD); + if(qIt != query.end()) radixPtr = &qIt->second; + + if(radixPtr->empty()) return failure(CIRCLE_URL_DATA_FIELD + " is empty"); + + if(!RsGenExchange::importGroupBase64( + *radixPtr, reinterpret_cast(circleId), errMsg) ) + return failure(errMsg); + + return true; +} + uint32_t p3GxsCircles::circleAuthenPolicy() { uint32_t policy = 0; @@ -454,7 +524,8 @@ void p3GxsCircles::notifyChanges(std::vector &changes) /******************* RsCircles Interface ***************************************/ /********************************************************************************/ -bool p3GxsCircles:: getCircleDetails(const RsGxsCircleId &id, RsGxsCircleDetails &details) +bool p3GxsCircles::getCircleDetails( + const RsGxsCircleId& id, RsGxsCircleDetails& details) { #ifdef DEBUG_CIRCLES @@ -2243,6 +2314,12 @@ bool p3GxsCircles::processMembershipRequests(uint32_t token) return true ; } +/*static*/ const std::string RsGxsCircles::DEFAULT_CIRCLE_BASE_URL = + "retroshare:///circles"; +/*static*/ const std::string RsGxsCircles::CIRCLE_URL_NAME_FIELD = "circleName"; +/*static*/ const std::string RsGxsCircles::CIRCLE_URL_ID_FIELD = "circleId"; +/*static*/ const std::string RsGxsCircles::CIRCLE_URL_DATA_FIELD = "circleData"; + RsGxsCircles::~RsGxsCircles() = default; RsGxsCircleMsg::~RsGxsCircleMsg() = default; RsGxsCircleDetails::~RsGxsCircleDetails() = default; diff --git a/libretroshare/src/services/p3gxscircles.h b/libretroshare/src/services/p3gxscircles.h index 9eb6c15bb..07dab3db7 100644 --- a/libretroshare/src/services/p3gxscircles.h +++ b/libretroshare/src/services/p3gxscircles.h @@ -169,14 +169,15 @@ class RsGxsCircleCache class PgpAuxUtils; -class p3GxsCircles: public RsGxsCircleExchange, public RsGxsCircles, public GxsTokenQueue, public RsTickEvent +class p3GxsCircles: public RsGxsCircleExchange, public RsGxsCircles, + public GxsTokenQueue, public RsTickEvent { - public: - p3GxsCircles(RsGeneralDataService* gds, RsNetworkExchangeService* nes, p3IdService *identities, PgpAuxUtils *pgpUtils); +public: + p3GxsCircles( + RsGeneralDataService* gds, RsNetworkExchangeService* nes, + p3IdService* identities, PgpAuxUtils* pgpUtils ); -virtual RsServiceInfo getServiceInfo(); - - /*********** External Interface ***************/ + RsServiceInfo getServiceInfo() override; /// @see RsGxsCircles bool createCircle( @@ -207,6 +208,21 @@ virtual RsServiceInfo getServiceInfo(); bool inviteIdsToCircle( const std::set& identities, const RsGxsCircleId& circleId ) override; + /// @see RsGxsCircles + bool exportCircleLink( + std::string& link, const RsGxsCircleId& circleId, + bool includeGxsData = true, + const std::string& baseUrl = DEFAULT_CIRCLE_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @see RsGxsCircles + bool importCircleLink( + const std::string& link, + RsGxsCircleId& circleId = RS_DEFAULT_STORAGE_PARAM(RsGxsCircleId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + virtual bool getCircleDetails(const RsGxsCircleId &id, RsGxsCircleDetails &details); virtual bool getCircleExternalIdList(std::list &circleIds); diff --git a/libretroshare/src/services/p3gxsforums.cc b/libretroshare/src/services/p3gxsforums.cc index 787ea22e1..775f0ddd3 100644 --- a/libretroshare/src/services/p3gxsforums.cc +++ b/libretroshare/src/services/p3gxsforums.cc @@ -676,6 +676,72 @@ bool p3GxsForums::subscribeToForum( return true; } +bool p3GxsForums::exportForumLink( + std::string& link, const RsGxsGroupId& forumId, bool includeGxsData, + const std::string& baseUrl, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(forumId.isNull()) return failure("forumId cannot be null"); + + const bool outputRadix = baseUrl.empty(); + if(outputRadix && !includeGxsData) return + failure("includeGxsData must be true if format requested is base64"); + + if( includeGxsData && + !RsGenExchange::exportGroupBase64(link, forumId, errMsg) ) + return failure(errMsg); + + if(outputRadix) return true; + + std::vector forumsInfo; + if( !getForumsInfo(std::list({forumId}), forumsInfo) + || forumsInfo.empty() ) + return failure("failure retrieving forum information"); + + RsUrl inviteUrl(baseUrl); + inviteUrl.setQueryKV(FORUM_URL_ID_FIELD, forumId.toStdString()); + inviteUrl.setQueryKV(FORUM_URL_NAME_FIELD, forumsInfo[0].mMeta.mGroupName); + if(includeGxsData) inviteUrl.setQueryKV(FORUM_URL_DATA_FIELD, link); + + link = inviteUrl.toString(); + return true; +} + +bool p3GxsForums::importForumLink( + const std::string& link, RsGxsGroupId& forumId, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(link.empty()) return failure("link is empty"); + + const std::string* radixPtr(&link); + + RsUrl url(link); + const auto& query = url.query(); + const auto qIt = query.find(FORUM_URL_DATA_FIELD); + if(qIt != query.end()) radixPtr = &qIt->second; + + if(radixPtr->empty()) return failure(FORUM_URL_DATA_FIELD + " is empty"); + + if(!RsGenExchange::importGroupBase64(*radixPtr, forumId, errMsg)) + return failure(errMsg); + + return true; +} + bool p3GxsForums::createGroup(uint32_t &token, RsGxsForumGroup &group) { std::cerr << "p3GxsForums::createGroup()" << std::endl; @@ -1005,6 +1071,19 @@ bool RsGxsForumGroup::canEditPosts(const RsGxsId& id) const id == mMeta.mAuthorId; } +/*static*/ const std::string RsGxsForums::DEFAULT_FORUM_BASE_URL = + "retroshare:///forums"; +/*static*/ const std::string RsGxsForums::FORUM_URL_NAME_FIELD = + "forumName"; +/*static*/ const std::string RsGxsForums::FORUM_URL_ID_FIELD = + "forumId"; +/*static*/ const std::string RsGxsForums::FORUM_URL_DATA_FIELD = + "forumData"; +/*static*/ const std::string RsGxsForums::FORUM_URL_MSG_TITLE_FIELD = + "forumMsgTitle"; +/*static*/ const std::string RsGxsForums::FORUM_URL_MSG_ID_FIELD = + "forumMsgId"; + RsGxsForumGroup::~RsGxsForumGroup() = default; RsGxsForumMsg::~RsGxsForumMsg() = default; RsGxsForums::~RsGxsForums() = default; diff --git a/libretroshare/src/services/p3gxsforums.h b/libretroshare/src/services/p3gxsforums.h index 910daff8d..8d2ea3294 100644 --- a/libretroshare/src/services/p3gxsforums.h +++ b/libretroshare/src/services/p3gxsforums.h @@ -110,6 +110,21 @@ public: virtual bool subscribeToForum( const RsGxsGroupId& forumId, bool subscribe ); + /// @see RsGxsForums + bool exportForumLink( + std::string& link, const RsGxsGroupId& forumId, + bool includeGxsData = true, + const std::string& baseUrl = DEFAULT_FORUM_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @see RsGxsForums + bool importForumLink( + const std::string& link, + RsGxsGroupId& forumId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + virtual bool getGroupData(const uint32_t &token, std::vector &groups); virtual bool getMsgData(const uint32_t &token, std::vector &msgs); virtual bool getMsgMetaData(const uint32_t &token, GxsMsgMetaMap& msg_metas); diff --git a/libretroshare/src/services/p3idservice.cc b/libretroshare/src/services/p3idservice.cc index dc2d53cb3..0821f3286 100644 --- a/libretroshare/src/services/p3idservice.cc +++ b/libretroshare/src/services/p3idservice.cc @@ -817,10 +817,6 @@ bool p3IdService::isKnownId(const RsGxsId& id) std::find(mOwnIds.begin(), mOwnIds.end(),id) != mOwnIds.end(); } -bool p3IdService::identityToBase64( const RsGxsId& id, - std::string& base64String ) -{ return serialiseIdentityToMemory(id, base64String); } - bool p3IdService::serialiseIdentityToMemory( const RsGxsId& id, std::string& radix_string ) { @@ -882,10 +878,6 @@ void p3IdService::handle_get_serialized_grp(uint32_t token) mSerialisedIdentities[RsGxsId(id)] = s ; } -bool p3IdService::identityFromBase64( - const std::string& base64String, RsGxsId& id ) -{ return deserialiseIdentityFromMemory(base64String, &id); } - bool p3IdService::deserialiseIdentityFromMemory(const std::string& radix_string, RsGxsId* id /* = nullptr */) { @@ -4717,15 +4709,89 @@ void p3IdService::handle_event(uint32_t event_type, const std::string &/*elabel* } } +/*static*/ const std::string RsIdentity::DEFAULT_IDENTITY_BASE_URL = + "retroshare:///identities"; +/*static*/ const std::string RsIdentity::IDENTITY_URL_NAME_FIELD = "identityName"; +/*static*/ const std::string RsIdentity::IDENTITY_URL_ID_FIELD = "identityId"; +/*static*/ const std::string RsIdentity::IDENTITY_URL_DATA_FIELD = "identityData"; + +bool p3IdService::exportIdentityLink( + std::string& link, const RsGxsId& id, bool includeGxsData, + const std::string& baseUrl, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(id.isNull()) return failure("id cannot be null"); + + const bool outputRadix = baseUrl.empty(); + if(outputRadix && !includeGxsData) return + failure("includeGxsData must be true if format requested is base64"); + + if( includeGxsData && + !RsGenExchange::exportGroupBase64( + link, reinterpret_cast(id), errMsg ) ) + return failure(errMsg); + + if(outputRadix) return true; + + std::vector idsInfo; + if( !getIdentitiesInfo(std::set({id}), idsInfo ) + || idsInfo.empty() ) + return failure("failure retrieving identity information"); + + RsUrl inviteUrl(baseUrl); + inviteUrl.setQueryKV(IDENTITY_URL_ID_FIELD, id.toStdString()); + inviteUrl.setQueryKV(IDENTITY_URL_NAME_FIELD, idsInfo[0].mMeta.mGroupName); + if(includeGxsData) inviteUrl.setQueryKV(IDENTITY_URL_DATA_FIELD, link); + + link = inviteUrl.toString(); + return true; +} + +bool p3IdService::importIdentityLink( + const std::string& link, RsGxsId& id, std::string& errMsg ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + errMsg = err; + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(link.empty()) return failure("link is empty"); + + const std::string* radixPtr(&link); + + RsUrl url(link); + const auto& query = url.query(); + const auto qIt = query.find(IDENTITY_URL_DATA_FIELD); + if(qIt != query.end()) radixPtr = &qIt->second; + + if(radixPtr->empty()) return failure(IDENTITY_URL_DATA_FIELD + " is empty"); + + if(!RsGenExchange::importGroupBase64( + *radixPtr, reinterpret_cast(id), errMsg )) + return failure(errMsg); + + return true; +} + + void RsGxsIdGroup::serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) { RS_SERIAL_PROCESS(mMeta); RS_SERIAL_PROCESS(mPgpIdHash); - //RS_SERIAL_PROCESS(mPgpIdSign); - RS_SERIAL_PROCESS(mRecognTags); - //RS_SERIAL_PROCESS(mImage); + RS_SERIAL_PROCESS(mPgpIdSign); + RS_SERIAL_PROCESS(mImage); RS_SERIAL_PROCESS(mLastUsageTS); RS_SERIAL_PROCESS(mPgpKnown); RS_SERIAL_PROCESS(mIsAContact); @@ -4798,3 +4864,6 @@ RsIdentityUsage::RsIdentityUsage() : RsIdentity::~RsIdentity() = default; RsReputationInfo::~RsReputationInfo() = default; RsGixs::~RsGixs() = default; +RsIdentityDetails::~RsIdentityDetails() = default; +GxsReputation::~GxsReputation() = default; +RsGxsIdGroup::~RsGxsIdGroup() = default; diff --git a/libretroshare/src/services/p3idservice.h b/libretroshare/src/services/p3idservice.h index 0b8f433b6..26121cd8a 100644 --- a/libretroshare/src/services/p3idservice.h +++ b/libretroshare/src/services/p3idservice.h @@ -293,13 +293,32 @@ public: /// @see RsIdentity bool getOwnPseudonimousIds(std::vector& ids) override; + /// @see RsIdentity bool getOwnIds( std::list &ownIds, bool signed_only = false ) override; + /// @see RsIdentity bool isKnownId(const RsGxsId& id) override; + /// @see RsIdentity bool isOwnId(const RsGxsId& key_id) override; + /// @see RsIdentity + bool exportIdentityLink( + std::string& link, const RsGxsId& id, + bool includeGxsData = true, + const std::string& baseUrl = DEFAULT_IDENTITY_BASE_URL, + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @see RsIdentity + bool importIdentityLink( + const std::string& link, + RsGxsId& id = RS_DEFAULT_STORAGE_PARAM(RsGxsId), + std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + virtual bool signData( const uint8_t* data, uint32_t data_size, const RsGxsId& signer_id, @@ -354,17 +373,10 @@ public: const RsIdentityUsage &use_info ); virtual bool requestPrivateKey(const RsGxsId &id); - - /// @see RsIdentity - bool identityToBase64( const RsGxsId& id, - std::string& base64String ) override; - - /// @see RsIdentity - bool identityFromBase64( const std::string& base64String, - RsGxsId& id ) override; - + RS_DEPRECATED_FOR(exportIdentityLink) virtual bool serialiseIdentityToMemory(const RsGxsId& id, std::string& radix_string); + RS_DEPRECATED_FOR(importIdentityLink) virtual bool deserialiseIdentityFromMemory(const std::string& radix_string, RsGxsId* id = nullptr);