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/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 );