From 80a60a2095d59111ee9be424e7ea8f505abb5f00 Mon Sep 17 00:00:00 2001 From: digital dreamer Date: Sun, 25 May 2014 18:10:52 +0200 Subject: [PATCH 1/4] add RSS support --- Makefile.am | 1 + src/bitcoinrpc.cpp | 23 +++++ src/twister_rss.cpp | 207 ++++++++++++++++++++++++++++++++++++++++++++ src/twister_rss.h | 18 ++++ twister-qt.pro | 2 + 5 files changed, 251 insertions(+) create mode 100644 src/twister_rss.cpp create mode 100644 src/twister_rss.h diff --git a/Makefile.am b/Makefile.am index 4802e0b8..02c30aaf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -157,6 +157,7 @@ BITCOIN_TWISTER_SOURCES = \ src/txdb.cpp \ src/chainparams.cpp \ src/twister.cpp \ + src/twister_rss.cpp \ src/twister_utils.cpp \ $(SSE2_SOURCES) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 2b53b3b2..7f16826a 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -13,6 +13,7 @@ #include "db.h" #include "twister_utils.h" +#include "twister_rss.h" #include #include @@ -979,6 +980,28 @@ void ServiceConnection(AcceptedConnection *conn) if(strMethod == "GET" && strURI == "/") strURI="/home.html"; + if(strMethod == "GET" && strURI.substr(0, 4) == "/rss" && !GetBoolArg("-public_server_mode",false)) + { + string rssOutput; + int rssResult = generateRSS(strURI, &rssOutput); + + switch(rssResult) + { + case RSS_OK: + conn->stream() << HTTPReply(HTTP_OK, rssOutput, false, "application/rss+xml") << std::flush; + continue; + case RSS_ERROR_NO_ACCOUNT: + conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "No accounts found - please register a username", false) << std::flush; + continue; + case RSS_ERROR_BAD_ACCOUNT: + conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "Requested account is not registered on this node", false) << std::flush; + continue; + case RSS_ERROR_NOT_A_NUMBER: + conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "Parameter 'max' must be a number", false) << std::flush; + continue; + } + } + if (strURI != "/" && strURI.find("..") == std::string::npos ) { filesystem::path pathFile = filesystem::path(GetHTMLDir()) / strURI; std::string fname = pathFile.string(); diff --git a/src/twister_rss.cpp b/src/twister_rss.cpp new file mode 100644 index 00000000..91aacacb --- /dev/null +++ b/src/twister_rss.cpp @@ -0,0 +1,207 @@ +#include "twister_rss.h" +#include "init.h" +#include "bitcoinrpc.h" +#include "json/json_spirit.h" + +#include +#include +#include +#include +#include + +using namespace std; +using namespace json_spirit; + +int generateRSS(string uri, string *output) +{ + string account = ""; + int max = 20; + + // get URI parameters if available + size_t startPosition, endPosition; + if(uri.find("max=")!=string::npos) + { + startPosition = uri.find("max=")+4; + if(uri.find("&",startPosition)!=string::npos) + endPosition=uri.find("&",startPosition); + else + endPosition=uri.length(); + try + { + max = boost::lexical_cast(uri.substr(startPosition,endPosition-startPosition)); + } + catch(boost::bad_lexical_cast e) + { + return RSS_ERROR_NOT_A_NUMBER; + } + } + if(uri.find("account=")!=string::npos) + { + startPosition = uri.find("account=")+8; + if(uri.find("&",startPosition)!=string::npos) + endPosition=uri.find("&",startPosition); + else + endPosition=uri.length(); + + account = uri.substr(startPosition,endPosition-startPosition); + } + + const Array emptyArray; + Array accountsArray = listwalletusers(emptyArray,false).get_array(); + + // if no account was specified, choose the first one + if(account=="") + { + if(accountsArray.size()>0) + { + account = accountsArray[0].get_str(); + } + else return RSS_ERROR_NO_ACCOUNT; + } + + // if an account name was specified, check that it exists + else + { + bool accountExists = false; + for(int i=0;i outputVector; + + for(int j=0;j\n" + << "\n" + << "\n" + << " Twister Postboard - " << account << "\n" + << " New posts from Twister\n"; + + int outputSize = (outputVector.size()>max)?max:outputVector.size(); + + for(int i=0;i\n" + << " " << find_value(item,"title").get_str() << "\n" + << " " << find_value(item,"author").get_str() << "\n" + << " " << find_value(item,"msg").get_str() << "\n" + << " " << timeString << "\n" + << " \n"; + } + + ret << "\n" + << "\n"; + + *output = ret.str(); + return RSS_OK; +} + +bool sortByTime (Object i,Object j) +{ + return (find_value(i,"time").get_int64()>find_value(j,"time").get_int64()); +} diff --git a/src/twister_rss.h b/src/twister_rss.h new file mode 100644 index 00000000..e618497e --- /dev/null +++ b/src/twister_rss.h @@ -0,0 +1,18 @@ +#ifndef TWISTER_RSS_H +#define TWISTER_RSS_H + +#include "json/json_spirit.h" +#include + +enum RSSResultCode +{ + RSS_OK = 0, + RSS_ERROR_NO_ACCOUNT = -1, + RSS_ERROR_BAD_ACCOUNT = -2, + RSS_ERROR_NOT_A_NUMBER = -3 +}; + +extern bool sortByTime (json_spirit::Object i,json_spirit::Object j); +extern int generateRSS(std::string uri, std::string *output); + +#endif // TWISTER_RSS_H diff --git a/twister-qt.pro b/twister-qt.pro index 0a9a1ebf..f86b9e9e 100644 --- a/twister-qt.pro +++ b/twister-qt.pro @@ -201,6 +201,7 @@ HEADERS += \ src/scrypt.h \ src/utf8core.h \ src/twister.h \ + src/twister_rss.h \ src/twister_utils.h # src/qt/bitcoingui.h @@ -277,6 +278,7 @@ SOURCES += \ #src/qt/bitcoin.cpp \ src/txdb.cpp \ src/scrypt.cpp \ src/twister.cpp \ + src/twister_rss.cpp \ src/twister_utils.cpp # src/qt/guiutil.cpp \ From cc13358b7341ea468efe16b70503ce09d6570f79 Mon Sep 17 00:00:00 2001 From: digital dreamer Date: Fri, 30 May 2014 12:41:16 +0200 Subject: [PATCH 2/4] rss authentication --- src/bitcoinrpc.cpp | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 7f16826a..ef57a6e6 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -980,29 +980,7 @@ void ServiceConnection(AcceptedConnection *conn) if(strMethod == "GET" && strURI == "/") strURI="/home.html"; - if(strMethod == "GET" && strURI.substr(0, 4) == "/rss" && !GetBoolArg("-public_server_mode",false)) - { - string rssOutput; - int rssResult = generateRSS(strURI, &rssOutput); - - switch(rssResult) - { - case RSS_OK: - conn->stream() << HTTPReply(HTTP_OK, rssOutput, false, "application/rss+xml") << std::flush; - continue; - case RSS_ERROR_NO_ACCOUNT: - conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "No accounts found - please register a username", false) << std::flush; - continue; - case RSS_ERROR_BAD_ACCOUNT: - conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "Requested account is not registered on this node", false) << std::flush; - continue; - case RSS_ERROR_NOT_A_NUMBER: - conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "Parameter 'max' must be a number", false) << std::flush; - continue; - } - } - - if (strURI != "/" && strURI.find("..") == std::string::npos ) { + if (strURI != "/" && strURI.substr(0, 4) != "/rss" && strURI.find("..") == std::string::npos ) { filesystem::path pathFile = filesystem::path(GetHTMLDir()) / strURI; std::string fname = pathFile.string(); size_t qMarkIdx = fname.find('?'); @@ -1059,6 +1037,28 @@ void ServiceConnection(AcceptedConnection *conn) } if (mapHeaders["connection"] == "close") fRun = false; + + if(strMethod == "GET" && strURI.substr(0, 4) == "/rss" && !GetBoolArg("-public_server_mode",false)) + { + string rssOutput; + int rssResult = generateRSS(strURI, &rssOutput); + + switch(rssResult) + { + case RSS_OK: + conn->stream() << HTTPReply(HTTP_OK, rssOutput, false, "application/rss+xml") << std::flush; + continue; + case RSS_ERROR_NO_ACCOUNT: + conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "No accounts found - please register a username", false) << std::flush; + continue; + case RSS_ERROR_BAD_ACCOUNT: + conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "Requested account is not registered on this node", false) << std::flush; + continue; + case RSS_ERROR_NOT_A_NUMBER: + conn->stream() << HTTPReply(HTTP_BAD_REQUEST, "Parameter 'max' must be a number", false) << std::flush; + continue; + } + } JSONRequest jreq; try From bb544a21b4f9b391c8e11d39119af31b97add76d Mon Sep 17 00:00:00 2001 From: digital dreamer Date: Sat, 31 May 2014 10:59:09 +0200 Subject: [PATCH 3/4] parse query as parameter map --- Makefile.am | 2 +- configure.ac | 2 + m4/ax_boost_regex.m4 | 111 +++++++++++++++++++++++++++++++++++++++++++ src/twister_rss.cpp | 46 +++++++++--------- src/twister_rss.h | 2 + 5 files changed, 140 insertions(+), 23 deletions(-) create mode 100644 m4/ax_boost_regex.m4 diff --git a/Makefile.am b/Makefile.am index 02c30aaf..cd07798d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -173,7 +173,7 @@ twisterd_DEPENDENCIES = $(LEVELDB_LIB) twisterd_LDADD = $(LEVELDB_LIB) $(UPNP_LIB) \ @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_PROGRAM_OPTIONS_LIB@ @BOOST_THREAD_LIB@ @BOOST_CHRONO_LIB@ @BOOST_LOCALE_LIB@ \ - @DB_CXX_LIBS@ @OPENSSL_LIBS@ + @BOOST_REGEX_LIB@ @DB_CXX_LIBS@ @OPENSSL_LIBS@ AM_CPPFLAGS = -ftemplate-depth-100 -DBOOST_SPIRIT_THREADSAFE -D_FILE_OFFSET_BITS=64 \ -I$(top_srcdir)/libtorrent/include \ diff --git a/configure.ac b/configure.ac index 446228f8..bc8f41d0 100644 --- a/configure.ac +++ b/configure.ac @@ -92,6 +92,7 @@ AX_BOOST_PROGRAM_OPTIONS() AX_BOOST_THREAD() AX_BOOST_CHRONO() AX_BOOST_LOCALE() +AX_BOOST_REGEX() ############################################################################### # Checking for Berkeley DB C++ @@ -773,6 +774,7 @@ Boost libraries: boost.program_opts: ${BOOST_PROGRAM_OPTIONS_LIB} boost.thread: ${BOOST_THREAD_LIB} boost.locale: ${BOOST_LOCALE_LIB} + boost.regex: ${BOOST_REGEX_LIB} Berkeley DB C++ library: header: ${DB_CXX_HEADER} diff --git a/m4/ax_boost_regex.m4 b/m4/ax_boost_regex.m4 new file mode 100644 index 00000000..918f16a4 --- /dev/null +++ b/m4/ax_boost_regex.m4 @@ -0,0 +1,111 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_regex.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_REGEX +# +# DESCRIPTION +# +# Test for Regex library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_REGEX_LIB) +# +# And sets: +# +# HAVE_BOOST_REGEX +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 22 + +AC_DEFUN([AX_BOOST_REGEX], +[ + AC_ARG_WITH([boost-regex], + AS_HELP_STRING([--with-boost-regex@<:@=special-lib@:>@], + [use the Regex library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-regex=boost_regex-gcc-mt-d-1_33_1 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_regex_lib="" + else + want_boost="yes" + ax_boost_user_regex_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::Regex library is available, + ax_cv_boost_regex, + [AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include + ]], + [[boost::regex r(); return 0;]])], + ax_cv_boost_regex=yes, ax_cv_boost_regex=no) + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_regex" = "xyes"; then + AC_DEFINE(HAVE_BOOST_REGEX,,[define if the Boost::Regex library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_regex_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_regex*.so* $BOOSTLIBDIR/libboost_regex*.dylib* $BOOSTLIBDIR/libboost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_regex.*\)\.so.*$;\1;' -e 's;^lib\(boost_regex.*\)\.dylib.*;\1;' -e 's;^lib\(boost_regex.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break], + [link_regex="no"]) + done + if test "x$link_regex" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_regex*.dll* $BOOSTLIBDIR/boost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_regex.*\)\.dll.*$;\1;' -e 's;^\(boost_regex.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break], + [link_regex="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_regex_lib boost_regex-$ax_boost_user_regex_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break], + [link_regex="no"]) + done + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the Boost::Regex library!) + fi + if test "x$link_regex" != "xyes"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/src/twister_rss.cpp b/src/twister_rss.cpp index 91aacacb..25508463 100644 --- a/src/twister_rss.cpp +++ b/src/twister_rss.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include using namespace std; @@ -14,37 +15,21 @@ using namespace json_spirit; int generateRSS(string uri, string *output) { - string account = ""; - int max = 20; - - // get URI parameters if available - size_t startPosition, endPosition; - if(uri.find("max=")!=string::npos) + map parameterMap = parseQuery(uri); + int max = 20; //default value + string account = parameterMap["account"]; + string strMax = parameterMap["max"]; + if(strMax!="") { - startPosition = uri.find("max=")+4; - if(uri.find("&",startPosition)!=string::npos) - endPosition=uri.find("&",startPosition); - else - endPosition=uri.length(); try { - max = boost::lexical_cast(uri.substr(startPosition,endPosition-startPosition)); + max = boost::lexical_cast(strMax); } catch(boost::bad_lexical_cast e) { return RSS_ERROR_NOT_A_NUMBER; } } - if(uri.find("account=")!=string::npos) - { - startPosition = uri.find("account=")+8; - if(uri.find("&",startPosition)!=string::npos) - endPosition=uri.find("&",startPosition); - else - endPosition=uri.length(); - - account = uri.substr(startPosition,endPosition-startPosition); - } const Array emptyArray; Array accountsArray = listwalletusers(emptyArray,false).get_array(); @@ -201,6 +186,23 @@ int generateRSS(string uri, string *output) return RSS_OK; } +map parseQuery(const string& query) +{ + map data; + boost::regex pattern("([\\w+%]+)=([^&]*)"); + boost::sregex_iterator words_begin = boost::sregex_iterator(query.begin(), query.end(), pattern); + boost::sregex_iterator words_end = boost::sregex_iterator(); + + for (boost::sregex_iterator i = words_begin; i != words_end; i++) + { + string key = (*i)[1].str(); + string value = (*i)[2].str(); + data[key] = value; + } + + return data; +} + bool sortByTime (Object i,Object j) { return (find_value(i,"time").get_int64()>find_value(j,"time").get_int64()); diff --git a/src/twister_rss.h b/src/twister_rss.h index e618497e..f57a42d6 100644 --- a/src/twister_rss.h +++ b/src/twister_rss.h @@ -3,6 +3,7 @@ #include "json/json_spirit.h" #include +#include enum RSSResultCode { @@ -13,6 +14,7 @@ enum RSSResultCode }; extern bool sortByTime (json_spirit::Object i,json_spirit::Object j); +extern std::map parseQuery(const std::string& query); extern int generateRSS(std::string uri, std::string *output); #endif // TWISTER_RSS_H From a6505ddcf994879e6615e1be9c8c171f6f21aec7 Mon Sep 17 00:00:00 2001 From: digital dreamer Date: Tue, 3 Jun 2014 09:17:33 +0200 Subject: [PATCH 4/4] use twister.conf argument to enable direct messages --- src/twister_rss.cpp | 62 ++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/twister_rss.cpp b/src/twister_rss.cpp index 25508463..355b1824 100644 --- a/src/twister_rss.cpp +++ b/src/twister_rss.cpp @@ -72,40 +72,44 @@ int generateRSS(string uri, string *output) params2.push_back(max); params2.push_back(postSources); Array posts = getposts(params2,false).get_array(); - Array params3; - params3.push_back(account); - params3.push_back(max); - params3.push_back(postSources); - Object messages = getdirectmsgs(params3,false).get_obj(); vector outputVector; - - for(int j=0;j