From e0af46eb854848d27d2d457264cbb1ae3bd3d987 Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 30 Mar 2019 22:53:14 +0100 Subject: [PATCH 1/4] added new method to create a channel with more explicit parameters and JSon API for it --- libretroshare/src/retroshare/rsgxschannels.h | 22 ++++ libretroshare/src/retroshare/rsgxsflags.h | 7 +- libretroshare/src/services/p3gxschannels.cc | 111 ++++++++++++++++++ libretroshare/src/services/p3gxschannels.h | 10 ++ retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp | 4 +- .../gui/gxschannels/GxsChannelGroupDialog.cpp | 4 + 6 files changed, 153 insertions(+), 5 deletions(-) diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 4d28cad22..8ed0dc285 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -107,6 +107,28 @@ public: */ virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + /** + * @brief Create channel. Blocking API. + * @jsonapi{development} + * @param[in] name Name of the channel + * @param[in] description Description of the channel + * @param[in] image Thumbnail that is shown to advertise the channel. Possibly empty. + * @param[in] author_id GxsId of the contact author. For an anonymous channel, leave this to RsGxsId()="00000....0000" + * @param[in] circle_type Type of visibility restriction, among { GXS_CIRCLE_TYPE_PUBLIC, GXS_CIRCLE_TYPE_EXTERNAL, GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GXS_CIRCLE_TYPE_YOUR_EYES_ONLY } + * @param[in] circle_id Id of the circle (should be an external circle or GXS_CIRCLE_TYPE_EXTERNAL, a local friend group for GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GxsCircleId()="000....000" otherwise + * @param[out] channel_group_id Group id of the created channel, if command succeeds. + * @param[out] error_message Error messsage supplied when the channel creation fails. + * @return False on error, true otherwise. + */ + virtual bool createChannel(const std::string& name, + const std::string& description, + const RsGxsImage& image, + const RsGxsId& author_id, + uint32_t circle_type, + RsGxsCircleId& circle_id, + RsGxsGroupId& channel_group_id, + std::string& error_message)=0; + /** * @brief Add a comment on a post or on another comment * @jsonapi{development} 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/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 3f34b6438..f332dfbac 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -31,6 +31,7 @@ #include "retroshare/rsgxsflags.h" #include "retroshare/rsfiles.h" +#include "retroshare/rspeers.h" #include "rsserver/p3face.h" #include "retroshare/rsnotify.h" @@ -1055,6 +1056,116 @@ bool p3GxsChannels::getChannelContent( const RsGxsGroupId& channelId, return getPostData(token, posts, comments); } +bool p3GxsChannels::createChannel(const std::string& name, + const std::string& description, + const RsGxsImage& image, + const RsGxsId& author_id, + uint32_t circle_type, + RsGxsCircleId& circle_id, + RsGxsGroupId& channel_group_id, + std::string& error_message) +{ + // do some checks + + if( circle_type != GXS_CIRCLE_TYPE_PUBLIC + && circle_type != GXS_CIRCLE_TYPE_EXTERNAL + && circle_type != GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY + && circle_type != GXS_CIRCLE_TYPE_LOCAL + && circle_type != GXS_CIRCLE_TYPE_YOUR_EYES_ONLY ) + { + error_message = std::string("circle_type has a non allowed value") ; + return false ; + } + + switch(circle_type) + { + case GXS_CIRCLE_TYPE_EXTERNAL: + if(circle_id.isNull()) + { + error_message = std::string("circle_type is GXS_CIRCLE_TYPE_EXTERNAL but circle_id is null"); + return false ; + } + break; + + case GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY: + { + RsGroupInfo ginfo; + + if(!rsPeers->getGroupInfo(RsNodeGroupId(circle_id),ginfo)) + { + error_message = std::string("circle_type is GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY but circle_id is not set or does not correspond to a actual group of friends"); + return false; + } + } + break; + + default: + if(!circle_id.isNull()) + { + error_message = std::string("circle_type requires a null circle id, but a non null circle id (" + circle_id.toStdString() + ") was supplied"); + return false; + } + } + + // Create a consistent channel group meta from the information supplied + + RsGxsChannelGroup channel ; + + channel.mMeta.mGroupName = name ; + channel.mMeta.mAuthorId = author_id ; + channel.mMeta.mCircleType = circle_type ; + + 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(); + + if(circle_type == GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY) + channel.mMeta.mInternalCircle = circle_id; + else if(circle_type == GXS_CIRCLE_TYPE_EXTERNAL) + channel.mMeta.mCircleId = circle_id; + + // Create the channel + + channel.mDescription = description ; + channel.mImage = image ; + + uint32_t token; + if(!createGroup(token, channel)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating group." + << std::endl; + return false; + } + + // wait for the group creation to complete. + + 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; + } + + channel_group_id = channel.mMeta.mGroupId; + +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelGroup(channel); +#endif // RS_DEEP_SEARCH + + return true; +} + bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) { uint32_t token; diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 7ac63f8e7..6ddcedadb 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -197,6 +197,16 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); /// Implementation of @see RsGxsChannels::createChannel virtual bool createChannel(RsGxsChannelGroup& channel); + /// Implementation of @see RsGxsChannels::createChannel + virtual bool createChannel(const std::string& name, + const std::string& description, + const RsGxsImage& image, + const RsGxsId& author_id, + uint32_t circle_type, + RsGxsCircleId& circle_id, + RsGxsGroupId& channel_group_id, + std::string& error_message); + /// Implementation of @see RsGxsChannels::createComment virtual bool createComment(RsGxsComment& comment); 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() From 44c1f1580f762d5d172526b703e7d63dfc611ef2 Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 31 Mar 2019 22:11:09 +0200 Subject: [PATCH 2/4] added other clean API entries for channel: createComment(), createVote() and createPost() --- libretroshare/src/retroshare/rsgxschannels.h | 63 ++++-- libretroshare/src/services/p3gxschannels.cc | 200 ++++++++++++++++--- libretroshare/src/services/p3gxschannels.h | 33 +++ 3 files changed, 256 insertions(+), 40 deletions(-) diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 8ed0dc285..9f76aec84 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -99,6 +99,7 @@ public: explicit RsGxsChannels(RsGxsIface& gxs) : RsGxsIfaceHelper(gxs) {} virtual ~RsGxsChannels() {} +#ifdef REMOVED /** * @brief Create channel. Blocking API. * @jsonapi{development} @@ -106,6 +107,7 @@ public: * @return false on error, true otherwise */ virtual bool createChannel(RsGxsChannelGroup& channel) = 0; +#endif /** * @brief Create channel. Blocking API. @@ -115,43 +117,76 @@ public: * @param[in] image Thumbnail that is shown to advertise the channel. Possibly empty. * @param[in] author_id GxsId of the contact author. For an anonymous channel, leave this to RsGxsId()="00000....0000" * @param[in] circle_type Type of visibility restriction, among { GXS_CIRCLE_TYPE_PUBLIC, GXS_CIRCLE_TYPE_EXTERNAL, GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GXS_CIRCLE_TYPE_YOUR_EYES_ONLY } - * @param[in] circle_id Id of the circle (should be an external circle or GXS_CIRCLE_TYPE_EXTERNAL, a local friend group for GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GxsCircleId()="000....000" otherwise + * @param[in] circle_id Id of the circle (should be an external circle for GXS_CIRCLE_TYPE_EXTERNAL, a local friend group for GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GxsCircleId()="000....000" otherwise * @param[out] channel_group_id Group id of the created channel, if command succeeds. * @param[out] error_message Error messsage supplied when the channel creation fails. * @return False on error, true otherwise. */ virtual bool createChannel(const std::string& name, const std::string& description, - const RsGxsImage& image, - const RsGxsId& author_id, - uint32_t circle_type, - RsGxsCircleId& circle_id, - RsGxsGroupId& channel_group_id, - std::string& error_message)=0; + const RsGxsImage& image, + const RsGxsId& author_id, + uint32_t circle_type, + RsGxsCircleId& circle_id, + RsGxsGroupId& channel_group_id, + std::string& error_message )=0; /** * @brief Add a comment on a post or on another comment * @jsonapi{development} - * @param[inout] comment + * @param[in] groupId Id of the channel in which the comment is to be posted + * @param[in] parentMsgId 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 Id of the comment that was created + * @param[out] error_string Error message supplied when the comment creation fails. * @return false on error, true otherwise */ - virtual bool createComment(RsGxsComment& comment) = 0; + virtual bool createComment(const RsGxsGroupId& groupId, + const RsGxsMessageId& parentMsgId, + const std::string& comment, + RsGxsMessageId& commentMessageId, + std::string& error_message )=0; /** * @brief Create channel post. Blocking API. * @jsonapi{development} - * @param[inout] post + * @param[in] groupId Id of the channel where to put the post (publish rights needed!) + * @param[in] origMsgId Id of the post you are replacing. If left blank (RsGxsMssageId()="0000.....0000", a new post will be created + * @param[in] msgName Title of the post + * @param[in] msg Text content of the post + * @param[in] files List of attached files. These are supposed to be shared otherwise (use ExtraFileHash() below) + * @param[in] thumbnail Image displayed in the list of posts. Can be left blank. + * @param[out] messsageId Id of the message that was created + * @param[out] error_message Error text if anything bad happens * @return false on error, true otherwise */ - virtual bool createPost(RsGxsChannelPost& post) = 0; - + virtual bool createPost(const RsGxsGroupId& groupId, + const RsGxsMessageId& origMsgId, + const std::string& msgName, + const std::string& msg, + const std::list& files, + const RsGxsImage& thumbnail, + RsGxsMessageId& messageId, + std::string& error_message) = 0; /** * @brief createVote * @jsonapi{development} - * @param[inout] vote + * @param[in] groupId Id of the channel where to put the post (publish rights needed!) + * @param[in] threadId Id of the channel post in which a comment is voted + * @param[in] commentMesssageId Id of the comment that is voted + * @param[in] authorId Id of the author. Needs to be your identity. + * @param[in] voteType Type of vote (GXS_VOTE_NONE=0x00, GXS_VOTE_DOWN=0x01, GXS_VOTE_UP=0x02) + * @param[out] voteMessageId Id of the vote message produced + * @param[out] error_message Error text if anything bad happens * @return false on error, true otherwise */ - virtual bool createVote(RsGxsVote& vote) = 0; + virtual bool createVote( const RsGxsGroupId& groupId, + const RsGxsMessageId& threadId, + const RsGxsMessageId& commentMessageId, + const RsGxsId& authorId, + uint32_t voteType, + RsGxsMessageId& voteMessageId, + std::string& error_message)=0; /** * @brief Edit channel details. diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index f332dfbac..e1fdd9281 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -1166,6 +1166,7 @@ bool p3GxsChannels::createChannel(const std::string& name, return true; } +#ifdef REMOVED bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) { uint32_t token; @@ -1196,58 +1197,91 @@ bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) return true; } +#endif -bool p3GxsChannels::createComment(RsGxsComment& comment) +bool p3GxsChannels::createVote( const RsGxsGroupId& groupId, + const RsGxsMessageId & threadId, + const RsGxsMessageId& commentMessageId, + const RsGxsId& authorId, + uint32_t voteType, + RsGxsMessageId& voteMessageId, + std::string& error_message) { - uint32_t token; - if(!createNewComment(token, comment)) + // Do some checks + + std::vector channelsInfo; + + if(!getChannelsInfo(std::list({groupId}),channelsInfo)) // does the channel actually exist? + { + error_message = std::string("Channel with Id " + groupId.toStdString() + " does not exist."); + return false; + } + + if(commentMessageId.isNull()) // has a correct comment msg id been supplied? + { + error_message = std::string("You cannot vote on null comment " + commentMessageId.toStdString() + " of channel with Id " + groupId.toStdString() + ": please supply a non null comment Id!"); + return false; + } + + std::set s({commentMessageId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent( groupId,s,posts,comments )) // does the comment to vote actually exist? { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating comment." - << std::endl; + error_message = std::string("You cannot vote on comment " + commentMessageId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this comment does not locally exist!"); return false; } - if(waitToken(token) != RsTokenService::COMPLETE) + if(posts.front().mMeta.mParentId.isNull()) // is the ID a comment ID or a post ID? It should be comment => should have a parent ID { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." - << std::endl; + error_message = std::string("You cannot vote on channel message " + commentMessageId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this ID refers to a post, not a comment!"); return false; } - if(!RsGenExchange::getPublishedMsgMeta(token, comment.mMeta)) - { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting generated " - << " comment data." << std::endl; + if(voteType != GXS_VOTE_NONE && voteType != GXS_VOTE_UP && voteType != GXS_VOTE_DOWN) // is voteType consistent? + { + error_message = std::string("Your vote to channel with Id " + groupId.toStdString() + " has wrong vote type. Only GXS_VOTE_NONE, GXS_VOTE_UP, GXS_VOTE_DOWN accepted."); return false; - } + } - return true; -} + if(!rsIdentity->isOwnId(authorId)) // is the voter ID actually ours? + { + error_message = std::string("You cannot vote to channel with Id " + groupId.toStdString() + " with identity " + authorId.toStdString() + " because it is not yours."); + return false; + } + + // Create the vote + + RsGxsVote vote; + + vote.mMeta.mGroupId = groupId; + vote.mMeta.mThreadId = threadId; + vote.mMeta.mParentId = commentMessageId; + vote.mMeta.mAuthorId = authorId; + + vote.mVoteType = voteType; -bool p3GxsChannels::createVote(RsGxsVote& vote) -{ uint32_t token; if(!createNewVote(token, vote)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating vote." - << std::endl; + error_message = std::string("Error! Failed creating vote."); return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." - << std::endl; + error_message = std::string("Error! GXS operation failed."); return false; } if(!RsGenExchange::getPublishedMsgMeta(token, vote.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting generated " - << " vote data." << std::endl; + error_message = std::string("Error! Failure getting generated vote data."); return false; } + voteMessageId = vote.mMeta.mMsgId; return true; } @@ -1282,11 +1316,60 @@ bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) return true; } -bool p3GxsChannels::createPost(RsGxsChannelPost& post) +bool p3GxsChannels::createPost(const RsGxsGroupId& groupId, + const RsGxsMessageId &origMsgId, + const std::string& msgName, + const std::string& msg, + const std::list& files, + const RsGxsImage& thumbnail, + RsGxsMessageId &message_id, + std::string& error_message) { + // Do some checks + + std::vector channelsInfo; + + if(!getChannelsInfo(std::list({groupId}),channelsInfo)) + { + error_message = std::string("Channel with Id " + groupId.toStdString() + " does not exist."); + return false; + } + + const RsGxsChannelGroup& cg(*channelsInfo.begin()); + + if(!(cg.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)) + { + error_message = std::string("You cannot post to channel with Id " + groupId.toStdString() + ": missing publish rights!"); + return false; + } + + if(!origMsgId.isNull()) + { + std::set s({origMsgId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent( groupId,s,posts,comments )) + { + error_message = std::string("You cannot edit post " + origMsgId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this post does not locally exist!"); + return false; + } + } + + // Create the post + RsGxsChannelPost post; + + post.mMeta.mGroupId = groupId; + post.mMeta.mOrigMsgId = origMsgId; + post.mMeta.mMsgName = msgName; + + post.mMsg = msg; + 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 ) + return false; if(RsGenExchange::getPublishedMsgMeta(token,post.mMeta)) { @@ -1294,12 +1377,77 @@ bool p3GxsChannels::createPost(RsGxsChannelPost& post) DeepSearch::indexChannelPost(post); #endif // RS_DEEP_SEARCH + message_id = post.mMeta.mMsgId; return true; } + error_message = std::string("cannot publish message. Check that you have publish rights on this channel?"); return false; } +bool p3GxsChannels::createComment(const RsGxsGroupId& groupId, + const RsGxsMessageId& parentMsgId, + const std::string& comment, + RsGxsMessageId& commentMessageId, + std::string& error_message) +{ + // Do some checks + + std::vector channelsInfo; + + if(!getChannelsInfo(std::list({groupId}),channelsInfo)) + { + error_message = std::string("Channel with Id " + groupId.toStdString() + " does not exist."); + return false; + } + + if(parentMsgId.isNull()) + { + error_message = std::string("You cannot comment post " + parentMsgId.toStdString() + " of channel with Id " + groupId.toStdString() + ": please supply a non null post Id!"); + return false; + } + + std::set s({parentMsgId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent( groupId,s,posts,comments )) + { + error_message = std::string("You cannot comment post " + parentMsgId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this post does not locally exist!"); + return false; + } + + // Now create the comment + + RsGxsComment cmt; + + cmt.mComment = comment; + cmt.mMeta.mGroupId = groupId; + cmt.mMeta.mParentId = parentMsgId; + + uint32_t token; + if(!createNewComment(token, cmt)) + { + error_message = std::string("Error! Failed creating comment."); + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + error_message = std::string("Error! GXS operation failed."); + return false; + } + + if(!RsGenExchange::getPublishedMsgMeta(token, cmt.mMeta)) + { + error_message = std::string("Error! Failure getting generated comment data."); + return false; + } + + commentMessageId = cmt.mMeta.mMsgId; + return true; +} + bool p3GxsChannels::subscribeToChannel( const RsGxsGroupId& groupId, bool subscribe ) { diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 6ddcedadb..3203be1dd 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -194,8 +194,10 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); virtual bool getContentSummaries( const RsGxsGroupId& channelId, std::vector& summaries ); +#ifdef REMOVED /// Implementation of @see RsGxsChannels::createChannel virtual bool createChannel(RsGxsChannelGroup& channel); +#endif /// Implementation of @see RsGxsChannels::createChannel virtual bool createChannel(const std::string& name, @@ -207,17 +209,48 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); RsGxsGroupId& channel_group_id, std::string& error_message); +#ifdef REMOVED /// Implementation of @see RsGxsChannels::createComment virtual bool createComment(RsGxsComment& comment); +#endif + + /// Implementation of @see RsGxsChannels::createComment + virtual bool createComment(const RsGxsGroupId& groupId, + const RsGxsMessageId& parentMsgId, + const std::string& comment, + RsGxsMessageId& commentMessageId, + std::string& error_message); /// Implementation of @see RsGxsChannels::editChannel virtual bool editChannel(RsGxsChannelGroup& channel); +#ifdef REMOVED /// Implementation of @see RsGxsChannels::createPost virtual bool createPost(RsGxsChannelPost& post); +#endif + /// Implementation of @see RsGxsChannels::createPost + virtual bool createPost(const RsGxsGroupId& groupId, + const RsGxsMessageId& origMsgId, + const std::string& msgName, + const std::string& msg, + const std::list& files, + const RsGxsImage& thumbnail, + RsGxsMessageId &message_id, + std::string& error_message) ; +#ifdef REMOVED /// Implementation of @see RsGxsChannels::createVote virtual bool createVote(RsGxsVote& vote); +#endif + + /// Implementation of @see RsGxsChannels::createVote + virtual bool createVote(const RsGxsGroupId& groupId, + const RsGxsMessageId& threadId, + const RsGxsMessageId& commentMessageId, + const RsGxsId& authorId, + uint32_t voteType, + RsGxsMessageId& voteMessageId, + std::string& error_message); /// Implementation of @see RsGxsChannels::subscribeToChannel virtual bool subscribeToChannel( const RsGxsGroupId &groupId, From ab80d9a374434ec85fa00682121c7a44a72c1a29 Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Fri, 5 Apr 2019 01:43:23 +0200 Subject: [PATCH 3/4] Fix compilation, retrocompatibility and enums Workaround miss-behaviour on old Android phones Cleanup indentation a bit Consistent param naming Introduce default parameter values also for output paramethers --- libretroshare/src/retroshare/rsgxschannels.h | 191 ++++--- libretroshare/src/retroshare/rsgxscircles.h | 69 ++- libretroshare/src/retroshare/rsgxscommon.h | 24 +- .../src/retroshare/rsgxsifacehelper.h | 44 +- libretroshare/src/retroshare/rstokenservice.h | 13 +- libretroshare/src/services/p3gxschannels.cc | 485 +++++++++++------- libretroshare/src/services/p3gxschannels.h | 120 ++--- libretroshare/src/util/rsmemory.h | 3 + 8 files changed, 584 insertions(+), 365 deletions(-) diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 9f76aec84..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; @@ -99,94 +101,101 @@ public: explicit RsGxsChannels(RsGxsIface& gxs) : RsGxsIfaceHelper(gxs) {} virtual ~RsGxsChannels() {} -#ifdef REMOVED /** * @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; -#endif - - /** - * @brief Create channel. Blocking API. - * @jsonapi{development} - * @param[in] name Name of the channel - * @param[in] description Description of the channel - * @param[in] image Thumbnail that is shown to advertise the channel. Possibly empty. - * @param[in] author_id GxsId of the contact author. For an anonymous channel, leave this to RsGxsId()="00000....0000" - * @param[in] circle_type Type of visibility restriction, among { GXS_CIRCLE_TYPE_PUBLIC, GXS_CIRCLE_TYPE_EXTERNAL, GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GXS_CIRCLE_TYPE_YOUR_EYES_ONLY } - * @param[in] circle_id Id of the circle (should be an external circle for GXS_CIRCLE_TYPE_EXTERNAL, a local friend group for GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY, GxsCircleId()="000....000" otherwise - * @param[out] channel_group_id Group id of the created channel, if command succeeds. - * @param[out] error_message Error messsage supplied when the channel creation fails. - * @return False on error, true otherwise. - */ - virtual bool createChannel(const std::string& name, - const std::string& description, - const RsGxsImage& image, - const RsGxsId& author_id, - uint32_t circle_type, - RsGxsCircleId& circle_id, - RsGxsGroupId& channel_group_id, - std::string& error_message )=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[in] groupId Id of the channel in which the comment is to be posted - * @param[in] parentMsgId 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 Id of the comment that was created - * @param[out] error_string Error message supplied when the comment creation fails. + * @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(const RsGxsGroupId& groupId, - const RsGxsMessageId& parentMsgId, - const std::string& comment, - RsGxsMessageId& commentMessageId, - std::string& error_message )=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[in] groupId Id of the channel where to put the post (publish rights needed!) - * @param[in] origMsgId Id of the post you are replacing. If left blank (RsGxsMssageId()="0000.....0000", a new post will be created - * @param[in] msgName Title of the post - * @param[in] msg Text content of the post - * @param[in] files List of attached files. These are supposed to be shared otherwise (use ExtraFileHash() below) - * @param[in] thumbnail Image displayed in the list of posts. Can be left blank. - * @param[out] messsageId Id of the message that was created - * @param[out] error_message Error text if anything bad happens + * @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(const RsGxsGroupId& groupId, - const RsGxsMessageId& origMsgId, - const std::string& msgName, - const std::string& msg, - const std::list& files, - const RsGxsImage& thumbnail, - RsGxsMessageId& messageId, - std::string& error_message) = 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[in] groupId Id of the channel where to put the post (publish rights needed!) - * @param[in] threadId Id of the channel post in which a comment is voted - * @param[in] commentMesssageId Id of the comment that is voted - * @param[in] authorId Id of the author. Needs to be your identity. - * @param[in] voteType Type of vote (GXS_VOTE_NONE=0x00, GXS_VOTE_DOWN=0x01, GXS_VOTE_UP=0x02) - * @param[out] voteMessageId Id of the vote message produced - * @param[out] error_message Error text if anything bad happens + * @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( const RsGxsGroupId& groupId, - const RsGxsMessageId& threadId, - const RsGxsMessageId& commentMessageId, - const RsGxsId& authorId, - uint32_t voteType, - RsGxsMessageId& voteMessageId, - std::string& error_message)=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. @@ -372,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; @@ -429,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 @@ -442,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/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index 09cf40dbf..f19d8720c 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 amd 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 e1fdd9281..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 * @@ -38,6 +38,7 @@ #include #include +#include // For Dummy Msgs. #include "util/rsrandom.h" @@ -1056,108 +1057,115 @@ bool p3GxsChannels::getChannelContent( const RsGxsGroupId& channelId, return getPostData(token, posts, comments); } -bool p3GxsChannels::createChannel(const std::string& name, - const std::string& description, - const RsGxsImage& image, - const RsGxsId& author_id, - uint32_t circle_type, - RsGxsCircleId& circle_id, - RsGxsGroupId& channel_group_id, - std::string& error_message) +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 + // do some checks - if( circle_type != GXS_CIRCLE_TYPE_PUBLIC - && circle_type != GXS_CIRCLE_TYPE_EXTERNAL - && circle_type != GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY - && circle_type != GXS_CIRCLE_TYPE_LOCAL - && circle_type != GXS_CIRCLE_TYPE_YOUR_EYES_ONLY ) - { - error_message = std::string("circle_type has a non allowed value") ; - return false ; - } + 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(circle_type) - { - case GXS_CIRCLE_TYPE_EXTERNAL: - if(circle_id.isNull()) - { - error_message = std::string("circle_type is GXS_CIRCLE_TYPE_EXTERNAL but circle_id is null"); - return false ; - } - break; - - case GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY: - { - RsGroupInfo ginfo; - - if(!rsPeers->getGroupInfo(RsNodeGroupId(circle_id),ginfo)) - { - error_message = std::string("circle_type is GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY but circle_id is not set or does not correspond to a actual group of friends"); - return false; - } + switch(circleType) + { + case RsGxsCircleType::EXTERNAL: + if(circleId.isNull()) + { + errorMessage = "circleType is EXTERNAL but circleId is null"; + return false; } break; - default: - if(!circle_id.isNull()) - { - error_message = std::string("circle_type requires a null circle id, but a non null circle id (" + circle_id.toStdString() + ") was supplied"); - return false; - } - } + case RsGxsCircleType::NODES_GROUP: + { + RsGroupInfo ginfo; - // Create a consistent channel group meta from the information supplied + 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; + } - RsGxsChannelGroup channel ; + // Create a consistent channel group meta from the information supplied + RsGxsChannelGroup channel; - channel.mMeta.mGroupName = name ; - channel.mMeta.mAuthorId = author_id ; - channel.mMeta.mCircleType = circle_type ; + 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.mSignFlags = GXS_SERV::FLAG_GROUP_SIGN_PUBLISH_NONEREQ + | GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_REQUIRED; - channel.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC; + channel.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC; channel.mMeta.mCircleId.clear(); channel.mMeta.mInternalCircle.clear(); - if(circle_type == GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY) - channel.mMeta.mInternalCircle = circle_id; - else if(circle_type == GXS_CIRCLE_TYPE_EXTERNAL) - channel.mMeta.mCircleId = circle_id; + 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 = image ; + // 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; } - // wait for the group creation to complete. - - 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 " - << " group data." << std::endl; + errorMessage = "Failure getting updated group data."; + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage + << std::endl; return false; } - channel_group_id = channel.mMeta.mGroupId; + channelId = channel.mMeta.mGroupId; #ifdef RS_DEEP_SEARCH DeepSearch::indexChannelGroup(channel); @@ -1166,27 +1174,26 @@ bool p3GxsChannels::createChannel(const std::string& name, return true; } -#ifdef REMOVED bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) { uint32_t token; if(!createGroup(token, channel)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating group." + 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::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; } @@ -1197,91 +1204,126 @@ bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) return true; } -#endif -bool p3GxsChannels::createVote( const RsGxsGroupId& groupId, - const RsGxsMessageId & threadId, - const RsGxsMessageId& commentMessageId, - const RsGxsId& authorId, - uint32_t voteType, - RsGxsMessageId& voteMessageId, - std::string& error_message) +bool p3GxsChannels::createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType tVote, RsGxsMessageId& voteId, std::string& errorMessage ) { - // Do some checks - std::vector channelsInfo; - - if(!getChannelsInfo(std::list({groupId}),channelsInfo)) // does the channel actually exist? - { - error_message = std::string("Channel with Id " + groupId.toStdString() + " does not exist."); - return false; - } - - if(commentMessageId.isNull()) // has a correct comment msg id been supplied? - { - error_message = std::string("You cannot vote on null comment " + commentMessageId.toStdString() + " of channel with Id " + groupId.toStdString() + ": please supply a non null comment Id!"); + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) + { + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; return false; - } + } - std::set s({commentMessageId}); + 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( groupId,s,posts,comments )) // does the comment to vote actually exist? + if(!getChannelContent(channelId, s, posts, comments)) { - error_message = std::string("You cannot vote on comment " + commentMessageId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this comment does not locally exist!"); + errorMessage = "You cannot vote on comment " + + commentId.toStdString() + " of channel with Id " + + channelId.toStdString() + + ": this comment does not exists locally!"; return false; } - if(posts.front().mMeta.mParentId.isNull()) // is the ID a comment ID or a post ID? It should be comment => should have a parent ID + // 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()) { - error_message = std::string("You cannot vote on channel message " + commentMessageId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this ID refers to a post, not a comment!"); + 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(voteType != GXS_VOTE_NONE && voteType != GXS_VOTE_UP && voteType != GXS_VOTE_DOWN) // is voteType consistent? - { - error_message = std::string("Your vote to channel with Id " + groupId.toStdString() + " has wrong vote type. Only GXS_VOTE_NONE, GXS_VOTE_UP, GXS_VOTE_DOWN accepted."); + 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)) // is the voter ID actually ours? - { - error_message = std::string("You cannot vote to channel with Id " + groupId.toStdString() + " with identity " + authorId.toStdString() + " because it is not yours."); + 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 + } + // Create the vote RsGxsVote vote; - - vote.mMeta.mGroupId = groupId; - vote.mMeta.mThreadId = threadId; - vote.mMeta.mParentId = commentMessageId; + vote.mMeta.mGroupId = channelId; + vote.mMeta.mThreadId = postId; + vote.mMeta.mParentId = commentId; vote.mMeta.mAuthorId = authorId; - - vote.mVoteType = voteType; + vote.mVoteType = static_cast(tVote); uint32_t token; if(!createNewVote(token, vote)) { - error_message = std::string("Error! Failed creating vote."); + errorMessage = "Error! Failed creating vote."; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - error_message = std::string("Error! GXS operation failed."); + errorMessage = "GXS operation failed."; return false; } if(!RsGenExchange::getPublishedMsgMeta(token, vote.mMeta)) { - error_message = std::string("Error! Failure getting generated vote data."); + 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::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + 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 " + << " vote data." << std::endl; return false; } - voteMessageId = vote.mMeta.mMsgId; return true; } @@ -1290,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; } @@ -1316,60 +1358,64 @@ bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) return true; } -bool p3GxsChannels::createPost(const RsGxsGroupId& groupId, - const RsGxsMessageId &origMsgId, - const std::string& msgName, - const std::string& msg, - const std::list& files, - const RsGxsImage& thumbnail, - RsGxsMessageId &message_id, - std::string& error_message) +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({groupId}),channelsInfo)) - { - error_message = std::string("Channel with Id " + groupId.toStdString() + " does not exist."); - return false; - } - - const RsGxsChannelGroup& cg(*channelsInfo.begin()); - - if(!(cg.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)) - { - error_message = std::string("You cannot post to channel with Id " + groupId.toStdString() + ": missing publish rights!"); - return false; - } - - if(!origMsgId.isNull()) + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) { - std::set s({origMsgId}); + 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( groupId,s,posts,comments )) - { - error_message = std::string("You cannot edit post " + origMsgId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this post does not locally exist!"); + 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 = groupId; - post.mMeta.mOrigMsgId = origMsgId; - post.mMeta.mMsgName = msgName; + post.mMeta.mGroupId = channelId; + post.mMeta.mOrigMsgId = origPostId; + post.mMeta.mMsgName = title; - post.mMsg = msg; - post.mFiles = files; - post.mThumbnail = thumbnail; + 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)) { @@ -1377,74 +1423,101 @@ bool p3GxsChannels::createPost(const RsGxsGroupId& groupId, DeepSearch::indexChannelPost(post); #endif // RS_DEEP_SEARCH - message_id = post.mMeta.mMsgId; + postId = post.mMeta.mMsgId; return true; } - error_message = std::string("cannot publish message. Check that you have publish rights on this channel?"); + errorMessage = "Failed to retrive created post metadata"; return false; } -bool p3GxsChannels::createComment(const RsGxsGroupId& groupId, - const RsGxsMessageId& parentMsgId, - const std::string& comment, - RsGxsMessageId& commentMessageId, - std::string& error_message) +bool p3GxsChannels::createCommentV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& parentId, + const std::string& comment, RsGxsMessageId& commentMessageId, + std::string& errorMessage ) { - // Do some checks - std::vector channelsInfo; - - if(!getChannelsInfo(std::list({groupId}),channelsInfo)) - { - error_message = std::string("Channel with Id " + groupId.toStdString() + " does not exist."); - return false; - } - - if(parentMsgId.isNull()) - { - error_message = std::string("You cannot comment post " + parentMsgId.toStdString() + " of channel with Id " + groupId.toStdString() + ": please supply a non null post Id!"); - return false; - } - - std::set s({parentMsgId}); - std::vector posts; - std::vector comments; - - if(!getChannelContent( groupId,s,posts,comments )) + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) { - error_message = std::string("You cannot comment post " + parentMsgId.toStdString() + " of channel with Id " + groupId.toStdString() + ": this post does not locally exist!"); + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; return false; } - // Now create the comment + 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 = groupId; - cmt.mMeta.mParentId = parentMsgId; + cmt.mComment = comment; + cmt.mMeta.mGroupId = channelId; + cmt.mMeta.mParentId = parentId; uint32_t token; if(!createNewComment(token, cmt)) { - error_message = std::string("Error! Failed creating comment."); + errorMessage = "Failed creating comment."; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - error_message = std::string("Error! GXS operation failed."); + errorMessage = "GXS operation failed."; return false; } if(!RsGenExchange::getPublishedMsgMeta(token, cmt.mMeta)) { - error_message = std::string("Error! Failure getting generated comment data."); + 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; } - commentMessageId = cmt.mMeta.mMsgId; return true; } @@ -1687,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(); @@ -2235,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 3203be1dd..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,66 +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; -#ifdef REMOVED - /// Implementation of @see RsGxsChannels::createChannel - virtual bool createChannel(RsGxsChannelGroup& channel); -#endif + /// 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; - /// Implementation of @see RsGxsChannels::createChannel - virtual bool createChannel(const std::string& name, - const std::string& description, - const RsGxsImage& image, - const RsGxsId& author_id, - uint32_t circle_type, - RsGxsCircleId& circle_id, - RsGxsGroupId& channel_group_id, - std::string& error_message); - -#ifdef REMOVED - /// Implementation of @see RsGxsChannels::createComment - virtual bool createComment(RsGxsComment& comment); -#endif + /// @deprecated Implementation of @see RsGxsChannels::createComment + RS_DEPRECATED_FOR(createCommentV2) + virtual bool createComment(RsGxsComment& comment) override; /// Implementation of @see RsGxsChannels::createComment - virtual bool createComment(const RsGxsGroupId& groupId, - const RsGxsMessageId& parentMsgId, - const std::string& comment, - RsGxsMessageId& commentMessageId, - std::string& error_message); + 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; -#ifdef REMOVED - /// Implementation of @see RsGxsChannels::createPost - virtual bool createPost(RsGxsChannelPost& post); -#endif - /// Implementation of @see RsGxsChannels::createPost - virtual bool createPost(const RsGxsGroupId& groupId, - const RsGxsMessageId& origMsgId, - const std::string& msgName, - const std::string& msg, - const std::list& files, - const RsGxsImage& thumbnail, - RsGxsMessageId &message_id, - std::string& error_message) ; + /// @deprecated Implementation of @see RsGxsChannels::createPost + RS_DEPRECATED_FOR(createPostV2) + virtual bool createPost(RsGxsChannelPost& post) override; -#ifdef REMOVED - /// Implementation of @see RsGxsChannels::createVote - virtual bool createVote(RsGxsVote& vote); -#endif + /// 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; - /// Implementation of @see RsGxsChannels::createVote - virtual bool createVote(const RsGxsGroupId& groupId, - const RsGxsMessageId& threadId, - const RsGxsMessageId& commentMessageId, - const RsGxsId& authorId, - uint32_t voteType, - RsGxsMessageId& voteMessageId, - std::string& error_message); + /// @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, @@ -262,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); @@ -306,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; @@ -352,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..d37984262 100644 --- a/libretroshare/src/util/rsmemory.h +++ b/libretroshare/src/util/rsmemory.h @@ -23,8 +23,11 @@ #include #include +#include #include +#define RS_DEFAULT_STORAGE_PARAM(Type) *std::unique_ptr(new Type) + void *rs_malloc(size_t size) ; // This is a scope guard to release the memory block when going of of the current scope. From 9ad79f0c89fe860d5624281cf52bea6944773bbb Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Wed, 10 Apr 2019 21:10:21 +0200 Subject: [PATCH 4/4] Improve documentation --- .../src/retroshare/rsgxsifacehelper.h | 2 +- libretroshare/src/util/rsmemory.h | 40 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/libretroshare/src/retroshare/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index f19d8720c..fce16d8f7 100644 --- a/libretroshare/src/retroshare/rsgxsifacehelper.h +++ b/libretroshare/src/retroshare/rsgxsifacehelper.h @@ -314,7 +314,7 @@ LLwaitTokenBeginLabel: #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 amd we return anyway. + * something worser is already happening elsewere and we return anyway. */ if( st > RsTokenService::FAILED && st < RsTokenService::COMPLETE && maxWorkAroundCnt-- > 0 ) diff --git a/libretroshare/src/util/rsmemory.h b/libretroshare/src/util/rsmemory.h index d37984262..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,11 +22,44 @@ *******************************************************************************/ #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) ;