diff --git a/Makefile.am b/Makefile.am index 4802e0b8..cd07798d 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) @@ -172,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/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 2b53b3b2..ef57a6e6 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,7 +980,7 @@ void ServiceConnection(AcceptedConnection *conn) if(strMethod == "GET" && strURI == "/") strURI="/home.html"; - 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('?'); @@ -1036,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 diff --git a/src/twister_rss.cpp b/src/twister_rss.cpp new file mode 100644 index 00000000..355b1824 --- /dev/null +++ b/src/twister_rss.cpp @@ -0,0 +1,213 @@ +#include "twister_rss.h" +#include "init.h" +#include "bitcoinrpc.h" +#include "json/json_spirit.h" + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace json_spirit; + +int generateRSS(string uri, string *output) +{ + map parameterMap = parseQuery(uri); + int max = 20; //default value + string account = parameterMap["account"]; + string strMax = parameterMap["max"]; + if(strMax!="") + { + try + { + max = boost::lexical_cast(strMax); + } + catch(boost::bad_lexical_cast e) + { + return RSS_ERROR_NOT_A_NUMBER; + } + } + + 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; + + if(GetBoolArg("-rss_dm",false)) //synchronizing direct messages is disabled by default + { + Array params3; + params3.push_back(account); + params3.push_back(max); + params3.push_back(postSources); + Object messages = getdirectmsgs(params3,false).get_obj(); + + 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; +} + +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 new file mode 100644 index 00000000..f57a42d6 --- /dev/null +++ b/src/twister_rss.h @@ -0,0 +1,20 @@ +#ifndef TWISTER_RSS_H +#define TWISTER_RSS_H + +#include "json/json_spirit.h" +#include +#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 std::map parseQuery(const std::string& query); +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 \