From 965e43178736e6635cf27410e6d73f4ec0fdced2 Mon Sep 17 00:00:00 2001 From: deva Date: Fri, 7 May 2010 09:31:26 +0000 Subject: LOTS of changes. libmicrohttpd fix for 'chunked' POST handling and LUA parameter checker from Pentominos among other things. --- server/etc/pracrod.conf | 3 + server/src/Makefile.am | 2 + server/src/artefact.cc | 97 ++++++++---- server/src/debug.cc | 2 +- server/src/entitylist.cc | 8 +- server/src/inotify.cc | 9 +- server/src/luaresume.cc | 34 ++--- server/src/luautil.cc | 312 +++++++++++++++++++++++++++++++++++++++ server/src/luautil.h | 81 ++++++++++ server/src/macrolist.cc | 5 + server/src/saxparser.cc | 25 +++- server/src/server.cc | 200 +++++++++++++++++-------- server/src/session.cc | 13 +- server/src/session.h | 8 + server/src/templatelist.cc | 5 + server/src/transactionhandler.cc | 27 ++-- server/src/transactionparser.cc | 73 ++++----- 17 files changed, 728 insertions(+), 176 deletions(-) create mode 100644 server/src/luautil.cc create mode 100644 server/src/luautil.h diff --git a/server/etc/pracrod.conf b/server/etc/pracrod.conf index 6d97619..5fb7494 100644 --- a/server/etc/pracrod.conf +++ b/server/etc/pracrod.conf @@ -2,3 +2,6 @@ port = 12345; journal_commit_addr = "localhost"; journal_commit_port = 18112; + +artefact_addr = "localhost"; +artefact_port = 11108; diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 1ff8a3a..afc72f9 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -27,6 +27,7 @@ pracrod_SOURCES = \ log.cc \ luaquerymapper.cc \ luaresume.cc \ + luautil.cc \ macroheaderparser.cc \ macrolist.cc \ macroparser.cc \ @@ -102,6 +103,7 @@ EXTRA_DIST = \ log.h \ luaquerymapper.h \ luaresume.h \ + luautil.h \ macroheaderparser.h \ macrolist.h \ macroparser.h \ diff --git a/server/src/artefact.cc b/server/src/artefact.cc index cc29b77..24114f9 100644 --- a/server/src/artefact.cc +++ b/server/src/artefact.cc @@ -35,7 +35,7 @@ Artefact::Artefact() { #ifndef WITHOUT_PENTOMINOS - PRACRO_DEBUG(artefact, "Creating artefact connection %s : %d", + PRACRO_DEBUG(artefact, "Creating artefact connection %s : %d\n", Conf::artefact_addr.c_str(), Conf::artefact_port); #endif/*WITHOUT_PENTOMINOS*/ @@ -54,47 +54,88 @@ Artefact::~Artefact() atf_close(atfh); } +static QueryResult node2result(atf_result_node_t *node, time_t timestamp) +{ + 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; + } + + return rnode; +} + QueryResult Artefact::exec(Query &query, std::string patientid, std::string user) { - QueryParser parser; + 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; + + QueryResult rroot; + rroot.timestamp = 0; + rroot.source = "pentominos"; + + if(query.attributes.find("class") == query.attributes.end()) { + PRACRO_ERR(artefact, "Missing 'class' attribute!\n"); + goto aaarg; + } - atf_transaction_t *trans = atf_new_transaction(conn, patientid.c_str()); - atf_querylist_t *qlist = atf_new_querylist(trans, user.c_str()); + atft = atf_new_transaction(conn, patientid.c_str()); + if(!atft) goto aaarg; - atf_id id = atf_add_query(qlist, - query.attributes["class"].c_str(), - 0/*oldest*/, "xml"/*replyformat*/); + id = atf_add_query(atft, query.attributes["class"].c_str(), + FILTER_LATEST, USE_NONE, 0, 0); + if(!atft) goto aaarg; - atf_reply_t *reply = atf_commit(trans, NULL, qlist); + reply = atf_commit(atft); + if(!reply) goto aaarg; - atf_status_t status = atf_get_reply_status(reply, id); + if(atf_get_num_results(reply, id) != 1) goto aaarg; - switch(status) { - case ATF_STATUS_OK: - { - size_t bufsize = atf_get_reply_size(reply, id); - char *buf = new char[bufsize]; - - ssize_t size = atf_get_reply_data(reply, id, buf, size); - parser.parse(buf, size); - - delete[] buf; - } - break; + 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; - case ATF_STATUS_ERROR: - PRACRO_ERR(artefact, "Error in artefact query.\n"); - break; + root = atf_get_result_node(result); + if(!root) goto aaarg; + + { + QueryResult qresult = node2result(root, timestamp); + rroot.groups[query.attributes["class"]] = qresult; } - atf_free_reply(reply); + goto cleanup; + + aaarg: + PRACRO_ERR(artefact, "Artefact comm error (%d)!\n", atf_get_last_error(atfh)); - atf_free_querylist(qlist); - atf_free_transaction(trans); + cleanup: + if(root) atf_free_result_node(root); + if(reply) atf_free_reply(reply); + if(atft) atf_free_transaction(atft); - return parser.result; + return rroot; } 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 index 04533c0..3ec15a5 100644 --- a/server/src/entitylist.cc +++ b/server/src/entitylist.cc @@ -197,11 +197,15 @@ std::string EntityList::getLatestVersion(std::string entity) throw(Exception) updateList(); - if(find(entity) == end()) + if(find(entity) == end()) { throw Exception("Entity ("+entityname+") ["+entity+"] does not exist"); + } + EntityListItem mli = (*this)[entity]; - if(mli.size() == 0) + 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(), diff --git a/server/src/inotify.cc b/server/src/inotify.cc index 5fb983e..1515387 100644 --- a/server/src/inotify.cc +++ b/server/src/inotify.cc @@ -264,10 +264,12 @@ void INotify::addDirectory(std::string name, depth_t depth, uint32_t mask) struct dirent *dirent; while( (dirent = readdir(dir)) != NULL ) { - if(std::string(dirent->d_name) == "." || std::string(dirent->d_name) == "..") + 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); + if(isdir(name+"/"+dirent->d_name)) + addDirectory(name+"/"+dirent->d_name, depth, mask); } closedir(dir); @@ -348,7 +350,8 @@ void INotify::readEvents() 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)) { + 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)); diff --git a/server/src/luaresume.cc b/server/src/luaresume.cc index 8ba5641..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,11 +92,11 @@ 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; } - PRACRO_DEBUG(lua, "Running %s\n", program.c_str()); + PRACRO_DEBUG(luaresume, "Running %s\n", program.c_str()); /* lua_pushstring(L, value.toStdString().c_str()); @@ -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; } @@ -140,5 +138,5 @@ std::string LUAResume::run(std::string program) void LUAResume::error(std::string message) { - printf("LUA ERROR: %s\n", message.c_str()); + PRACRO_ERR(luaresume, "LUA ERROR: %s\n", message.c_str()); } 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 954149a..908de1e 100644 --- a/server/src/macrolist.cc +++ b/server/src/macrolist.cc @@ -42,6 +42,11 @@ MacroList::MacroList(std::string path) void MacroList::addFile(std::string file) { + if(file.substr(file.size() - 4) != ".xml") { + PRACRO_DEBUG(macrolist, "Skipping file: %s\n", file.c_str()); + return; + } + PRACRO_DEBUG(macrolist, "Adding file: %s\n", file.c_str()); MacroHeaderParser parser(file); try { diff --git a/server/src/saxparser.cc b/server/src/saxparser.cc index fcdf4fa..f26c965 100644 --- a/server/src/saxparser.cc +++ b/server/src/saxparser.cc @@ -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); diff --git a/server/src/server.cc b/server/src/server.cc index 84c3463..4283a11 100644 --- a/server/src/server.cc +++ b/server/src/server.cc @@ -61,18 +61,18 @@ static std::string error_box(std::string message) return errorbox; } -static std::string handleConnection(const char *buf, size_t size, +static std::string handleConnection(const std::string &data, Environment &env, - char **sessionid, - const char *sessioncommit) + std::string &sessionid, + bool sessioncommit) { std::string res; Session *session = NULL; - if(*sessionid == NULL) { + if(sessionid == "") { session = env.sessions.newSession(); } else { - session = env.sessions.session(*sessionid); + session = env.sessions.session(sessionid); if(session == NULL) session = env.sessions.newSession(); } @@ -81,51 +81,44 @@ static std::string handleConnection(const char *buf, size_t size, return error_box(xml_encode("New session could not be created.")); } - session->lock(); + sessionid = session->id(); - if(asprintf(sessionid, "%s", session->id().c_str()) == -1) *sessionid = NULL; + { + SessionAutolock lock(*session); - Transaction transaction; - TransactionParser parser(&transaction); + if(data.length()) { + Transaction transaction; + TransactionParser parser(&transaction); + + if(!parser.parse(data.c_str(), data.length())) { + PRACRO_ERR(server, "Failed to parse data!\n"); + PRACRO_ERR(server, "DATA:[[%s]]\n", data.c_str()); + res = error_box(xml_encode("XML Parse error.")); + } else { + res = handleTransaction(transaction, env, *session); + } + } - if(!parser.parse(buf, size)) { - PRACRO_ERR(server, "Failed to parse data!\n"); - res = error_box(xml_encode("XML Parse error.")); - } else { - res = handleTransaction(transaction, env, *session); } - session->unlock(); - - if(sessioncommit != NULL) { + if(sessioncommit) { session->commit(); env.sessions.deleteSession(session->id()); } return res; +} - /* - Transaction transaction; - TransactionParser parser(&transaction); - - PRACRO_DEBUG(server, "Read %d bytes from network\n", size); - - std::string res; - if(size) { - if(parser.parse(buf, size)) { - PRACRO_DEBUG(server, "Got complete XML document, %d bytes in current buffer.\n", size); - - res = handleTransaction(&transaction, pentominos_socket, - *conn->db, session, macrolist, templatelist); - } else { - PRACRO_ERR(server, "Failed to parse data!\n"); - res = error_box(xml_encode("XML Parse error.")); - } - } - return res; +struct condata { + std::map headers; + std::string data; + size_t data_size; + std::string url; + std::string method; + std::string version; +}; - */ -} +//static std::map condata; static int handle_request_callback(void *cls, struct MHD_Connection *con, @@ -134,40 +127,116 @@ static int handle_request_callback(void *cls, const char *version, const char *data, unsigned int *data_size, - void **ptr) + void **con_cls) { - Environment *env = (Environment *)cls; + PRACRO_DEBUG(httpd, "handle_request_callback con:%p condata:%p\n", + con, *con_cls); + + // Test for new connection + if(*con_cls == NULL) { + PRACRO_DEBUG(httpd, + "handle_request(url=\"%s\", method=\"%s\"," + " version=\"%s\", data_size=\"%d\")\n", + url, method, version, *data_size); + + struct condata *cd = new struct condata; + cd->url = url; + cd->method = method; + cd->version = version; + cd->data_size = 0; + cd->data = ""; + + const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "SessionID"); + if(sid) cd->headers["SessionID"] = sid; + + const char *scm = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "SessionCommit"); + if(scm) cd->headers["SessionCommit"] = scm; + + const char *csz = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "Content-Length"); + if(csz) { + cd->headers["Content-Length"] = csz; + cd->data_size = atol(csz); + PRACRO_DEBUG(httpd, "Content-Length: %s (%d)\n", csz, cd->data_size); + } - PRACRO_DEBUG(httpd, - "handle_request(url=\"%s\", method=\"%s\"," - " version=\"%s\", data_size=\"%d\")\n", - url, method, version, *data_size); + *con_cls = cd; + } - const char *sessionid = - MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionID"); - const char *sessioncommit = - MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionCommit"); + struct condata *cd = (struct condata*)*con_cls; + cd->data.append(data, *data_size); - std::string reply = - handleConnection(data, *data_size, *env, (char**)&sessionid, sessioncommit); - - struct MHD_Response *rsp = - MHD_create_response_from_data(reply.length(), (char*)reply.c_str(), MHD_NO, MHD_YES); - MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain; charset=UTF-8"); + int ret = MHD_YES; - if(*sessionid) { - MHD_add_response_header(rsp, "SessionID", sessionid); - free((char*)sessionid); - } + PRACRO_DEBUG(httpd, "Waiting for %d bytes of data. Got %d (total %d)\n", + cd->data_size, *data_size, cd->data.length()); + + if(cd->data.length() >= cd->data_size) { + + Environment *env = (Environment *)cls; + + const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND, + "SessionID"); + std::string sessionid; + if(sid) sessionid = sid; + + const char *sessioncommit = + MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionCommit"); + + PRACRO_DEBUG(httpd, "Starting to handle SessionID: %s%s\n", + sessionid.c_str(), + sessioncommit != NULL?" COMMIT":""); + + PRACRO_DEBUG(httpd, "Data: [%s]\n", cd->data.c_str()); + + std::string reply = + handleConnection(cd->data, *env, sessionid, sessioncommit != NULL); + + struct MHD_Response *rsp = + MHD_create_response_from_data(reply.length(), (char*)reply.c_str(), + MHD_NO, MHD_YES); + MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE, + "text/plain; charset=UTF-8"); + + if(sessionid != "") { + MHD_add_response_header(rsp, "SessionID", sessionid.c_str()); + } + + ret = MHD_queue_response(con, MHD_HTTP_OK, rsp); + MHD_destroy_response(rsp); + + PRACRO_DEBUG(httpd, "Finished handling SessionID: %s%s\n", + sessionid.c_str(), + sessioncommit != NULL?" COMMIT":""); - int ret = MHD_queue_response(con, MHD_HTTP_OK, rsp); - MHD_destroy_response(rsp); + + delete cd; + *con_cls = NULL; + + } *data_size = 0; return ret; } +void requestCompletedCallback(void *cls, + struct MHD_Connection *con, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + PRACRO_DEBUG(httpd, "requestCompletedCallback %p\n", con); + + // If connection was interrupted prematurely delete the content data here. + if(*con_cls) { + struct condata *cd = (struct condata*)*con_cls; + delete cd; + *con_cls = NULL; + } +} + static void httpderr(void *arg, const char *fmt, va_list ap) { PRACRO_ERR_VA(server, fmt, ap); @@ -181,7 +250,8 @@ void server() bool forceshutdown = false; port_t port = Conf::server_port; - int flags = MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY; // | MHD_USE_PEDANTIC_CHECKS + 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 @@ -193,7 +263,8 @@ void server() struct MHD_Daemon *d; d = MHD_start_daemon(flags, port, NULL, NULL, handle_request_callback, &env, - MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL, + 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(), @@ -246,10 +317,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 index 675bf56..74adb32 100644 --- a/server/src/session.cc +++ b/server/src/session.cc @@ -66,7 +66,7 @@ void Session::commit() JournalWriter *Session::journal() { if(_journal == NULL) { - _journal = + _journal = new JournalWriter(Conf::journal_commit_addr, Conf::journal_commit_port); } return _journal; @@ -114,6 +114,17 @@ size_t Sessions::size() return sessions.size(); } +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 //cflags: -I.. $(PTHREAD_CFLAGS) diff --git a/server/src/session.h b/server/src/session.h index 12c831a..48c01d7 100644 --- a/server/src/session.h +++ b/server/src/session.h @@ -90,5 +90,13 @@ private: std::map sessions; }; +class SessionAutolock { +public: + SessionAutolock(Session &session); + ~SessionAutolock(); + +private: + Session &session; +}; #endif/*__PRACRO_SESSION_H__*/ diff --git a/server/src/templatelist.cc b/server/src/templatelist.cc index b8e8462..af03586 100644 --- a/server/src/templatelist.cc +++ b/server/src/templatelist.cc @@ -39,6 +39,11 @@ TemplateList::TemplateList(std::string path) void TemplateList::addFile(std::string file) { + if(file.substr(file.size() - 4) != ".xml") { + PRACRO_DEBUG(templatelist, "Skipping file: %s\n", file.c_str()); + return; + } + PRACRO_DEBUG(templatelist, "Adding file: %s\n", file.c_str()); TemplateHeaderParser parser(file); try { diff --git a/server/src/transactionhandler.cc b/server/src/transactionhandler.cc index a042a6b..7a45800 100644 --- a/server/src/transactionhandler.cc +++ b/server/src/transactionhandler.cc @@ -48,7 +48,8 @@ static std::string error_box(std::string message) return errorbox; } -static std::string handleCommits(Transaction &transaction, Environment &env, Session &session) +static std::string handleCommits(Transaction &transaction, Environment &env, + Session &session) { std::string answer; @@ -66,7 +67,8 @@ static std::string handleCommits(Transaction &transaction, Environment &env, Ses std::string resume = resume_parser(macro->resume, commit); commit.fields["journal.resume"] = resume; - db->commitTransaction(transaction.user, transaction.cpr, *macro, commit.fields); + db->commitTransaction(transaction.user, transaction.cpr, *macro, + commit.fields); if(resume != "") { @@ -84,7 +86,8 @@ static std::string handleCommits(Transaction &transaction, Environment &env, Ses return answer; } -static std::string handleRequest(Transaction &transaction, Environment &env, Session &session) +static std::string handleRequest(Transaction &transaction, Environment &env, + Session &session) { std::string answer; @@ -120,7 +123,8 @@ static std::string handleRequest(Transaction &transaction, Environment &env, Ses Macro ¯o = (*mi2); if(macro.isHeader) { - answer += "
\n"; + answer += "
\n"; mi2++; continue; } @@ -133,7 +137,8 @@ static std::string handleRequest(Transaction &transaction, Environment &env, Ses if(completed) answer += "\"true\""; else answer += "\"false\""; - std::map< std::string, std::string >::iterator ai = macro.attributes.begin(); + std::map< std::string, std::string >::iterator ai = + macro.attributes.begin(); while(ai != macro.attributes.end()) { std::string name = ai->first; std::string value = ai->second; @@ -167,7 +172,8 @@ static std::string handleRequest(Transaction &transaction, Environment &env, Ses if(service == "pentominos") { // Send the queries to Pentominos (if any) - QueryHandlerPentominos qh(*atf, transaction.cpr, "pracrod"); + QueryHandlerPentominos qh(*atf, transaction.cpr, + "pracrod"/*user*/); QueryResult queryresult = qh.exec(*qi); lqm.addQueryResult(queryresult); @@ -190,7 +196,8 @@ static std::string handleRequest(Transaction &transaction, Environment &env, Ses std::vector< Script >::iterator spi = m->scripts.begin(); while(spi != m->scripts.end()) { - answer += " \n"; @@ -212,7 +219,8 @@ static std::string handleRequest(Transaction &transaction, Environment &env, Ses if(completed) { answer += " "; - answer += db->getResume(transaction.cpr, macro, time(NULL) - Conf::db_max_ttl); + answer += db->getResume(transaction.cpr, macro, time(NULL) - + Conf::db_max_ttl); answer += "\n"; } @@ -233,7 +241,8 @@ static std::string handleRequest(Transaction &transaction, Environment &env, Ses return answer; } -std::string handleTransaction(Transaction &transaction, Environment &env, Session &session) +std::string handleTransaction(Transaction &transaction, Environment &env, + Session &session) { std::string answer; diff --git a/server/src/transactionparser.cc b/server/src/transactionparser.cc index f1f79c6..7422a13 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,26 +70,37 @@ 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!"); + return; + } + + if(attributes.find("name") == attributes.end()) { + PRACRO_ERR(transactionparser, "Field is missing 'name' attribute"); + return; + } + + if(attributes.find("value") == attributes.end()) { + PRACRO_ERR(transactionparser, "Field is missing 'value' attribute"); + return; + } + + 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()); } #ifdef TEST_TRANSACTIONPARSER -- cgit v1.2.3