/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * server.cc * * Wed Aug 22 12:16:03 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 "server.h" #include "tcpsocket.h" #include #include // For fork #include #include #include "configuration.h" #include "transaction.h" #include "transactionparser.h" #include "templateparser.h" #include "macroparser.h" #include "queryhandler.h" #include "queryparser.h" #include "luaquerymapper.h" #include "database.h" #include "widgetgenerator.h" #include "resumeparser.h" #include "journal_commit.h" #include "xml_encode_decode.h" static std::string error_box(std::string message) { std::string errorbox = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; return errorbox; } class NotFoundException : public Exception { public: NotFoundException(Request &r) : Exception("Macro " + r.macro + " not found in course " + r.course) {} }; static std::string handleTransaction(Transaction &transaction) { std::string answer; answer += "\n"; answer += "\n"; try { Database db(Conf::database_addr, Conf::database_user, Conf::database_passwd); // // Handle commits // { Commits::iterator i = transaction.commits.begin(); while(i != transaction.commits.end()) { Commit &commit = *i; MacroParser mp(commit.macro); mp.parse(); Macro *macro = mp.getMacro(); db.commit(transaction.user, transaction.cpr, *macro, commit.fields); std::string resume = resume_parser(macro->attributes["resume"].c_str(), commit); // if(resume != "") { journal_commit(transaction.cpr.c_str(), transaction.user.c_str(), Conf::journal_commit_addr.c_str(), Conf::journal_commit_port, resume.c_str(), resume.length()); db.putJournal(transaction.user, transaction.cpr, *macro, resume, time(NULL)); // } i++; } } // // Handle requests // Requests::iterator i = transaction.requests.begin(); while(i != transaction.requests.end()) { Request &request = *i; printf("Handling request - macro: %s, course: %s\n", request.macro.c_str(), request.course.c_str()); // Read and parse the template file. TemplateParser tp(request.course); tp.parse(); Template *templ = tp.getTemplate(); answer += " course.attributes["name"]; answer += "\">\n"; bool foundmacro = false; // Generate the macro and return it to the client std::vector< Macro >::iterator mi2 = templ->course.macroes.begin(); while(mi2 != templ->course.macroes.end()) { Macro ¯o = (*mi2); bool completed = db.checkMacro(transaction.cpr, macro.attributes["name"]); answer += " ::iterator ai = macro.attributes.begin(); while(ai != macro.attributes.end()) { std::string name = ai->first; std::string value = ai->second; answer += " "+name+"=\"" + value + "\""; ai++; } if(macro.attributes["name"] == request.macro || (macro.attributes.find("static") != macro.attributes.end() && macro.attributes["static"] == "true") ) { foundmacro = true; MacroParser mp(macro.attributes["name"]); mp.parse(); Macro *m = mp.getMacro(); answer += " caption=\"" + m->window.attributes["caption"] + "\""; answer += ">\n"; LUAQueryMapper lqm; //////////////////////// std::vector< Query >::iterator qi = m->queries.begin(); while(qi != m->queries.end()) { /////////////////////////////// // Send the queries to Pentominos (if any) TCPSocket s; #ifndef WITHOUT_PENTOMINOS s.connect(Conf::pentominos_addr, Conf::pentominos_port); #endif/*WITHOUT_PENTOMINOS*/ QueryHandler qh(&s, transaction.cpr); QueryResult queryresult = qh.exec(*qi); lqm.addQueryResult(queryresult); qi++; } // Handle scripts if(m->scripts.size()) { answer += " \n"; std::vector< Script >::iterator spi = m->scripts.begin(); while(spi != m->scripts.end()) { answer += " \n"; spi++; } answer += " \n"; } answer += widgetgenerator(transaction.cpr, *m, lqm, db); } else { // only find macro title MacroParser mp(macro.attributes["name"]); mp.parse(); Macro *m = mp.getMacro(); answer += " caption=\"" + m->window.attributes["caption"] + "\""; answer += ">\n"; } if(completed) { answer += " "; answer += db.getResume(transaction.cpr, macro.attributes["name"], time(NULL) - Conf::db_max_ttl); answer += " \n"; } answer += " \n"; mi2++; } if(foundmacro == false && request.macro != "") throw NotFoundException(request); answer += " \n"; i++; } answer += "\n"; } catch( PGSTD::runtime_error &e ) { answer = error_box(xml_encode(std::string("PostgreSQL server error:\n") + e.what())); } catch( std::exception &e ) { answer = error_box(xml_encode(e.what())); } return answer; } static void handleConnection(TCPSocket *socket) { size_t bufsize = 4096; int size; char buf[bufsize]; memset(buf, 0, bufsize); Transaction *transaction = NULL; TransactionParser *parser = NULL; while((size = socket->read(buf, bufsize)) > 0) { while(size) { if(transaction == NULL) { transaction = new Transaction(); parser = new TransactionParser(transaction); } printf("Got %d bytes in read loop\n", size); if(parser->parse(buf, size)) { printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" "!! Got complete XML document %d bytes used, %d bytes in current buffer.\n" "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", parser->usedBytes(), strlen(buf)); socket->write(handleTransaction(*transaction)); delete transaction; transaction = NULL; delete parser; parser = NULL; } size = size - parser->usedBytes(); if(size) memcpy(buf, buf + parser->usedBytes(), size); } memset(buf, 0, bufsize); } if(transaction) { delete transaction; transaction = NULL; delete parser; parser = NULL; } printf("Out of read loop!\n"); } //#define NON_FORKING #include extern bool pracro_is_running; void server() { port_t port = Conf::server_port; TCPSocket *socket = NULL; try { socket = new TCPSocket("Listen socket"); socket->listen(port); } catch (Exception &e) { fprintf(stderr, "Error during parsing:\n%s\n", e.what()); delete socket; socket = NULL; 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 fprintf(stderr, "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*/ } } //socket->shutdown(); delete socket; printf("Server gracefully shut down.\n"); } #ifdef TEST_SERVER char request[] = "\n" "\n" " \n" "\n"; int main() { Configuration conf("../etc/pracrod.conf"); initConfig(&conf); switch(fork()) { case -1: // error return 1; case 0: // child server(); return 0; default: // parent { TCPSocket socket; int port = config()->lookup("port"); socket.connect("localhost", port); socket.write(request); char buf[32]; memset(buf, 0, sizeof(buf)); while(socket.read(buf, 31)) { printf(buf); fflush(stdout); memset(buf, 0, sizeof(buf)); } } return 0; } return 1; } #endif/*TEST_SERVER*/