diff --git a/libtorrent/include/libtorrent/piece_picker.hpp b/libtorrent/include/libtorrent/piece_picker.hpp index 77365a56..f333c599 100644 --- a/libtorrent/include/libtorrent/piece_picker.hpp +++ b/libtorrent/include/libtorrent/piece_picker.hpp @@ -428,8 +428,8 @@ namespace libtorrent #else boost::uint32_t peer_count : 16; #endif - // post flags (1 = rt, 2 = dm) - boost::uint32_t post_flags : 2; + // post flags (1 = rt, 2 = dm, 4 = fav, 12 = pfav) + boost::uint32_t post_flags : 4; // is 1 if the piece is marked as being downloaded boost::uint32_t downloading : 1; // set when downloading, but no free blocks to request left diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index b8841cc5..ed44faea 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -252,9 +252,11 @@ static const CRPCCommand vRPCCommands[] = { "newpostraw", &newpostraw, false, true, true }, { "newdirectmsg", &newdirectmsg, false, true, false }, { "newrtmsg", &newrtmsg, false, true, false }, + { "newfavmsg", &newfavmsg, false, true, false }, { "getposts", &getposts, false, true, false }, { "getdirectmsgs", &getdirectmsgs, false, true, false }, { "getmentions", &getmentions, false, true, false }, + { "getfavs", &getfavs, false, true, false }, { "setspammsg", &setspammsg, false, false, false }, { "getspammsg", &getspammsg, false, false, false }, { "setpreferredspamlang", &setpreferredspamlang, false, false, false }, @@ -1316,6 +1318,9 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 4) ConvertTo(params[4]); if (strMethod == "newrtmsg" && n > 1) ConvertTo(params[1]); if (strMethod == "newrtmsg" && n > 2) ConvertTo(params[2]); + if (strMethod == "newfavmsg" && n > 1) ConvertTo(params[1]); + if (strMethod == "newfavmsg" && n > 2) ConvertTo(params[2]); + if (strMethod == "newfavmsg" && n > 3) ConvertTo(params[3]); if (strMethod == "getlasthave" && n > 1) ConvertTo(params[1]); if (strMethod == "getposts" && n > 0) ConvertTo(params[0]); if (strMethod == "getposts" && n > 1) ConvertTo(params[1]); @@ -1325,6 +1330,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 2) ConvertTo(params[2]); if (strMethod == "getmentions" && n > 1) ConvertTo(params[1]); if (strMethod == "getmentions" && n > 2) ConvertTo(params[2]); + if (strMethod == "getfavs" && n > 1) ConvertTo(params[1]); + if (strMethod == "getfavs" && n > 2) ConvertTo(params[2]); if (strMethod == "follow" && n > 1) ConvertTo(params[1]); if (strMethod == "unfollow" && n > 1) ConvertTo(params[1]); if (strMethod == "listusernamespartial" && n > 1) ConvertTo(params[1]); diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 31af2c01..128a1bb3 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -205,9 +205,11 @@ extern json_spirit::Value newpostmsg(const json_spirit::Array& params, bool fHel extern json_spirit::Value newpostraw(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value newdirectmsg(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value newrtmsg(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value newfavmsg(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getposts(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getdirectmsgs(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmentions(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfavs(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value setspammsg(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getspammsg(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value setpreferredspamlang(const json_spirit::Array& params, bool fHelp); diff --git a/src/twister.cpp b/src/twister.cpp index f0bedcbf..71ca7bd8 100644 --- a/src/twister.cpp +++ b/src/twister.cpp @@ -1457,11 +1457,30 @@ bool acceptSignedPost(char const *data, int data_size, std::string username, int } } + lazy_entry const* fav = post->dict_find_dict("fav"); + string sig_fav = post->dict_find_string_value("sig_fav"); + + if ( fav ) { + if ( flags ) + (*flags) |= USERPOST_FLAG_FAV; + string username_fav = fav->dict_find_string_value("n"); + int height_fav = fav->dict_find_int_value("height", -1); + + pair favbuf = fav->data_section(); + ret = verifySignature(string(favbuf.first, favbuf.second), + username_fav, sig_fav, height_fav); + if ( !ret ) + sprintf(errbuf, "bad FAV signature"); + } + if( flags ) { lazy_entry const* dm = post->dict_find_dict("dm"); + lazy_entry const* pfav = post->dict_find_dict("pfav"); if( dm ) { (*flags) |= USERPOST_FLAG_DM; processReceivedDM(post); + } else if (pfav) { + (*flags) |= USERPOST_FLAG_P_FAV; } else { processReceivedPost(v, username, time, msg); } @@ -1533,6 +1552,8 @@ bool createSignedUserpost(entry &v, std::string const &username, int k, std::string const &msg, // either msg.size() or entry const *rt, entry const *sig_rt, // rt != NULL or entry const *dm, // dm != NULL. + entry const *fav, entry const *sig_fav, + entry const *pfav, std::string const &reply_n, int reply_k ) { @@ -1547,14 +1568,20 @@ bool createSignedUserpost(entry &v, std::string const &username, int k, if( msg.size() ) { //userpost["t"] = "post"; userpost["msg"] = msg; - } else if ( rt != NULL && sig_rt != NULL ) { + } + if ( rt != NULL && sig_rt != NULL ) { //userpost["t"] = "rt"; userpost["rt"] = *rt; userpost["sig_rt"] = *sig_rt; + } else if ( fav != NULL && sig_fav != NULL ) { + userpost["fav"] = *fav; + userpost["sig_fav"] = *sig_fav; } else if ( dm != NULL ) { //userpost["t"] = "dm"; userpost["dm"] = *dm; - } else { + } else if ( pfav != NULL ) { + userpost["pfav"] = *pfav; + } else if ( !msg.size() ) { printf("createSignedUserpost: unknown type\n"); return false; } @@ -2029,7 +2056,7 @@ int findLastPublicPostLocalUser( std::string strUsername ) std::vector pieces; int max_id = std::numeric_limits::max(); int since_id = -1; - h.get_pieces(pieces, 1, max_id, since_id, ~USERPOST_FLAG_DM, 0); + h.get_pieces(pieces, 1, max_id, since_id, USERPOST_FLAG_HOME, 0); if( pieces.size() ) { string const& piece = pieces.front(); @@ -2076,6 +2103,7 @@ Value newpostmsg(const Array& params, bool fHelp) v["userpost"]["lastk"] = lastk; if( !createSignedUserpost(v, strUsername, k, strMsg, + NULL, NULL, NULL, NULL, NULL, NULL, strReplyN, replyK) ) throw JSONRPCError(RPC_INTERNAL_ERROR,"error signing post with private key of user"); @@ -2227,6 +2255,7 @@ Value newdirectmsg(const Array& params, bool fHelp) entry v; if( !createSignedUserpost(v, strFrom, k, "", NULL, NULL, dm, + NULL, NULL, NULL, std::string(""), 0) ) throw JSONRPCError(RPC_INTERNAL_ERROR,"error signing post with private key of user"); @@ -2283,6 +2312,7 @@ Value newrtmsg(const Array& params, bool fHelp) if( !createSignedUserpost(v, strUsername, k, "", rt, sig_rt, NULL, + NULL, NULL, NULL, std::string(""), 0) ) throw JSONRPCError(RPC_INTERNAL_ERROR,"error signing post with private key of user"); @@ -2321,6 +2351,68 @@ Value newrtmsg(const Array& params, bool fHelp) return entryToJson(v); } + +Value newfavmsg(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "newfavmsg [private=false] [msg=''] \n" + "Add a post to swarm as a favorite"); + + EnsureWalletIsUnlocked(); + + string strUsername = params[0].get_str(); + int k = params[1].get_int(); + string strK = boost::lexical_cast(k); + string msg = (params.size() > 4) ? params[4].get_str() : ""; + entry vfav = jsonToEntry(params[2].get_obj()); + bool isPriv = (params.size() > 3) ? params[3].get_bool() : false; + unHexcapePost(vfav); + entry const *fav = vfav.find_key("userpost"); + entry const *sig_fav= vfav.find_key("sig_userpost"); + + entry v; + + if (isPriv) + { + std::vector payloadbuf; + bencode(std::back_inserter(payloadbuf), vfav); + std::string strMsgData = std::string(payloadbuf.data(),payloadbuf.size()); + + entry pfav; + if( !createDirectMessage(pfav, strUsername, strMsgData) ) + throw JSONRPCError(RPC_INTERNAL_ERROR, + "error encrypting to pubkey of destination user"); + + if( !createSignedUserpost(v, strUsername, k, msg, + NULL, NULL, NULL, + NULL, NULL, &pfav, + std::string(""), 0) ) + throw JSONRPCError(RPC_INTERNAL_ERROR,"error signing post with private key of user"); + } + else if( !createSignedUserpost(v, strUsername, k, msg, + NULL, NULL, NULL, + fav, sig_fav, NULL, + std::string(""), 0) ) + throw JSONRPCError(RPC_INTERNAL_ERROR,"error signing post with private key of user"); + + vector buf; + bencode(std::back_inserter(buf), v); + + std::string errmsg; + if( !acceptSignedPost(buf.data(),buf.size(),strUsername,k,errmsg,NULL) ) + throw JSONRPCError(RPC_INVALID_PARAMS,errmsg); + + torrent_handle h = startTorrentUser(strUsername, true); + if( h.is_valid() ) { + // if member of torrent post it directly + h.add_piece(k,buf.data(),buf.size()); + } + + hexcapePost(v); + return entryToJson(v); +} + Value getposts(const Array& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 4) @@ -2328,11 +2420,11 @@ Value getposts(const Array& params, bool fHelp) "getposts '[{\"username\":username,\"max_id\":max_id,\"since_id\":since_id},...]' [allowed_flags=~2] [required_flags=0]\n" "get posts from users\n" "max_id and since_id may be omited\n" - "(optional) allowed/required flags are bitwise fields (1=RT,2=DM)"); + "(optional) allowed/required flags are bitwise fields (1=RT,2=DM,4=FAV,12=PFAV)"); int count = params[0].get_int(); Array users = params[1].get_array(); - int allowed_flags = (params.size() > 2) ? params[2].get_int() : ~USERPOST_FLAG_DM; + int allowed_flags = (params.size() > 2) ? params[2].get_int() : USERPOST_FLAG_HOME; int required_flags = (params.size() > 3) ? params[3].get_int() : 0; std::multimap postsByTime; @@ -2506,6 +2598,114 @@ Value getmentions(const Array& params, bool fHelp) return ret; } +Value getfavs(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3 ) + throw runtime_error( + "getfavs '{\"max_id\":max_id,\"since_id\":since_id}'\n" + "Get favorite posts (private favorites are included) of localuser\n" + "max_id and since_id may be omited. up to posts are returned."); + + string strUsername = params[0].get_str(); + int cnt = params[1].get_int(); + int max_id = std::numeric_limits::max(); + int since_id = -1; + + if( params.size() > 2 ) { + Object optParms = params[2].get_obj(); + for (Object::const_iterator i = optParms.begin(); i != optParms.end(); ++i) { + if( i->name_ == "max_id" ) max_id = i->value_.get_int(); + if( i->name_ == "since_id" ) since_id = i->value_.get_int(); + } + } + + multimap postsByTime; + + torrent_handle h = startTorrentUser(strUsername, true); + if( h.is_valid() ) { + std::vector pieces; + h.get_pieces(pieces, cnt, max_id, since_id, USERPOST_FLAG_P_FAV, USERPOST_FLAG_FAV); + + BOOST_FOREACH(string const& piece, pieces) { + lazy_entry v; + int pos; + libtorrent::error_code ec; + if (lazy_bdecode(piece.data(), piece.data()+piece.size(), v, ec, &pos) == 0 && + v.type() == lazy_entry::dict_t) { + lazy_entry const* post = v.dict_find_dict("userpost"); + if (!post || post->type() != lazy_entry::dict_t) + continue; + int64 time = post->dict_find_int_value("time",-1); + + if(time == -1 || time > GetAdjustedTime() + MAX_TIME_IN_FUTURE ) { + printf("getposts: ignoring far-future message by '%s'\n", strUsername.c_str()); + continue; + } + + lazy_entry const* fav = post->dict_find_dict("fav"); + lazy_entry const* pfav = post->dict_find_dict("pfav"); + if (fav && fav->type() == lazy_entry::dict_t) + { + entry vEntry; + vEntry = v; + vEntry["isPrivate"] = false; + hexcapePost(vEntry); + postsByTime.insert( pair(time, vEntry) ); + } + else if (pfav && pfav->type() == lazy_entry::dict_t) + { + ecies_secure_t sec; + sec.key = pfav->dict_find_string_value("key"); + sec.mac = pfav->dict_find_string_value("mac"); + sec.orig = pfav->dict_find_int_value("orig"); + sec.body = pfav->dict_find_string_value("body"); + + CKey key; + CKeyID keyID; + if (pwalletMain->GetKeyIdFromUsername(strUsername, keyID) && + pwalletMain->GetKey( keyID, key) ) { + /* success: key obtained from wallet */ + + string textOut; + if (key.Decrypt(sec, textOut)) + { + lazy_entry dfav; + if (lazy_bdecode(textOut.data(), textOut.data()+textOut.size(), dfav, ec, &pos) == 0 + && dfav.type() == lazy_entry::dict_t) { + entry vEntry, upst; + + upst["fav"] = *(dfav.dict_find_dict("userpost")); + upst["sig_fav"] = dfav.dict_find_string_value("sig_userpost"); + upst["n"] = post->dict_find_string_value("n"); + upst["k"] = post->dict_find_int_value("k"); + upst["msg"] = post->dict_find_string_value("msg"); + upst["time"] = post->dict_find_int_value("time"); + upst["height"] = post->dict_find_int_value("height"); + + vEntry["isPrivate"] = true; + vEntry["userpost"] = upst; + + hexcapePost(vEntry); + postsByTime.insert( pair(time, vEntry) ); + + } + } + } else + printf("getfavs: no key for user '%s'\n", strUsername.c_str()); + } + } + } + } + + Array ret; + std::multimap::reverse_iterator rit; + for (rit=postsByTime.rbegin(); rit!=postsByTime.rend() && (int)ret.size() < cnt; ++rit) { + ret.push_back( entryToJson(rit->second) ); + } + + return ret; +} + Value setspammsg(const Array& params, bool fHelp) { if (fHelp || (params.size() != 2)) @@ -3448,6 +3648,7 @@ static void signAndAddDM(const std::string &strFrom, int k, const entry *dm) entry v; if( !createSignedUserpost(v, strFrom, k, "", NULL, NULL, dm, + NULL, NULL, NULL, std::string(""), 0) ) throw JSONRPCError(RPC_INTERNAL_ERROR,"error signing post with private key of user"); diff --git a/src/twister.h b/src/twister.h index a8b192ed..cc8c3d4e 100644 --- a/src/twister.h +++ b/src/twister.h @@ -8,8 +8,12 @@ #define LIBTORRENT_PORT_OFFSET 1000 -#define USERPOST_FLAG_RT 0x01 -#define USERPOST_FLAG_DM 0x02 +#define USERPOST_FLAG_RT 0x01 +#define USERPOST_FLAG_DM 0x02 +#define USERPOST_FLAG_FAV 0x04 +#define USERPOST_FLAG_P_FAV 0x0C + +#define USERPOST_FLAG_HOME (~USERPOST_FLAG_DM & ~USERPOST_FLAG_FAV & ~USERPOST_FLAG_P_FAV) #define BLOCK_AGE_TO_EXPIRE_DHT_ENTRY (2016) // about 2 weeks #define BLOCK_AGE_TO_EXPIRE_DHT_POSTS (4320*2) // about 2 months diff --git a/src/twister_utils.cpp b/src/twister_utils.cpp index 810abacb..4ccc41c0 100644 --- a/src/twister_utils.cpp +++ b/src/twister_utils.cpp @@ -375,6 +375,7 @@ void hexcapePost(libtorrent::entry &e) entry &userpost = e["userpost"]; if( userpost.type() == libtorrent::entry::dictionary_t ) { findAndHexcape(userpost,"sig_rt"); + findAndHexcape(userpost, "sig_fav"); if( userpost.find_key("dm") ) { entry &dm = userpost["dm"]; if( dm.type() == libtorrent::entry::dictionary_t ) { @@ -382,6 +383,13 @@ void hexcapePost(libtorrent::entry &e) findAndHexcape(dm,"key"); findAndHexcape(dm,"mac"); } + } else if( userpost.find_key("pfav") ) { + entry &pfav = userpost["pfav"]; + if( pfav.type() == libtorrent::entry::dictionary_t ) { + findAndHexcape(pfav,"body"); + findAndHexcape(pfav,"key"); + findAndHexcape(pfav,"mac"); + } } } }