From 74a28aa7125be6a603128ad600c98c4882f3b5c2 Mon Sep 17 00:00:00 2001 From: deva Date: Tue, 1 Jun 2010 12:58:32 +0000 Subject: From new_protocol branch. --- server/src/.cvsignore | 3 +- server/src/Makefile.am | 272 +++-------------- server/src/artefact.cc | 188 ++++++++++++ server/src/artefact.h | 54 ++++ server/src/configuration.cc | 18 +- server/src/configuration.h | 18 +- server/src/configurationparser.cc | 62 +++- server/src/connection.cc | 339 +++++++++++++++++++++ server/src/connection.h | 63 ++++ server/src/connectionpool.cc | 162 +++++++++++ server/src/connectionpool.h | 226 ++++++++++++++ server/src/debug.cc | 2 +- server/src/entitylist.cc | 331 +++++++++++++++++++++ server/src/entitylist.h | 91 ++++++ server/src/environment.cc | 85 ++++++ server/src/environment.h | 50 ++++ server/src/inotify.cc | 549 +++++++++++++++++++++++++++++++++++ server/src/inotify.h | 118 ++++++++ server/src/journal_commit.cc | 96 +----- server/src/journal_commit.h | 33 +-- server/src/journalwriter.cc | 350 ++++++++++++++++++++++ server/src/journalwriter.h | 67 +++++ server/src/luaresume.cc | 30 +- server/src/luautil.cc | 312 ++++++++++++++++++++ server/src/luautil.h | 81 ++++++ server/src/macrolist.cc | 83 ++---- server/src/macrolist.h | 41 +-- server/src/mutex.cc | 76 ++++- server/src/mutex.h | 10 + server/src/pracrod.cc | 53 +++- server/src/queryhandlerpentominos.cc | 262 +++++++++++++---- server/src/queryhandlerpentominos.h | 17 +- server/src/queryparser.cc | 168 +++++++++++ server/src/queryparser.h | 81 ++++++ server/src/resumeparser.cc | 3 +- server/src/saxparser.cc | 251 ++++++++-------- server/src/semaphore.cc | 47 +++ server/src/semaphore.h | 44 +++ server/src/server.cc | 462 ++++++++--------------------- server/src/session.cc | 238 +++++++++++++++ server/src/session.h | 109 +++++++ server/src/sessionparser.cc | 110 +++++++ server/src/sessionparser.h | 64 ++++ server/src/sessionserialiser.cc | 200 +++++++++++++ server/src/sessionserialiser.h | 53 ++++ server/src/tcpsocket.cc | 34 +-- server/src/templatelist.cc | 82 ++---- server/src/templatelist.h | 39 +-- server/src/transactionhandler.cc | 287 ++++++++++++++++++ server/src/transactionhandler.h | 45 +++ server/src/transactionparser.cc | 148 ++++------ server/src/widgetgenerator.cc | 28 +- 52 files changed, 5390 insertions(+), 1245 deletions(-) create mode 100644 server/src/artefact.cc create mode 100644 server/src/artefact.h create mode 100644 server/src/connection.cc create mode 100644 server/src/connection.h create mode 100644 server/src/connectionpool.cc create mode 100644 server/src/connectionpool.h create mode 100644 server/src/entitylist.cc create mode 100644 server/src/entitylist.h create mode 100644 server/src/environment.cc create mode 100644 server/src/environment.h create mode 100644 server/src/inotify.cc create mode 100644 server/src/inotify.h create mode 100644 server/src/journalwriter.cc create mode 100644 server/src/journalwriter.h create mode 100644 server/src/luautil.cc create mode 100644 server/src/luautil.h create mode 100644 server/src/queryparser.cc create mode 100644 server/src/queryparser.h create mode 100644 server/src/semaphore.cc create mode 100644 server/src/semaphore.h create mode 100644 server/src/session.cc create mode 100644 server/src/session.h create mode 100644 server/src/sessionparser.cc create mode 100644 server/src/sessionparser.h create mode 100644 server/src/sessionserialiser.cc create mode 100644 server/src/sessionserialiser.h create mode 100644 server/src/transactionhandler.cc create mode 100644 server/src/transactionhandler.h (limited to 'server/src') diff --git a/server/src/.cvsignore b/server/src/.cvsignore index e7654ee..6fa7094 100644 --- a/server/src/.cvsignore +++ b/server/src/.cvsignore @@ -4,4 +4,5 @@ Makefile.in Makefile .libs .deps -test_* \ No newline at end of file +test_* +tests.make \ No newline at end of file diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 98a154e..942ced5 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -2,25 +2,33 @@ bin_PROGRAMS = pracrod macrotool pracrod_LDADD = $(LD_EFENCE) $(PQXX_LIBS) $(CONFIG_LIBS) \ - $(LUA_LIBS) $(HTTPD_LIBS) -lpthread $(ATF_LIBS) + $(LUA_LIBS) $(HTTPD_LIBS) $(PTHREAD_LIBS) \ + $(EXPAT_LIBS) $(ATF_LIBS) pracrod_CXXFLAGS = $(PQXX_CXXFLAGS) $(CONFIG_CXXFLAGS) \ - $(LUA_CXXFLAGS) $(HTTPD_CFLAGS) $(ATF_CFLAGS) + $(LUA_CXXFLAGS) $(HTTPD_CFLAGS) $(EXPAT_CFLAGS) \ + $(PTHREAD_CFLAGS) $(ATF_CFLAGS) pracrod_SOURCES = \ pracrod.cc \ + artefact.cc \ daemon.cc \ database.cc \ configuration.cc \ configurationparser.cc \ + connection.cc \ + connectionpool.cc \ debug.cc \ + entitylist.cc \ + environment.cc \ exception.cc \ - queryhandlerpentominos.cc \ - queryhandlerpracro.cc \ + inotify.cc \ journal_commit.cc \ + journalwriter.cc \ log.cc \ luaquerymapper.cc \ luaresume.cc \ + luautil.cc \ macroheaderparser.cc \ macrolist.cc \ macroparser.cc \ @@ -28,12 +36,20 @@ pracrod_SOURCES = \ pracrodao.cc \ pracrodaopgsql.cc \ pracrodaotest.cc \ + queryhandlerpentominos.cc \ + queryhandlerpracro.cc \ + queryparser.cc \ resumeparser.cc \ saxparser.cc \ + semaphore.cc \ server.cc \ + session.cc \ + sessionparser.cc \ + sessionserialiser.cc \ templatelist.cc \ templateheaderparser.cc \ templateparser.cc \ + transactionhandler.cc \ transactionparser.cc \ tcpsocket.cc \ utf8.cc \ @@ -41,9 +57,11 @@ pracrod_SOURCES = \ widgetgenerator.cc \ xml_encode_decode.cc -macrotool_LDADD = $(LD_EFENCE) $(PQXX_LIBS) $(CONFIG_LIBS) $(LUA_LIBS) -lpthread +macrotool_LDADD = $(LD_EFENCE) $(PQXX_LIBS) $(CONFIG_LIBS) \ + $(LUA_LIBS) $(PTHREAD_LIBS) $(EXPAT_LIBS) -macrotool_CXXFLAGS = $(PQXX_CXXFLAGS) $(CONFIG_CXXFLAGS) $(LUA_CXXFLAGS) +macrotool_CXXFLAGS = $(PQXX_CXXFLAGS) $(CONFIG_CXXFLAGS) \ + $(LUA_CXXFLAGS) $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) macrotool_SOURCES = \ macrotool.cc \ @@ -51,7 +69,9 @@ macrotool_SOURCES = \ configuration.cc \ configurationparser.cc \ database.cc \ + entitylist.cc \ exception.cc \ + inotify.cc \ log.cc \ macroheaderparser.cc \ macrolist.cc \ @@ -69,20 +89,25 @@ macrotool_SOURCES = \ versionstr.cc EXTRA_DIST = \ + artefact.h \ configuration.h \ configurationparser.h \ + connection.h \ + connectionpool.h \ daemon.h \ database.h \ dbtypes.h \ debug.h \ + environment.h \ + entitylist.h \ exception.h \ - queryhandler.h \ - queryhandlerpentominos.h \ - queryhandlerpracro.h \ + inotify.h \ journal_commit.h \ + journalwriter.h \ log.h \ luaquerymapper.h \ luaresume.h \ + luautil.h \ macroheaderparser.h \ macrolist.h \ macroparser.h \ @@ -94,15 +119,21 @@ EXTRA_DIST = \ pracrodao.h \ pracrodaopgsql.h \ pracrodaotest.h \ - queryresult.h \ + queryhandler.h \ + queryhandlerpentominos.h \ + queryhandlerpracro.h \ + queryparser.h \ resumeparser.h \ saxparser.h \ + semaphore.h \ server.h \ - template.h \ + session.h \ + sessionparser.h \ + sessionserialiser.h \ templatelist.h \ templateheaderparser.h \ templateparser.h \ - transaction.h \ + transactionhandler.h \ transactionparser.h \ tcpsocket.h \ utf8.h \ @@ -114,217 +145,10 @@ EXTRA_DIST = \ # Test Section # ################ -TESTFILES = \ - test_tcpsocket \ - test_pracrodaotest \ - test_widgetgenerator \ - test_configurationparser \ - test_exception \ - test_templateheaderparser \ - test_macroheaderparser \ - test_templatelist \ - test_saxparser \ - test_transactionparser \ - test_versionstr \ - test_macrolist \ - test_queryhandlerpentominos \ - test_queryhandlerpracro \ - test_luaquerymapper \ - test_templateparser \ - test_server \ - test_pracrodaopgsql \ - test_macroparser \ - test_xml_encode_decode \ - test_journal_commit - -TESTLOGS = `for F in ${TESTFILES}; do echo $$F.log; done` - -BASICFILES = exception.cc log.cc debug.cc configuration.cc utf8.cc -BASICFLAGS = -I.. -DHAVE_CONFIG_H $(CONFIG_CXXFLAGS) $(CONFIG_LIBS) - -PARSERFILES = saxparser.cc -PARSERFLAGS = -lexpat - -DBFILES = database.cc pracrodao.cc pracrodaopgsql.cc pracrodaotest.cc mutex.cc -DBFLAGS = $(PQXX_LIBS) $(PQXX_CXXFLAGS) - -test: $(TESTFILES) - @echo "All tests done." - -test_clean: - rm -f $(TESTFILES) $(TESTLOGS) - -TEST_TCPSOCKET_FILES = \ - tcpsocket.cc \ - $(BASICFILES) -test_tcpsocket: $(TEST_TCPSOCKET_FILES) - @../../tools/test $(TEST_TCPSOCKET_FILES) $(BASICFLAGS) - -TEST_PRACRODAOTEST_FILES = \ - pracrodaotest.cc \ - pracrodao.cc \ - $(BASICFILES) -test_pracrodaotest: $(TEST_PRACRODAOTEST_FILES) - @../../tools/test $(TEST_PRACRODAOTEST_FILES) $(BASICFLAGS) - -TEST_WIDGETGENERATOR_FILES = \ - widgetgenerator.cc \ - xml_encode_decode.cc \ - luaquerymapper.cc \ - $(BASICFILES) \ - $(DBFILES) -test_widgetgenerator: $(TEST_WIDGETGENERATOR_FILES) - @../../tools/test $(TEST_WIDGETGENERATOR_FILES) $(BASICFLAGS) $(LUA_LIBS) $(DBFLAGS) - -TEST_CONFIGURATIONPARSER_FILES = \ - configurationparser.cc \ - configuration.cc \ - exception.cc \ - log.cc -test_configurationparser: $(TEST_CONFIGURATIONPARSER_FILES) - @../../tools/test $(TEST_CONFIGURATIONPARSER_FILES) $(BASICFLAGS) - -TEST_EXCEPTION_FILES = \ - exception.cc \ - log.cc -test_exception: $(TEST_EXCEPTION_FILES) - @../../tools/test $(TEST_EXCEPTION_FILES) - -TEST_TEMPLATEHEADERPARSER_FILES = \ - templateheaderparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_templateheaderparser: $(TEST_TEMPLATEHEADERPARSER_FILES) - @../../tools/test $(TEST_TEMPLATEHEADERPARSER_FILES) $(BASICFLAGS) $(PARSERFLAGS) - -TEST_MACROHEADERPARSER_FILES = \ - macroheaderparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_macroheaderparser: $(TEST_MACROHEADERPARSER_FILES) - @../../tools/test $(TEST_MACROHEADERPARSER_FILES) $(BASICFLAGS) $(PARSERFLAGS) - -TEST_TEMPLATELIST_FILES = \ - templatelist.cc \ - versionstr.cc \ - templateheaderparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_templatelist: $(TEST_TEMPLATELIST_FILES) - @../../tools/test $(TEST_TEMPLATELIST_FILES) $(PARSERFLAGS) $(BASICFLAGS) - -TEST_MACROLIST_FILES = \ - macrolist.cc \ - versionstr.cc \ - macroheaderparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_macrolist: $(TEST_MACROLIST_FILES) - @../../tools/test $(TEST_MACROLIST_FILES) $(PARSERFLAGS) $(BASICFLAGS) - -TEST_SAXPARSER_FILES = \ - saxparser.cc \ - $(BASICFILES) -test_saxparser: $(TEST_SAXPARSER_FILES) - @../../tools/test $(TEST_SAXPARSER_FILES) $(BASICFLAGS) $(PARSERFLAGS) - -TEST_TRANSACTIONPARSER_FILES = \ - transactionparser.cc \ - $(BASICFILES) \ - $(PARSERFILES) -test_transactionparser: $(TEST_TRANSACTIONPARSER_FILES) - @../../tools/test $(TEST_TRANSACTIONPARSER_FILES) $(BASICFLAGS) $(PARSERFLAGS) - -TEST_VERSIONSTR_FILES = \ - versionstr.cc \ - $(BASICFILES) -test_versionstr: $(TEST_VERSIONSTR_FILES) - @../../tools/test $(TEST_VERSIONSTR_FILES) $(BASICFLAGS) - -TEST_QUERYHANDLERPENTOMINOS_FILES = \ - queryhandlerpentominos.cc \ - tcpsocket.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_queryhandlerpentominos: $(TEST_QUERYHANDLERPENTOMINOS_FILES) - @../../tools/test $(TEST_QUERYHANDLERPENTOMINOS_FILES) $(PARSERFLAGS) $(BASICFLAGS) $(ATF_CFLAGS) $(ATF_LIBS) +TEST_SOURCE_DEPS = ${pracrod_SOURCES} ${macrotool_SOURCES} \ + ${EXTRA_DIST} +TEST_SCRIPT_DIR = $(top_srcdir)/../tools -TEST_QUERYHANDLERPRACRO_FILES = \ - queryhandlerpracro.cc \ - tcpsocket.cc \ - $(DBFILES) \ - $(PARSERFILES) \ - $(BASICFILES) -test_queryhandlerpracro: $(TEST_QUERYHANDLERPRACRO_FILES) - @../../tools/test $(TEST_QUERYHANDLERPRACRO_FILES) $(DBFLAGS) $(PARSERFLAGS) $(BASICFLAGS) - -TEST_LUAQUERMAPPER_FILES = \ - luaquerymapper.cc \ - $(BASICFILES) -test_luaquerymapper: $(TEST_LUAQUERMAPPER_FILES) - @../../tools/test $(TEST_LUAQUERMAPPER_FILES) $(LUA_LIBS) $(BASICFLAGS) - -TEST_TEMPLATEPARSER_FILES = \ - templateparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_templateparser: $(TEST_TEMPLATEPARSER_FILES) - @../../tools/test $(TEST_TEMPLATEPARSER_FILES) $(BASICFLAGS) $(PARSERFLAGS) - -TEST_MACROPARSER_FILES = \ - macroparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_macroparser: $(TEST_MACROPARSER_FILES) - @../../tools/test $(TEST_MACROPARSER_FILES) $(BASICFLAGS) $(PARSERFLAGS) - -TEST_SERVER_FILES = \ - server.cc \ - templateparser.cc \ - templatelist.cc \ - templateheaderparser.cc \ - queryhandlerpentominos.cc \ - journal_commit.cc \ - macrolist.cc \ - queryhandlerpracro.cc \ - macroheaderparser.cc \ - versionstr.cc \ - resumeparser.cc \ - luaquerymapper.cc \ - tcpsocket.cc \ - transactionparser.cc \ - widgetgenerator.cc \ - database.cc \ - pracrodao.cc \ - pracrodaotest.cc \ - pracrodaopgsql.cc \ - luaresume.cc \ - macroparser.cc \ - mutex.cc \ - xml_encode_decode.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_server: $(TEST_SERVER_FILES) - @../../tools/test $(TEST_SERVER_FILES) $(BASICFLAGS) $(LUA_LIBS) $(DBFLAGS) $(PARSERFLAGS) $(HTTPD_LIBS) $(HTTPD_CFLAGS) - -TEST_PRACRODAOPGSQL_FILES = \ - pracrodaopgsql.cc \ - pracrodao.cc \ - $(BASICFILES) -test_pracrodaopgsql: $(TEST_PRACRODAOPGSQL_FILES) - @../../tools/test $(TEST_PRACRODAOPGSQL_FILES) $(DBFLAGS) $(BASICFLAGS) - -TEST_XMLENCODEDECODE_FILES = \ - xml_encode_decode.cc -test_xml_encode_decode: $(TEST_XMLENCODEDECODE_FILES) - @../../tools/test $(TEST_XMLENCODEDECODE_FILES) - -TEST_JOURNALCOMMIT_FILES = \ - journal_commit.cc \ - templateparser.cc \ - $(PARSERFILES) \ - $(BASICFILES) -test_journal_commit: $(TEST_JOURNALCOMMIT_FILES) - @../../tools/test $(TEST_JOURNALCOMMIT_FILES) $(PARSERFLAGS) $(BASICFLAGS) +include ${TEST_SCRIPT_DIR}/Makefile.am.test -CLEANFILES = $(TESTFILES) $(TESTLOGS) *~ +include Makefile.am.test \ No newline at end of file diff --git a/server/src/artefact.cc b/server/src/artefact.cc new file mode 100644 index 0000000..f3e739d --- /dev/null +++ b/server/src/artefact.cc @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * artefact.cc + * + * Tue Jan 5 14:45:34 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "artefact.h" + +#include "debug.h" +#include "configuration.h" + +#include "queryparser.h" + +Artefact::Artefact() +{ +#ifndef WITHOUT_ARTEFACT + + PRACRO_DEBUG(artefact, "Creating artefact connection %s : %d\n", + Conf::artefact_addr.c_str(), Conf::artefact_port); + + atfh = atf_init(); + if(!atfh) PRACRO_ERR(artefact, "Out of memory!\n"); + + conn = atf_connect(atfh, + Conf::artefact_addr.c_str(), + Conf::artefact_port, + Conf::artefact_use_ssl); + +#endif/*WITHOUT_ARTEFACT*/ +} + +Artefact::~Artefact() +{ +#ifndef WITHOUT_ARTEFACT + + atf_disconnect(conn); + atf_close(atfh); + +#endif/*WITHOUT_ARTEFACT*/ +} + +#ifndef WITHOUT_ARTEFACT +static QueryResult node2result(atf_result_node_t *node, time_t timestamp) +{ + QueryResult rnode; + rnode.timestamp = timestamp; + rnode.source = "artefact"; + + if(!node) return rnode; + + struct _atf_result_node_t *child = node->child; + while(child) { + if(child->value == NULL) { + rnode.groups[child->name] = node2result(child, timestamp); + } else { + rnode.values[child->name] = child->value; + } + child = child->next; + } + + return rnode; +} +#endif/*WITHOUT_ARTEFACT*/ + +QueryResult Artefact::exec(Query &query, + std::string patientid, + std::string user) +{ + QueryResult rroot; + rroot.timestamp = 0; + rroot.source = "pentominos"; + +#ifndef WITHOUT_ARTEFACT + + atf_transaction_t* atft = NULL; + atf_reply_t *reply = NULL; + atf_result_t *result = NULL; + atf_result_node_t *root = NULL; + atf_status_t status; + time_t timestamp; + atf_id id; + + if(query.attributes.find("class") == query.attributes.end()) { + PRACRO_ERR(artefact, "Missing 'class' attribute!\n"); + goto aaarg; + } + + atft = atf_new_transaction(conn, patientid.c_str()); + if(!atft) goto aaarg; + + id = atf_add_query(atft, query.attributes["class"].c_str(), + FILTER_LATEST, USE_NONE, 0, 0); + if(!atft) goto aaarg; + + reply = atf_commit(atft); + if(!reply) goto aaarg; + + if(atf_get_num_results(reply, id) != 1) goto aaarg; + + result = atf_get_result(reply, id, 0); + if(!result) goto aaarg; + + status = atf_get_result_status(result, NULL, 0); + if(status != ATF_STATUS_OK) goto aaarg; + + timestamp = atf_get_result_timestamp(result); + rroot.timestamp = timestamp; + + root = atf_get_result_node(result); + if(!root) goto aaarg; + + { + QueryResult qresult = node2result(root, timestamp); + rroot.groups[query.attributes["class"]] = qresult; + } + + goto cleanup; + + aaarg: + PRACRO_ERR(artefact, "Artefact comm error (%d)!\n", atf_get_last_error(atfh)); + + cleanup: + if(root) atf_free_result_node(root); + if(reply) atf_free_reply(reply); + if(atft) atf_free_transaction(atft); + +#endif/*WITHOUT_ARTEFACT*/ + + return rroot; +} + + +#ifdef TEST_ARTEFACT +//deps: configuration.cc debug.cc +//cflags: $(ATF_CFLAGS) -I.. +//libs: $(ATF_LIBS) +#include "test.h" + +TEST_BEGIN; + +pracro_debug_init(); +pracro_debug_parse("+all"); + +{ + Artefact atf; + Query q; + q.attributes["class"] = "echo"; + QueryResult res = atf.exec(q, "1505050505", "me"); + + res.print(); +} + +{ + Conf::artefact_addr = "nowhere_at_all.com"; + Conf::artefact_port = 10000; + + Artefact atf; + Query q; + q.attributes["class"] = "echo"; + QueryResult res = atf.exec(q, "1505050505", "me"); + + res.print(); +} + +TEST_END; + +#endif/*TEST_ARTEFACT*/ diff --git a/server/src/artefact.h b/server/src/artefact.h new file mode 100644 index 0000000..fa1602d --- /dev/null +++ b/server/src/artefact.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * artefact.h + * + * Tue Jan 5 14:45:34 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_ARTEFACT_H__ +#define __PRACRO_ARTEFACT_H__ + +#include "template.h" +#include "queryresult.h" + +#ifndef WITHOUT_ARTEFACT +#include +#endif/*WITHOUT_ARTEFACT*/ + +class Artefact { +public: + Artefact(); + ~Artefact(); + + QueryResult exec(Query &query, + std::string patientid, + std::string user); + +private: +#ifndef WITHOUT_ARTEFACT + atf_handle_t *atfh; + atf_connection_t *conn; +#endif/*WITHOUT_ARTEFACT*/ +}; + +#endif/*__PRACRO_ARTEFACT_H__*/ diff --git a/server/src/configuration.cc b/server/src/configuration.cc index c1dcab1..b222639 100644 --- a/server/src/configuration.cc +++ b/server/src/configuration.cc @@ -38,12 +38,26 @@ port_t Conf::journal_commit_port = 18112; time_t Conf::db_max_ttl = 7 * 60 * 60 * 24; time_t Conf::pentominos_max_ttl = 7 * 60 * 60 * 24; -std::string Conf::pentominos_addr = "localhost"; -port_t Conf::pentominos_port = 11108; +std::string Conf::artefact_addr = "localhost"; +port_t Conf::artefact_port = 11108; +bool Conf::artefact_use_ssl = false; + +int Conf::artefact_poolsize = 1; std::string Conf::database_backend = "pgsql"; std::string Conf::database_addr = "localhost"; std::string Conf::database_user = "pracro"; std::string Conf::database_passwd = "pracro"; +int Conf::database_poolsize = 2; + std::string Conf::xml_basedir = XML; + +bool Conf::use_ssl = false; +std::string Conf::ssl_key = ""; +std::string Conf::ssl_cert = ""; + +int Conf::connection_limit = 42; +int Conf::connection_timeout = 0; + +std::string Conf::session_path = "/tmp"; diff --git a/server/src/configuration.h b/server/src/configuration.h index 3d9f7f1..8980bfd 100644 --- a/server/src/configuration.h +++ b/server/src/configuration.h @@ -45,15 +45,29 @@ namespace Conf { extern time_t db_max_ttl; extern time_t pentominos_max_ttl; - extern std::string pentominos_addr; - extern port_t pentominos_port; + extern std::string artefact_addr; + extern port_t artefact_port; + extern bool artefact_use_ssl; + + extern int artefact_poolsize; extern std::string database_backend; extern std::string database_addr; extern std::string database_user; extern std::string database_passwd; + extern int database_poolsize; + extern std::string xml_basedir; + + extern bool use_ssl; + extern std::string ssl_key; + extern std::string ssl_cert; + + extern int connection_limit; + extern int connection_timeout; + + extern std::string session_path; }; #endif/*__ARTEFACT_CONFIGURATION_H__*/ diff --git a/server/src/configurationparser.cc b/server/src/configurationparser.cc index 8247a45..bc10e2b 100644 --- a/server/src/configurationparser.cc +++ b/server/src/configurationparser.cc @@ -93,14 +93,26 @@ void ConfigurationParser::reload() } try { - std::string a = lookup("pentominos_addr"); - Conf::pentominos_addr = a; + std::string s = lookup("artefact_addr"); + Conf::artefact_addr = s; } catch( ... ) { } try { - int p = lookup("pentominos_port"); - Conf::pentominos_port = p; + int i = lookup("artefact_port"); + Conf::artefact_port = i; + } catch( ... ) { + } + + try { + bool b = lookup("artefact_use_ssl"); + Conf::artefact_use_ssl = b; + } catch( ... ) { + } + + try { + int i = lookup("artefact_poolsize"); + Conf::artefact_poolsize = i; } catch( ... ) { } @@ -128,11 +140,53 @@ void ConfigurationParser::reload() } catch( ... ) { } + try { + int i = lookup("database_poolsize"); + Conf::database_poolsize = i; + } catch( ... ) { + } + try { std::string p = lookup("xml_basedir"); Conf::xml_basedir = p; } catch( ... ) { } + + try { + bool b = lookup("use_ssl"); + Conf::use_ssl = b; + } catch( ... ) { + } + + try { + std::string s = lookup("ssl_key"); + Conf::ssl_key = s; + } catch( ... ) { + } + + try { + std::string s = lookup("ssl_cert"); + Conf::ssl_cert = s; + } catch( ... ) { + } + + try { + int i = lookup("connection_limit"); + Conf::connection_limit = i; + } catch( ... ) { + } + + try { + int i = lookup("connection_timeout"); + Conf::connection_timeout = i; + } catch( ... ) { + } + + try { + std::string s = lookup("session_path"); + Conf::session_path = s; + } catch( ... ) { + } } #ifdef TEST_CONFIGURATIONPARSER diff --git a/server/src/connection.cc b/server/src/connection.cc new file mode 100644 index 0000000..ed5a7a9 --- /dev/null +++ b/server/src/connection.cc @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * connection.cc + * + * Fri May 7 11:35:44 CEST 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "connection.h" + +#include "transactionhandler.h" +#include "xml_encode_decode.h" + +static std::string error_box(std::string message) +{ + std::string errorbox = + "\n" + "\n" + " " + message + "\n" + "\n"; + return errorbox; +} + +#ifdef TEST_CONNECTION +static bool did_commit = false; +#endif + +Connection::Connection(Environment &e, std::string sid, bool c, bool d) + : env(e), parser(&transaction) +{ + PRACRO_DEBUG(connection, "[%p] CREATE\n", this); + + sessionid = sid; + docommit = c; + dodiscard = d; + +#ifdef TEST_CONNECTION + did_commit = false; +#endif + +} + +Connection::~Connection() +{ + PRACRO_DEBUG(connection, "[%p] DESTROY\n", this); +} + +void Connection::commit(Session *session) +{ + if(docommit) { + session->commit(); + env.sessions.deleteSession(session->id()); + sessionid = ""; +#ifdef TEST_CONNECTION + did_commit = true; +#endif + } +} + +void Connection::discard(Session *session) +{ + if(dodiscard) { + session->discard(); + env.sessions.deleteSession(session->id()); + sessionid = ""; + } +} + +bool Connection::handle(const char *data, size_t size) +{ + Session *session = NULL; + if(sessionid == "") { + // Create new session + session = env.sessions.newSession(); + } else { + // Attach to old session + session = env.sessions.session(sessionid); + + // Session didn't exist - create a new one anyway. + if(session == NULL) session = env.sessions.newSession(); + } + + if(session == NULL) { + PRACRO_ERR(connection, "New session could not be created."); + response = error_box(xml_encode("New session could not be created.")); + return true; + } + + sessionid = session->id(); + + try { + + if(!data || !size) { + commit(session); + discard(session); + return true; + } + + if(parser.parse(data, size)) { + { + SessionAutolock lock(*session); + response = handleTransaction(transaction, env, *session); + } + + commit(session); + discard(session); + return true; + } + } catch(...) { + PRACRO_ERR(server, "Failed to parse data!\n"); + response = error_box(xml_encode("XML Parse error.")); + return true; + } + + return false; +} + +std::string Connection::getResponse() +{ + return response; +} + +std::string Connection::getSessionID() +{ + return sessionid; +} + +#ifdef TEST_CONNECTION +//deps: debug.cc transactionparser.cc session.cc xml_encode_decode.cc saxparser.cc transactionhandler.cc journalwriter.cc mutex.cc templateparser.cc exception.cc configuration.cc macroparser.cc semaphore.cc entitylist.cc luaquerymapper.cc inotify.cc log.cc queryhandlerpentominos.cc widgetgenerator.cc queryhandlerpracro.cc resumeparser.cc journal_commit.cc versionstr.cc luaresume.cc luautil.cc artefact.cc environment.cc database.cc macrolist.cc templatelist.cc pracrodao.cc templateheaderparser.cc macroheaderparser.cc pracrodaotest.cc pracrodaopgsql.cc +//cflags: -DWITHOUT_DATABASE -DWITHOUT_ARTEFACT -I.. $(LUA_CFLAGS) $(EXPAT_CFLAGS) $(PTHREAD_CFLAGS) $(PQXX_CXXFLAGS) +//libs: $(LUA_LIBS) $(EXPAT_LIBS) $(PTHREAD_LIBS) $(PQXX_LIBS) +#include "test.h" + +static char xml_request[] = +"\n" +"\n" +" \n" +"\n" + ; + +static char xml_commit[] = +"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" + ; + +static char xml_commit_p1[] = +"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +"\n" + ; + +TEST_BEGIN; + +Environment env; +std::string sid; + +// Without data +{ + Connection con(env, "", false); + TEST_TRUE(con.handle("", 0), "Test handler return value."); + TEST_EQUAL_STR(con.getResponse(), "", "Test response value."); + sid = con.getSessionID(); + TEST_NOTEQUAL_STR(sid, "", "Test new session id."); + TEST_FALSE(did_commit, "No commit."); +} + +{ + Connection con(env, sid, false); + TEST_TRUE(con.handle("", 0), "Test handler return value."); + TEST_EQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_NOTEQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_EQUAL_STR(con.getSessionID(), sid, "Test existing session id."); + TEST_FALSE(did_commit, "No commit."); +} + +{ + Connection con(env, sid, true); + TEST_TRUE(con.handle("", 0), "Test handler return value."); + TEST_EQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_EQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_TRUE(did_commit, "Commit."); +} + +{ + Connection con(env, sid, false); + TEST_TRUE(con.handle("", 0), "Test handler return value."); + TEST_EQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_NOTEQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_NOTEQUAL_STR(con.getSessionID(), sid, "Test new session id."); + TEST_FALSE(did_commit, "No commit."); +} + +// With commit partial data +{ + Connection con(env, "", false); + TEST_FALSE(con.handle(xml_commit_p1, sizeof(xml_commit_p1) - 1), + "Test handler return value."); + sid = con.getSessionID(); + TEST_NOTEQUAL_STR(sid, "", "Test new session id."); + TEST_FALSE(did_commit, "No commit."); + TEST_EQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_TRUE(con.handle(xml_commit_p2, sizeof(xml_commit_p2) - 1), + "Test handler return value."); + TEST_EQUAL_STR(con.getSessionID(), sid, "Test session id."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_FALSE(did_commit, "No commit."); +} + +// With commit partial data and journal commit +{ + Connection con(env, "", true); + TEST_FALSE(con.handle(xml_commit_p1, sizeof(xml_commit_p1) - 1), + "Test handler return value."); + sid = con.getSessionID(); + TEST_NOTEQUAL_STR(sid, "", "Test new session id."); + TEST_EQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_FALSE(did_commit, "No commit."); + TEST_TRUE(con.handle(xml_commit_p2, sizeof(xml_commit_p2) - 1), + "Test handler return value."); + TEST_EQUAL_STR(con.getSessionID(), "", "Test session id."); + TEST_TRUE(did_commit, "No commit."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); +} + +// With commit data +{ + Connection con(env, "", false); + TEST_TRUE(con.handle(xml_commit, sizeof(xml_commit) - 1), + "Test handler return value."); + sid = con.getSessionID(); + TEST_NOTEQUAL_STR(sid, "", "Test new session id."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_FALSE(did_commit, "No commit."); +} + +{ + Connection con(env, sid, false); + TEST_TRUE(con.handle(xml_commit, sizeof(xml_commit) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_NOTEQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_EQUAL_STR(con.getSessionID(), sid, "Test existing session id."); + TEST_FALSE(did_commit, "No commit."); +} + +{ + Connection con(env, sid, true); + TEST_TRUE(con.handle(xml_commit, sizeof(xml_commit) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_EQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_TRUE(did_commit, "Commit."); +} + +{ + Connection con(env, sid, false); + TEST_TRUE(con.handle(xml_commit, sizeof(xml_commit) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_NOTEQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_NOTEQUAL_STR(con.getSessionID(), sid, "Test new session id."); + TEST_FALSE(did_commit, "No commit."); +} + +// With request data +{ + Connection con(env, "", false); + TEST_TRUE(con.handle(xml_request, sizeof(xml_request) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + sid = con.getSessionID(); + TEST_NOTEQUAL_STR(sid, "", "Test new session id."); + TEST_FALSE(did_commit, "No commit."); +} + +{ + Connection con(env, sid, false); + TEST_TRUE(con.handle(xml_request, sizeof(xml_request) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_NOTEQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_EQUAL_STR(con.getSessionID(), sid, "Test existing session id."); + TEST_FALSE(did_commit, "No commit."); +} + +{ + Connection con(env, sid, true); + TEST_TRUE(con.handle(xml_request, sizeof(xml_request) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getResponse(), "", "Test response value."); + TEST_EQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_TRUE(did_commit, "Commit."); +} + +{ + Connection con(env, sid, false); + TEST_TRUE(con.handle(xml_request, sizeof(xml_request) - 1), + "Test handler return value."); + TEST_NOTEQUAL_STR(con.getSessionID(), "", "Test existing session id."); + TEST_NOTEQUAL_STR(con.getSessionID(), sid, "Test new session id."); + TEST_FALSE(did_commit, "No commit."); +} + +TEST_END; + +#endif/*TEST_CONNECTION*/ diff --git a/server/src/connection.h b/server/src/connection.h new file mode 100644 index 0000000..61997da --- /dev/null +++ b/server/src/connection.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * connection.h + * + * Fri May 7 11:35:43 CEST 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_CONNECTION_H__ +#define __PRACRO_CONNECTION_H__ + +#include +#include "environment.h" +#include "transaction.h" +#include "transactionparser.h" + +class Session; + +class Connection { +public: + Connection(Environment &e, std::string sessionid, bool commit, bool discard); + ~Connection(); + + bool handle(const char *data, size_t size); + + std::string getResponse(); + std::string getSessionID(); + +private: + void commit(Session *session); + void discard(Session *session); + + std::string sessionid; + bool docommit; + bool dodiscard; + Environment &env; + + Transaction transaction; + TransactionParser parser; + + std::string response; +}; + +#endif/*__PRACRO_CONNECTION_H__*/ diff --git a/server/src/connectionpool.cc b/server/src/connectionpool.cc new file mode 100644 index 0000000..e2b6bfc --- /dev/null +++ b/server/src/connectionpool.cc @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * connectionpool.cc + * + * Wed Dec 16 12:20:44 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "connectionpool.h" + +// +// Implementation is in the header file. +// + +#ifdef TEST_CONNECTIONPOOL +//deps: mutex.cc semaphore.cc +//cflags: $(PTHREAD_CFLAGS) +//libs: $(PTHREAD_LIBS) +#include +#include +#include + +static void* thread_run(void *data) +{ + ConnectionPool *pool = (ConnectionPool*)data; + + int db1 = pool->borrow(); + int db2 = pool->borrow(); + int db3 = pool->borrow(); + int db4 = pool->borrow(); + + usleep(100); + + pool->giveBack(db1); + pool->giveBack(db2); + pool->giveBack(db3); + pool->giveBack(db4); + + return NULL; +} + +static void* thread_run_clear_test(void *data) +{ + ConnectionPool *pool = (ConnectionPool*)data; + pool->giveBack(1); + pool->giveBack(2); + + sleep(1); + + pool->giveBack(3); + pool->giveBack(4); + + return NULL; +} + +TEST_BEGIN; + +ConnectionPool pool; + +int db1 = 1; +int db2 = 2; +int db3 = 3; +int db4 = 4; + +pool.add(db1); +pool.add(db2); +pool.add(db3); +pool.add(db4); + +TEST_TRUE(pool.testFree(db1), "Testing if db1 is free."); +TEST_TRUE(pool.testFree(db2), "Testing if db2 is free."); +TEST_TRUE(pool.testFree(db3), "Testing if db3 is free."); +TEST_TRUE(pool.testFree(db4), "Testing if db4 is free."); +TEST_EQUAL(pool.numFree(), 4, "Testing number of free databases."); + +int b_db1 = pool.borrow(); +TEST_FALSE(pool.testFree(b_db1), "Testing if borrowed db is free."); + +int b_db2 = pool.borrow(); +TEST_NOTEQUAL(b_db1, b_db2, "Testing if borrowed db is unique."); + +pool.giveBack(b_db1); +TEST_TRUE(pool.testFree(b_db1), "Testing if returned db is free."); +pool.giveBack(b_db2); + +TEST_EQUAL(pool.numFree(), 4, "Testing number of free databases."); + +{ + pthread_attr_t attr; + pthread_t tid; + pthread_attr_init(&attr); + pthread_create(&tid, &attr, thread_run, &pool); + + while(pool.numFree() > 0) { usleep(10); } + + int b_db3 = pool.borrow(); + TEST_FALSE(pool.testFree(b_db3), "Testing if returned db is free (semaphore test)."); + pool.giveBack(db3); + + pthread_join(tid, NULL); + pthread_attr_destroy(&attr); +} + +TEST_EQUAL(pool.numFree(), 4, "Testing if all database are now available again"); + +{ + TEST_EQUAL(pool.numFree(), 4, "Testing if autoborrower has not yet borrowed a db."); + AutoBorrower b(pool); + TEST_EQUAL(pool.numFree(), 3, "Testing if autoborrower has borrowed a db."); + TEST_FALSE(pool.testFree(b.get()), "Testing if the autoborrowed db is actually taken."); +} +TEST_EQUAL(pool.numFree(), 4, "Testing if autoborrower has returned the db."); + +// Force remove all elements. +pool.borrow(); +pool.clear(true); +TEST_EQUAL(pool.numFree(), 0, "Testing number of free databases."); + +// Add them again. +pool.add(db1); +pool.add(db2); +pool.add(db3); +pool.add(db4); +TEST_EQUAL(pool.numFree(), 4, "Testing number of free databases."); + +pool.borrow(); +pool.borrow(); +pool.borrow(); +pool.borrow(); + +{ + pthread_attr_t attr; + pthread_t tid; + pthread_attr_init(&attr); + pthread_create(&tid, &attr, thread_run_clear_test, &pool); + pool.clear(false); + pthread_join(tid, NULL); + pthread_attr_destroy(&attr); +} + +TEST_END; + +#endif/*TEST_CONNECTIONPOOL*/ diff --git a/server/src/connectionpool.h b/server/src/connectionpool.h new file mode 100644 index 0000000..33473e5 --- /dev/null +++ b/server/src/connectionpool.h @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * connectionpool.h + * + * Wed Dec 16 12:20:44 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_CONNECTIONPOOL_H__ +#define __PRACRO_CONNECTIONPOOL_H__ + +#include + +#include "mutex.h" +#include "semaphore.h" + +template +class ConnectionPool { +public: + void add(T t); + void remove(T t); + + bool testFree(T t); + int numFree(); + + T borrow(); + void giveBack(T t); + + std::list clear(bool force = true); + +private: + bool contains(std::list &list, T t); + + Semaphore semaphore; + Mutex mutex; + std::list active; + std::list passive; +}; + +template +class AutoBorrower { +public: + AutoBorrower(ConnectionPool &pool); + ~AutoBorrower(); + + T get(); + +private: + ConnectionPool &pool; + T t; +}; + + +// +// Implementation is below +// + +template +void ConnectionPool::add(T t) +{ + MutexAutolock lock(mutex); + + passive.push_back(t); + semaphore.post(); + +} + +template +bool ConnectionPool::contains(std::list &list, T t) +{ + typename std::list::iterator i = list.begin(); + while(i != list.end()) { + if(*i == t) return true; + i++; + } + + return false; +} + +template +void ConnectionPool::remove(T t) +{ + MutexAutolock lock(mutex); + + if(contains(passive, t)) { + semaphore.post(); + passive.remove(t); + } + +} + +template +bool ConnectionPool::testFree(T t) +{ + bool testfree = false; + + MutexAutolock lock(mutex); + testfree = contains(passive, t); + + return testfree; +} + +template +int ConnectionPool::numFree() +{ + int num; + + MutexAutolock lock(mutex); + num = passive.size(); + + return num; +} + +template +T ConnectionPool::borrow() +{ + T t = NULL; + + semaphore.wait(); + + { + MutexAutolock lock(mutex); + + t = passive.front(); + passive.remove(t); + active.push_back(t); + } + + return t; +} + +template +void ConnectionPool::giveBack(T t) +{ + MutexAutolock lock(mutex); + + if(contains(active, t)) { + active.remove(t); + passive.push_back(t); + semaphore.post(); + } +} + +template +std::list ConnectionPool::clear(bool force) +{ + typename std::list lst; + + if(force == false) { + size_t num = 0; + { + MutexAutolock lock(mutex); + num = active.size() + passive.size(); + } + + while(num) { + borrow(); + num--; + } + } + + { + MutexAutolock lock(mutex); + + typename std::list::iterator i; + + i = active.begin(); + while(i != active.end()) { + lst.push_back(*i); + // i = active.erase(i); + semaphore.post(); + i++; + } + active.clear(); + + i = passive.begin(); + while(i != passive.end()) { + lst.push_back(*i); + // i = passive.erase(i); + i++; + } + passive.clear(); + } + + return lst; +} + +template +AutoBorrower::AutoBorrower(ConnectionPool &p) + : pool(p) +{ + t = pool.borrow(); +} + +template +AutoBorrower::~AutoBorrower() +{ + pool.giveBack(t); +} + +template +T AutoBorrower::get() +{ + return t; +} + +#endif/*__PRACRO_CONNECTIONPOOL_H__*/ diff --git a/server/src/debug.cc b/server/src/debug.cc index 54a2191..8bb45db 100644 --- a/server/src/debug.cc +++ b/server/src/debug.cc @@ -37,7 +37,7 @@ #define NELEM(x) (sizeof(x)/sizeof((x)[0])) struct __pracro_debug_channel { - char name[16]; + char name[64]; unsigned flags; }; diff --git a/server/src/entitylist.cc b/server/src/entitylist.cc new file mode 100644 index 0000000..3ec15a5 --- /dev/null +++ b/server/src/entitylist.cc @@ -0,0 +1,331 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * entitylist.cc + * + * Thu Jan 14 14:17:34 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "entitylist.h" + +#include +#include +#include +#include + +#include + +#include "debug.h" + +static inline bool isdir(std::string name) +{ + struct stat s; + stat(name.c_str(), &s); + return S_ISDIR(s.st_mode); +} + +static inline bool isfile(std::string name) +{ + struct stat s; + stat(name.c_str(), &s); + return S_ISREG(s.st_mode); +} + +static std::vector listdir(std::string path) +{ + std::vector files; + + DIR* dir = opendir(path.c_str()); + if(!dir) { + PRACRO_ERR(entitylist, "Could not open directory: %s\n", path.c_str()); + return files; + } + + struct dirent *d; + while((d = readdir(dir)) != 0) { + std::string name = d->d_name; + + if(name == "." || name == "..") continue; + + if(isdir(path + "/" + name)) { + std::vector sub = listdir(path + "/" + name); + files.insert(files.end(), sub.begin(), sub.end()); + continue; + } + + if(isfile(path + "/" + name)) { + if(name.length() >= 4 && name.substr(name.length() - 4) == ".xml") + files.push_back(path + "/" + name); + } + } + closedir(dir); + + return files; +} + +EntityList::EntityList(std::string entitypath, std::string entityname) +{ + MutexAutolock lock(mutex); + + this->entityname = entityname; + this->entitypath = entitypath; +} + +EntityList::~EntityList() +{ +} + +void EntityList::rescan() +{ + clear(); + inotify.clear(); + + inotify.addDirectory(entitypath, WATCH_DEEP_FOLLOW, + IN_CLOSE_WRITE | + IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF | + IN_DELETE | IN_DELETE_SELF | + IN_CREATE); + + std::vector entitys = listdir(entitypath); + std::vector::iterator i = entitys.begin(); + while(i != entitys.end()) { + addFile(*i); + i++; + } + + { + iterator i = begin(); + while(i != end()) { + EntityListItem::iterator j = i->second.begin(); + while(j != i->second.end()) { + PRACRO_DEBUG(entitylist, "%s - v%s file: %s\n", + i->first.c_str(), + ((std::string)j->first).c_str(), + j->second.c_str()); + j++; + } + i++; + } + } +} + +bool EntityList::removeFile(std::string file) +{ + // Check if the file is already in the tree. + iterator i = begin(); + while(i != end()) { + EntityListItem::iterator j = i->second.begin(); + while(j != i->second.end()) { + if(file == j->second) { + PRACRO_DEBUG(entitylist, "Removing file: %s\n", file.c_str()); + i->second.erase(j); + if(i->second.size() == 0) erase(i); + return true; + } + j++; + } + i++; + } + + return false; +} + +void EntityList::updateFile(std::string file) +{ + removeFile(file); + addFile(file); +} + +void EntityList::updateList() +{ + while(inotify.hasEvents()) { + INotify::Event event = inotify.getNextEvent(); + + PRACRO_DEBUG(entitylist, "Handling event %s on %s, with param %s\n", + event.maskstr().c_str(), + event.name().c_str(), + event.file().c_str()); + + if(event.isDir()) { + if(event.isCreateEvent()) { + // A new directory was ceated. Scan it for files. + std::vector entitys = listdir(event.name()+"/"+event.file()); + std::vector::iterator i = entitys.begin(); + while(i != entitys.end()) { + updateFile(*i); + i++; + } + } + + if(event.isMoveSelfEvent()) rescan(); + if(event.isDeleteSelfEvent()) rescan(); + if(event.isDeleteEvent()) rescan(); + if(event.isMovedFromEvent()) rescan(); + if(event.isMovedToEvent()) rescan(); + + } else { + if(event.isCloseWriteEvent()) updateFile(event.name()+"/"+event.file()); + if(event.isMovedFromEvent()) removeFile(event.name()+"/"+event.file()); + if(event.isMovedToEvent()) updateFile(event.name()+"/"+event.file()); + if(event.isDeleteEvent()) removeFile(event.name()+"/"+event.file()); + } + } +} + +std::string EntityList::getLatestVersion(std::string entity) throw(Exception) +{ + MutexAutolock lock(mutex); + + updateList(); + + if(find(entity) == end()) { + throw Exception("Entity ("+entityname+") ["+entity+"] does not exist"); + } + + EntityListItem mli = (*this)[entity]; + if(mli.size() == 0) { + throw Exception("Entity ("+entityname+") ["+entity+"] does not exist."); + } + + PRACRO_DEBUG(entitylist, "Search for %s - found %s v%s\n", + entity.c_str(), + mli.begin()->second.c_str(), + ((std::string)mli.begin()->first).c_str()); + + return mli.begin()->second; +} + +void EntityList::insertEntity(std::string entity, std::string version, std::string file) +{ + std::pair p(VersionStr(version), file); + (*this)[entity].insert(p); +} + +#ifdef TEST_ENTITYLIST +//deps: inotify.cc debug.cc mutex.cc exception.cc versionstr.cc log.cc +//cflags: -I.. $(PTHREAD_CFLAGS) +//libs: $(PTHREAD_LIBS) +#include "test.h" + +#include + +#define _DIR "/tmp/entitylist_test_dir" + +class TestList : public EntityList { +public: + TestList(std::string path) : EntityList(path, "test") { rescan(); } + +private: + void addFile(std::string file) + { + char version[32]; + FILE *fp = fopen(file.c_str(), "r"); + if(!fp) return; + memset(version, 0, sizeof(version)); + fread(version, sizeof(version), 1, fp); + fclose(fp); + + fprintf(stderr, "Inserting: %s\n", version); + insertEntity("test", version, file); + } +}; + +bool createfile(TestList &lst, std::string filename, + std::string name, std::string version) +{ + FILE *fp = fopen(filename.c_str(), "w"); + if(!fp) return false; + fprintf(fp, "%s", version.c_str()); + fclose(fp); + return true; +} + +TEST_BEGIN; + +pracro_debug_parse("-all,+entitylist"); + +if(mkdir(_DIR, 0777) == -1) TEST_FATAL("Could not create test dir."); + +TestList lst(_DIR); + +TEST_EXCEPTION(lst.getLatestVersion("test"), Exception, + "Test lookup of macro in empty tree."); + +if(!createfile(lst, _DIR"/file1.xml", "test", "1.0")) + TEST_FATAL("Unable to write to the file"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/file1.xml", "Test"); + +if(!createfile(lst, _DIR"/file2.xml", "test", "2.0")) + TEST_FATAL("Unable to write to the file"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/file2.xml", "Test"); + +unlink(_DIR"/file2.xml"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/file1.xml", "Test"); + +if(mkdir(_DIR"/more", 0777) == -1) TEST_FATAL("Could not create test dir."); + +if(!createfile(lst, _DIR"/more/file1.xml", "test", "3.0")) + TEST_FATAL("Unable to write to the file"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/more/file1.xml", "Test"); + +if(!createfile(lst, _DIR"/more/file2.xml", "test", "4.0")) + TEST_FATAL("Unable to write to the file"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/more/file2.xml", "Test"); + +unlink(_DIR"/more/file2.xml"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/more/file1.xml", "Test"); + +rename(_DIR"/more/file1.xml", _DIR"/more/file2.xml"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/more/file2.xml", "Test"); + +rename(_DIR"/more", _DIR"/more2"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/more2/file2.xml", "Test"); + +rename(_DIR"/more2/file2.xml", _DIR"/file3.xml"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/file3.xml", "Test"); + +unlink(_DIR"/file3.xml"); + +rmdir(_DIR"/more2"); + +TEST_EQUAL_STR(lst.getLatestVersion("test"), _DIR"/file1.xml", "Test"); + +unlink(_DIR"/file1.xml"); + +TEST_EXCEPTION(lst.getLatestVersion("test"), Exception, "Test lookup of missing macro."); + +rmdir(_DIR); + +TEST_EXCEPTION(lst.getLatestVersion("test"), Exception, "Test lookup in missing folder."); + +TEST_END; + +#endif/*TEST_ENTITYLIST*/ diff --git a/server/src/entitylist.h b/server/src/entitylist.h new file mode 100644 index 0000000..3bcdfd0 --- /dev/null +++ b/server/src/entitylist.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * entitylist.h + * + * Thu Jan 14 14:17:34 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_ENTITYLIST_H__ +#define __PRACRO_ENTITYLIST_H__ + +#include +#include + +#include "versionstr.h" +#include "mutex.h" + +#include "inotify.h" + +#include "exception.h" + +/** + * The Items contained in the EntityList. + */ +typedef std::multimap EntityListItem; + +/** + * The EntityList class is intended for entity file caching, so that all entitys + * do not need to be parsed on each entity query. + * It builds a list of entitys and versions based on the informations read from + * the EntityHeaderParser. + * This means that just because a entity gets into the list doesn't means that it + * will validate as a correct entity (not even nessecarily correct XML). + */ +class EntityList : public std::map { +public: + /** + * Constructor. + * @param entitypath A std::string containing the path in which we should look + * for xml files. + */ + EntityList(std::string entitypath, std::string entityname); + virtual ~EntityList(); + + /** + * Convenience method, to gain the filename of the latest version of a given entity. + * This method throws an Exception if the entity does not exist in the tree. + * @param entity A std::string containing the name of the wanted entity. + * @return A std::string containing the file containing the entity with full path + * included. + */ + std::string getLatestVersion(std::string entity) throw(Exception); + +protected: + void rescan(); + void insertEntity(std::string entity, std::string version, std::string file); + +private: + virtual void addFile(std::string file) = 0; + + bool removeFile(std::string file); + void updateFile(std::string file); + void updateList(); + + Mutex mutex; + INotify inotify; + + std::string entityname; + std::string entitypath; +}; + +#endif/*__PRACRO_ENTITYLIST_H__*/ diff --git a/server/src/environment.cc b/server/src/environment.cc new file mode 100644 index 0000000..9904afc --- /dev/null +++ b/server/src/environment.cc @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * environment.cc + * + * Tue Jan 5 11:41:23 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "environment.h" + +#include "configuration.h" +#include "database.h" + +Environment::Environment() + : macrolist(Conf::xml_basedir + "/macros"), + templatelist(Conf::xml_basedir + "/templates") +{ + for(int i = 0; i < Conf::database_poolsize; i++) { + dbpool.add(new Database(Conf::database_backend, Conf::database_addr, + "", Conf::database_user, Conf::database_passwd, "")); + } + + for(int i = 0; i < Conf::artefact_poolsize; i++) { + atfpool.add(new Artefact); + } +} + +Environment::~Environment() +{ + // Remove, but wait until resources are released + std::list dblst = dbpool.clear(false); + std::list::iterator i = dblst.begin(); + while(i != dblst.end()) { + delete *i; + i++; + } + + // Remove, but wait until resources are released + std::list atflst = atfpool.clear(false); + std::list::iterator j = atflst.begin(); + while(j != atflst.end()) { + delete *j; + j++; + } +} + +#ifdef TEST_ENVIRONMENT +//deps: configuration.cc database.cc artefact.cc pracrodao.cc session.cc mutex.cc semaphore.cc debug.cc pracrodaotest.cc pracrodaopgsql.cc journalwriter.cc journal_commit.cc entitylist.cc inotify.cc exception.cc versionstr.cc tcpsocket.cc macrolist.cc templatelist.cc saxparser.cc log.cc macroheaderparser.cc templateheaderparser.cc +//cflags: -DWITHOUT_ARTEFACT -I.. $(PQXX_CXXFLAGS) $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) +//libs: $(PQXX_LIBS) -lpthread $(EXPAT_LIBS) $(PTHREAD_LIBS) +#include "test.h" + +TEST_BEGIN; + +Conf::database_backend = "testdb"; +Conf::database_poolsize = 1; + +Conf::artefact_poolsize = 1; + +Conf::xml_basedir = "/tmp"; + +TEST_NOEXCEPTION(Environment env, "Check if the Enviroment can be created."); + +TEST_END; + +#endif/*TEST_ENVIRONMENT*/ diff --git a/server/src/environment.h b/server/src/environment.h new file mode 100644 index 0000000..a7b9677 --- /dev/null +++ b/server/src/environment.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * environment.h + * + * Tue Jan 5 11:41:23 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_ENVIRONMENT_H__ +#define __PRACRO_ENVIRONMENT_H__ + +#include "database.h" +#include "artefact.h" +#include "connectionpool.h" +#include "session.h" +#include "templatelist.h" +#include "macrolist.h" + +class Environment { +public: + Environment(); + ~Environment(); + + ConnectionPool dbpool; + ConnectionPool atfpool; + Sessions sessions; + MacroList macrolist; + TemplateList templatelist; +}; + +#endif/*__PRACRO_ENVIRONMENT_H__*/ diff --git a/server/src/inotify.cc b/server/src/inotify.cc new file mode 100644 index 0000000..1515387 --- /dev/null +++ b/server/src/inotify.cc @@ -0,0 +1,549 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * inotify.cc + * + * Wed Jan 6 09:58:47 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "inotify.h" + +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST(x, m) ((x & m) == m) + +#ifdef WITH_DEBUG +#define STEST(x, m) (TEST(x, m)?#m" ":"") + +static std::string mask2asc(uint32_t mask) +{ + std::string str; + + str += STEST(mask, IN_ACCESS); + str += STEST(mask, IN_ATTRIB); + str += STEST(mask, IN_CLOSE_WRITE); + str += STEST(mask, IN_CLOSE_NOWRITE); + str += STEST(mask, IN_CREATE); + str += STEST(mask, IN_DELETE); + str += STEST(mask, IN_DELETE_SELF); + str += STEST(mask, IN_MODIFY); + str += STEST(mask, IN_MOVE_SELF); + str += STEST(mask, IN_MOVED_FROM); + str += STEST(mask, IN_MOVED_TO); + str += STEST(mask, IN_OPEN); + + str += STEST(mask, IN_ALL_EVENTS); + str += STEST(mask, IN_CLOSE); + str += STEST(mask, IN_MOVE); + + str += STEST(mask, IN_DONT_FOLLOW); + str += STEST(mask, IN_MASK_ADD); + str += STEST(mask, IN_ONESHOT); + str += STEST(mask, IN_ONLYDIR); + + str += STEST(mask, IN_IGNORED); + str += STEST(mask, IN_ISDIR); + str += STEST(mask, IN_Q_OVERFLOW); + str += STEST(mask, IN_UNMOUNT); + + return str; +} +#endif + +static inline bool isdir(const char *name) +{ + struct stat s; + stat(name, &s); + return S_ISDIR(s.st_mode); +} + +INotify::Event::Event(struct inotify_event *event, std::string name) +{ + this->_name = name; + if(event) { + this->_mask = event->mask; + this->_file = event->name; + } else { + this->_mask = 0; + this->_file = ""; + } + + PRACRO_DEBUG(inotify, "Event [%s] %s (%s)\n", + mask2asc(_mask).c_str(), + _name.c_str(), + _file.c_str()); +} + +bool INotify::Event::isAttributeChangeEvent() +{ + return TEST(_mask, IN_ATTRIB); +} + +bool INotify::Event::isCloseEvent() +{ + return isCloseWriteEvent() || isCloseNoWriteEvent(); +} + +bool INotify::Event::isCloseWriteEvent() +{ + return TEST(_mask, IN_CLOSE_WRITE); +} + +bool INotify::Event::isCloseNoWriteEvent() +{ + return TEST(_mask, IN_CLOSE_NOWRITE); +} + +bool INotify::Event::isCreateEvent() +{ + return TEST(_mask, IN_CREATE); +} + +bool INotify::Event::isOpenEvent() +{ + return TEST(_mask, IN_OPEN); +} + +bool INotify::Event::isModifyEvent() +{ + return TEST(_mask, IN_MODIFY); +} + +bool INotify::Event::isAccessEvent() +{ + return TEST(_mask, IN_ACCESS); +} + +bool INotify::Event::isDeleteEvent() +{ + return TEST(_mask, IN_DELETE); +} + +bool INotify::Event::isDeleteSelfEvent() +{ + return TEST(_mask, IN_DELETE_SELF); +} + +bool INotify::Event::isIgnoredEvent() +{ + return TEST(_mask, IN_IGNORED); +} + +bool INotify::Event::isMoveSelfEvent() +{ + return TEST(_mask, IN_MOVE_SELF); +} + +bool INotify::Event::isMovedFromEvent() +{ + return TEST(_mask, IN_MOVED_FROM); +} + +bool INotify::Event::isMovedToEvent() +{ + return TEST(_mask, IN_MOVED_TO); +} + +bool INotify::Event::isDir() +{ + return TEST(_mask, IN_ISDIR); +} + +std::string INotify::Event::name() +{ + return _name; +} + +std::string INotify::Event::file() +{ + return _file; +} + +uint32_t INotify::Event::mask() +{ + return _mask; +} + +std::string INotify::Event::maskstr() +{ + return mask2asc(_mask); +} + +INotify::INotify() +{ + ifd = inotify_init1(O_NONBLOCK); + if(ifd == -1) { + perror("Inotify init failed.\n"); + return; + } +} + +INotify::~INotify() +{ + if(ifd != -1) close(ifd); +} + +void INotify::addFile(std::string name, uint32_t mask) +{ + // Extra mask bitflags: + //IN_DONT_FOLLOW (since Linux 2.6.15) + // Don't dereference pathname if it is a symbolic link. + //IN_MASK_ADD + // Add (OR) events to watch mask for this pathname if it already + // exists (instead of replacing mask). + //IN_ONESHOT + // Monitor pathname for one event, then remove from watch list. + //IN_ONLYDIR (since Linux 2.6.15) + // Only watch pathname if it is a directory. + + int wd = inotify_add_watch(ifd, name.c_str(), mask); + if(wd == -1) { + perror("INotify: Add watch failed!"); + return; + } + + Watch w; + w.mask = mask; + w.name = name; + w.depth = WATCH_SINGLE; + dirmap[wd] = w; +} + +static inline bool isdir(std::string name) +{ + struct stat s; + stat(name.c_str(), &s); + return S_ISDIR(s.st_mode); +} + +void INotify::addDirectory(std::string name, depth_t depth, uint32_t mask) +{ + PRACRO_DEBUG(inotify, "Adding dir: %s\n", name.c_str()); + + int depth_mask = 0; + if(depth == WATCH_DEEP || depth == WATCH_DEEP_FOLLOW) { + depth_mask = IN_CREATE; // We need to watch for create in order to catch + // creation of new subdirs. + + DIR *dir = opendir(name.c_str()); + if(!dir) { + PRACRO_ERR(inotify, "Could not open directory: %s - %s\n", + name.c_str(), strerror(errno)); + return; + } + + struct dirent *dirent; + while( (dirent = readdir(dir)) != NULL ) { + + if(std::string(dirent->d_name) == "." || + std::string(dirent->d_name) == "..") + continue; + + if(isdir(name+"/"+dirent->d_name)) + addDirectory(name+"/"+dirent->d_name, depth, mask); + } + + closedir(dir); + } + + int wd = inotify_add_watch(ifd, name.c_str(), mask | IN_ONLYDIR | depth_mask); + if(wd == -1) { + PRACRO_ERR(inotify, "INotify: Add watch failed on %s\n", name.c_str()); + return; + } + + Watch w; + w.mask = mask; + w.name = name; + w.depth = depth; + dirmap[wd] = w; +} + +void INotify::remove(int wd) +{ + if(inotify_rm_watch(ifd, wd) == -1) { + perror("inotify_rm_watch failed"); + return; + } + dirmap.erase(wd); +} + +void INotify::remove(std::string name) +{ + std::map::iterator i = dirmap.begin(); + while(i != dirmap.end()) { + Watch w = i->second; + if(w.name == name) this->remove(i->first); + i++; + } +} + +void INotify::readEvents() +{ + size_t size = 64; + char *buf = (char*)malloc(size); + + ssize_t r; + while( ((r = read(ifd, buf, size)) == -1 && errno == EINVAL) || r == 0 ) { + // fprintf(stderr, "Doubling buffer size: %d\n", size); + size *= 2; + buf = (char*)realloc(buf, size); + } + + int p = 0; + while(p < r) { + struct inotify_event *event = (struct inotify_event*)(buf + p); + p += sizeof(struct inotify_event) + event->len; + + /* + switch(event.mask) { + case IN_IGNORED: + //Watch was removed explicitly (inotify_rm_watch(2)) or automatically + // (file was deleted, or file system was unmounted). + case IN_ISDIR: + //Subject of this event is a directory. + case IN_Q_OVERFLOW: + //Event queue overflowed (wd is -1 for this event). + case IN_UNMOUNT: + //File system containing watched object was unmounted. + break; + default: + break; + } + */ + + // TODO: We need to figure out what the new filename/destination is... + if(TEST(event->mask, IN_MOVE_SELF)) dirmap[event->wd].name = "????????"; + + if(dirmap[event->wd].depth == WATCH_DEEP_FOLLOW && + TEST(event->mask, IN_CREATE) && + TEST(event->mask, IN_ISDIR)) + addDirectory(dirmap[event->wd].name + "/" + event->name, + dirmap[event->wd].depth, dirmap[event->wd].mask); + + if(TEST(event->mask, IN_CREATE) && + !TEST(dirmap[event->wd].mask, IN_CREATE)) { + // Ignore this event, it was not requested by the user. + } else { + eventlist.push_back(INotify::Event(event, dirmap[event->wd].name)); + } + + if(TEST(event->mask, IN_IGNORED)) dirmap.erase(event->wd); + } + + free(buf); +} + +bool INotify::hasEvents() +{ + readEvents(); + return eventlist.size() > 0; +} + +INotify::Event INotify::getNextEvent() +{ + readEvents(); + if(eventlist.size() == 0) return Event(NULL, ""); + Event e = eventlist.front(); + eventlist.pop_front(); + return e; +} + +void INotify::clear() +{ + if(ifd != -1) close(ifd); + ifd = inotify_init1(O_NONBLOCK); + if(ifd == -1) { + perror("Inotify init failed.\n"); + } +} + +#ifdef TEST_INOTIFY +//deps: debug.cc +//cflags: -I.. +//libs: +#include "test.h" + +#include + +#define _BASEDIR "/tmp" +#define _DIR _BASEDIR"/inotify_test_dir" +#define _FILE _BASEDIR"/inotify_test" + +TEST_BEGIN; + +pracro_debug_parse("+all"); + +INotify inotify; + +// Create file +FILE *fp = fopen(_FILE, "w"); +if(!fp) TEST_FATAL("Unable to write to the file"); +fprintf(fp, "something"); +fclose(fp); + +inotify.addFile(_FILE); + +TEST_MSG("Positive tests on file watch."); + +// Append to file +fp = fopen(_FILE, "r"); +if(!fp) TEST_FATAL("Unable to read from the file"); +TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered."); +TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event."); + +char buf[32]; +fread(buf, sizeof(buf), 1, fp); +TEST_TRUE(inotify.hasEvents(), "Test if the read event was triggered."); +TEST_TRUE(inotify.getNextEvent().isAccessEvent(), "Test if the event was a access event."); + +fclose(fp); +TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered."); +TEST_TRUE(inotify.getNextEvent().isCloseNoWriteEvent(), "Test if the event was a close-nowrite event."); + + +// Append to file +fp = fopen(_FILE, "a"); +if(!fp) TEST_FATAL("Unable to write to the file"); +TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered."); +TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event."); + +fprintf(fp, "else"); fflush(fp); +TEST_TRUE(inotify.hasEvents(), "Test if the append event was triggered."); +TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modified event."); + +fclose(fp); +TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered."); +TEST_TRUE(inotify.getNextEvent().isCloseWriteEvent(), "Test if the event was a close event."); + + +// Overwrite file +fp = fopen(_FILE, "w"); +if(!fp) TEST_FATAL("Unable to write to the file"); + +// Open for write initially empties the file content, thus provoking a changed event. +TEST_TRUE(inotify.hasEvents(), "Test if the modified event was triggered."); +TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modified event."); + +TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered."); +TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event."); + +fprintf(fp, "else"); fflush(fp); +TEST_TRUE(inotify.hasEvents(), "Test if the write event was triggered."); +TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modified event."); + +fclose(fp); +TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered."); +TEST_TRUE(inotify.getNextEvent().isCloseWriteEvent(), "Test if the event was a close event."); + +// Rename file +rename(_FILE, _FILE"2"); +TEST_TRUE(inotify.hasEvents(), "Test if the rename event was triggered."); +TEST_TRUE(inotify.getNextEvent().isMoveSelfEvent(), "Test if the event was a move self event."); + +// Delete file +unlink(_FILE"2"); + +// Unlink initially counts down the link counter, which provokes an attributes changed event. +TEST_TRUE(inotify.hasEvents(), "Test if the delete event was triggered."); +TEST_TRUE(inotify.getNextEvent().isAttributeChangeEvent(), "Test if the event was an attribute change event."); + +// Since the linkcount should now be zero, the file should be deleted. +TEST_TRUE(inotify.hasEvents(), "Test if the delete event was triggered."); +TEST_TRUE(inotify.getNextEvent().isDeleteSelfEvent(), "Test if the event was a delete self event."); + +// Watch is removed upon delete. +//inotify.remove(_FILE); + +TEST_TRUE(inotify.hasEvents(), "Test if the delete event triggered an ignored event."); +TEST_TRUE(inotify.getNextEvent().isIgnoredEvent(), "Test if the event was an ignored event."); + +// Create file again +fp = fopen(_FILE, "w"); +if(!fp) TEST_FATAL("Unable to write to the file"); +fprintf(fp, "something"); +fclose(fp); + +inotify.addFile(_FILE); +inotify.remove(_FILE); + +TEST_TRUE(inotify.hasEvents(), "Test if the call to remove triggered an ignored event."); +TEST_TRUE(inotify.getNextEvent().isIgnoredEvent(), "Test if the event was an ignored event."); + +// Delete file +unlink(_FILE); +inotify.getNextEvent(); +TEST_FALSE(inotify.hasEvents(), "Test if the delete event was ignored."); + +TEST_FALSE(inotify.hasEvents(), "Test if the event queue is now empty."); + +TEST_MSG("Positive tests on directory watch."); + +if(mkdir(_DIR, 0777) == -1) TEST_FATAL("Could not create test dir."); +inotify.addDirectory(_DIR, WATCH_DEEP_FOLLOW); + +// Create file again +fp = fopen(_DIR"/file1", "w"); +if(!fp) TEST_FATAL("Unable to write to the file"); +fprintf(fp, "something"); +fclose(fp); + +TEST_TRUE(inotify.hasEvents(), "Test if the file creation triggered events."); +TEST_TRUE(inotify.getNextEvent().isCreateEvent(), "..."); +TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "..."); +TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "..."); +TEST_TRUE(inotify.getNextEvent().isCloseWriteEvent(), "..."); + +rename(_DIR"/file1", _DIR"/file2"); +TEST_TRUE(inotify.hasEvents(), "Test if the file renaming triggered events."); +TEST_TRUE(inotify.getNextEvent().isMovedFromEvent(), "..."); +TEST_TRUE(inotify.getNextEvent().isMovedToEvent(), "..."); + +unlink(_DIR"/file2"); + +if(mkdir(_DIR"/dir", 0777) == -1) TEST_FATAL("Could not create test dir."); + +if(mkdir(_DIR"/dir/anotherdir", 0777) == -1) TEST_FATAL("Could not create test dir."); + +while(inotify.hasEvents()) inotify.getNextEvent(); + +rmdir(_DIR"/dir/anotherdir"); + +rmdir(_DIR"/dir"); + +rmdir(_DIR); + +while(inotify.hasEvents()) inotify.getNextEvent(); + +TEST_END; + +#endif/*TEST_INOTIFY*/ diff --git a/server/src/inotify.h b/server/src/inotify.h new file mode 100644 index 0000000..d63e384 --- /dev/null +++ b/server/src/inotify.h @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * inotify.h + * + * Wed Jan 6 09:58:47 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_INOTIFY_H__ +#define __PRACRO_INOTIFY_H__ + +#include +#include + +#include +#include +#include + +typedef enum { + WATCH_SINGLE, // Watch only the specified directory. + WATCH_DEEP, // Watch all current subdirs as well + WATCH_DEEP_FOLLOW // Watch all current subdir as well as subdirs + // being created after the watch is initiated +} depth_t; + + +class INotify { +public: + class Event { + public: + Event(struct inotify_event *event, std::string name); + + bool isAttributeChangeEvent(); + bool isCreateEvent(); + bool isOpenEvent(); + bool isCloseEvent(); + bool isCloseWriteEvent(); + bool isCloseNoWriteEvent(); + bool isModifyEvent(); + bool isAccessEvent(); + bool isDeleteEvent(); + bool isDeleteSelfEvent(); + bool isIgnoredEvent(); + bool isMoveSelfEvent(); + bool isMovedFromEvent(); + bool isMovedToEvent(); + + bool isDir(); + + std::string name(); + std::string file(); + uint32_t mask(); + std::string maskstr(); + + private: + std::string _name; + std::string _file; + uint32_t _mask; + }; + + INotify(); + ~INotify(); + + void addFile(std::string name, uint32_t mask = IN_ALL_EVENTS); + + /** + * WARNING: If a directory is added with WATCH_DEEP_FOLLOW, newly + * created folders will not be added to the watch before the next call + * to hasEvents or getNextEvent. thie means that any files created in + * that folder prior to htese calls will not be caught. This will need + * to be done mnually by a recursive scan. + */ + void addDirectory(std::string name, + depth_t depth = WATCH_SINGLE, + uint32_t mask = IN_ALL_EVENTS); + void remove(std::string name); + void remove(int fd); + + bool hasEvents(); + Event getNextEvent(); + + void clear(); + +private: + class Watch { + public: + std::string name; + uint32_t mask; + depth_t depth; + }; + + void readEvents(); + + int ifd; + std::map dirmap; + std::list eventlist; +}; + +#endif/*__PRACRO_INOTIFY_H__*/ diff --git a/server/src/journal_commit.cc b/server/src/journal_commit.cc index a94f1d4..56e7baf 100644 --- a/server/src/journal_commit.cc +++ b/server/src/journal_commit.cc @@ -51,8 +51,8 @@ #include "template.h" #include "templateparser.h" -#include "xml_encode_decode.h" +#if 0 static inline bool iswhitespace(char c) { return c == ' ' || c == '\n' || c == '\t'; @@ -110,6 +110,7 @@ static std::string addNewlines(std::string str, size_t width) return output; } +#endif static int mwrite(int sock, const char *fmt, ...) { @@ -201,103 +202,10 @@ int journal_commit(const char *cpr, const char *user, return 0; } -JournalWriter::JournalWriter(std::string host, unsigned short int port) -{ - this->host = host; - this->port = port; -} - -void JournalWriter::addEntry(Transaction &transaction, Commit &commit, - std::string resume, Template *templ) -{ - size_t index = 0; - std::vector< Macro >::iterator i = templ->macros.begin(); - while(i != templ->macros.end()) { - Macro &m = *i; - if(commit.macro == m.attributes["name"]) break; - index++; - i++; - } - - if(index >= templ->macros.size()) { - PRACRO_ERR(journal, "Could not find macro %s in template %s\n", - commit.macro.c_str(), templ->attributes["name"].c_str()); - // return; - } else { - PRACRO_DEBUG(journal, "Found macro %s as index %u in template %s\n", - commit.macro.c_str(), index, templ->attributes["name"].c_str()); - } - - // First run - initialize username and cpr. - if(currentuser == "" && entrylist.size() == 0) currentuser = transaction.user; - if(currentcpr == "" && entrylist.size() == 0) currentcpr = transaction.cpr; - - PRACRO_DEBUG(journal, "addEntry: template(%s)\n", templ->attributes["name"].c_str()); - - // Add the template resume as the header (ie. first entry) of the journal entry. - if(entrylist.size() == 0 && templ->attributes["name"] != "") { - std::string template_resume = templ->attributes["resume"]; - - PRACRO_DEBUG(journal, "TemplateResume: %s\n", template_resume.c_str()); - - if(template_resume != "") { - ResumeEntry re; - re.resume = template_resume; - re.macro = "template_header"; - entrylist[-1] = re; // Make sure it comes first. - } - } - - // Test if the username or the cpr has changed... if so, commit and clear the list. - if(currentuser != transaction.user || currentcpr != transaction.cpr) { - this->commit(); - entrylist.clear(); - } - - // Strip trailing whitespace, and add newlines. - std::string r = stripTrailingWhitepace(addNewlines(xml_decode(resume), 60)); - std::string m = commit.macro; - - ResumeEntry re; - re.resume = r; - re.macro = m; - entrylist[index] = re; -} - -void JournalWriter::commit() -{ - std::string resume; - - // Iterate through all resumes, and create a string containing them all. - std::map< int, ResumeEntry >::iterator i = entrylist.begin(); - while(i != entrylist.end()) { - if(resume != "") resume += "\n\n"; - // resume += i->macro + "\n"; - resume += i->second.resume; - i++; - } - - if(resume == "") return; - - // Connect to praxisuploadserver and commit all resumes in one bulk. - journal_commit(currentcpr.c_str(), currentuser.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); -} - - #ifdef TEST_JOURNAL_COMMIT int main() { - std::string text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do\neiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \n\n \t"; - - std::string resume = stripTrailingWhitepace(addNewlines(text, 60)); - printf("[%s]\n", resume.c_str()); - - resume = stripTrailingWhitepace(addNewlines("", 60)); - printf("[%s]\n", resume.c_str()); - return 0; } diff --git a/server/src/journal_commit.h b/server/src/journal_commit.h index 4f7f211..1bc8086 100644 --- a/server/src/journal_commit.h +++ b/server/src/journal_commit.h @@ -27,35 +27,10 @@ #ifndef __PRACRO_JOURNAL_COMMIT_H__ #define __PRACRO_JOURNAL_COMMIT_H__ -#include -#include +#include -#include "transaction.h" -#include "template.h" - -class JournalWriter { -public: - JournalWriter(std::string host, unsigned short int port); - - void addEntry(Transaction &transaction, Commit &commit, - std::string resume, Template *templ); - - void commit(); - -private: - std::string host; - unsigned short int port; - - std::string currentuser; - std::string currentcpr; - - class ResumeEntry { - public: - std::string resume; - std::string macro; - }; - - std::map< int, ResumeEntry > entrylist; -}; +int journal_commit(const char *cpr, const char *user, + const char *addr, unsigned short int port, + const char *buf, size_t size); #endif/*__PRACRO_JOURNAL_COMMIT_H__*/ diff --git a/server/src/journalwriter.cc b/server/src/journalwriter.cc new file mode 100644 index 0000000..4b2b4be --- /dev/null +++ b/server/src/journalwriter.cc @@ -0,0 +1,350 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * journalwriter.cc + * + * Tue Jan 5 15:52:54 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "journalwriter.h" + +#include "debug.h" +#include "journal_commit.h" + +static inline bool iswhitespace(char c) +{ + return c == ' ' || c == '\n' || c == '\t' || c == '\r'; +} + +/** + * Remove all spaces, tabs and newline trailing the string. + */ +static std::string stripTrailingWhitepace(const std::string &str) +{ + if(str == "") return str; + + ssize_t end = str.size() - 1; + + while(end >= 0 && iswhitespace(str[end])) end--; + end++; + + return str.substr(0, end); +} + +static bool isInsideUTF8(const std::string &str, size_t idx) +{ + // Two byte character + if(idx > 0 && + (str[idx] & 0xC0 ) == 0x80 && + (str[idx - 1] & 0xE0) == 0xC0) + return true; + + // Three byte character + if(idx > 1 && + (str[idx] & 0xC0 ) == 0x80 && + (str[idx - 1] & 0xC0 ) == 0x80 && + (str[idx - 2] & 0xF0) == 0xE0) + return true; + + if(idx > 0 && + (str[idx] & 0xC0 ) == 0x80 && + (str[idx - 1] & 0xF0) == 0xE0) + return true; + + // Four byte character + if(idx > 2 && + (str[idx] & 0xC0 ) == 0x80 && + (str[idx - 1] & 0xC0 ) == 0x80 && + (str[idx - 2] & 0xC0 ) == 0x80 && + (str[idx - 3] & 0xF8) == 0xF0) + return true; + + if(idx > 1 && + (str[idx] & 0xC0 ) == 0x80 && + (str[idx - 1] & 0xC0 ) == 0x80 && + (str[idx - 2] & 0xF8) == 0xF0) + return true; + + if(idx > 0 && + (str[idx] & 0xC0 ) == 0x80 && + (str[idx - 1] & 0xF8) == 0xF0) + return true; + + return false; +} + +static size_t UTF8Length(const std::string &str) +{ + size_t size = 0; + for(size_t i = 0; i < str.size(); i++) { + if(!isInsideUTF8(str, i)) size++; + } + return size; +} + +/** + * Find all lines longer than 'width', and insert a newline in the + * first backward occurring space. Force split any lines without a space. + */ +static std::string addNewlines(const std::string &str, size_t width) +{ + std::string output; + size_t len = 0; + for(size_t i = 0; i < str.size(); i++) { + char c = str[i]; + + /* + fprintf(stderr, "i: %d, char: '%c', width: %d, len: %d, output: '%s'\n", + i, c, width, len, output.c_str()); + */ + + output += c; + + if(isInsideUTF8(str, i)) continue; + + len++; + if(c == '\n') len = 0; + + // Try to split line at whitespace. + if(len > width) { + size_t p = 0; + while(p < width) { + p++; + + size_t pos = output.size() - p; + + if(isInsideUTF8(output, pos)) continue; + + if(iswhitespace(output[pos])) { + output[pos] = '\n'; + len = UTF8Length(output.substr(pos+1)); + break; + } + } + } + + // Force split line at current pos. + if(len > width) { + // replace last char with a newline, and append the character again, after the newline. + output[output.size()-1] = '\n'; + output += c; + len = 1; + } + } + + return output; +} + +JournalWriter::JournalWriter(std::string host, unsigned short int port) +{ + this->host = host; + this->port = port; +} + +void JournalWriter::addEntry(Transaction &transaction, Commit &commit, + std::string resume, Template *templ) +{ + size_t index = 0; + std::vector< Macro >::iterator i = templ->macros.begin(); + while(i != templ->macros.end()) { + Macro &m = *i; + if(commit.macro == m.attributes["name"]) break; + index++; + i++; + } + + if(index >= templ->macros.size()) { + PRACRO_ERR(journal, "Could not find macro %s in template %s\n", + commit.macro.c_str(), templ->attributes["name"].c_str()); + // return; + } else { + PRACRO_DEBUG(journal, "Found macro %s as index %u in template %s\n", + commit.macro.c_str(), index, templ->attributes["name"].c_str()); + } + + // First run - initialize username and cpr. + if(currentuser == "" && entrylist.size() == 0) currentuser = transaction.user; + if(currentcpr == "" && entrylist.size() == 0) currentcpr = transaction.cpr; + + PRACRO_DEBUG(journal, "addEntry: template(%s)\n", templ->attributes["name"].c_str()); + +#if 0 // this feature is no longer nessecary... + // Add the template resume as the header (ie. first entry) + // of the journal entry. + if(entrylist.size() == 0 && templ->attributes["name"] != "") { + std::string template_resume = templ->attributes["resume"]; + + PRACRO_DEBUG(journal, "TemplateResume: %s\n", template_resume.c_str()); + + if(template_resume != "") { + ResumeEntry re; + re.resume = template_resume; + re.macro = "template_header"; + entrylist[-1] = re; // Make sure it comes first. + } + } +#endif + + // Test if the username or the cpr has changed... + // if so, commit and clear the list. + if(currentuser != transaction.user || currentcpr != transaction.cpr) { + this->commit(); + entrylist.clear(); + } + + addEntry(resume, commit.macro, index); +} + +void JournalWriter::addEntry(std::string resume, std::string macro, int index) +{ + // Strip trailing whitespace, and add newlines. + std::string r = stripTrailingWhitepace(addNewlines(resume, 60)); + std::string m = macro; + + ResumeEntry re; + re.resume = r; + re.macro = m; + entrylist[index] = re; +} + +void JournalWriter::commit() +{ + std::string resume; + + // Iterate through all resumes, and create a string containing them all. + std::map< int, ResumeEntry >::iterator i = entrylist.begin(); + while(i != entrylist.end()) { + if(resume != "") resume += "\n\n"; + // resume += i->macro + "\n"; + resume += i->second.resume; + i++; + } + + if(resume == "") return; + + // Connect to praxisuploadserver and commit all resumes in one bulk. + journal_commit(currentcpr.c_str(), currentuser.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); +} + +#ifdef TEST_JOURNALWRITER +//deps: debug.cc journal_commit.cc +//cflags: -I.. +//libs: +#include "test.h" + +#define LONG "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do\neiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \n\n \t"; + +TEST_BEGIN; + +TEST_EQUAL_STR(stripTrailingWhitepace + ("Lorem ipsum dolor sit amet. \n\n \t"), + "Lorem ipsum dolor sit amet.", "Test wspace remover."); + +TEST_EQUAL_STR(stripTrailingWhitepace(""), "", "Test wspace remover on empty string."); + +TEST_EQUAL_STR(stripTrailingWhitepace("\n\t "), "", "Test wspace remover on wspace-only string."); + +TEST_EQUAL_STR(stripTrailingWhitepace("\n"), "", "Test wspace remover on newline only."); +TEST_EQUAL_STR(stripTrailingWhitepace("\t"), "", "Test wspace remover on tab only."); +TEST_EQUAL_STR(stripTrailingWhitepace("\r"), "", "Test wspace remover on space only."); +TEST_EQUAL_STR(stripTrailingWhitepace(" "), "", "Test wspace remover on space only."); + +TEST_EQUAL_STR(stripTrailingWhitepace("ø "), "ø", "Test wspace remover on utf-8 char."); +TEST_EQUAL_STR(stripTrailingWhitepace("ø"), "ø", "Test wspace remover on utf-8 char only."); + +TEST_EQUAL_STR(stripTrailingWhitepace("a "), "a", "Test wspace remover on single char only."); +TEST_EQUAL_STR(stripTrailingWhitepace("a"), "a", "Test wspace remover on single char only."); + +TEST_EQUAL_STR(addNewlines + ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do.", 60), + "Lorem ipsum dolor sit amet, consectetur adipisicing elit,\nsed do.", + "Test single linesplit."); + +TEST_EQUAL_STR(addNewlines + ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, øsed do.", 60), + "Lorem ipsum dolor sit amet, consectetur adipisicing elit,\nøsed do.", + "Test single linesplit around utf-8 char."); + +TEST_EQUAL_STR(addNewlines + ("Lorem ipsum dolor sit amet, consectetur adipisicing elitø, sed do.", 60), + "Lorem ipsum dolor sit amet, consectetur adipisicing elitø,\nsed do.", + "Test single linesplit around utf-8 char."); + +TEST_EQUAL_STR(addNewlines + ("Lorem\nipsum dolor sit amet.", 12), + "Lorem\nipsum dolor\nsit amet.", + "Test single linesplit with contained newline."); + +TEST_EQUAL_STR(addNewlines + ("Lorem ipsum dolor sitan met.", 11), + "Lorem ipsum\ndolor sitan\nmet.", + "Test single linesplit on exact border."); + +TEST_EQUAL_STR(addNewlines + ("Loremipsum", 6), + "Loremi\npsum", + "Test single linesplit inside word."); + +TEST_EQUAL_STR(addNewlines + ("abc Loremipsum", 6), + "abc\nLoremi\npsum", + "Test single linesplit inside word."); + +TEST_TRUE(isInsideUTF8("ø", 1), "Test positive utf8 match."); +TEST_TRUE(isInsideUTF8("aæb", 2), "Test positive utf8 match."); +TEST_TRUE(isInsideUTF8("aøb", 2), "Test positive utf8 match."); +TEST_TRUE(isInsideUTF8("aÃ¥b", 2), "Test positive utf8 match."); +TEST_TRUE(isInsideUTF8("aÆb", 2), "Test positive utf8 match."); +TEST_TRUE(isInsideUTF8("aØb", 2), "Test positive utf8 match."); +TEST_TRUE(isInsideUTF8("aÃ…b", 2), "Test positive utf8 match."); +TEST_FALSE(isInsideUTF8("ø", 0), "Test negative utf8 match."); +TEST_FALSE(isInsideUTF8("aæøb", 3), "Test negative utf8 match (between two utf8 chars)."); +TEST_FALSE(isInsideUTF8("aøb", 0), "Test negative utf8 match (before utf8 char)."); + +TEST_FALSE(isInsideUTF8("𤭢", 0), "Test positive utf8 match, len 4."); +TEST_TRUE(isInsideUTF8("𤭢", 1), "Test positive utf8 match, len 4."); +TEST_TRUE(isInsideUTF8("𤭢", 2), "Test positive utf8 match, len 4."); +TEST_TRUE(isInsideUTF8("𤭢", 3), "Test positive utf8 match, len 4."); + +TEST_FALSE(isInsideUTF8("€", 0), "Test positive utf8 match, len 3."); +TEST_TRUE(isInsideUTF8("€", 1), "Test positive utf8 match, len 3."); +TEST_TRUE(isInsideUTF8("€", 2), "Test positive utf8 match, len 3."); + +TEST_FALSE(isInsideUTF8("¢", 0), "Test positive utf8 match, len 2."); +TEST_TRUE(isInsideUTF8("¢", 1), "Test positive utf8 match, len 2."); + +TEST_EQUAL_INT(UTF8Length("ø"), 1, "Test utf8 string length."); +TEST_EQUAL_INT(UTF8Length("æø"), 2, "Test utf8 string length."); +TEST_EQUAL_INT(UTF8Length(""), 0, "Test utf8 string length."); +TEST_EQUAL_INT(UTF8Length("a"), 1, "Test utf8 string length."); +TEST_EQUAL_INT(UTF8Length("aø"), 2, "Test utf8 string length."); +TEST_EQUAL_INT(UTF8Length("aøb"), 3, "Test utf8 string length."); + +TEST_EQUAL_INT(UTF8Length("a𤭢€¢ø𤭢€¢øa"), 10, "Test utf8 string length, combi."); + +TEST_EQUAL_STR(stripTrailingWhitepace(addNewlines("", 60)), "", "Test on empty input."); + +TEST_END; + +#endif/*TEST_JOURNALWRITER*/ diff --git a/server/src/journalwriter.h b/server/src/journalwriter.h new file mode 100644 index 0000000..2cd191d --- /dev/null +++ b/server/src/journalwriter.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * journalwriter.h + * + * Tue Jan 5 15:52:54 CET 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_JOURNALWRITER_H__ +#define __PRACRO_JOURNALWRITER_H__ + +#include +#include + +#include "transaction.h" +#include "template.h" + +class SessionSerialiser; + +class JournalWriter { + friend class SessionSerialiser; +public: + JournalWriter(std::string host, unsigned short int port); + + void addEntry(Transaction &transaction, Commit &commit, + std::string resume, Template *templ); + + void addEntry(std::string resume, std::string macro, int index); + + void commit(); + +private: + std::string host; + unsigned short int port; + + std::string currentuser; + std::string currentcpr; + + class ResumeEntry { + public: + std::string resume; + std::string macro; + }; + + std::map< int, ResumeEntry > entrylist; +}; + +#endif/*__PRACRO_JOURNALWRITER_H__*/ diff --git a/server/src/luaresume.cc b/server/src/luaresume.cc index b7e7348..099c3bd 100644 --- a/server/src/luaresume.cc +++ b/server/src/luaresume.cc @@ -26,6 +26,8 @@ */ #include "luaresume.h" +#include "luautil.h" + #include "debug.h" #include @@ -34,14 +36,9 @@ static int _getValue(lua_State *L) { - int n = lua_gettop(L); // number of arguments - if(n != 1) { - char errstr[512]; - sprintf(errstr, "Number of args expected 0, got %d", n); - lua_pushstring(L, errstr); - lua_error(L); - return 1; - } + Pracro::checkParameters(L, + Pracro::T_STRING, + Pracro::T_END); std::string name = lua_tostring(L, lua_gettop(L)); @@ -65,7 +62,7 @@ LUAResume::LUAResume(Commit &c) { L = luaL_newstate(); if(L == NULL) { - error("Could not create LUA state."); + PRACRO_ERR(luaresume, "Could not create LUA state.\n"); return; } @@ -85,7 +82,7 @@ LUAResume::~LUAResume() std::string LUAResume::getValue(std::string name) { if(commit.fields.find(name) == commit.fields.end()) { - error("LUAResume: No such field '" + name + "'"); + PRACRO_ERR(luaresume, "LUAResume: No such field '%s'\n", name.c_str()); return ""; } @@ -95,7 +92,7 @@ std::string LUAResume::getValue(std::string name) std::string LUAResume::run(std::string program) { if(L == NULL) { - error("LUA state not initialized!"); + PRACRO_ERR(luaresume, "LUA state not initialized!"); return false; } @@ -111,24 +108,25 @@ std::string LUAResume::run(std::string program) int top = lua_gettop(L); - if(luaL_loadbuffer(L, program.c_str(), program.size(), "lua resume generator")) { - error(lua_tostring(L, lua_gettop(L))); + if(luaL_loadbuffer(L, program.c_str(), program.size(), + "lua resume generator")) { + PRACRO_ERR(luaresume, "loadbufer: %s\n", lua_tostring(L, lua_gettop(L))); return false; } // Run the loaded code if(lua_pcall(L, 0, LUA_MULTRET, 0)) { - error(lua_tostring(L, lua_gettop(L))); + PRACRO_ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); return false; } if(top != lua_gettop(L) - 1) { - error("Program did not return a single value.\n"); + PRACRO_ERR(luaresume, "Program did not return a single value.\n"); return false; } if(lua_isstring(L, lua_gettop(L)) == false) { - error("Program did not return a string value.\n"); + PRACRO_ERR(luaresume, "Program did not return a string value.\n"); return false; } diff --git a/server/src/luautil.cc b/server/src/luautil.cc new file mode 100644 index 0000000..116d219 --- /dev/null +++ b/server/src/luautil.cc @@ -0,0 +1,312 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * luautil.cc + * + * Fri Apr 13 14:38:53 CEST 2007 + * Copyright 2007 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "luautil.h" + +#include "debug.h" + +#include + +#include + +std::vector< void * > pointers; + +// for ntohl and htonl +#include + +#define GLOBAL_PREFIX "magic_global_" + +// +// Get the (somewhat hacky) global pointer to the parser object. +// It is set in the preload method. +// +void *Pracro::getGlobal(lua_State *L, std::string name) +{ + unsigned int top; + unsigned int index; + + std::string var = std::string(GLOBAL_PREFIX) + name; + + lua_getfield(L, LUA_GLOBALSINDEX, var.c_str()); + top = lua_gettop(L); + index = lua_tointeger(L, top); + + return pointers.at(index); +} + +void Pracro::setGlobal(lua_State *L, std::string name, void *p) +{ + // Put the value of this in the globals + char value_of_this[256]; + + std::string val = std::string(GLOBAL_PREFIX) + name; + + /* + unsigned int parser = (unsigned int)p; + parser = htonl(parser); + */ + + pointers.push_back(p); + unsigned int index = pointers.size() - 1; + + sprintf(value_of_this, "%s = %u\n", val.c_str(), index); + int s = luaL_loadstring(L, value_of_this); + switch(s) { + case 0: //no errors; + break; + case LUA_ERRSYNTAX: //syntax error during pre-compilation; + case LUA_ERRMEM: //memory allocation error. + fprintf(stderr, "Error: %s\n", lua_tostring(L, lua_gettop(L))); + default: + fprintf(stderr, "Unknown return value of luaL_loadstring.\n"); + } + + // Run program (init) + s = lua_pcall(L, 0, LUA_MULTRET, 0); + // Check for errors + switch(s) { + case 0: // Success + break; + case LUA_ERRRUN:// a runtime error. + case LUA_ERRMEM:// memory allocation error. + // For such errors, Lua does not call the error handler function. + case LUA_ERRERR:// error while running the error handler function. + fprintf(stderr, "Error: %s\n", lua_tostring(L, lua_gettop(L))); + break; + default: + fprintf(stderr, "Error: Unknown return value of lua_pcall.\n"); + break; + } +} + +void Pracro::call(lua_State *L, std::string function, int numargs) +{ + // Get function + lua_getglobal(L, function.c_str()); + + // Call it + int s = lua_pcall(L, numargs, LUA_MULTRET, 0); + + // Check for errors + switch(s) { + case 0: // Success + break; + case LUA_ERRRUN:// a runtime error. + case LUA_ERRMEM:// memory allocation error. + // For such errors, Lua does not call the error handler function. + case LUA_ERRERR:// error while running the error handler function. + fprintf(stderr, "Error: %s\n", lua_tostring(L, lua_gettop(L))); + break; + default: + fprintf(stderr, "Error: Unknown return value of lua_pcall.\n"); + break; + } +} + + +/* +lua_isboolean +lua_iscfunction +lua_isfunction +lua_islightuserdata +lua_isnil +lua_isnone +lua_isnoneornil +lua_isnumber +lua_isstring +lua_istable +lua_isthread +lua_isuserdata +*/ +int Pracro::checkParameters(lua_State *L, ...) +{ + va_list ap; + va_start(ap, L); + + size_t nargs = lua_gettop(L); // number of arguments + + size_t size = 0; + int t; + while(1) { + t = va_arg(ap, int); + if(t == T_END) break; + + switch(t) { + case T_STRING: + case T_NUMBER: + case T_BOOLEAN: + break; + + default: + return luaL_error(L,"Unknown type specifier [%d] at position %d. " + "Missing TYPE_END?", t, size+1); + } + + size++; + } + + va_end(ap); + + if(nargs != size) { + return luaL_error(L, "Number of args expected %d, got %d", size, nargs); + } + + va_start(ap, L); + + size_t idx = 0; + while(1) { + t = va_arg(ap, int); + if(t == T_END) break; + + switch(t) { + case T_STRING: + if(lua_isstring(L, lua_gettop(L)-(size-idx-1)) == 0) { + va_end(ap); + return luaL_error(L, "Parameter %d should be of type string.", idx+1); + } + break; + + case T_NUMBER: + if(lua_isnumber(L, lua_gettop(L)-(size-idx-1)) == 0) { + va_end(ap); + return luaL_error(L, "Parameter %d should be of type number.", idx+1); + } + break; + + case T_BOOLEAN: + if(lua_isboolean(L, lua_gettop(L)-(size-idx-1)) == 0) { + va_end(ap); + return luaL_error(L, "Parameter %d should be of type boolean.", idx+1); + } + break; + + default: + va_end(ap); + return luaL_error(L,"Unknown type specifier [%d] at position %d. " + "Missing TYPE_END?", t, idx+1); + } + + idx++; + } + + va_end(ap); + + return 0; +} + +#ifdef TEST_LUAUTIL +//deps: +//cflags: $(LUA_CFLAGS) -I.. +//libs: $(LUA_LIBS) +#include "test.h" + +#define LUAPROG \ + "testfunc('a string', 42, true)" + +#define LUAPROG_BAD1 \ + "testfunc('a string', 42)" + +#define LUAPROG_BAD2 \ + "testfunc('a string', 42, true, 'another one')" + +#define LUAPROG_BAD3 \ + "testfunc(false, 42, 'string')" + +#define LUAPROG_MISSING_END \ + "testfunc_bad('a string', 42, true, 1)" + +static int testfunc(lua_State *L) +{ + Pracro::checkParameters(L, + Pracro::T_STRING, + Pracro::T_NUMBER, + Pracro::T_BOOLEAN, + Pracro::T_END); + return 0; +} + +static int testfunc_bad(lua_State *L) +{ + Pracro::checkParameters(L, + Pracro::T_STRING, + Pracro::T_NUMBER, + Pracro::T_BOOLEAN, + Pracro::T_NUMBER); + return 0; +} + + +TEST_BEGIN; + +int a, b, c; + +lua_State *L; +L = luaL_newstate(); +if(L == NULL) TEST_FATAL("Could not allocate lua state."); +luaL_openlibs(L); + +Pracro::setGlobal(L, "a", &a); +Pracro::setGlobal(L, "b", &b); +Pracro::setGlobal(L, "c", &c); + +TEST_EQUAL(Pracro::getGlobal(L, "b"), &b, "Test get global"); +TEST_EQUAL(Pracro::getGlobal(L, "c"), &c, "Test get global"); +TEST_EQUAL(Pracro::getGlobal(L, "a"), &a, "Test get global"); + +lua_register(L, "testfunc", testfunc); + +if(luaL_loadstring(L, LUAPROG)) { + TEST_FATAL(lua_tostring(L, lua_gettop(L))) +} +TEST_EQUAL_INT(lua_pcall(L, 0, LUA_MULTRET, 0), 0, "Good one"); + +if(luaL_loadstring(L, LUAPROG_BAD1)) { + TEST_FATAL(lua_tostring(L, lua_gettop(L))) +} +TEST_NOTEQUAL_INT(lua_pcall(L, 0, LUA_MULTRET, 0), 0, "Too short one"); + +if(luaL_loadstring(L, LUAPROG_BAD2)) { + TEST_FATAL(lua_tostring(L, lua_gettop(L))) +} +TEST_NOTEQUAL_INT(lua_pcall(L, 0, LUA_MULTRET, 0), 0, "Too long one"); + +if(luaL_loadstring(L, LUAPROG_BAD3)) { + TEST_FATAL(lua_tostring(L, lua_gettop(L))) +} +TEST_NOTEQUAL_INT(lua_pcall(L, 0, LUA_MULTRET, 0), 0, "Plain wrong"); + + +lua_register(L, "testfunc_bad", testfunc_bad); +if(luaL_loadstring(L, LUAPROG_MISSING_END)) { + TEST_FATAL(lua_tostring(L, lua_gettop(L))) +} +TEST_NOTEQUAL_INT(lua_pcall(L, 0, LUA_MULTRET, 0), 0, "Missing T_END"); + +lua_close(L); + +TEST_END; + +#endif/*TEST_LUAUTIL*/ diff --git a/server/src/luautil.h b/server/src/luautil.h new file mode 100644 index 0000000..ebbba45 --- /dev/null +++ b/server/src/luautil.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * luautil.h + * + * Fri Apr 13 14:38:53 CEST 2007 + * Copyright 2007 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_LUAUTIL_H__ +#define __PRACRO_LUAUTIL_H__ + +#include +#include + +#include + +namespace Pracro { + + /** + * Set a global pointer that can be reaced from the cFunctions at a later time, + * using the getGlobal function. + * @param L The lua_State (active program) from which to get the pointer. + * @param name The symbolic name in which to store the pointer. + * @param p The pointer to set. + */ + void setGlobal(lua_State *L, std::string name, void *p); + + /** + * Get a global pointer set by the setGlobal function. + * @param L The lua_State (active program) in which to set the pointer. + * @param name The symbolic name in which the pointer is stored. + * @return The pointer. + */ + void *getGlobal(lua_State *L, std::string name); + + /** + * Call a function in a lua program. + * @param L The lua_State (active program) in which to set the function resides. + * @param function The name of the function to be called. + */ + void call(lua_State *L, std::string function, int numargs = 0); + + typedef enum { + T_STRING, + T_NUMBER, + T_BOOLEAN, + T_END + } types_t; + + /** + * Check parameter types and number. + * @param L The lua_State (active program) in which to set the function resides. + * @param types The type list (c-vector), describing the required types + * on the stack. The last type must be a terminating T_END. + * @return 0 on success. On error a long jump is made through lua_error, thus + * the function never returns. + */ + // int checkParameters(lua_State *L, types_t types[]); + int checkParameters(lua_State *L, ...); + +}; + +#endif/*__PRACRO_LUAUTIL_H__*/ diff --git a/server/src/macrolist.cc b/server/src/macrolist.cc index b67f239..908de1e 100644 --- a/server/src/macrolist.cc +++ b/server/src/macrolist.cc @@ -27,80 +27,39 @@ */ #include "macrolist.h" -#include -#include +#include -#include "debug.h" #include "macroheaderparser.h" -static std::vector listdir(std::string path) -{ - std::vector files; - - DIR* dir = opendir(path.c_str()); - if(!dir) { - PRACRO_ERR(dump, "Could not open directory: %s\n", path.c_str()); - return files; - } - - struct dirent *d; - while((d = readdir(dir)) != 0) { - //if(d->d_type == DT_DIR) { - std::string name = d->d_name; - if(name.length() >= 4 && name.substr(name.length() - 4) == ".xml") - files.push_back(name); - //} - } - closedir(dir); +#include "debug.h" - return files; +MacroList::MacroList(std::string path) + : EntityList(path, "macro") +{ + rescan(); } -MacroList::MacroList(std::string macropath) + +void MacroList::addFile(std::string file) { - this->macropath = macropath; - std::vector macros = listdir(macropath); - std::vector::iterator i = macros.begin(); - while(i != macros.end()) { - MacroHeaderParser parser(macropath + "/" + *i); - try { - parser.parse(); - Macro *macro = parser.getMacro(); - (*this)[macro->attributes["name"]][VersionStr(macro->attributes["version"])] = *i; - } catch(Exception &e) { - PRACRO_WARN(macrolist, "Skipping %s: %s\n", i->c_str(), e.what()); - } - i++; + if(file.substr(file.size() - 4) != ".xml") { + PRACRO_DEBUG(macrolist, "Skipping file: %s\n", file.c_str()); + return; } - { - iterator i = begin(); - while(i != end()) { - MacroListItem::iterator j = i->second.begin(); - while(j != i->second.end()) { - PRACRO_DEBUG(macrolist, "%s - v%s file: %s\n", - i->first.c_str(), - ((std::string)j->first).c_str(), - j->second.c_str()); - j++; - } - i++; - } + PRACRO_DEBUG(macrolist, "Adding file: %s\n", file.c_str()); + MacroHeaderParser parser(file); + try { + parser.parse(); + Macro *macro = parser.getMacro(); + insertEntity(macro->attributes["name"], + macro->attributes["version"], + file); + } catch(Exception &e) { + PRACRO_WARN(macrolist, "Skipping %s: %s\n", file.c_str(), e.what()); } } -std::string MacroList::getLatestVersion(std::string macro) throw(Exception) -{ - if(find(macro) == end()) throw Exception("Macro ["+macro+"] does not exist"); - MacroListItem mli = (*this)[macro]; - if(mli.size() == 0) return ""; - PRACRO_DEBUG(macrolist, "Search for %s - found %s v%s\n", - macro.c_str(), - (macropath + "/" + mli.begin()->second).c_str(), - ((std::string)mli.begin()->first).c_str()); - return macropath + "/" + mli.begin()->second; -} - #ifdef TEST_MACROLIST #define MACRODIR "/home" // We assume this directory exists and does not contain any xml files! diff --git a/server/src/macrolist.h b/server/src/macrolist.h index 9b9b0d2..51a30ec 100644 --- a/server/src/macrolist.h +++ b/server/src/macrolist.h @@ -28,45 +28,14 @@ #ifndef __PRACRO_MACROLIST_H__ #define __PRACRO_MACROLIST_H__ -#include -#include -#include "versionstr.h" +#include "entitylist.h" -#include "exception.h" - -/** - * The Items contained in the MacroList. - */ -typedef std::map MacroListItem; - -/** - * The MacroList class is intended for macro file caching, so that all macros - * do not need to be parsed on each macro query. - * It builds a list of macros and versions based on the informations read from - * the MacroHeaderParser. - * This means that just because a macro gets into the list doesn't means that it - * will validate as a correct macro (not even nessecarily correct XML). - */ -class MacroList : public std::map { +class MacroList : public EntityList { public: - /** - * Constructor. - * @param macropath A std::string containing the path in which we should look - * for xml files. - */ - MacroList(std::string macropath); - - /** - * Convenience method, to gain the filename of the latest version of a given macro. - * This method throws an Exception if the macro does not exist in the tree. - * @param macro A std::string containing the name of the wanted macro. - * @return A std::string containing the file containing the macro with full path - * included. - */ - std::string getLatestVersion(std::string macro) throw(Exception); + MacroList(std::string path); -private: - std::string macropath; +protected: + void addFile(std::string file); }; #endif/*__PRACRO_MACROLIST_H__*/ diff --git a/server/src/mutex.cc b/server/src/mutex.cc index 2cc75cc..ec0d0e8 100644 --- a/server/src/mutex.cc +++ b/server/src/mutex.cc @@ -37,21 +37,89 @@ Mutex::~Mutex() pthread_mutex_destroy(&mutex); } +bool Mutex::trylock() +{ + return pthread_mutex_trylock(&mutex) == 0; +} + void Mutex::lock() { - pthread_mutex_lock( &mutex ); + pthread_mutex_lock(&mutex); } void Mutex::unlock() { - pthread_mutex_unlock( &mutex ); + pthread_mutex_unlock(&mutex); +} + + +MutexAutolock::MutexAutolock(Mutex &m) + : mutex(m) +{ + mutex.lock(); +} + +MutexAutolock::~MutexAutolock() +{ + mutex.unlock(); } #ifdef TEST_MUTEX +//deps: +//cflags: $(PTHREAD_CFLAGS) +//libs: $(PTHREAD_LIBS) +#include -int main() +#include + +volatile int cnt = 0; + +static void* thread_run(void *data) { - return 0; + Mutex *mutex = (Mutex*)data; + mutex->lock(); + cnt++; + mutex->unlock(); + return NULL; } +TEST_BEGIN; + +Mutex mutex; + +mutex.lock(); +TEST_FALSE(mutex.trylock(), "Testing if trylock works negative."); +mutex.unlock(); +TEST_TRUE(mutex.trylock(), "Testing if trylock works positive."); +mutex.unlock(); + +mutex.lock(); + +pthread_attr_t attr; +pthread_t tid; +pthread_attr_init(&attr); +pthread_create(&tid, &attr, thread_run, &mutex); + +sleep(1); +TEST_EQUAL_INT(cnt, 0, "Testing if lock prevent cnt from increasing."); +mutex.unlock(); + +sleep(1); +TEST_EQUAL_INT(cnt, 1, "Testing if unlock makes cnt increase."); + +pthread_join(tid, NULL); +pthread_attr_destroy(&attr); + +{ + TEST_TRUE(mutex.trylock(), "Testing if autolock has not yet locked the mutex."); + mutex.unlock(); + MutexAutolock mlock(mutex); + TEST_FALSE(mutex.trylock(), "Testing if autolock worked."); +} + +TEST_TRUE(mutex.trylock(), "Testing if autolock has released the lock on the mutex."); +mutex.unlock(); + +TEST_END; + #endif/*TEST_MUTEX*/ diff --git a/server/src/mutex.h b/server/src/mutex.h index 8b35042..cf052ad 100644 --- a/server/src/mutex.h +++ b/server/src/mutex.h @@ -35,6 +35,7 @@ public: Mutex(); ~Mutex(); + bool trylock(); void lock(); void unlock(); @@ -42,4 +43,13 @@ private: pthread_mutex_t mutex; }; +class MutexAutolock { +public: + MutexAutolock(Mutex &mutex); + ~MutexAutolock(); + +private: + Mutex &mutex; +}; + #endif/*__PRACRO_MUTEX_H__*/ diff --git a/server/src/pracrod.cc b/server/src/pracrod.cc index d17001c..e15b0f0 100644 --- a/server/src/pracrod.cc +++ b/server/src/pracrod.cc @@ -89,6 +89,7 @@ static const char usage_str[] = " -h, --help Print this message and exit.\n" " -D, --debug ddd Enable debug messages on 'ddd'; see documentation for details\n" " -d --database db Use db as the database backend. Can be one of pgsql or testdb (default pgsql).\n" +" -s, --ssl keyfile Enable ssl encryption with the key stored in keyfile.\n" ; ConfigurationParser *configparser = NULL; @@ -137,6 +138,39 @@ int PracroDaemon::daemon_main() return 0; } +#define CERT "\ +-----BEGIN CERTIFICATE-----\n\ +MIICFTCCAX6gAwIBAgIBAjANBgkqhkiG9w0BAQUFADBVMRswGQYDVQQKExJBcGFj\n\ +aGUgSFRUUCBTZXJ2ZXIxIjAgBgNVBAsTGUZvciB0ZXN0aW5nIHB1cnBvc2VzIG9u\n\ +bHkxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0wNzA2MjEwODE4MzZaFw0wODA2MjAw\n\ +ODE4MzZaMEwxGzAZBgNVBAoTEkFwYWNoZSBIVFRQIFNlcnZlcjEZMBcGA1UECxMQ\n\ +VGVzdCBDZXJ0aWZpY2F0ZTESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3\n\ +DQEBAQUAA4GNADCBiQKBgQDWTACKSoxd5cL06w7RtPIhFqY1l3UE/aRGmPmh8gEo\n\ +w3zNf+gWxco2yjQgBTQhGww1ybOsAUtXPIsUOSFAGvPUKJZf8ibZMiJEzl2919uz\n\ +IcV9+cUm7k3jFPQx4ALQEalbV++o/lfT5lhgsSiH1t1eln2omVrGCjI/1HeYrw7X\n\ +owIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALVFzprK6rYkWVZZZwq85w2lCYJpEl9a\n\ +66IMzIwNNRfyZMoc9D9PSwsXKYfYOg1RpMt7RhWT/bpggGlsFqctsAgJSv8Ol5Cz\n\ +DqTXhpV+8WOG6l4xDYZz3U3ajiu2jth2+aaMuWKy9Wkr8bzHGDufltToLalucne2\n\ +npM7yCJ83Ana\n\ +-----END CERTIFICATE-----" + +#define KEY "\ +-----BEGIN RSA PRIVATE KEY-----\n\ +MIICXAIBAAKBgQDWTACKSoxd5cL06w7RtPIhFqY1l3UE/aRGmPmh8gEow3zNf+gW\n\ +xco2yjQgBTQhGww1ybOsAUtXPIsUOSFAGvPUKJZf8ibZMiJEzl2919uzIcV9+cUm\n\ +7k3jFPQx4ALQEalbV++o/lfT5lhgsSiH1t1eln2omVrGCjI/1HeYrw7XowIDAQAB\n\ +AoGANUXHjJljs6P+hyw4DuHQn3El+ISiTo9PW02EIUIsD5opWFzHsYGR93Tk6GDi\n\ +yKgUrPprdAMOW61tVaWuImWQ32R2xyrJogjGYo9XE2xAej9N37jM0AGBtn/vd4Dr\n\ +LsYfpjNaM3gqIChD73iYfO+CrNbdLqTxIdG53g/u05GJ4cECQQD0vMm5+a8N82Jb\n\ +oHJgE2jb83WqaYBHe0O03ujtiq3+hPZHoVV3iJWmA/aMlgdtunkJT3PdEsVfQNkH\n\ +fvzR9JhbAkEA4CiZRk5Gcz7cEqyogDTMQYtmrE8hbgofISLuz1rpTEzd8hFAcerU\n\ +nuwFIT3go3hO7oIHMlKU1H5iT1BsFvegWQJBAOSa6A+5A+STIKAX+l52Iu+5tYKN\n\ +885RfMgZpBgm/yoMxwPX1r7GLYsajpV5mszLbz3cIo0xeH3mVBOlccEoqZsCQECP\n\ +8PWq/eebp09Jo46pplsKh5wBfqNvDuBAa4AVszRiv1pFVcZ52JudZyzX4aezsyhH\n\ +E0OPPYamkDI/+6Hx2KECQHF9xV1XatyXuFmfRAInK2BtfGY5UIvJaLxVD3Z1+i6q\n\ +/enz7/wUwvC6G4FSWNMYgAYJOfwZ3BerdkqcRNxyR/Q=\n\ +-----END RSA PRIVATE KEY-----" + int main(int argc, char *argv[]) { int c; @@ -163,10 +197,11 @@ int main(int argc, char *argv[]) {"xml-basedir", required_argument, 0, 'x'}, {"debug", required_argument, 0, 'D'}, {"database", required_argument, 0, 'd'}, + {"ssl", required_argument, 0, 's'}, {0, 0, 0, 0} }; - c = getopt_long (argc, argv, "D:hvfc:u:g:x:d:", long_options, &option_index); + c = getopt_long (argc, argv, "D:hvfc:u:g:x:d:s:", long_options, &option_index); if (c == -1) break; @@ -200,6 +235,17 @@ int main(int argc, char *argv[]) debugstr = strdup(optarg); break; + case 's': +#ifdef WITHOUT_SSL + PRACRO_ERR(server, "Pracro was not compiled with SSL support!\n"); + return 1; +#else + Conf::use_ssl = true; + Conf::ssl_key = KEY; + Conf::ssl_cert = CERT; +#endif + break; + case '?': case 'h': printf("%s", version_str); @@ -228,6 +274,11 @@ int main(int argc, char *argv[]) Conf::database_backend = database; } + if(Conf::database_backend == "testdb") { + // Test db (memory only db) does not work in plural. + Conf::database_poolsize = 1; + } + if(!user) { user = strdup(Conf::server_user.c_str()); } diff --git a/server/src/queryhandlerpentominos.cc b/server/src/queryhandlerpentominos.cc index c5450b3..b67f731 100644 --- a/server/src/queryhandlerpentominos.cc +++ b/server/src/queryhandlerpentominos.cc @@ -28,103 +28,241 @@ #include "debug.h" -#include "configuration.h" +#include -QueryHandlerPentominos::QueryHandlerPentominos(std::string cpr) - : QueryHandler() +// For time +#include + +#include +#include +#include + +// For getpid +#include +#include + +// For time +#include + +// For strerror and errno +#include + +// For socket and friends +#include +#include +#include +#include + +// For ioctl +#include + +#include "queryparser.h" + +typedef struct { + in_addr_t ip; + time_t time; + pid_t pid; + unsigned short int count; +} UID; + +#define SIOCGIFCONF 0x8912 // get iface list +/* +static in_addr_t getIP(const char *interface) { - this->cpr = cpr; + in_addr_t ret = 0; + int numreqs = 30, sd, n; + struct ifconf ifc; + struct ifreq *ifr; + struct in_addr *ia; - atfh = atf_init(); + sd = socket(AF_INET, SOCK_STREAM, 0); + if(sd == -1) { + // throw Pentominos::UIDCouldNotConnectException(strerror(errno)); + } + + ifc.ifc_buf = NULL; + ifc.ifc_len = sizeof(struct ifreq) * numreqs; + + ifc.ifc_buf = (char*)malloc(ifc.ifc_len); + if(ifc.ifc_buf == NULL) { + // throw Pentominos::UIDOutOfMemoryException(); + } + + if (ioctl(sd, SIOCGIFCONF, &ifc) < 0) { + // throw Pentominos::UIDInterfaceListException(strerror(errno)); + } - bool use_https = false; - atfc = atf_connect(atfh, Conf::pentominos_addr.c_str(), - Conf::pentominos_port, use_https); + ifr = ifc.ifc_req; + for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { + ia = (struct in_addr *)((ifr->ifr_ifru.ifru_addr.sa_data)+2); + if(!strcmp(ifr->ifr_ifrn.ifrn_name, interface)) { + ret = *(in_addr_t*)ia; + } + ifr++; + } + if(!ret) { // Still no interface... We're fucked! + // throw Pentominos::UIDInterfaceException(interface); + } + + free(ifc.ifc_buf); + return ret; } -QueryHandlerPentominos::~QueryHandlerPentominos() + +static unsigned short counter = 0; +static unsigned short getCounter() { - atf_disconnect(atfc); - atf_close(atfh); + return counter++; } -static QueryResult node2result(atf_result_node_t *node, time_t timestamp) + +static UID uid = {0,0,0,0}; +static std::string getUID(const char *interface) { - QueryResult rnode; - rnode.timestamp = timestamp; - rnode.source = "pentominos"; - - if(!node) return rnode; - - struct _atf_result_node_t *child = node->child; - while(child) { - if(child->value == NULL) { - rnode.groups[child->name] = node2result(child, timestamp); - } else { - rnode.values[child->name] = child->value; - } - child = child->next; - } + if(!uid.ip) uid.ip = getIP(interface); + + time_t t = time(NULL); + if(uid.time != t) counter = 0; // If time differes, reset the counter + uid.time = t; // We need this value every time. + + if(!uid.pid) uid.pid = getpid(); - return rnode; + uid.count = getCounter(); + + char buf[32]; + sprintf(buf, "%08x%08x%04x%04x", uid.ip, (unsigned int)uid.time, uid.pid, uid.count); + return std::string(buf); +} +*/ + +QueryHandlerPentominos::QueryHandlerPentominos(Artefact &atf, + std::string patientid, + std::string user) + : QueryHandler(), artefact(atf) +{ + this->patientid = patientid; + this->user = user; } QueryResult QueryHandlerPentominos::exec(Query &query) { - atf_transaction_t* atft = NULL; - atf_reply_t *reply = NULL; - atf_result_t *result = NULL; - atf_result_node_t *root = NULL; - atf_status_t status; - time_t timestamp; - atf_id id; + return artefact.exec(query, patientid, user); + +#if 0 + time_t timestamp = time(NULL); + std::string uid = getUID("eth0"); - QueryResult rroot; - rroot.timestamp = timestamp; - rroot.source = "pentominos"; + char buf[512]; + char header[] = + "\n" + "\n"; +#ifndef WITHOUT_PENTOMINOS + artefact.socket.write(header, strlen(header)); +#endif/*WITHOUT_PENTOMINOS*/ - atft = atf_new_transaction(atfc, cpr.c_str()); - if(!atft) goto aaarg; + PRACRO_DEBUG(queryhandler, "%s", header); - id = atf_add_query(atft, query.attributes["class"].c_str(), - FILTER_LATEST, USE_NONE, 0, 0); - if(!atft) goto aaarg; + sprintf(buf, " \n", + cpr.c_str(), + "pracro", +#ifndef WITHOUT_PENTOMINOS + artefact.socket.srcaddr().c_str(), + artefact.socket.dstaddr().c_str(), +#else + "127.0.0.1", + "127.0.0.1", +#endif/*WITHOUT_PENTOMINOS*/ + (unsigned int)timestamp, + uid.c_str()); +#ifndef WITHOUT_PENTOMINOS + artefact.socket.write(buf, strlen(buf)); +#endif/*WITHOUT_PENTOMINOS*/ - reply = atf_commit(atft); - if(!reply) goto aaarg; + PRACRO_DEBUG(queryhandler, "%s", buf); - if(atf_get_num_results(reply, id) != 1) goto aaarg; + sprintf(buf, " \n", + query.attributes["class"].c_str(), + query.attributes["class"].c_str()); + +#ifndef WITHOUT_PENTOMINOS + artefact.socket.write(buf, strlen(buf)); +#endif/*WITHOUT_PENTOMINOS*/ - result = atf_get_result(reply, id, 0); - if(!result) goto aaarg; + PRACRO_DEBUG(queryhandler, "%s", buf); - status = atf_get_result_status(result, NULL, 0); - if(status != ATF_STATUS_OK) goto aaarg; - - timestamp = atf_get_result_timestamp(result); + sprintf(buf, ""); - root = atf_get_result_node(result); - if(!root) goto aaarg; +#ifndef WITHOUT_PENTOMINOS + artefact.socket.write(buf, strlen(buf)); +#endif/*WITHOUT_PENTOMINOS*/ - { - QueryResult qresult = node2result(root, timestamp); - rroot.groups[query.attributes["class"]] = qresult; + PRACRO_DEBUG(queryhandler, "%s", buf); + + QueryResult result; + +#ifndef WITHOUT_PENTOMINOS + QueryParser parser; + + ssize_t size; + + // Read until we've got the entire result. + while((size = artefact.socket.read(buf, sizeof(buf))) > 0) { + // fwrite(buf, size, 1, stdout); fflush(stdout); + if(parser.parse(buf, size)) break; } - aaarg: - if(root) atf_free_result_node(root); - if(reply) atf_free_reply(reply); - if(atft) atf_free_transaction(atft); + result = parser.result; +#endif/*WITHOUT_PENTOMINOS*/ + + PRACRO_INFO(queryhandler, "Done handling query\n"); + + result.print(); - return rroot; + return result; +#endif + return QueryResult(); } #ifdef TEST_QUERYHANDLERPENTOMINOS int main() { +#ifdef WITHOUT_PENTOMINOS + printf("The project need to be configured for use of Pentominos in order to run this test.\n"); return 1; +#endif/*WITHOUT_PENTOMINOS*/ + + TCPSocket s; + try { + s.connect("localhost", 11108); + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + printf("A running instance of the artefact server in needed on localhost, port 11108 in order for this test to run.\n"); + return 1; + } + + QueryHandlerPentominos qh(s, "2003791613"); + + Query q1; + q1.attributes["device_id"] = "lensmeter"; + q1.attributes["device_type"] = "lensmeter"; + QueryResult res = qh.exec(q1); + res.print(); + + return 0; } #endif/*TEST_QUERYHANDLERPENTOMINOS*/ diff --git a/server/src/queryhandlerpentominos.h b/server/src/queryhandlerpentominos.h index 53b01ad..b96c097 100644 --- a/server/src/queryhandlerpentominos.h +++ b/server/src/queryhandlerpentominos.h @@ -29,29 +29,30 @@ #include "queryhandler.h" +#include "artefact.h" #include "template.h" #include "queryresult.h" +#include #include -#include - /** * This class handles the query of external data. */ class QueryHandlerPentominos : public QueryHandler { public: - QueryHandlerPentominos(std::string cpr); - ~QueryHandlerPentominos(); + QueryHandlerPentominos(Artefact &artefact, + std::string patientid, + std::string user); + ~QueryHandlerPentominos() {} // Execute all queries. QueryResult exec(Query &query); private: - atf_handle_t *atfh; - atf_connection_t *atfc; - - std::string cpr; + Artefact &artefact; + std::string patientid; + std::string user; }; #endif/*__PRACRO_QUERYHANDLERPENTOMINOS_H__*/ diff --git a/server/src/queryparser.cc b/server/src/queryparser.cc new file mode 100644 index 0000000..db562ae --- /dev/null +++ b/server/src/queryparser.cc @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * queryparser.cc + * + * Tue May 6 17:02:37 CEST 2008 + * Copyright 2008 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "queryparser.h" + +QueryParser::QueryParser() +{ + this->timestamp = 0; + + p = 0; + stack.push_back(&result); +} + +void QueryParser::startTag(std::string name, std::map< std::string, std::string> attributes) +{ + + if(name == "results") { + // What to do here!? + } + + if(name == "result") { + this->timestamp = atol(attributes["timestamp"].c_str()); + + QueryResult q; + q.source = "pentominos"; + q.timestamp = this->timestamp; + stack.back()->groups[attributes["class"]] = q; + stack.push_back(&stack.back()->groups[attributes["class"]]); + } + + if(name == "group") { + QueryResult q; + q.timestamp = this->timestamp; + stack.back()->groups[attributes["name"]] = q; + stack.push_back(&stack.back()->groups[attributes["name"]]); + } + + if(name == "value") { + stack.back()->values[attributes["name"]] = utf8.decode(attributes["value"]); + } + +} + +void QueryParser::endTag(std::string name) +{ + if(name == "group" || name == "result") stack.pop_back(); +} + +void QueryParser::parseError(const char *buf, size_t len, std::string error, int lineno) +{ + fprintf(stderr, "QueryParser error at line %d: %s\n", lineno, error.c_str()); + fprintf(stderr, "\tBuffer %u bytes: [", len); + if(fwrite(buf, len, 1, stderr) != len) {} + fprintf(stderr, "]\n"); + fflush(stderr); + + char *slineno; + if(asprintf(&slineno, " at line %d\n", lineno) != -1) { + throw Exception(error + slineno); + free(slineno); + } + +} + +#ifdef TEST_QUERYPARSER + +#include + +static char xml[] = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" +; + +static char badxml[] = + "\n" + "\n" + "\n" +; + +static std::string loadresultstring(QueryResult &res, std::string group = "") +{ + std::string s; + + std::map< std::string, std::string >::iterator v = res.values.begin(); + while(v != res.values.end()) { + s += group + (*v).first + " = \"" + (*v).second + "\"\n"; + v++; + } + + std::map< std::string, QueryResult >::iterator g = res.groups.begin(); + while(g != res.groups.end()) { + s += group + (*g).first + " = {}\n"; + s += loadresultstring((*g).second, group + (*g).first + "."); + g++; + } + + return s; +} + +int main() +{ + // Parse something + { + QueryParser parser; + parser.parse(xml, strlen(xml)); + printf("%s\n", loadresultstring(parser.result).c_str()); + } + + // Parse something, and fail! + try { + QueryParser parser; + parser.parse(badxml, strlen(badxml)); + printf("%s\n", loadresultstring(parser.result).c_str()); + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + goto weitergehen; + } + return 1; + weitergehen: + + return 0; +} + +#endif/*TEST_QUERYPARSER*/ diff --git a/server/src/queryparser.h b/server/src/queryparser.h new file mode 100644 index 0000000..f901d2c --- /dev/null +++ b/server/src/queryparser.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * queryparser.h + * + * Tue May 6 17:02:36 CEST 2008 + * Copyright 2008 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_QUERYPARSER_H__ +#define __PRACRO_QUERYPARSER_H__ + +#include "saxparser.h" + +#include +#include + +#include "queryresult.h" +#include "utf8.h" +#include "exception.h" + +/** + * This class parses xml entities into a QueryResult structure. + * Call the parent (SAXParser) method parse in order to actually parse something. + * If the parser fails (syntax error) it will throw an Exception. + * @see QueryResult result, in order to get the parsed data. + */ +class QueryParser : public SAXParser { +public: + /** + * Constructor. + */ + QueryParser(); + + /** + * The object will contain the result when the parsing is done. + */ + QueryResult result; + + /** + * Private parser callback. + */ + void startTag(std::string name, std::map< std::string, std::string> attributes); + + /** + * Private parser callback. + */ + void endTag(std::string name); + + /** + * Private parser callback. + */ + void parseError(const char *buf, size_t len, std::string error, int lineno); + +private: + // For read + int p; + std::string document; + time_t timestamp; + std::vector< QueryResult * > stack; + UTF8 utf8; +}; + +#endif/*__PRACRO_QUERYPARSER_H__*/ diff --git a/server/src/resumeparser.cc b/server/src/resumeparser.cc index bf3483e..4d514ac 100644 --- a/server/src/resumeparser.cc +++ b/server/src/resumeparser.cc @@ -26,9 +26,10 @@ */ #include "resumeparser.h" -#include "luaresume.h" #include +#include "luaresume.h" + static std::string resume_parser_format(Resume &r, Commit &commit) { const char* format = r.attributes["format"].c_str(); diff --git a/server/src/saxparser.cc b/server/src/saxparser.cc index 9072ab6..f26c965 100644 --- a/server/src/saxparser.cc +++ b/server/src/saxparser.cc @@ -74,7 +74,7 @@ SAXParser::SAXParser() { p = XML_ParserCreate(NULL); if(!p) { - PRACRO_ERR_LOG(sax, "Couldn't allocate memory for parser\n"); + PRACRO_ERR(sax, "Couldn't allocate memory for parser\n"); // throw Exception(...); return; } @@ -103,7 +103,8 @@ int SAXParser::parse() do { len = readData(buf, sizeof(buf) - 1); if (! XML_Parse(p, buf, len, len == 0)) { - parseError(buf, len, XML_ErrorString(XML_GetErrorCode(p)), (int)XML_GetCurrentLineNumber(p)); + parseError(buf, len, XML_ErrorString(XML_GetErrorCode(p)), + (int)XML_GetCurrentLineNumber(p)); return 1; } @@ -123,35 +124,43 @@ static bool iswhitespace(const char *buf, size_t size) bool SAXParser::parse(const char *data, size_t size) { - PRACRO_DEBUG(sax, "parse %d bytes\n", size); + std::string xml; + xml.append(data, size); + PRACRO_DEBUG(sax, "parse %d bytes [%s]\n", size, xml.c_str()); bufferbytes = size; totalbytes += bufferbytes; if(! XML_Parse(p, data, size, false) ) { if(XML_GetErrorCode(p) == XML_ERROR_JUNK_AFTER_DOC_ELEMENT) return true; - if(XML_GetErrorCode(p) == XML_ERROR_FINISHED && iswhitespace(data, size)) return true; + if(XML_GetErrorCode(p) == XML_ERROR_FINISHED && + iswhitespace(data, size)) return true; if(done && XML_GetErrorCode(p) == XML_ERROR_UNCLOSED_TOKEN) return true; - parseError(data, size, XML_ErrorString(XML_GetErrorCode(p)), (int)XML_GetCurrentLineNumber(p)); + parseError(data, size, XML_ErrorString(XML_GetErrorCode(p)), + (int)XML_GetCurrentLineNumber(p)); return false; } if(done) { if(! XML_Parse(p, data, 0, true) ) { if(XML_GetErrorCode(p) == XML_ERROR_JUNK_AFTER_DOC_ELEMENT) return true; - if(XML_GetErrorCode(p) == XML_ERROR_FINISHED && iswhitespace(data, size)) return true; + if(XML_GetErrorCode(p) == XML_ERROR_FINISHED && + iswhitespace(data, size)) return true; if(XML_GetErrorCode(p) == XML_ERROR_UNCLOSED_TOKEN) return true; - parseError(data, 0, XML_ErrorString(XML_GetErrorCode(p)), (int)XML_GetCurrentLineNumber(p)); + parseError(data, 0, XML_ErrorString(XML_GetErrorCode(p)), + (int)XML_GetCurrentLineNumber(p)); return false; } } - if(done) PRACRO_DEBUG(sax, "Got END_OF_DOCUMENT [%s] at %ld\n", outertag.c_str(), XML_GetCurrentByteIndex(p)); + if(done) PRACRO_DEBUG(sax, "Got END_OF_DOCUMENT [%s] at %ld\n", + outertag.c_str(), XML_GetCurrentByteIndex(p)); return done; } -void SAXParser::parseError(const char *buf, size_t len, std::string error, int lineno) +void SAXParser::parseError(const char *buf, size_t len, + std::string error, int lineno) { fprintf(stderr, "SAXParser error at line %d: %s\n", lineno, error.c_str()); fprintf(stderr, "\tBuffer %u bytes: [", len); @@ -167,6 +176,10 @@ unsigned int SAXParser::usedBytes() #ifdef TEST_SAXPARSER +//deps: log.cc debug.cc exception.cc +//cflags: -I.. +//libs: -lexpat +#include #define XMLFILE "/tmp/saxparsertest.xml" @@ -253,139 +266,125 @@ public: } }; -int main(int argc, char *argv[]) -{ - FILE *fp = fopen(XMLFILE, "w"); - if(!fp) { - printf("Could not write to %s\n", XMLFILE); - return 1; - } - fprintf(fp, xml); - fclose(fp); +TEST_BEGIN; - // Test callback parser - { - MyFileParser parser(XMLFILE); - parser.parse(); - } +FILE *fp = fopen(XMLFILE, "w"); +TEST_NOTEQUAL(fp, NULL, "Test if file \""XMLFILE"\" could be written."); +if(!fp) TEST_FATAL("Could not write "XMLFILE); +fprintf(fp, "%s", xml); +fclose(fp); - // Test buffer parser - for(size_t sz = 1; sz < 1000; sz++) { - bool test = false; - MyBufferParser parser; - std::string buf = xml; - size_t pos = 0; - while(pos < buf.length()) { - std::string substr = buf.substr(pos, sz); - - try { - test |= parser.parse((char*)substr.c_str(), substr.length()); - } catch(Exception &e) { - printf("Buffer parser failed on size %d: %s [%s]\n", sz, e.what(), substr.c_str()); - } - pos += sz; - } +TEST_MSG("Test callback parser."); +{ + MyFileParser parser(XMLFILE); + parser.parse(); +} - if(!test) { - printf("Buffer parser failed on size %d\n", sz); - return 1; +TEST_MSG("Test buffer parser."); +for(size_t sz = 1; sz < 1000; sz++) { + bool test = false; + MyBufferParser parser; + std::string buf = xml; + size_t pos = 0; + while(pos < buf.length()) { + std::string substr = buf.substr(pos, sz); + + try { + test |= parser.parse((char*)substr.c_str(), substr.length()); + } catch(Exception &e) { + TEST_TRUE(true, "Buffer parser failed on size %d: %s [%s]", + sz, e.what(), substr.c_str()); } + pos += sz; } + + TEST_TRUE(test, "Test buffer parser on %d bytes", sz); + } - fp = fopen(XMLFILE, "w"); - if(!fp) { - printf("Could not write to %s\n", XMLFILE); - return 1; - } - fprintf(fp, xml_notrailingwhitespace); - fprintf(fp, xml_notrailingwhitespace); - fclose(fp); - // Test buffer parser with multiple documents in the same buffer - { - fp = fopen(XMLFILE, "r"); - if(!fp) { - printf("Could not write to %s\n", XMLFILE); - return 1; - } - for(size_t sz = 1; sz < 1000; sz++) { - MyBufferParser *parser = NULL; - rewind(fp); - size_t numdocs = 0; - char *buf = new char[sz + 1]; - memset(buf, 0, sz + 1); - size_t size; - while( (size = fread(buf, 1, sz, fp)) > 0) { - while(size) { - if(parser == NULL) { - parser = new MyBufferParser(); - } - if(parser->parse(buf, size)) { - - // Got one - numdocs++; - - size = size - parser->usedBytes(); - strcpy(buf, buf + parser->usedBytes()); - delete parser; parser = NULL; - } else { - size = 0; - memset(buf, 0, sz + 1); - } +fp = fopen(XMLFILE, "w"); +TEST_NOTEQUAL(fp, NULL, "Test if file \""XMLFILE"\" could be written."); +if(!fp) TEST_FATAL("Could not write "XMLFILE); +fprintf(fp, "%s", xml_notrailingwhitespace); +fprintf(fp, "%s", xml_notrailingwhitespace); +fclose(fp); + +TEST_MSG("Test buffer parser with multiple documents in the same buffer."); +{ + fp = fopen(XMLFILE, "r"); + TEST_NOTEQUAL(fp, NULL, "Test if file \""XMLFILE"\" could be read."); + if(!fp) TEST_FATAL("Could not read from "XMLFILE); + + for(size_t sz = 1; sz < 1000; sz++) { + MyBufferParser *parser = NULL; + rewind(fp); + size_t numdocs = 0; + char *buf = new char[sz + 1]; + memset(buf, 0, sz + 1); + size_t size; + while( (size = fread(buf, 1, sz, fp)) > 0) { + while(size) { + if(parser == NULL) { + parser = new MyBufferParser(); + } + if(parser->parse(buf, size)) { + + // Got one + numdocs++; + + size = size - parser->usedBytes(); + strcpy(buf, buf + parser->usedBytes()); + delete parser; parser = NULL; + } else { + size = 0; + memset(buf, 0, sz + 1); } } - if(numdocs != 2) { - printf("Failed to parse two documents.\n"); - return 1; - } - if(parser) delete parser; parser = NULL; - delete[] buf; } - fclose(fp); - } - - fp = fopen(XMLFILE, "w"); - if(!fp) { - printf("Could not write to %s\n", XMLFILE); - return 1; + TEST_EQUAL(numdocs, 2, "Test if 2 documents were parsed on docsize %d.", sz); + if(parser) delete parser; parser = NULL; + delete[] buf; } - fprintf(fp, xml_fail); fclose(fp); +} - // Test failure - { - MyFileParser parser(XMLFILE); - try { - parser.parse(); - } catch(Exception &e) { - goto goon; - } - printf("This test should fail...\n"); - return 1; - } - goon: +fp = fopen(XMLFILE, "w"); +TEST_NOTEQUAL(fp, NULL, "Test if file \""XMLFILE"\" could be written."); +if(!fp) TEST_FATAL("Could not write "XMLFILE); +fprintf(fp, "%s", xml_fail); +fclose(fp); - fp = fopen(XMLFILE, "w"); - if(!fp) { - printf("Could not write to %s\n", XMLFILE); - return 1; +TEST_MSG("Test failure"); +{ + MyFileParser parser(XMLFILE); + try { + parser.parse(); + } catch(Exception &e) { + goto goon; } - fprintf(fp, xml_fail2); - fclose(fp); + TEST_TRUE(false, "This test should fail...\n"); +} +goon: - // Test failure - { - MyFileParser parser(XMLFILE); - try { - parser.parse(); - } catch(Exception &e) { - goto goonagain; - } - printf("This test should fail...\n"); - return 1; - } - goonagain: +fp = fopen(XMLFILE, "w"); +TEST_NOTEQUAL(fp, NULL, "Test if file \""XMLFILE"\" could be written."); +if(!fp) TEST_FATAL("Could not write "XMLFILE); +fprintf(fp, "%s", xml_fail2); +fclose(fp); - unlink(XMLFILE); +// Test failure +{ + MyFileParser parser(XMLFILE); + try { + parser.parse(); + } catch(Exception &e) { + goto goonagain; + } + TEST_TRUE(false, "This test should fail...\n"); } +goonagain: + +unlink(XMLFILE); + +TEST_END; #endif/*TEST_SAXPARSER*/ diff --git a/server/src/semaphore.cc b/server/src/semaphore.cc new file mode 100644 index 0000000..e5f1f31 --- /dev/null +++ b/server/src/semaphore.cc @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * semaphore.cc + * + * Sat Oct 8 17:44:13 CEST 2005 + * Copyright 2005 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "semaphore.h" + +Semaphore::Semaphore() +{ + sem_init(&semaphore, 0, 0); +} + +Semaphore::~Semaphore() +{ + sem_destroy(&semaphore); +} + +void Semaphore::post() +{ + sem_post(&semaphore); +} + +void Semaphore::wait() +{ + sem_wait(&semaphore); +} diff --git a/server/src/semaphore.h b/server/src/semaphore.h new file mode 100644 index 0000000..72121f9 --- /dev/null +++ b/server/src/semaphore.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * semaphore.h + * + * Sat Oct 8 17:44:13 CEST 2005 + * Copyright 2005 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_SEMAPHORE_H__ +#define __PRACRO_SEMAPHORE_H__ + +#include + +class Semaphore { +public: + Semaphore(); + ~Semaphore(); + + void post(); + void wait(); + +private: + sem_t semaphore; +}; + +#endif/*__PRACRO_SEMAPHORE_H__*/ diff --git a/server/src/server.cc b/server/src/server.cc index 727ac28..1f85479 100644 --- a/server/src/server.cc +++ b/server/src/server.cc @@ -26,6 +26,8 @@ */ #include "server.h" +#include + #include "tcpsocket.h" #include @@ -36,388 +38,153 @@ #include #include +#include + #include "configuration.h" -#include "transaction.h" -#include "transactionparser.h" -#include "templateparser.h" -#include "macroparser.h" - -#include "queryhandler.h" -#include "queryhandlerpracro.h" -#include "queryhandlerpentominos.h" - -#include "luaquerymapper.h" -#include "database.h" -#include "widgetgenerator.h" -#include "resumeparser.h" -#include "journal_commit.h" -#include "xml_encode_decode.h" - -#include "macrolist.h" -#include "templatelist.h" -#include "versionstr.h" - -static std::string error_box(std::string message) +#include "connection.h" +#include "log.h" + +static int handle_request_callback(void *cls, + struct MHD_Connection *con, + const char *url, + const char *method, + const char *version, + const char *data, + unsigned int *data_size, + void **con_cls) { - std::string errorbox = - "\n" - "\n" - " \n" - "\n"; - return errorbox; -} + int ret = MHD_YES; -class NotFoundException : public Exception { -public: - NotFoundException(Request &r) - : Exception("Macro " + r.macro + " not found in template " + r.templ) {} -}; + Connection *connection = (Connection*)*con_cls; + PRACRO_DEBUG(httpd, "handle_request_callback con:%p condata:%p\n", + con, *con_cls); -static std::string handleCommits(Transaction *transaction, Database &db, - JournalWriter &journalwriter, MacroList ¯olist, - TemplateList &templatelist) -{ - std::string answer; + // Test for new connection + if(connection == NULL) { + std::string sessionid; + const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "SessionID"); + if(sid) sessionid = sid; - Commits::iterator i = transaction->commits.begin(); - while(i != transaction->commits.end()) { - Commit &commit = *i; - - MacroParser mp(macrolist.getLatestVersion(commit.macro)); - mp.parse(); - Macro *macro = mp.getMacro(); - - std::string resume = resume_parser(macro->resume, commit); - commit.fields["journal.resume"] = resume; - db.commitTransaction(transaction->user, transaction->cpr, *macro, commit.fields); - - if(resume != "") { + const char *scm = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "SessionCommit"); - TemplateParser tp(templatelist.getLatestVersion(commit.templ)); - tp.parse(); - Template *templ = tp.getTemplate(); + const char *sdc = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "SessionDiscard"); - journalwriter.addEntry(*transaction, commit, resume, templ); - } - - i++; + Environment *env = (Environment *)cls; + connection = new Connection(*env, sessionid, scm != NULL, sdc != NULL); + *con_cls = connection; } - return answer; -} - - -static std::string handleRequest(Transaction *transaction, - Database &db, - JournalWriter &journalwriter, - MacroList ¯olist, - TemplateList &templatelist) -{ - std::string answer; - - // Reuse connection throughout entire template. - QueryHandlerPentominos qh(transaction->cpr); + if(!connection) return MHD_NO; - Requests::iterator i = transaction->requests.begin(); - while(i != transaction->requests.end()) { - Request &request = *i; + if(connection->handle(data, *data_size)) { + std::string response = connection->getResponse(); - PRACRO_DEBUG(server, "Handling request - macro: %s, template: %s\n", - request.macro.c_str(), request.templ.c_str()); - - // Read and parse the template file. - TemplateParser tp(templatelist.getLatestVersion(request.templ)); - tp.parse(); + PRACRO_DEBUG(httpd, "Sending response: [[%s]]\n", response.c_str()); + + struct MHD_Response *rsp = + MHD_create_response_from_data(response.size(), + (void*)response.data(), + MHD_NO, // must free + MHD_YES); // must copy - Template *templ = tp.getTemplate(); + MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE, + "text/plain; charset=UTF-8"); - answer += " \n"; - - i++; + delete connection; + *con_cls = NULL; } - return answer; + *data_size = 0; + + return ret; } -static std::string handleTransaction(Transaction *transaction, - Database &db, - JournalWriter &journalwriter, - MacroList ¯olist, - TemplateList &templatelist) +void requestCompletedCallback(void *cls, + struct MHD_Connection *con, + void **con_cls, + enum MHD_RequestTerminationCode toe) { - std::string answer; - answer += "\n"; - answer += "\n"; - - try { - answer += handleCommits(transaction, db, journalwriter, macrolist, templatelist); - } catch( std::exception &e ) { - PRACRO_ERR(server, "Commit error: %s\n", e.what()); - return error_box(xml_encode(e.what())); - } + PRACRO_DEBUG(httpd, "requestCompletedCallback %p\n", con); - try { - answer += handleRequest(transaction, db, journalwriter, - macrolist, templatelist); - } catch( std::exception &e ) { - PRACRO_ERR(server, "Request error: %s\n", e.what()); - return error_box(xml_encode(e.what())); + // If connection was interrupted prematurely delete the content data here. + if(*con_cls) { + Connection *connection = (Connection*)*con_cls; + delete connection; + *con_cls = NULL; } - - answer += "\n"; - - PRACRO_DEBUG(server, "Done handling transaction\n"); - PRACRO_DEBUG(serverxml, "%s\n", answer.c_str()); - return answer; } - -static void handleConnection(TCPSocket *socket) +static void httpderr(void *arg, const char *fmt, va_list ap) { - Database db(Conf::database_backend, Conf::database_addr, "", Conf::database_user, Conf::database_passwd, ""); - - JournalWriter journalwriter(Conf::journal_commit_addr.c_str(), Conf::journal_commit_port); - - MacroList macrolist(Conf::xml_basedir + "/macros"); - TemplateList templatelist(Conf::xml_basedir + "/templates"); - - ssize_t size; - char buf[4096]; - - Transaction *transaction = NULL; - TransactionParser *parser = NULL; - - // while( (size = socket->read(buf, sizeof(buf))) != -1) { - while( (size = socket->read(buf, sizeof(buf))) > 0) { - - PRACRO_DEBUG(server, "Read %d bytes from network\n", size); - - while(size) { - - if(transaction == NULL) { - transaction = new Transaction(); - } - - if(parser == NULL) { - parser = new TransactionParser(transaction); - } - - PRACRO_DEBUG(server, "Got %d bytes in read loop\n", size); - if(parser->parse(buf, size)) { - PRACRO_DEBUG(server, "Got complete XML document %d bytes used, %d bytes in current buffer.\n", parser->usedBytes(), size); - - socket->write(handleTransaction(transaction, db, journalwriter, - macrolist, templatelist)); - size = size - parser->usedBytes(); - if(size) { - strcpy(buf, buf + parser->usedBytes()); - PRACRO_DEBUG(server, "Replaying %d bytes.\n", size); - } - - delete transaction; transaction = NULL; - delete parser; parser = NULL; - } else { - size = 0; - memset(buf, 0, sizeof(buf)); - } - } - } - - if(transaction) { - delete transaction; - transaction = NULL; - } - - if(parser) { - delete parser; - parser = NULL; - } - - journalwriter.commit(); - - PRACRO_DEBUG(server, "Out of read loop!\n"); + PRACRO_ERR_VA(server, fmt, ap); } -//#define NON_FORKING -#include extern bool pracro_is_running; void server() { + srand(time(NULL)); + + // bool forceshutdown = false; port_t port = Conf::server_port; - TCPSocket *socket = NULL; - - try { - socket = new TCPSocket("Listen socket"); - socket->listen(port); - } catch (Exception &e) { - PRACRO_ERR_LOG(server, "Error in listen:\n%s\n", e.what()); - delete socket; - socket = NULL; + + int flags = MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY; + // | MHD_USE_PEDANTIC_CHECKS +#ifndef WITHOUT_SSL + if(Conf::use_ssl) flags |= MHD_USE_SSL; +#endif + + PRACRO_DEBUG(server, "Server running on port %d.\n", port); + + Environment env; + + struct MHD_Daemon *d; + d = MHD_start_daemon(flags, port, NULL, NULL, + handle_request_callback, &env, + MHD_OPTION_NOTIFY_COMPLETED, + requestCompletedCallback, NULL, + MHD_OPTION_CONNECTION_LIMIT, Conf::connection_limit, +#ifndef WITHOUT_SSL + MHD_OPTION_HTTPS_MEM_KEY, Conf::ssl_key.c_str(), + MHD_OPTION_HTTPS_MEM_CERT, Conf::ssl_cert.c_str(), +#endif + MHD_OPTION_CONNECTION_TIMEOUT, Conf::connection_timeout, + MHD_OPTION_EXTERNAL_LOGGER, httpderr, NULL, + MHD_OPTION_END); + + if(!d) { + PRACRO_ERR(server, "Failed to initialise MHD_start_daemon!\n"); return; } - - while(pracro_is_running && socket->connected()) { - - { // Reload if new port is assigned. - int old_port = port; - port = Conf::server_port; - - if(port != old_port) { - // Start listening on the new port - delete socket; - socket = new TCPSocket("Listen socket (reloaded)"); - socket->listen(port); - } - } - - TCPSocket *child = socket->accept(); - if(child) { - -#ifndef NON_FORKING - switch(fork()) { - case -1: // error - PRACRO_ERR_LOG(server, "Could not fork: %s\n", strerror(errno)); - break; - - case 0: // child - delete socket; -#endif/*NON_FORKING*/ - handleConnection(child); - delete child; -#ifndef NON_FORKING - return; - - default: // parent - delete child; - break; - } -#endif/*NON_FORKING*/ - + // again: + while(pracro_is_running) sleep(1); + /* + if(!forceshutdown && env.sessions.size() != 0) { + char *errbuf; + if(asprintf(&errbuf, "There are %d live sessions." + " Kill again to force shutdown.\n", + env.sessions.size()) != -1) { + PRACRO_ERR_LOG(server, "%s", errbuf); + log(errbuf); + free(errbuf); } + pracro_is_running = true; + forceshutdown = true; + goto again; } + */ + env.sessions.store(); - //socket->shutdown(); - delete socket; + MHD_stop_daemon(d); PRACRO_DEBUG(server, "Server gracefully shut down.\n"); } @@ -439,10 +206,11 @@ char request[] = int main() { Conf::xml_basedir = "../xml/"; - Conf::server_port = 32100; // Make sure wo don't interrupt an already running server. + // Make sure wo don't interrupt an already running server. + Conf::server_port = 32100; Conf::database_backend = "testdb"; pid_t pid = fork(); - + switch(pid) { case -1: // error perror("fork() failed!\n"); diff --git a/server/src/session.cc b/server/src/session.cc new file mode 100644 index 0000000..803a515 --- /dev/null +++ b/server/src/session.cc @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * session.cc + * + * Tue Dec 15 13:36:49 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "session.h" + +#include + +// for stat +#include +#include +#include +#include + +#include "journalwriter.h" +#include "configuration.h" +#include "connectionpool.h" +#include "sessionserialiser.h" + +Session::Session(std::string sessionid) +{ + _id = sessionid; + _journal = NULL; +} + +Session::~Session() +{ + if(_journal) delete _journal; +} + +std::string Session::id() +{ + return _id; +} + +void Session::lock() +{ + mutex.lock(); +} + +void Session::unlock() +{ + mutex.unlock(); +} + +void Session::commit() +{ + if(_journal != NULL) { + _journal->commit(); + delete _journal; + _journal = NULL; + } +} + +void Session::discard() +{ + if(_journal) { + delete _journal; + _journal = NULL; + } +} + +JournalWriter *Session::journal() +{ + if(_journal == NULL) { + _journal = + new JournalWriter(Conf::journal_commit_addr, Conf::journal_commit_port); + } + return _journal; +} + +Sessions::Sessions() +{ +} + +static bool fexists(const std::string &f) +{ + bool ret; + +/* + struct stat sbuf; + int n = stat(f.c_str(), &sbuf); + if(n != -1) ret = true; + ret = errno != ENOENT; +*/ + + FILE *fp = fopen(f.c_str(), "r"); + ret = fp != NULL; + if(fp) fclose(fp); + + return ret; +} + +Session *Sessions::newSession() +{ + char sessionid[32]; + std::string filename; + do { + snprintf(sessionid, sizeof(sessionid)-1, "%d", rand()); + filename = getSessionFilename(Conf::session_path, sessionid); + } while(sessions.find(sessionid) != sessions.end() || fexists(filename)); + + Session *session = new Session(sessionid); + sessions[session->id()] = session; + return session; +} + +Session *Sessions::session(std::string sessionid) +{ + if(sessions.find(sessionid) != sessions.end()) + return sessions[sessionid]; + + std::string filename = getSessionFilename(Conf::session_path, sessionid); + if(fexists(filename)) { + Session *s = new Session(sessionid); + SessionSerialiser ser(Conf::session_path, s); + ser.load(); + sessions[s->id()] = s; + + fprintf(stderr, "s: %p\n",s); + + return s; + } + + return NULL; +} + +Session *Sessions::takeSession(std::string sessionid) +{ + Session *s = NULL; + if(sessions.find(sessionid) != sessions.end()) { + s = sessions[sessionid]; + } + + if(s) { + sessions.erase(sessionid); + } + + return s; +} + +void Sessions::deleteSession(std::string sessionid) +{ + Session *s = takeSession(sessionid); + if(s) delete s; +} + +size_t Sessions::size() +{ + return sessions.size(); +} + +void Sessions::store() +{ + std::map::iterator i = sessions.begin(); + while(i != sessions.end()) { + SessionSerialiser ser(Conf::session_path, i->second); + ser.save(); + delete i->second; + sessions.erase(i); + i++; + } + sessions.clear(); +} + +SessionAutolock::SessionAutolock(Session &s) + : session(s) +{ + session.lock(); +} + +SessionAutolock::~SessionAutolock() +{ + session.unlock(); +} + +#ifdef TEST_SESSION +//deps: configuration.cc journalwriter.cc journal_commit.cc mutex.cc debug.cc sessionserialiser.cc sessionparser.cc saxparser.cc +//cflags: -I.. $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) +//libs: $(PTHREAD_LIBS) $(EXPAT_LIBS) +#include + +TEST_BEGIN; +Sessions sessions; + +Conf::session_path = "/tmp"; + +srand(0); // force seed +Session *s1 = sessions.newSession(); +srand(0); // force seed +Session *s2 = sessions.newSession(); + +TEST_NOTEQUAL(s1->id(), s2->id(), "Testing if IDs are unique."); + +TEST_EQUAL(sessions.size(), 2, "Testing if size match."); + +std::string sessionid = s1->id(); +SessionSerialiser ser(Conf::session_path, s1); +ser.save(); + +sessions.deleteSession(sessionid); +TEST_EQUAL(sessions.size(), 1, "Testing if size match."); + +s1 = sessions.session(sessionid); +TEST_NOTEQUAL(s1, NULL, "Did we reload the session from disk?"); + +sessions.store(); +TEST_EQUAL(sessions.size(), 0, "Testing if size match."); + +s1 = sessions.session(sessionid); +TEST_NOTEQUAL(s1, NULL, "Did we reload the session from disk?"); + +TEST_END; + +#endif/*TEST_SESSION*/ diff --git a/server/src/session.h b/server/src/session.h new file mode 100644 index 0000000..6c614c7 --- /dev/null +++ b/server/src/session.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * session.h + * + * Tue Dec 15 13:36:49 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_SESSION_H__ +#define __PRACRO_SESSION_H__ + +#include +#include + +#include "mutex.h" + +class JournalWriter; + +class Session { +public: + Session(std::string sessionid); + ~Session(); + + std::string id(); + + void lock(); + void unlock(); + + void commit(); + void discard(); + + JournalWriter *journal(); + +private: + JournalWriter *_journal; + std::string _id; + Mutex mutex; +}; + +class Sessions { +public: + Sessions(); + + /** + * Create a new session, with a unique id. Insert it into the session list, + * and return its pointer. + */ + Session *newSession(); + + /** + * Lookup session in session list. Returns the session or NULL if no session + * exists with that sessionid. + */ + Session *session(std::string sessionid); + + /** + * Remove session from the session list and return its pointer. It is up to + * the caller to delete it. + */ + Session *takeSession(std::string sessionid); + + /** + * Remove session from the session list and delete it. + */ + void deleteSession(std::string sessionid); + + /** + * Return number of active sessions. + */ + size_t size(); + + /** + * Write all active sessions to disc. + */ + void store(); + +private: + std::map sessions; +}; + +class SessionAutolock { +public: + SessionAutolock(Session &session); + ~SessionAutolock(); + +private: + Session &session; +}; + +#endif/*__PRACRO_SESSION_H__*/ diff --git a/server/src/sessionparser.cc b/server/src/sessionparser.cc new file mode 100644 index 0000000..ba3693d --- /dev/null +++ b/server/src/sessionparser.cc @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * sessionparser.cc + * + * Thu May 20 14:30:23 CEST 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "sessionparser.h" + +#include "debug.h" + +SessionParser::SessionParser() +{ + done = false; + totalbytes = 0; + inresume = false; +} + +SessionParser::~SessionParser() +{ +} + +void SessionParser::characterData(std::string &data) +{ + if(inresume) { + entries[entries.size()-1].resume += data; + } +} + +void SessionParser::startTag(std::string name, + std::map attributes) +{ + PRACRO_DEBUG(sessionparser, "<%s>\n", name.c_str()); + + if(name == "session") { + sessionid = attributes["id"]; + } + + if(name == "journal") { + patientid = attributes["patientid"]; + userid = attributes["userid"]; + } + + if(name == "entry") { + Entry e; + e.index = atoi(attributes["index"].c_str()); + e.macro = attributes["macro"]; + entries.push_back(e); + } + + if(name == "resume") { + inresume = true; + } +} + +void SessionParser::endTag(std::string name) +{ + if(name == "resume") { + inresume = false; + } +} + +void SessionParser::parseError(const char *buf, size_t len, + std::string error, int lineno) +{ + PRACRO_ERR(sessionnparser, "SessionParser error at line %d: %s\n", + lineno, error.c_str()); + + std::string xml; + xml.append(buf, len); + + PRACRO_ERR(sessionparser, "\tBuffer %u bytes: [%s]\n", + len, xml.c_str()); + + throw std::exception(); +} + +#ifdef TEST_SESSIONPARSER +//deps: +//cflags: +//libs: +#include "test.h" + +TEST_BEGIN; + +#error "Put some testcode here (see test.h for usable macros)." + +TEST_END; + +#endif/*TEST_SESSIONPARSER*/ diff --git a/server/src/sessionparser.h b/server/src/sessionparser.h new file mode 100644 index 0000000..37899aa --- /dev/null +++ b/server/src/sessionparser.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * sessionparser.h + * + * Thu May 20 14:30:23 CEST 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_SESSIONPARSER_H__ +#define __PRACRO_SESSIONPARSER_H__ + +#include "saxparser.h" + +#include +#include + +class SessionParser : public SAXParser { +public: + SessionParser(); + ~SessionParser(); + + void characterData(std::string &data); + void startTag(std::string name, + std::map< std::string, std::string> attributes); + void endTag(std::string name); + void parseError(const char *buf, size_t len, std::string error, int lineno); + + std::string sessionid; + std::string patientid; + std::string userid; + + class Entry { + public: + int index; + std::string macro; + std::string resume; + }; + + std::vector entries; + +private: + bool inresume; +}; + +#endif/*__PRACRO_SESSIONPARSER_H__*/ diff --git a/server/src/sessionserialiser.cc b/server/src/sessionserialiser.cc new file mode 100644 index 0000000..10d449d --- /dev/null +++ b/server/src/sessionserialiser.cc @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * sessionserialiser.cc + * + * Thu May 20 11:26:18 CEST 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "sessionserialiser.h" + +#include "journalwriter.h" + +#include "sessionparser.h" + +#include +#include + +std::string getSessionFilename(const std::string &path, + const std::string &sessionid) +{ + return path + "/pracro_session." + sessionid; +} + + +static std::string itostr(int i) +{ + char sid[32]; + snprintf(sid, sizeof(sid), "%d", i); + return sid; +} + +SessionSerialiser::SessionSerialiser(std::string path, Session *session) +{ + this->session = session; + this->path = path; +} + +void SessionSerialiser::loadStr(const std::string &xml) +{ + // SessionAutolock lock(*session); + SessionParser parser; + parser.parse(xml.data(), xml.length()); + JournalWriter *j = session->journal(); + j->currentuser = parser.userid; + j->currentcpr = parser.patientid; + std::vector::iterator i = parser.entries.begin(); + while(i != parser.entries.end()) { + j->addEntry(i->resume, i->macro, i->index); + i++; + } +} + +std::string SessionSerialiser::saveStr() +{ + // SessionAutolock lock(*session); + + std::string xml; + + xml += "\n"; + xml += "id()+"\">\n"; + + JournalWriter *journal = session->journal(); + + xml += " currentcpr + + "\" userid=\"" + journal->currentuser + "\">\n"; + + std::map< int, JournalWriter::ResumeEntry >::iterator i = + journal->entrylist.begin(); + while(i != journal->entrylist.end()) { + + xml += " first) + "\"" + " macro=\"" + i->second.macro + "\">\n"; + xml += " " + i->second.resume + "\n"; + xml += " \n"; + + i++; + } + + xml += " \n"; + xml += "\n"; + + return xml; +} + +void SessionSerialiser::load() +{ + // read xml from file + std::string filename = getSessionFilename(path, session->id()); + + FILE *fp = fopen(filename.c_str(), "r"); + char xml[2048]; + memset(xml, 0, sizeof(xml)); + fread(xml, sizeof(xml), 1, fp); + fclose(fp); + + loadStr(xml); + + // delete file + unlink(filename.c_str()); + +} + +void SessionSerialiser::save() +{ + std::string filename = getSessionFilename(path, session->id()); + + std::string xml = saveStr(); + + // write xml to file + FILE *fp = fopen(filename.c_str(), "w"); + fwrite(xml.data(), xml.size(), 1, fp); + fclose(fp); +} + +#ifdef TEST_SESSIONSERIALISER +//deps: session.cc journalwriter.cc debug.cc configuration.cc mutex.cc journal_commit.cc sessionparser.cc saxparser.cc +//cflags: -I.. $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) +//libs: $(PTHREAD_LIBS) $(EXPAT_LIBS) +#include "test.h" + +#define SID "42" +#define SPATH "/tmp" + +TEST_BEGIN; + +std::string xml; + +{ + Session session(SID); + JournalWriter *j = session.journal(); + j->addEntry("some text", "macro1", 0); + j->addEntry("some more text", "macro2", 2); + j->addEntry("yet some more text", "macro3", 1); + SessionSerialiser s(SPATH, &session); + xml = s.saveStr(); + s.loadStr(xml); + std::string xml2 = s.saveStr(); + TEST_EQUAL_STR(xml, xml2, "Compare"); +} + +{ + Session session(SID); + JournalWriter *j = session.journal(); + j->addEntry("some text", "macro1", 0); + j->addEntry("some more text", "macro2", 2); + j->addEntry("yet some more text", "macro3", 1); + SessionSerialiser s(SPATH, &session); + xml = s.saveStr(); +} + +{ + Session session(SID); + SessionSerialiser s(SPATH, &session); + s.loadStr(xml); + std::string xml2 = s.saveStr(); + TEST_EQUAL_STR(xml, xml2, "Compare"); +} + +{ + Session session(SID); + JournalWriter *j = session.journal(); + j->addEntry("some text", "macro1", 0); + j->addEntry("some more text", "macro2", 2); + j->addEntry("yet some more text", "macro3", 1); + SessionSerialiser s(SPATH, &session); + s.save(); +} + +{ + Session session(SID); + SessionSerialiser s(SPATH, &session); + s.load(); + std::string xml2 = s.saveStr(); + TEST_EQUAL_STR(xml, xml2, "Compare"); +} + + +TEST_END; + +#endif/*TEST_SESSIONSERIALISER*/ diff --git a/server/src/sessionserialiser.h b/server/src/sessionserialiser.h new file mode 100644 index 0000000..4002760 --- /dev/null +++ b/server/src/sessionserialiser.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * sessionserialiser.h + * + * Thu May 20 11:26:18 CEST 2010 + * Copyright 2010 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_SESSIONSERIALISER_H__ +#define __PRACRO_SESSIONSERIALISER_H__ + +#include + +#include "session.h" + +class SessionSerialiser { +public: + SessionSerialiser(std::string path, Session *session); + + void loadStr(const std::string &xml); + std::string saveStr(); + + void load(); + void save(); + +private: + Session *session; + std::string path; +}; + +std::string getSessionFilename(const std::string &path, + const std::string &sessionid); + +#endif/*__PRACRO_SESSIONSERIALISER_H__*/ diff --git a/server/src/tcpsocket.cc b/server/src/tcpsocket.cc index 818934e..f748e0a 100644 --- a/server/src/tcpsocket.cc +++ b/server/src/tcpsocket.cc @@ -90,14 +90,12 @@ TCPSocket::TCPSocket(std::string name, int sock) isconnected = false; this->sock = sock; - PRACRO_DEBUG(socket, "TCPSocket %s: %p %d (%d)\n", name.c_str(), - this, sock, getpid()); + PRACRO_DEBUG(socket, "TCPSocket %s: %p %d (%d)\n", name.c_str(), this, sock, getpid()); } TCPSocket::~TCPSocket() { - PRACRO_DEBUG(socket, "~TCPSocket %s: %p %d (%d)\n", name.c_str(), - this, sock, getpid()); + PRACRO_DEBUG(socket, "~TCPSocket %s: %p %d (%d)\n", name.c_str(), this, sock, getpid()); disconnect(); } @@ -111,7 +109,7 @@ void TCPSocket::listen(unsigned short int port) if(isconnected) throw TCPListenException("Socket alread connected."); struct sockaddr_in socketaddr; - memset((char *) &socketaddr, 0, sizeof(socketaddr)); + memset((char *) &socketaddr, sizeof(socketaddr), 0); socketaddr.sin_family = AF_INET; socketaddr.sin_port = htons(port); socketaddr.sin_addr.s_addr = htonl(INADDR_ANY); @@ -161,32 +159,27 @@ TCPSocket *TCPSocket::accept() } } if(FD_ISSET(sock, &fset)) { - int csock = _accept(sock, (struct sockaddr*)&ssocketaddr, - (socklen_t*)&csalen); + int csock = _accept(sock, (struct sockaddr*)&ssocketaddr, (socklen_t*)&csalen); TCPSocket *child = new TCPSocket(name + "-child", csock); if (child->sock == -1) { - throw TCPAcceptException(std::string("accept failed - ") + - strerror(errno)); + throw TCPAcceptException(std::string("accept failed - ") + strerror(errno)); } child->isconnected = true; return child; } else { - PRACRO_ERR_LOG(socket, "Accept returned with no socket" - " - This should not happen!\n"); + PRACRO_ERR_LOG(socket, "Accept returned with no socket - This should not happen!\n"); return NULL; } } -static int _connect(int sockfd, const struct sockaddr *serv_addr, - socklen_t addrlen) +static int _connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) {return connect(sockfd, serv_addr, addrlen);} void TCPSocket::connect(std::string addr, unsigned short int port) throw(TCPConnectException) { - if(isconnected) - throw TCPConnectException("Socket already connected.", "", ""); + if(isconnected) throw TCPConnectException("Socket already connected.", "", ""); #ifndef BYPASS_STATICALLOCATIONS // Do DNS lookup @@ -198,8 +191,7 @@ void TCPSocket::connect(std::string addr, unsigned short int port) char portno[32]; sprintf(portno, "%d", port); throw TCPConnectException(addr, portno, - std::string("host lookup failed: ") + - hstrerror(h_errno)); + std::string("host lookup failed: ") + hstrerror(h_errno)); } addr_list = (struct in_addr **)he->h_addr_list; @@ -210,7 +202,7 @@ void TCPSocket::connect(std::string addr, unsigned short int port) #endif/*BYPASS_STATICALLOCATIONS*/ struct sockaddr_in socketaddr; - memset((char *) &socketaddr, 0, sizeof(socketaddr)); + memset((char *) &socketaddr, sizeof(socketaddr), 0); socketaddr.sin_family = AF_INET; socketaddr.sin_port = htons(port); socketaddr.sin_addr.s_addr = inet_addr(ip); @@ -227,8 +219,7 @@ void TCPSocket::connect(std::string addr, unsigned short int port) void TCPSocket::disconnect() { if(sock != -1) { - PRACRO_DEBUG(socket, "Closing TCPSocket %s: %p %d (%d)\n", - name.c_str(), this, sock, getpid()); + PRACRO_DEBUG(socket, "Closing TCPSocket %s: %p %d (%d)\n", name.c_str(), this, sock, getpid()); int ret = close(sock); if(ret == -1) { perror(name.c_str()); @@ -305,8 +296,7 @@ int TCPSocket::read(char *buf, int size, long timeout) return res; } -ssize_t _write(int fd, const void *buf, size_t count) -{ return write(fd, buf, count); } +ssize_t _write(int fd, const void *buf, size_t count) { return write(fd, buf, count); } int TCPSocket::write(char *data, int size) throw(TCPWriteException) { diff --git a/server/src/templatelist.cc b/server/src/templatelist.cc index cdf2b4b..af03586 100644 --- a/server/src/templatelist.cc +++ b/server/src/templatelist.cc @@ -27,80 +27,34 @@ */ #include "templatelist.h" -#include -#include +#include "templateheaderparser.h" #include "debug.h" -#include "templateheaderparser.h" -static std::vector listdir(std::string path) +TemplateList::TemplateList(std::string path) + : EntityList(path, "template") { - std::vector files; - - DIR* dir = opendir(path.c_str()); - if(!dir) { - PRACRO_ERR(dump, "Could not open directory: %s\n", path.c_str()); - return files; - } - - struct dirent *d; - while((d = readdir(dir)) != 0) { - //if(d->d_type == DT_DIR) { - std::string name = d->d_name; - if(name.length() >= 4 && name.substr(name.length() - 4) == ".xml") - files.push_back(name); - //} - } - closedir(dir); - - return files; + rescan(); } -TemplateList::TemplateList(std::string templatepath) +void TemplateList::addFile(std::string file) { - this->templatepath = templatepath; - std::vector templates = listdir(templatepath); - std::vector::iterator i = templates.begin(); - while(i != templates.end()) { - TemplateHeaderParser parser(templatepath + "/" + *i); - try { - parser.parse(); - Template *templ = parser.getTemplate(); - (*this)[templ->attributes["name"]][VersionStr(templ->attributes["version"])] = *i; - } catch(Exception &e) { - PRACRO_WARN(templatelist, "Skipping %s: %s\n", i->c_str(), e.what()); - } - - i++; + if(file.substr(file.size() - 4) != ".xml") { + PRACRO_DEBUG(templatelist, "Skipping file: %s\n", file.c_str()); + return; } - { - iterator i = begin(); - while(i != end()) { - TemplateListItem::iterator j = i->second.begin(); - while(j != i->second.end()) { - PRACRO_DEBUG(templatelist, "%s - v%s file: %s\n", - i->first.c_str(), - ((std::string)j->first).c_str(), - j->second.c_str()); - j++; - } - i++; - } + PRACRO_DEBUG(templatelist, "Adding file: %s\n", file.c_str()); + TemplateHeaderParser parser(file); + try { + parser.parse(); + Template *templ = parser.getTemplate(); + insertEntity(templ->attributes["name"], + templ->attributes["version"], + file); + } catch(Exception &e) { + PRACRO_WARN(templatelist, "Skipping %s: %s\n", file.c_str(), e.what()); } - -} - -std::string TemplateList::getLatestVersion(std::string templ) throw(Exception) -{ - if(find(templ) == end()) throw Exception("Template ["+templ+"] does not exist"); - TemplateListItem mli = (*this)[templ]; - if(mli.size() == 0) return ""; - PRACRO_DEBUG(templatelist, "Search for %s - found %s v%s\n", - templ.c_str(), - (templatepath + "/" + mli.begin()->second).c_str(), - ((std::string)mli.begin()->first).c_str()); - return templatepath + "/" + mli.begin()->second; } #ifdef TEST_TEMPLATELIST diff --git a/server/src/templatelist.h b/server/src/templatelist.h index bcf22f1..ce2d1b3 100644 --- a/server/src/templatelist.h +++ b/server/src/templatelist.h @@ -28,45 +28,14 @@ #ifndef __PRACRO_TEMPLATELIST_H__ #define __PRACRO_TEMPLATELIST_H__ -#include -#include -#include "versionstr.h" +#include "entitylist.h" -#include "exception.h" - -/** - * The Items contained in the TemplateList. - */ -typedef std::map TemplateListItem; - -/** - * The TemplateList class is intended for template file caching, so that all templates - * do not need to be parsed on each template query. - * It builds a list of templates and versions based on the informations read from - * the TemplateHeaderParser. - * This means that just because a template gets into the list doesn't means that it - * will validate as a correct template (not even nessecarily correct XML). - */ -class TemplateList : public std::map { +class TemplateList : public EntityList { public: - /** - * Constructor. - * @param templatepath A std::string containing the path in which we should look - * for xml files. - */ - TemplateList(std::string templatepath); - - /** - * Convenience method, to gain the filename of the latest version of a given template. - * This method throws an Exception if the template does not exist in the tree. - * @param template A std::string containing the name of the wanted template. - * @return A std::string containing the file containing the template with full path - * included. - */ - std::string getLatestVersion(std::string templ) throw(Exception); + TemplateList(std::string path); private: - std::string templatepath; + void addFile(std::string file); }; #endif/*__PRACRO_TEMPLATELIST_H__*/ diff --git a/server/src/transactionhandler.cc b/server/src/transactionhandler.cc new file mode 100644 index 0000000..c9c58b6 --- /dev/null +++ b/server/src/transactionhandler.cc @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * transactionhandler.cc + * + * Fri Dec 18 10:00:50 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "transactionhandler.h" + +#include "macroparser.h" +#include "resumeparser.h" +#include "templateparser.h" +#include "configuration.h" +#include "luaquerymapper.h" +#include "queryhandlerpentominos.h" +#include "queryhandlerpracro.h" +#include "xml_encode_decode.h" +#include "widgetgenerator.h" +#include "journalwriter.h" + +static std::string error_box(std::string message) +{ + std::string errorbox = + "\n" + "\n" + " " + message + "\n" + "\n"; + return errorbox; +} + +static std::string handleCommits(Transaction &transaction, Environment &env, + Session &session) +{ + std::string answer; + + if(transaction.commits.size() > 0) { + AutoBorrower borrower(env.dbpool); + Database *db = borrower.get(); + + Commits::iterator i = transaction.commits.begin(); + while(i != transaction.commits.end()) { + Commit &commit = *i; + + MacroParser mp(env.macrolist.getLatestVersion(commit.macro)); + mp.parse(); + Macro *macro = mp.getMacro(); + + std::string resume = resume_parser(macro->resume, commit); + commit.fields["journal.resume"] = resume; + db->commitTransaction(transaction.user, transaction.cpr, *macro, + commit.fields); + + if(resume != "") { + + TemplateParser tp(env.templatelist.getLatestVersion(commit.templ)); + tp.parse(); + Template *templ = tp.getTemplate(); + + session.journal()->addEntry(transaction, commit, resume, templ); + } + + i++; + } + } + + return answer; +} + +static std::string handleRequest(Transaction &transaction, Environment &env, + Session &session) +{ + std::string answer; + + if(transaction.requests.size() > 0) { + + AutoBorrower borrower(env.dbpool); + Database *db = borrower.get(); + + Requests::iterator i = transaction.requests.begin(); + while(i != transaction.requests.end()) { + Request &request = *i; + + PRACRO_DEBUG(server, "Handling request - macro: %s, template: %s\n", + request.macro.c_str(), request.templ.c_str()); + + // Read and parse the template file. + TemplateParser tp(env.templatelist.getLatestVersion(request.templ)); + tp.parse(); + + Template *templ = tp.getTemplate(); + + answer += " \n"; + + i++; + } + } + + return answer; +} + +std::string handleTransaction(Transaction &transaction, Environment &env, + Session &session) +{ + std::string answer; + + answer += "\n"; + answer += "\n"; + + try { + answer += handleCommits(transaction, env, session); + } catch( std::exception &e ) { + PRACRO_ERR(server, "Commit error: %s\n", e.what()); + return error_box(xml_encode(e.what())); + } + + try { + answer += handleRequest(transaction, env, session); + } catch( std::exception &e ) { + PRACRO_ERR(server, "Request error: %s\n", e.what()); + return error_box(xml_encode(e.what())); + } + + answer += "\n"; + + PRACRO_DEBUG(server, "Done handling transaction\n"); + PRACRO_DEBUG(serverxml, "%s\n", answer.c_str()); + + return answer; +} + +#ifdef TEST_TRANSACTIONHANDLER +//deps: +//cflags: -I.. +//libs: +#include "test.h" + +TEST_BEGIN; + +#error TODO: Put some testcode here (see test.h for usable macros). + +TEST_END; + +#endif/*TEST_TRANSACTIONHANDLER*/ diff --git a/server/src/transactionhandler.h b/server/src/transactionhandler.h new file mode 100644 index 0000000..ae1857c --- /dev/null +++ b/server/src/transactionhandler.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * transactionhandler.h + * + * Fri Dec 18 10:00:50 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_TRANSACTIONHANDLER_H__ +#define __PRACRO_TRANSACTIONHANDLER_H__ + +#include "transaction.h" +#include "session.h" +#include "environment.h" + +#include "exception.h" + +class NotFoundException : public Exception { +public: + NotFoundException(Request &r) + : Exception("Macro " + r.macro + " not found in template " + r.templ) {} +}; + +std::string handleTransaction(Transaction &transaction, Environment &env, Session &session); + +#endif/*__PRACRO_TRANSACTIONHANDLER_H__*/ diff --git a/server/src/transactionparser.cc b/server/src/transactionparser.cc index f1f79c6..b33cac6 100644 --- a/server/src/transactionparser.cc +++ b/server/src/transactionparser.cc @@ -36,31 +36,6 @@ #include "debug.h" #include "exception.h" -static void error(const char* fmt, ...) -{ - PRACRO_ERR_LOG(transactionparser, "Error in TransactionParser: "); - - { - va_list argp; - va_start(argp, fmt); - PRACRO_ERR_LOG_VA(macro, fmt, argp); - va_end(argp); - - fprintf(stderr, "\n"); - } - - { - char *p; - va_list argp; - va_start(argp, fmt); - if(vasprintf(&p, fmt, argp) != -1) { - throw Exception("Error in TransactionParser: " + std::string(p)); - free(p); - } - va_end(argp); - } -} - TransactionParser::TransactionParser(Transaction *transaction) { this->transaction = transaction; @@ -68,8 +43,11 @@ TransactionParser::TransactionParser(Transaction *transaction) totalbytes = 0; } -void TransactionParser::startTag(std::string name, std::map< std::string, std::string> attributes) +void TransactionParser::startTag(std::string name, + std::map attributes) { + PRACRO_DEBUG(transactionparser, "<%s>\n", name.c_str()); + if(name == "pracro") { transaction->user = attributes["user"]; transaction->cpr = attributes["cpr"]; @@ -92,29 +70,46 @@ void TransactionParser::startTag(std::string name, std::map< std::string, std::s } if(name == "field") { - if(!transaction->commits.size()) error("Field without a commit tag!"); - if(attributes.find("name") == attributes.end()) error("Field is missing 'name' attribute"); - if(attributes.find("value") == attributes.end()) error("Field is missing 'value' attribute"); - transaction->commits.back().fields[attributes["name"]] = attributes["value"]; + if(!transaction->commits.size()) { + PRACRO_ERR(transactionparser, "Field without a commit tag!"); + throw std::exception(); + } + + if(attributes.find("name") == attributes.end()) { + PRACRO_ERR(transactionparser, "Field is missing 'name' attribute"); + throw std::exception(); + } + + if(attributes.find("value") == attributes.end()) { + PRACRO_ERR(transactionparser, "Field is missing 'value' attribute"); + throw std::exception(); + } + + transaction->commits.back().fields[attributes["name"]] = + attributes["value"]; } } -void TransactionParser::parseError(const char *buf, size_t len, std::string error, int lineno) +void TransactionParser::parseError(const char *buf, size_t len, + std::string error, int lineno) { - PRACRO_ERR_LOG(transactionparser, "TransactionParser error at line %d: %s\n", - lineno, error.c_str()); - PRACRO_ERR_LOG(transactionparser, "\tBuffer %u bytes: [", len); - if(fwrite(buf, len, 1, stderr) != len) {} - PRACRO_ERR_LOG(transactionparser, "]\n"); - - char *slineno; - if(asprintf(&slineno, " at line %d\n", lineno) != -1) { - throw Exception(error + slineno); - free(slineno); - } + PRACRO_ERR(transactionparser, "TransactionParser error at line %d: %s\n", + lineno, error.c_str()); + + std::string xml; + xml.append(buf, len); + + PRACRO_ERR(transactionparser, "\tBuffer %u bytes: [%s]\n", + len, xml.c_str()); + + throw std::exception(); } #ifdef TEST_TRANSACTIONPARSER +//deps: saxparser.cc debug.cc exception.cc log.cc +//cflags: -I.. $(EXPAT_CFLAGS) +//libs: $(EXPAT_LIBS) +#include "test.h" static char xml_minimal[] = "\n" @@ -146,53 +141,36 @@ static char xml_fail[] = "\n" ; -int main() -{ - // Test minimal - try { - Transaction transaction; - TransactionParser parser(&transaction); - parser.parse(xml_minimal, strlen(xml_minimal)); - } catch(Exception &e) { - printf("ERROR: %s\n", e.what()); - return 1; - } +TEST_BEGIN; - // Test request - try { - Transaction transaction; - TransactionParser parser(&transaction); - parser.parse(xml_request, strlen(xml_request)); - } catch(Exception &e) { - printf("ERROR: %s\n", e.what()); - return 1; - } - - // Test commit - try { - Transaction transaction; - TransactionParser parser(&transaction); - parser.parse(xml_commit, strlen(xml_commit)); - } catch(Exception &e) { - printf("ERROR: %s\n", e.what()); - return 1; - } +// Test minimal +{ + Transaction transaction; + TransactionParser parser(&transaction); + TEST_NOEXCEPTION(parser.parse(xml_minimal, sizeof(xml_minimal)-1), "minimal"); +} - // Test parse error (should throw an exception) - try { - Transaction transaction; - TransactionParser parser(&transaction); - parser.parse(xml_fail, strlen(xml_fail)); - } catch(Exception &e) { - printf("ERROR: %s\n", e.what()); - goto onandon; - } - printf("We should fail here...\n"); - return 1; +// Test request +{ + Transaction transaction; + TransactionParser parser(&transaction); + TEST_NOEXCEPTION(parser.parse(xml_request, sizeof(xml_request)-1), "request"); +} - onandon: +// Test commit +{ + Transaction transaction; + TransactionParser parser(&transaction); + TEST_NOEXCEPTION(parser.parse(xml_commit, sizeof(xml_commit)-1), "commit"); +} - return 0; +// Test parse error (should throw an exception) +{ + Transaction transaction; + TransactionParser parser(&transaction); + TEST_EXCEPTION(parser.parse(xml_fail, sizeof(xml_fail)-1), std::exception, "parse error"); } +TEST_END; + #endif/*TEST_TRANSACTIONPARSER*/ diff --git a/server/src/widgetgenerator.cc b/server/src/widgetgenerator.cc index 9fdacd2..425c71e 100644 --- a/server/src/widgetgenerator.cc +++ b/server/src/widgetgenerator.cc @@ -40,8 +40,7 @@ static std::string automap(std::string name) if(name[i] == '.') groupcheck += " and " + group; else groupcheck += name[i]; } - groupcheck += " and " + name + ".value and " + name + - ".timestamp and " + name + ".source"; + groupcheck += " and " + name + ".value and " + name + ".timestamp and " + name + ".source"; groupcheck += ")\n"; std::string automapstring = @@ -74,12 +73,9 @@ static std::string send_macro_widget(Macro ¯o, std::string prefilled; time_t timestamp = 0; time_t now = time(NULL); - /* - if(widget.attributes.find("value") != widget.attributes.end()) { - widget.attributes["value"] = ""; - } - */ + result = tabs + "<" + widget.attributes["tagname"]; + std::map< std::string, std::string >::iterator p = widget.attributes.begin(); PRACRO_DEBUG(prefill, "%s: %s\n", widget.attributes["tagname"].c_str(), @@ -113,9 +109,9 @@ static std::string send_macro_widget(Macro ¯o, if(luamap != "") { Value value = mapper.map(luamap); if(value.timestamp > now - Conf::pentominos_max_ttl) { - widget.attributes["value"] = xml_encode(value.value); + widget.attributes["value"] = value.value; timestamp = value.timestamp; - prefilled = xml_encode(value.source); + prefilled = value.source; } PRACRO_DEBUG(prefill, "map: (%s, %d)\n", @@ -141,8 +137,7 @@ static std::string send_macro_widget(Macro ¯o, if(values[widget.attributes["name"]].timestamp > timestamp) { if(values[widget.attributes["name"]].timestamp > now - Conf::db_max_ttl) { - widget.attributes["value"] = - xml_encode(values[widget.attributes["name"]].value); + widget.attributes["value"] = values[widget.attributes["name"]].value; timestamp = values[widget.attributes["name"]].timestamp; prefilled = "pracro"; } @@ -154,7 +149,6 @@ static std::string send_macro_widget(Macro ¯o, widget.attributes["value"].c_str(), (int)timestamp); - std::map< std::string, std::string >::iterator p = widget.attributes.begin(); while(p != widget.attributes.end()) { if(p->first != "tagname" && p->first != "map") { if( ! (p->first == "name" && p->second == "") ) @@ -184,12 +178,9 @@ static std::string send_macro_widget(Macro ¯o, static void get_fields(Widget &widget, Fieldnames &fields) { - // if(widget.attributes.find("value") != widget.attributes.end()) { - if(widget.attributes.find("name") != widget.attributes.end()) { - if(widget.attributes["name"] != "") - fields.push_back(widget.attributes["name"]); + if(widget.attributes.find("value") != widget.attributes.end()) { + if(widget.attributes["name"] != "") fields.push_back(widget.attributes["name"]); } - // } std::vector< Widget >::iterator w = widget.widgets.begin(); while(w != widget.widgets.end()) { @@ -198,8 +189,7 @@ static void get_fields(Widget &widget, Fieldnames &fields) } } -std::string widgetgenerator(std::string cpr, Macro ¯o, - LUAQueryMapper &mapper, Database &db) +std::string widgetgenerator(std::string cpr, Macro ¯o, LUAQueryMapper &mapper, Database &db) { Fieldnames fields; get_fields(macro.widgets, fields); -- cgit v1.2.3