diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 4d28cad22..b341a4448 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018 Gioacchino Mazzurco * + * 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 * @@ -33,6 +33,8 @@ #include "serialiser/rsserializable.h" #include "retroshare/rsturtle.h" #include "util/rsdeprecate.h" +#include "retroshare/rsgxscircles.h" +#include "util/rsmemory.h" class RsGxsChannels; @@ -102,34 +104,98 @@ public: /** * @brief Create channel. Blocking API. * @jsonapi{development} - * @param[inout] channel Channel data (name, description...) - * @return false on error, true otherwise + * @param[in] name Name of the channel + * @param[in] description Description of the channel + * @param[in] thumbnail Optional image to show as channel thumbnail. + * @param[in] authorId Optional id of the author. Leave empty for an + * anonymous channel. + * @param[in] circleType Optional visibility rule, default public. + * @param[in] circleId If the channel is not public specify the id of the + * circle who can see the channel. Depending on the value you pass for + * circleType this should be be an external circle if EXTERNAL is passed, a + * local friend group id if NODES_GROUP is passed, empty otherwise. + * @param[out] channelId Optional storage for the id of the created channel, + * meaningful only if creations succeeds. + * @param[out] errorMessage Optional storage for error messsage, meaningful + * only if creation fail. + * @return False on error, true otherwise. */ - virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + virtual bool createChannelV2( + const std::string& name, const std::string& description, + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsId& authorId = RsGxsId(), + RsGxsCircleType circleType = RsGxsCircleType::PUBLIC, + const RsGxsCircleId& circleId = RsGxsCircleId(), + RsGxsGroupId& channelId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; /** * @brief Add a comment on a post or on another comment * @jsonapi{development} - * @param[inout] comment + * @param[in] channelId Id of the channel in which the comment is to be posted + * @param[in] parentId Id of the parent of the comment that is either a + * channel post Id or the Id of another comment. + * @param[in] comment UTF-8 string containing the comment + * @param[out] commentMessageId Optional storage for the id of the comment + * that was created, meaningful only on success. + * @param[out] errorMessage Optional storage for error message, meaningful + * only on failure. * @return false on error, true otherwise */ - virtual bool createComment(RsGxsComment& comment) = 0; + virtual bool createCommentV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& parentId, + const std::string& comment, + RsGxsMessageId& commentMessageId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) )=0; /** * @brief Create channel post. Blocking API. * @jsonapi{development} - * @param[inout] post + * @param[in] channelId Id of the channel where to put the post. Beware you + * need publish rights on that channel to post. + * @param[in] title Title of the post + * @param[in] mBody Text content of the post + * @param[in] files Optional list of attached files. These are supposed to + * be already shared, @see ExtraFileHash() below otherwise. + * @param[in] thumbnail Optional thumbnail image for the post. + * @param[in] origPostId If this is supposed to replace an already existent + * post, the id of the old post. If left blank a new post will be created. + * @param[out] postId Optional storage for the id of the created post, + * meaningful only on success. + * @param[out] errorMessage Optional storage for the error message, + * meaningful only on failure. * @return false on error, true otherwise */ - virtual bool createPost(RsGxsChannelPost& post) = 0; + virtual bool createPostV2( + const RsGxsGroupId& channelId, const std::string& title, + const std::string& mBody, + const std::list& files = std::list(), + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsMessageId& origPostId = RsGxsMessageId(), + RsGxsMessageId& postId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; /** - * @brief createVote + * @brief Create a vote * @jsonapi{development} - * @param[inout] vote + * @param[in] channelId Id of the channel where to vote + * @param[in] postId Id of the channel post of which a comment is voted + * @param[in] commentId Id of the comment that is voted + * @param[in] authorId Id of the author. Needs to be of an owned identity. + * @param[in] vote Vote value, either RsGxsVoteType::DOWN or + * RsGxsVoteType::UP + * @param[out] voteId Optional storage for the id of the created vote, + * meaningful only on success. + * @param[out] errorMessage Optional storage for error message, meaningful + * only on failure. * @return false on error, true otherwise */ - virtual bool createVote(RsGxsVote& vote) = 0; + virtual bool createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType vote, + RsGxsMessageId& voteId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; /** * @brief Edit channel details. @@ -315,6 +381,16 @@ public: /* Following functions are deprecated as they expose internal functioning * semantic, instead of a safe to use API */ + /** + * @brief Create channel. Blocking API. + * @jsonapi{development} + * @deprecated { substituted by createChannelV2 } + * @param[inout] channel Channel data (name, description...) + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createChannelV2) + virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + RS_DEPRECATED_FOR(getChannelsInfo) virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; @@ -372,9 +448,29 @@ public: * @param[in] group Channel data (name, description...) * @return false on error, true otherwise */ - RS_DEPRECATED_FOR(createChannel) + RS_DEPRECATED_FOR(createChannelV2) virtual bool createGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; + /** + * @brief Add a comment on a post or on another comment + * @jsonapi{development} + * @deprecated + * @param[inout] comment + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createCommentV2) + virtual bool createComment(RsGxsComment& comment) = 0; + + /** + * @brief Create channel post. Blocking API. + * @jsonapi{development} + * @deprecated + * @param[inout] post + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createPostV2) + virtual bool createPost(RsGxsChannelPost& post) = 0; + /** * @brief Request post creation. * The action is performed asyncronously, so it could fail in a subsequent @@ -385,9 +481,19 @@ public: * @param[in] post * @return false on error, true otherwise */ - RS_DEPRECATED + RS_DEPRECATED_FOR(createPostV2) virtual bool createPost(uint32_t& token, RsGxsChannelPost& post) = 0; + /** + * @brief createVote + * @jsonapi{development} + * @deprecated + * @param[inout] vote + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createVoteV2) + virtual bool createVote(RsGxsVote& vote) = 0; + /** * @brief Request channel change. * The action is performed asyncronously, so it could fail in a subsequent diff --git a/libretroshare/src/retroshare/rsgxscircles.h b/libretroshare/src/retroshare/rsgxscircles.h index d5f3543d0..83f8e840c 100644 --- a/libretroshare/src/retroshare/rsgxscircles.h +++ b/libretroshare/src/retroshare/rsgxscircles.h @@ -42,24 +42,35 @@ class RsGxsCircles; */ extern RsGxsCircles* rsGxsCircles; +enum class RsGxsCircleType : uint32_t // 32 bit overkill, just for retrocompat +{ + UNKNOWN = 0, /// Used to detect uninizialized values. + PUBLIC = 1, /// Public distribution + EXTERNAL = 2, /// Restricted to an external circle -// TODO: convert to enum -/// The meaning of the different circle types is: -static const uint32_t GXS_CIRCLE_TYPE_UNKNOWN = 0x0000 ; /// Used to detect uninizialized values. -static const uint32_t GXS_CIRCLE_TYPE_PUBLIC = 0x0001 ; // not restricted to a circle -static const uint32_t GXS_CIRCLE_TYPE_EXTERNAL = 0x0002 ; // restricted to an external circle, made of RsGxsId -static const uint32_t GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY = 0x0003 ; // restricted to a subset of friend nodes of a given RS node given by a RsPgpId list -static const uint32_t GXS_CIRCLE_TYPE_LOCAL = 0x0004 ; // not distributed at all -static const uint32_t GXS_CIRCLE_TYPE_EXT_SELF = 0x0005 ; // self-restricted. Not used, except at creation time when the circle ID isn't known yet. Set to EXTERNAL afterwards. -static const uint32_t GXS_CIRCLE_TYPE_YOUR_EYES_ONLY = 0x0006 ; // distributed to nodes signed by your own PGP key only. + /** Restricted to a group of friend nodes, the administrator of the circle + * behave as a hub for them */ + NODES_GROUP = 3, + LOCAL = 4, /// not distributed at all + + /** Self-restricted. Used only at creation time of self-restricted circles + * when the circle id isn't known yet. Once the circle id is known the type + * is set to EXTERNAL, and the external circle id is set to the id of the + * circle itself. + */ + EXT_SELF = 5, + + /// distributed to nodes signed by your own PGP key only. + YOUR_EYES_ONLY = 6 +}; + +// TODO: convert to enum class static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST = 0x0001 ;// user is validated by circle admin static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED = 0x0002 ;// user has subscribed the group static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_KEY_AVAILABLE = 0x0004 ;// key is available, so we can encrypt for this circle static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_ALLOWED = 0x0007 ;// user is allowed. Combines all flags above. -static const uint32_t GXS_CIRCLE_FLAGS_IS_EXTERNAL = 0x0008 ;// user is allowed - struct RsGxsCircleGroup : RsSerializable { @@ -110,8 +121,9 @@ struct RsGxsCircleMsg : RsSerializable struct RsGxsCircleDetails : RsSerializable { RsGxsCircleDetails() : - mCircleType(GXS_CIRCLE_TYPE_EXTERNAL), mAmIAllowed(false) {} - ~RsGxsCircleDetails() {} + mCircleType(static_cast(RsGxsCircleType::EXTERNAL)), + mAmIAllowed(false) {} + ~RsGxsCircleDetails() override {} RsGxsCircleId mCircleId; std::string mCircleName; @@ -264,3 +276,34 @@ public: RS_DEPRECATED_FOR("editCircle, inviteIdsToCircle") virtual void updateGroup(uint32_t &token, RsGxsCircleGroup &group) = 0; }; + + +/// @deprecated Used to detect uninizialized values. +RS_DEPRECATED_FOR("RsGxsCircleType::UNKNOWN") +static const uint32_t GXS_CIRCLE_TYPE_UNKNOWN = 0x0000; + +/// @deprecated not restricted to a circle +RS_DEPRECATED_FOR("RsGxsCircleType::PUBLIC") +static const uint32_t GXS_CIRCLE_TYPE_PUBLIC = 0x0001; + +/// @deprecated restricted to an external circle, made of RsGxsId +RS_DEPRECATED_FOR("RsGxsCircleType::EXTERNAL") +static const uint32_t GXS_CIRCLE_TYPE_EXTERNAL = 0x0002; + +/// @deprecated restricted to a subset of friend nodes of a given RS node given +/// by a RsPgpId list +RS_DEPRECATED_FOR("RsGxsCircleType::NODES_GROUP") +static const uint32_t GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY = 0x0003; + +/// @deprecated not distributed at all +RS_DEPRECATED_FOR("RsGxsCircleType::LOCAL") +static const uint32_t GXS_CIRCLE_TYPE_LOCAL = 0x0004; + +/// @deprecated self-restricted. Not used, except at creation time when the +/// circle ID isn't known yet. Set to EXTERNAL afterwards. +RS_DEPRECATED_FOR("RsGxsCircleType::EXT_SELF") +static const uint32_t GXS_CIRCLE_TYPE_EXT_SELF = 0x0005; + +/// @deprecated distributed to nodes signed by your own PGP key only. +RS_DEPRECATED_FOR("RsGxsCircleType::YOUR_EYES_ONLY") +static const uint32_t GXS_CIRCLE_TYPE_YOUR_EYES_ONLY = 0x0006; diff --git a/libretroshare/src/retroshare/rsgxscommon.h b/libretroshare/src/retroshare/rsgxscommon.h index c8b8ec7a4..aded69f83 100644 --- a/libretroshare/src/retroshare/rsgxscommon.h +++ b/libretroshare/src/retroshare/rsgxscommon.h @@ -19,11 +19,9 @@ * along with this program. If not, see . * * * *******************************************************************************/ +#pragma once -#ifndef RETROSHARE_GXS_COMMON_OBJS_INTERFACE_H -#define RETROSHARE_GXS_COMMON_OBJS_INTERFACE_H - -#include +#include #include #include @@ -79,10 +77,12 @@ struct RsGxsImage : RsSerializable } }; - -#define GXS_VOTE_NONE 0x0000 -#define GXS_VOTE_DOWN 0x0001 -#define GXS_VOTE_UP 0x0002 +enum class RsGxsVoteType : uint32_t +{ + NONE = 0, /// Used to detect unset vote? + DOWN = 1, /// Negative vote + UP = 2 /// Positive vote +}; // Status Flags to indicate Voting.... @@ -181,7 +181,11 @@ struct RsGxsCommentService std::pair& msgId ) = 0; }; +/// @deprecated use RsGxsVoteType::NONE instead @see RsGxsVoteType +#define GXS_VOTE_NONE 0x0000 +/// @deprecated use RsGxsVoteType::DOWN instead @see RsGxsVoteType +#define GXS_VOTE_DOWN 0x0001 -#endif - +/// @deprecated use RsGxsVoteType::UP instead @see RsGxsVoteType +#define GXS_VOTE_UP 0x0002 diff --git a/libretroshare/src/retroshare/rsgxsflags.h b/libretroshare/src/retroshare/rsgxsflags.h index 7a737c74c..ff6f164c2 100644 --- a/libretroshare/src/retroshare/rsgxsflags.h +++ b/libretroshare/src/retroshare/rsgxsflags.h @@ -52,14 +52,15 @@ namespace GXS_SERV { static const uint32_t FLAG_AUTHOR_AUTHENTICATION_MASK = 0x0000ff00; static const uint32_t FLAG_AUTHOR_AUTHENTICATION_NONE = 0x00000000; static const uint32_t FLAG_AUTHOR_AUTHENTICATION_GPG = 0x00000100; // Anti-spam feature. Allows to ask higher reputation to anonymous IDs - static const uint32_t FLAG_AUTHOR_AUTHENTICATION_REQUIRED = 0x00000200; - static const uint32_t FLAG_AUTHOR_AUTHENTICATION_IFNOPUBSIGN = 0x00000400; + static const uint32_t FLAG_AUTHOR_AUTHENTICATION_REQUIRED = 0x00000200; // unused + static const uint32_t FLAG_AUTHOR_AUTHENTICATION_IFNOPUBSIGN = 0x00000400; // ??? static const uint32_t FLAG_AUTHOR_AUTHENTICATION_TRACK_MESSAGES = 0x00000800; // not used anymore static const uint32_t FLAG_AUTHOR_AUTHENTICATION_GPG_KNOWN = 0x00001000; // Anti-spam feature. Allows to ask higher reputation to unknown IDs and anonymous IDs + // These are *not used* static const uint32_t FLAG_GROUP_SIGN_PUBLISH_MASK = 0x000000ff; static const uint32_t FLAG_GROUP_SIGN_PUBLISH_ENCRYPTED = 0x00000001; - static const uint32_t FLAG_GROUP_SIGN_PUBLISH_ALLSIGNED = 0x00000002; + static const uint32_t FLAG_GROUP_SIGN_PUBLISH_ALLSIGNED = 0x00000002; // unused static const uint32_t FLAG_GROUP_SIGN_PUBLISH_THREADHEAD = 0x00000004; static const uint32_t FLAG_GROUP_SIGN_PUBLISH_NONEREQ = 0x00000008; diff --git a/libretroshare/src/retroshare/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index 09cf40dbf..fce16d8f7 100644 --- a/libretroshare/src/retroshare/rsgxsifacehelper.h +++ b/libretroshare/src/retroshare/rsgxsifacehelper.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright 2011 by Christopher Evi-Parker * - * Copyright (C) 2018 Gioacchino Mazzurco * + * 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 * @@ -20,9 +20,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ - -#ifndef RSGXSIFACEIMPL_H -#define RSGXSIFACEIMPL_H +#pragma once #include #include @@ -292,19 +290,51 @@ protected: * 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 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(std::chrono::milliseconds(2)); + 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; } @@ -312,5 +342,3 @@ private: RsGxsIface& mGxs; RsTokenService& mTokenService; }; - -#endif // RSGXSIFACEIMPL_H diff --git a/libretroshare/src/retroshare/rstokenservice.h b/libretroshare/src/retroshare/rstokenservice.h index 58314c62f..515579a07 100644 --- a/libretroshare/src/retroshare/rstokenservice.h +++ b/libretroshare/src/retroshare/rstokenservice.h @@ -121,15 +121,14 @@ public: enum GxsRequestStatus : uint8_t { - FAILED, - PENDING, - PARTIAL, - COMPLETE, - DONE, /// Once all data has been retrived - CANCELLED + FAILED = 0, + PENDING = 1, + PARTIAL = 2, + COMPLETE = 3, + DONE = 4, /// Once all data has been retrived + CANCELLED = 5 }; - RsTokenService() {} virtual ~RsTokenService() {} diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 3f34b6438..b3ff3f78e 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018 Gioacchino Mazzurco * + * 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 * @@ -31,12 +31,14 @@ #include "retroshare/rsgxsflags.h" #include "retroshare/rsfiles.h" +#include "retroshare/rspeers.h" #include "rsserver/p3face.h" #include "retroshare/rsnotify.h" #include #include +#include // For Dummy Msgs. #include "util/rsrandom.h" @@ -1055,26 +1057,143 @@ bool p3GxsChannels::getChannelContent( const RsGxsGroupId& channelId, return getPostData(token, posts, comments); } -bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) +bool p3GxsChannels::createChannelV2( + const std::string& name, const std::string& description, + const RsGxsImage& thumbnail, const RsGxsId& authorId, + RsGxsCircleType circleType, const RsGxsCircleId& circleId, + RsGxsGroupId& channelId, std::string& errorMessage ) { + // do some checks + + if( circleType != RsGxsCircleType::PUBLIC + && circleType != RsGxsCircleType::EXTERNAL + && circleType != RsGxsCircleType::NODES_GROUP + && circleType != RsGxsCircleType::LOCAL + && circleType != RsGxsCircleType::YOUR_EYES_ONLY) + { + errorMessage = "circleType has invalid value"; + return false; + } + + switch(circleType) + { + case RsGxsCircleType::EXTERNAL: + if(circleId.isNull()) + { + errorMessage = "circleType is EXTERNAL but circleId is null"; + return false; + } + break; + + case RsGxsCircleType::NODES_GROUP: + { + RsGroupInfo ginfo; + + if(!rsPeers->getGroupInfo(RsNodeGroupId(circleId), ginfo)) + { + errorMessage = "circleType is NODES_GROUP but circleId does not " + "correspond to an actual group of friends"; + return false; + } + break; + } + default: + if(!circleId.isNull()) + { + errorMessage = "circleType requires a null circleId, but a non " + "null circleId ("; + errorMessage += circleId.toStdString(); + errorMessage += ") was supplied"; + return false; + } + break; + } + + // Create a consistent channel group meta from the information supplied + RsGxsChannelGroup channel; + + channel.mMeta.mGroupName = name; + channel.mMeta.mAuthorId = authorId; + channel.mMeta.mCircleType = static_cast(circleType); + + channel.mMeta.mSignFlags = GXS_SERV::FLAG_GROUP_SIGN_PUBLISH_NONEREQ + | GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_REQUIRED; + + channel.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC; + + channel.mMeta.mCircleId.clear(); + channel.mMeta.mInternalCircle.clear(); + + switch(circleType) + { + case RsGxsCircleType::NODES_GROUP: + channel.mMeta.mInternalCircle = circleId; break; + case RsGxsCircleType::EXTERNAL: + channel.mMeta.mCircleId = circleId; break; + default: break; + } + + // Create the channel + channel.mDescription = description; + channel.mImage = thumbnail; + uint32_t token; if(!createGroup(token, channel)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating group." + errorMessage = "Failed creating GXS group."; + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage << std::endl; return false; } - if(waitToken(token) != RsTokenService::COMPLETE) + // wait for the group creation to complete. + RsTokenService::GxsRequestStatus wSt = waitToken(token); + if(wSt != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + errorMessage = "GXS operation waitToken failed with: " + + std::to_string(wSt); + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage << std::endl; return false; } if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " + errorMessage = "Failure getting updated group data."; + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage + << std::endl; + return false; + } + + channelId = channel.mMeta.mGroupId; + +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelGroup(channel); +#endif // RS_DEEP_SEARCH + + return true; +} + +bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) +{ + uint32_t token; + if(!createGroup(token, channel)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed creating group." + << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting updated " << " group data." << std::endl; return false; } @@ -1086,53 +1205,121 @@ bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) return true; } -bool p3GxsChannels::createComment(RsGxsComment& comment) +bool p3GxsChannels::createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType tVote, RsGxsMessageId& voteId, std::string& errorMessage ) { - uint32_t token; - if(!createNewComment(token, comment)) + std::vector channelsInfo; + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating comment." - << std::endl; + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; + return false; + } + + if(commentId.isNull()) + { + errorMessage = "You cannot vote on null comment " + + commentId.toStdString(); + return false; + } + + std::set s({commentId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent(channelId, s, posts, comments)) + { + errorMessage = "You cannot vote on comment " + + commentId.toStdString() + " of channel with Id " + + channelId.toStdString() + + ": this comment does not exists locally!"; + return false; + } + + // is the ID a comment ID or a post ID? + // It should be comment => should have a parent ID + if(posts.front().mMeta.mParentId.isNull()) + { + errorMessage = "You cannot vote on channel message " + + commentId.toStdString() + " of channel with Id " + + channelId.toStdString() + + ": given id refers to a post, not a comment!"; + return false; + } + + if( tVote != RsGxsVoteType::NONE + && tVote != RsGxsVoteType::UP + && tVote != RsGxsVoteType::DOWN ) + { + errorMessage = "Your vote to channel with Id " + + channelId.toStdString() + " has wrong vote type. " + + " Only RsGxsVoteType::NONE, RsGxsVoteType::UP, " + + "RsGxsVoteType::DOWN are accepted."; + return false; + } + + if(!rsIdentity->isOwnId(authorId)) + { + errorMessage = "You cannot vote to channel with Id " + + channelId.toStdString() + " with identity " + + authorId.toStdString() + " because it is not yours."; + return false; + } + + // Create the vote + RsGxsVote vote; + vote.mMeta.mGroupId = channelId; + vote.mMeta.mThreadId = postId; + vote.mMeta.mParentId = commentId; + vote.mMeta.mAuthorId = authorId; + vote.mVoteType = static_cast(tVote); + + uint32_t token; + if(!createNewVote(token, vote)) + { + errorMessage = "Error! Failed creating vote."; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." - << std::endl; + errorMessage = "GXS operation failed."; return false; } - if(!RsGenExchange::getPublishedMsgMeta(token, comment.mMeta)) + if(!RsGenExchange::getPublishedMsgMeta(token, vote.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting generated " - << " comment data." << std::endl; + errorMessage = "Failure getting generated vote data."; return false; } + voteId = vote.mMeta.mMsgId; return true; } +/// @deprecated use createVoteV2 instead bool p3GxsChannels::createVote(RsGxsVote& vote) { uint32_t token; if(!createNewVote(token, vote)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating vote." + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed creating vote." << std::endl; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." << std::endl; return false; } if(!RsGenExchange::getPublishedMsgMeta(token, vote.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting generated " + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting generated " << " vote data." << std::endl; return false; } @@ -1145,21 +1332,21 @@ bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) uint32_t token; if(!updateGroup(token, channel)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed updating group." + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed updating group." << std::endl; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." << std::endl; return false; } if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting updated " << " group data." << std::endl; return false; } @@ -1171,11 +1358,64 @@ bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) return true; } -bool p3GxsChannels::createPost(RsGxsChannelPost& post) +bool p3GxsChannels::createPostV2( + const RsGxsGroupId& channelId, const std::string& title, + const std::string& body, const std::list& files, + const RsGxsImage& thumbnail, const RsGxsMessageId& origPostId, + RsGxsMessageId& postId, std::string& errorMessage ) { + // Do some checks + + std::vector channelsInfo; + + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) + { + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; + return false; + } + + const RsGxsChannelGroup& cg(*channelsInfo.begin()); + + if(!(cg.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)) + { + errorMessage = "You cannot post to channel with Id " + + channelId.toStdString() + ": missing publish rights!"; + return false; + } + + if(!origPostId.isNull()) + { + std::set s({origPostId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent(channelId,s,posts,comments)) + { + errorMessage = "You cannot edit post " + origPostId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": this post does not exist locally!"; + return false; + } + } + + // Create the post + RsGxsChannelPost post; + + post.mMeta.mGroupId = channelId; + post.mMeta.mOrigMsgId = origPostId; + post.mMeta.mMsgName = title; + + post.mMsg = body; + post.mFiles = files; + post.mThumbnail = thumbnail; + uint32_t token; - if( !createPost(token, post) - || waitToken(token) != RsTokenService::COMPLETE ) return false; + if(!createPost(token, post) || waitToken(token) != RsTokenService::COMPLETE) + { + errorMessage = "GXS operation failed"; + return false; + } if(RsGenExchange::getPublishedMsgMeta(token,post.mMeta)) { @@ -1183,12 +1423,104 @@ bool p3GxsChannels::createPost(RsGxsChannelPost& post) DeepSearch::indexChannelPost(post); #endif // RS_DEEP_SEARCH + postId = post.mMeta.mMsgId; return true; } + errorMessage = "Failed to retrive created post metadata"; return false; } +bool p3GxsChannels::createCommentV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& parentId, + const std::string& comment, RsGxsMessageId& commentMessageId, + std::string& errorMessage ) +{ + std::vector channelsInfo; + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) + { + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; + return false; + } + + if(parentId.isNull()) + { + errorMessage = "You cannot comment post " + parentId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": please supply a non null post Id!"; + return false; + } + + std::set s({parentId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent(channelId,s,posts,comments)) + { + errorMessage = "You cannot comment post " + parentId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": this post does not exists locally!"; + return false; + } + + // Now create the comment + RsGxsComment cmt; + + cmt.mComment = comment; + cmt.mMeta.mGroupId = channelId; + cmt.mMeta.mParentId = parentId; + + uint32_t token; + if(!createNewComment(token, cmt)) + { + errorMessage = "Failed creating comment."; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + errorMessage = "GXS operation failed."; + return false; + } + + if(!RsGenExchange::getPublishedMsgMeta(token, cmt.mMeta)) + { + errorMessage = "Failure getting generated comment data."; + return false; + } + + commentMessageId = cmt.mMeta.mMsgId; + return true; +} + +bool p3GxsChannels::createComment(RsGxsComment& comment) // deprecated +{ + uint32_t token; + if(!createNewComment(token, comment)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed creating comment." + << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedMsgMeta(token, comment.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting generated " + << " comment data." << std::endl; + return false; + } + + return true; +} + bool p3GxsChannels::subscribeToChannel( const RsGxsGroupId& groupId, bool subscribe ) { @@ -1428,13 +1760,31 @@ bool p3GxsChannels::updateGroup(uint32_t &token, RsGxsChannelGroup &group) return true; } +/// @deprecated use createPostV2 instead +bool p3GxsChannels::createPost(RsGxsChannelPost& post) +{ + uint32_t token; + if( !createPost(token, post) + || waitToken(token) != RsTokenService::COMPLETE ) return false; + + if(RsGenExchange::getPublishedMsgMeta(token,post.mMeta)) + { +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelPost(post); +#endif // RS_DEEP_SEARCH + + return true; + } + + return false; +} bool p3GxsChannels::createPost(uint32_t &token, RsGxsChannelPost &msg) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::createChannelPost() GroupId: " << msg.mMeta.mGroupId; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " GroupId: " << msg.mMeta.mGroupId + << std::endl; #endif RsGxsChannelPostItem* msgItem = new RsGxsChannelPostItem(); @@ -1976,7 +2326,7 @@ bool p3GxsChannels::turtleChannelRequest( { if(channelId.isNull()) { - std::cerr << __PRETTY_FUNCTION__ << "Error! channelId can't be null!" + std::cerr << __PRETTY_FUNCTION__ << " Error! channelId can't be null!" << std::endl; return false; } diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 7ac63f8e7..0261d7bb6 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018 Gioacchino Mazzurco * + * 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 * @@ -20,14 +20,14 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef P3_GXSCHANNELS_SERVICE_HEADER -#define P3_GXSCHANNELS_SERVICE_HEADER +#pragma once #include "retroshare/rsgxschannels.h" #include "services/p3gxscommon.h" #include "gxs/rsgenexchange.h" #include "gxs/gxstokenqueue.h" +#include "util/rsmemory.h" #include "util/rstickevent.h" @@ -35,6 +35,7 @@ #include + class SSGxsChannelGroup { public: @@ -191,23 +192,63 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); std::vector& comments ); /// Implementation of @see RsGxsChannels::getContentSummaries - virtual bool getContentSummaries( const RsGxsGroupId& channelId, - std::vector& summaries ); + virtual bool getContentSummaries( + const RsGxsGroupId& channelId, + std::vector& summaries ) override; - /// Implementation of @see RsGxsChannels::createChannel - virtual bool createChannel(RsGxsChannelGroup& channel); + /// Implementation of @see RsGxsChannels::createChannelV2 + virtual bool createChannelV2( + const std::string& name, const std::string& description, + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsId& authorId = RsGxsId(), + RsGxsCircleType circleType = RsGxsCircleType::PUBLIC, + const RsGxsCircleId& circleId = RsGxsCircleId(), + RsGxsGroupId& channelId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @deprecated Implementation of @see RsGxsChannels::createComment + RS_DEPRECATED_FOR(createCommentV2) + virtual bool createComment(RsGxsComment& comment) override; /// Implementation of @see RsGxsChannels::createComment - virtual bool createComment(RsGxsComment& comment); + virtual bool createCommentV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& parentId, + const std::string& comment, + RsGxsMessageId& commentMessageId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; /// Implementation of @see RsGxsChannels::editChannel - virtual bool editChannel(RsGxsChannelGroup& channel); + virtual bool editChannel(RsGxsChannelGroup& channel) override; - /// Implementation of @see RsGxsChannels::createPost - virtual bool createPost(RsGxsChannelPost& post); + /// @deprecated Implementation of @see RsGxsChannels::createPost + RS_DEPRECATED_FOR(createPostV2) + virtual bool createPost(RsGxsChannelPost& post) override; - /// Implementation of @see RsGxsChannels::createVote - virtual bool createVote(RsGxsVote& vote); + /// Implementation of @see RsGxsChannels::createPostV2 + bool createPostV2( + const RsGxsGroupId& channelId, const std::string& title, + const std::string& body, + const std::list& files = std::list(), + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsMessageId& origPostId = RsGxsMessageId(), + RsGxsMessageId& postId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @deprecated Implementation of @see RsGxsChannels::createVote + RS_DEPRECATED_FOR(createVoteV2) + virtual bool createVote(RsGxsVote& vote) override; + + /// Implementation of @see RsGxsChannels::createVoteV2 + virtual bool createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType vote, + RsGxsMessageId& voteId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; /// Implementation of @see RsGxsChannels::subscribeToChannel virtual bool subscribeToChannel( const RsGxsGroupId &groupId, @@ -219,6 +260,10 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); virtual bool shareChannelKeys( const RsGxsGroupId& channelId, const std::set& peers ); + /// Implementation of @see RsGxsChannels::createChannel + RS_DEPRECATED_FOR(createChannelV2) + virtual bool createChannel(RsGxsChannelGroup& channel) override; + protected: // Overloaded from GxsTokenQueue for Request callbacks. virtual void handleResponse(uint32_t token, uint32_t req_type); @@ -263,9 +308,11 @@ bool generateGroup(uint32_t &token, std::string groupName); class ChannelDummyRef { public: - ChannelDummyRef() { return; } - ChannelDummyRef(const RsGxsGroupId &grpId, const RsGxsMessageId &threadId, const RsGxsMessageId &msgId) - :mGroupId(grpId), mThreadId(threadId), mMsgId(msgId) { return; } + ChannelDummyRef() {} + ChannelDummyRef( + const RsGxsGroupId &grpId, const RsGxsMessageId &threadId, + const RsGxsMessageId &msgId ) : + mGroupId(grpId), mThreadId(threadId), mMsgId(msgId) {} RsGxsGroupId mGroupId; RsGxsMessageId mThreadId; @@ -309,5 +356,3 @@ bool generateGroup(uint32_t &token, std::string groupName); /// Cleanup mSearchCallbacksMap and mDistantChannelsCallbacksMap void cleanTimedOutCallbacks(); }; - -#endif diff --git a/libretroshare/src/util/rsmemory.h b/libretroshare/src/util/rsmemory.h index b46e12ad1..7b475bcaf 100644 --- a/libretroshare/src/util/rsmemory.h +++ b/libretroshare/src/util/rsmemory.h @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2012 by Cyril Soler * + * Copyright 2012 Cyril Soler * + * Copyright 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 * @@ -21,9 +22,45 @@ *******************************************************************************/ #pragma once -#include +#include #include -#include +#include + +#include "util/stacktrace.h" + +/** + * @brief Shorthand macro to declare optional functions output parameters + * To define an optional output paramether use the following syntax + * +\code{.cpp} +bool myFunnyFunction( + int mandatoryParamether, + BigType& myOptionalOutput = RS_DEFAULT_STORAGE_PARAM(BigType) ) +\endcode + * + * The function caller then can call myFunnyFunction either passing + * myOptionalOutput parameter or not. + * @see RsGxsChannels methods for real usage examples. + * + * @details + * When const references are used to pass function parameters it is easy do make + * those params optional by defining a default value in the function + * declaration, because a temp is accepted as default parameter in those cases. + * It is not as simple when one want to make optional a non-const reference + * parameter that is usually used as output, in that case as a temp is in theory + * not acceptable. + * Yet it is possible to overcome that limitation with the following trick: + * If not passed as parameter the storage for the output parameter can be + * dinamically allocated directly by the function call, to avoid leaking memory + * on each function call the pointer to that storage is made unique so once the + * function returns it goes out of scope and is automatically deleted. + * About performance overhead: std::unique_ptr have very good performance and + * modern compilers may be even able to avoid the dynamic allocation in this + * case, any way the allocation would only happen if the parameter is not + * passed, so any effect on performace would happen only in case where the + * function is called without the parameter. + */ +#define RS_DEFAULT_STORAGE_PARAM(Type) *std::unique_ptr(new Type) void *rs_malloc(size_t size) ; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp index 4bf950a5e..fb50f9acc 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp @@ -755,12 +755,12 @@ void GxsGroupDialog::setGroupSignFlags(uint32_t signFlags) // (cyril) very weird piece of code. Need to clear this up. ui.comments_allowed->setChecked(true); - ui.commentsValueLabel->setText("Allowed") ; + ui.commentsValueLabel->setText("Allowed") ; } else { ui.comments_no->setChecked(true); - ui.commentsValueLabel->setText("Allowed") ; + ui.commentsValueLabel->setText("Forbidden") ; } } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp index b33c49b60..6fb832a69 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp @@ -61,11 +61,15 @@ const uint32_t ChannelEditDefaultsFlags = ChannelCreateDefaultsFlags; GxsChannelGroupDialog::GxsChannelGroupDialog(TokenQueue *tokenQueue, QWidget *parent) : GxsGroupDialog(tokenQueue, ChannelCreateEnabledFlags, ChannelCreateDefaultsFlags, parent) { + ui.commentGroupBox->setEnabled(false); // These are here because comments_allowed are actually not used yet, so the group will not be changed by the setting and when + ui.comments_allowed->setChecked(true); // the group info is displayed it will therefore be set to "disabled" in all cases although it is enabled. } GxsChannelGroupDialog::GxsChannelGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) : GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, ChannelEditEnabledFlags, ChannelEditDefaultsFlags, parent) { + ui.commentGroupBox->setEnabled(false); // These are here because comments_allowed are actually not used yet, so the group will not be changed by the setting and when + ui.comments_allowed->setChecked(true); // the group info is displayed it will therefore be set to "disabled" in all cases although it is enabled. } void GxsChannelGroupDialog::initUi()