diff options
Diffstat (limited to 'server')
50 files changed, 2413 insertions, 521 deletions
diff --git a/server/configure.in b/server/configure.in index 63e2e24..e65ac33 100644 --- a/server/configure.in +++ b/server/configure.in @@ -2,7 +2,7 @@  AC_INIT(src/pracrod.cc) -VERSION="2.2.0" +VERSION="2.2.2"  AM_INIT_AUTOMAKE( pracrod, $VERSION )  dnl ====================== @@ -95,7 +95,9 @@ else      dnl ======================      dnl Check for libpqxx      dnl ====================== -    PKG_CHECK_MODULES(PQXX, libpqxx >= 2.6.8) +    PKG_CHECK_MODULES(PQXX, libpqxx >= 4.0, +        [AC_DEFINE_UNQUOTED([USE_NEW_PQXX], [1], [Use new pqx api])], +    	[PKG_CHECK_MODULES(PQXX, libpqxx >= 2.6.8)] )  fi   dnl ====================== diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 167f4e4..c852eb0 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -36,6 +36,8 @@ pracrod_SOURCES = \  	journal.cc \  	journal_uploadserver.cc \  	log.cc \ +	luascript.cc \ +	luaoncommit.cc \  	luapraxisd.cc \  	luaquerymapper.cc \  	luaresume.cc \ @@ -51,11 +53,11 @@ pracrod_SOURCES = \  	queryhandlerpentominos.cc \  	queryhandlerpracro.cc \  	queryparser.cc \ -	resumeparser.cc \  	saxparser.cc \  	semaphore.cc \  	server.cc \  	session.cc \ +	sessionheaderparser.cc \  	sessionparser.cc \  	sessionserialiser.cc \  	templatelist.cc \ @@ -97,6 +99,8 @@ EXTRA_DIST = \  	journal.h \  	journal_uploadserver.h \  	log.h \ +	luascript.h \ +	luaoncommit.h \  	luapraxisd.h \  	luaquerymapper.h \  	luaresume.h \ @@ -114,11 +118,11 @@ EXTRA_DIST = \  	queryhandlerpracro.h \  	queryparser.h \  	queryresult.h \ -	resumeparser.h \  	saxparser.h \  	semaphore.h \  	server.h \  	session.h \ +	sessionheaderparser.h \  	sessionparser.h \  	sessionserialiser.h \  	template.h \ diff --git a/server/src/admin_connection.cc b/server/src/admin_connection.cc index fac52a1..76c88ea 100644 --- a/server/src/admin_connection.cc +++ b/server/src/admin_connection.cc @@ -35,6 +35,7 @@  #include "debug.h"  #include "configuration.h" +#include "sessionserialiser.h"  static std::string admin_sessionunlock(Environment &env, std::string id)  { @@ -59,18 +60,28 @@ static std::string admin_listactivesessions(Environment &env)  {    std::string str; -  std::vector<std::string> act = env.sessions.activeSessions(); -  std::vector<std::string>::iterator i = act.begin(); +  std::vector<Sessions::SessionInfo> act = env.sessions.activeSessions(); +  std::vector<Sessions::SessionInfo>::iterator i = act.begin();    while(i != act.end()) { -    // NOTE: Returned session is returned in locked state! -    Session *s = NULL; -    SessionAutounlock l(&s); -    s = env.sessions.lockedSession(*i); -    str += "Session " + *i + ": "+s->templ+" on "+s->patientid+" "+ -      std::string(s->idle()?"[idle]":"[active]")+"\n"; +    str += "Session " + i->id + ": "+i->templ+" on "+i->patientid+" "+ +        std::string(i->idle?"[idle]":"[active]")+"\n";      i++;    } +  SessionSerialiser ser(&env, Conf::session_path); +  std::map<std::string, SessionHeaderParser::Header> files = ser.sessionFiles(); + +  std::map<std::string, SessionHeaderParser::Header>::iterator j = files.begin(); +  while(j != files.end()) { +    std::string file = j->first; +    SessionHeaderParser::Header header = j->second; + +    str += "Session " + header.id + ": " + header.templ + " on " +      + header.patientid + " [session file: " + file + "]\n"; + +    j++; +  } +    return str;  } @@ -90,70 +101,68 @@ AdminConnection::AdminConnection(Environment &e, headers_t a, std::string u)  AdminConnection::~AdminConnection() {} -bool AdminConnection::handle(const char *data, size_t size) +bool AdminConnection::data(const char *, size_t) { return true; } + +bool AdminConnection::handle()  {    status = 200; // OK -  if(data == NULL && size == 0) { -    DEBUG(admin, "URI: %s\n", uri.c_str()); - -    if(uri == "/") { -      reply = admin_header(uri) +  -        "Command list:\n" -        "<strong>/sessionunlock?id=<em>[ID]</em></strong> unlock session with [ID] as its session id.\n" -        "<strong>/listactivesessions</strong> lists all active sessions on the server.\n" -        "<strong>/flushsessions</strong> flushes all active sessions to disc.\n" -        "<strong>/export?template=<em>[TEMPLATE]</em></strong> export template with name [TEMPLATE] to a\n  csf file (comma seperated file, that can be opened in OOCalc or Excel).\n" -        + admin_rc("footer"); -      return true; -    } - -    if(uri == "/sessionunlock" && args.find("id") != args.end()) { -      reply = admin_header(uri) + admin_sessionunlock(env, args["id"]) -        + admin_rc("footer"); -      return true; -    } - -    if(uri == "/listactivesessions") { -      reply = admin_header(uri) + admin_listactivesessions(env) -        + admin_rc("footer"); -      return true; -    } - -    if(uri == "/flushsessions") { -      reply = admin_header(uri) + admin_flush(env) + admin_rc("footer"); -      return true; -    } - -    if(uri == "/export" && args.find("template") != args.end()) { -      time_t from = 0; -      if(args.find("from") != args.end()) from = atoi(args["from"].c_str()); - -      time_t to = time(NULL); -      if(args.find("to") != args.end()) to = atoi(args["to"].c_str()); -      bool ok; -      std::string res = admin_export(env, args["template"], &ok, from, to); -      if(!ok) reply = admin_header(uri) + res + admin_rc("footer"); -      else { -        reply = res; -        hdrs["Content-Type"] = "text/csv; charset=UTF-8"; -        hdrs["Content-Disposition"] = "attachment; filename=\""+args["template"]+".csv\""; -      } -      return true; -    } - -    if(uri == "/favicon.ico") { -      hdrs["Content-Type"] = "image/ico"; -      reply = admin_rc("favicon"); -      return true; -    } +  DEBUG(admin, "URI: %s\n", uri.c_str()); -    reply = admin_header(uri) + -      "'" + uri + "' not recognised as a valid command." +  if(uri == "/") { +    reply = admin_header(uri) +  +      "Command list:\n" +      "<strong>/sessionunlock?id=<em>[ID]</em></strong> unlock session with [ID] as its session id.\n" +      "<strong>/listactivesessions</strong> lists all active sessions on the server.\n" +      "<strong>/flushsessions</strong> flushes all active sessions to disc.\n" +      "<strong>/export?template=<em>[TEMPLATE]</em></strong> export template with name [TEMPLATE] to a\n  csf file (comma seperated file, that can be opened in OOCalc or Excel).\n" +      + admin_rc("footer"); +    return true; +  } +   +  if(uri == "/sessionunlock" && args.find("id") != args.end()) { +    reply = admin_header(uri) + admin_sessionunlock(env, args["id"]) +      + admin_rc("footer"); +    return true; +  } +   +  if(uri == "/listactivesessions") { +    reply = admin_header(uri) + admin_listactivesessions(env)        + admin_rc("footer");      return true;    } +   +  if(uri == "/flushsessions") { +    reply = admin_header(uri) + admin_flush(env) + admin_rc("footer"); +    return true; +  } +   +  if(uri == "/export" && args.find("template") != args.end()) { +    time_t from = 0; +    if(args.find("from") != args.end()) from = atoi(args["from"].c_str()); +     +    time_t to = time(NULL); +    if(args.find("to") != args.end()) to = atoi(args["to"].c_str()); +    bool ok; +    std::string res = admin_export(env, args["template"], &ok, from, to); +    if(!ok) reply = admin_header(uri) + res + admin_rc("footer"); +    else { +      reply = res; +      hdrs["Content-Type"] = "text/csv; charset=UTF-8"; +      hdrs["Content-Disposition"] = "attachment; filename=\""+args["template"]+".csv\""; +    } +    return true; +  } +  if(uri == "/favicon.ico") { +    hdrs["Content-Type"] = "image/ico"; +    reply = admin_rc("favicon"); +    return true; +  } +   +  reply = admin_header(uri) + +    "'" + uri + "' not recognised as a valid command." +    + admin_rc("footer");    return true;  } diff --git a/server/src/admin_connection.h b/server/src/admin_connection.h index 4a413a6..591c128 100644 --- a/server/src/admin_connection.h +++ b/server/src/admin_connection.h @@ -38,7 +38,8 @@ public:    AdminConnection(Environment &e, headers_t args, std::string uri);    ~AdminConnection(); -  bool handle(const char *data, size_t size); +  bool data(const char *data, size_t size); +  bool handle();    void getReply(Httpd::Reply &reply); diff --git a/server/src/admin_export.cc b/server/src/admin_export.cc index 0aa290c..3bdfcf6 100644 --- a/server/src/admin_export.cc +++ b/server/src/admin_export.cc @@ -41,6 +41,13 @@  #define SEP "\t" +#ifdef USE_NEW_PQXX +#include <pqxx/tuple.hxx> +typedef pqxx::tuple tuple_t; +#else +typedef pqxx::result::tuple tuple_t; +#endif +  static std::string escape(std::string &str)  {    std::string out = "\""; @@ -91,7 +98,7 @@ public:                    " WHERE extract='true';");        pqxx::result::const_iterator ri = result.begin();        for(unsigned int r = 0; r < result.size(); r++) { -        pqxx::result::tuple tuple = result.at(r);  +        tuple_t tuple = result.at(r);           std::string name = tuple.at(0).c_str();          std::string caption = tuple.at(1).c_str();          addcell(name, caption); @@ -166,7 +173,7 @@ static std::string do_export(Environment &env, std::string templ, bool *ok,                  " WHERE extract='true';");      pqxx::result::const_iterator ri = result.begin();      for(unsigned int r = 0; r < result.size(); r++) { -      pqxx::result::tuple tuple = result.at(r);  +      tuple_t tuple = result.at(r);         std::string name = tuple.at(0).c_str();        filter.insert(name);        //printf("filter: '%s'\n", name.c_str()); @@ -209,7 +216,7 @@ static std::string do_export(Environment &env, std::string templ, bool *ok,      pqxx::result result = work.exec(q);      pqxx::result::const_iterator ri = result.begin();      for(unsigned int r = 0; r < result.size(); r++) { -      pqxx::result::tuple tuple = result.at(r);  +      tuple_t tuple = result.at(r);         std::string patientid = tuple.at(0).c_str();        std::string templ = tuple.at(1).c_str();        std::string version = tuple.at(2).c_str(); @@ -239,7 +246,7 @@ static std::string do_export(Environment &env, std::string templ, bool *ok,                      " ORDER BY t.timestamp;");          pqxx::result::const_iterator ri = result.begin();          for(unsigned int r = 0; r < result.size(); r++) { -          pqxx::result::tuple tuple = result.at(r);  +          tuple_t tuple = result.at(r);             std::string name = tuple.at(0).c_str();            std::string value = tuple.at(1).c_str(); @@ -269,9 +276,9 @@ std::string admin_export(Environment &env, std::string templ, bool *ok,  }  #ifdef TEST_ADMIN_EXPORT -//deps: environment.cc connectionpool.cc mutex.cc semaphore.cc configuration.cc entitylist.cc artefact.cc database.cc pracrodao.cc pracrodaotest.cc pracrodaopgsql.cc templatelist.cc macrolist.cc debug.cc macroheaderparser.cc exception.cc log.cc saxparser.cc templateheaderparser.cc versionstr.cc inotify.cc session.cc sessionserialiser.cc journal.cc journal_uploadserver.cc journal_commit.cc xml_encode_decode.cc sessionparser.cc fieldnamescanner.cc util.cc macroparser.cc templateparser.cc -//cflags: -I.. -DWITHOUT_ARTEFACT $(PQXX_CFLAGS) $(EXPAT_CFLAGS) -//libs: $(PQXX_LIBS) $(EXPAT_LIBS) +//deps: environment.cc connectionpool.cc mutex.cc semaphore.cc configuration.cc entitylist.cc artefact.cc database.cc pracrodao.cc pracrodaotest.cc pracrodaopgsql.cc templatelist.cc macrolist.cc debug.cc macroheaderparser.cc exception.cc log.cc saxparser.cc templateheaderparser.cc versionstr.cc inotify.cc session.cc sessionserialiser.cc journal.cc journal_uploadserver.cc journal_commit.cc xml_encode_decode.cc sessionparser.cc fieldnamescanner.cc util.cc macroparser.cc templateparser.cc courselist.cc luascript.cc sessionheaderparser.cc courseparser.cc luautil.cc luapraxisd.cc praxisd.cc +//cflags: -I.. -DWITHOUT_ARTEFACT $(PQXX_CFLAGS) $(EXPAT_CFLAGS) $(PTHREAD_CFLAGS) $(LUA_CFLAGS) $(CURL_CFLAGS) +//libs: $(PQXX_LIBS) $(EXPAT_LIBS) $(PTHREAD_LIBS) $(LUA_LIBS) $(CURL_LIBS)  #include "test.h"  TEST_BEGIN; diff --git a/server/src/client_connection.cc b/server/src/client_connection.cc index 075dc46..c3de7b0 100644 --- a/server/src/client_connection.cc +++ b/server/src/client_connection.cc @@ -85,7 +85,7 @@ ClientConnection::ClientConnection(Environment &e, headers_t headers,    did_commit = false;  #endif -  parser_complete = false; +  parser_complete = true;  }  ClientConnection::~ClientConnection() @@ -108,6 +108,7 @@ void ClientConnection::nocommit(Session *session)  }  void ClientConnection::commit(Session *session) +  throw(LUAScript::Exception, Journal::Exception)  {    if(docommit) {      if(session->isReadonly()) { // Commit of an empty session discards it. @@ -117,7 +118,13 @@ void ClientConnection::commit(Session *session)      DEBUG(connection, "Commit (%s)\n", session->id().c_str());      std::string sid = session->id(); -    session->commit(); +    try { +      session->commit(); +    } catch(LUAScript::Exception &e) { +      throw e; +    } catch(Journal::Exception &e) { +      throw e; +    }      env.sessions.deleteSession(sid);      sessionid = "";      docommit = false; @@ -139,8 +146,19 @@ void ClientConnection::discard(Session *session)    }  } -bool ClientConnection::handle(const char *data, size_t size) +bool ClientConnection::data(const char *data, size_t size)  { +  DEBUG(connection, "data(%p %d)\n", data, size); + +  parser_complete = parser.parse(data, size); + +  return true; +} + +bool ClientConnection::handle() +{ +  DEBUG(connection, "handle\n"); +    if(patientid == "") {      response = error_box(xml_encode("Missing patientid."));      parser_complete = true; @@ -189,33 +207,28 @@ bool ClientConnection::handle(const char *data, size_t size)    sessionid = session->id(); -  try { +  // Force session discard on empty template name. +  if(templ == "") dodiscard = true; -    if((!data || !size) && (docommit || donocommit || dodiscard)) { -      parser_complete = true; -      commit(session); -      nocommit(session); -      discard(session); -      return true; +  try { +    if(parser_complete) { +      response = handleTransaction(request, transaction, env, *session);      } -    // Force session discard on empty template name. -    if(templ == "") dodiscard = true; - -    if(size == 0 || parser.parse(data, size)) { -      parser_complete = true; - -      { -        //SessionAutolock lock(session); -        response = handleTransaction(request, transaction, env, *session); -      } - +    try {        commit(session); -      nocommit(session); -      discard(session); - +    } catch(LUAScript::Exception &e) { +      response = error_box(xml_encode(e.msg)); +      return true; +    } catch(Journal::Exception &e) { +      response = error_box(xml_encode(e.msg));        return true;      } +    nocommit(session); +    discard(session); +     +    return true; +        } catch(...) {      ERR(connection, "Failed to parse data!\n");      response = error_box(xml_encode("XML Parse error.")); diff --git a/server/src/client_connection.h b/server/src/client_connection.h index e64dcad..0bfa2e4 100644 --- a/server/src/client_connection.h +++ b/server/src/client_connection.h @@ -36,6 +36,8 @@  #include "transaction.h"  #include "transactionparser.h" +#include "luascript.h" +  class Session;  class ClientConnection : public Connection { @@ -49,12 +51,13 @@ public:                     headers_t args, std::string uri);    ~ClientConnection(); -  bool handle(const char *data, size_t size); +  bool data(const char *data, size_t size); +  bool handle();    void getReply(Httpd::Reply &reply);  private: -  void commit(Session *session); +  void commit(Session *session) throw(LUAScript::Exception, Journal::Exception);    void nocommit(Session *session);    void discard(Session *session); diff --git a/server/src/connection.h b/server/src/connection.h index b7d17f4..098c735 100644 --- a/server/src/connection.h +++ b/server/src/connection.h @@ -36,7 +36,8 @@ class Connection {  public:    virtual ~Connection() {} -  virtual bool handle(const char *data, size_t size) = 0; +  virtual bool data(const char *data, size_t size) = 0; +  virtual bool handle() = 0;    virtual void getReply(Httpd::Reply &reply) = 0;  }; diff --git a/server/src/daemon.cc b/server/src/daemon.cc index 938170c..e8e08ee 100644 --- a/server/src/daemon.cc +++ b/server/src/daemon.cc @@ -134,6 +134,10 @@ int Daemon::run(const char *user, const char* group, bool detach,      } else {        fprintf(fp, "%lu", (unsigned long int)pid);        fclose(fp); +      /* +      fprintf(stderr, "Wrote pid %lu to file \"%s\"", +              (unsigned long int)pid, pidfile.c_str()); +      */      }    } diff --git a/server/src/debug.cc b/server/src/debug.cc index fa83a80..e42baae 100644 --- a/server/src/debug.cc +++ b/server/src/debug.cc @@ -34,6 +34,17 @@  #include "log.h" +#include <pthread.h> + +#include "mutex.h" + +static Mutex mutex; + +static unsigned int gettid() +{ +  return (unsigned int)pthread_self(); +} +  static FILE *logfp = stderr;  #define NELEM(x)	(sizeof(x)/sizeof((x)[0])) @@ -69,11 +80,12 @@ int __debug(const char *func, const int line,              const enum __debug_class cl,              const char *ch, const char *fmt, ...)  { +  MutexAutolock m(mutex);  	int ret = 0;  	if(__debug_enabled(cl, ch)) {  		if((unsigned)cl < NELEM(debug_class_str)) -			ret += fprintf(logfp, "%s:%s:%s:%d ", -                     debug_class_str[(unsigned)cl], ch, func, line); +			ret += fprintf(logfp, "%u %s:%s:%s:%d ", +                     gettid(), debug_class_str[(unsigned)cl], ch, func, line);  		if(fmt) {  			va_list va;  			va_start(va, fmt); @@ -91,11 +103,12 @@ int __debug_va(const char *func, const int line,                 const enum __debug_class cl,                 const char *ch, const char *fmt, va_list va)  { +  MutexAutolock m(mutex);  	int ret = 0;  	if(__debug_enabled(cl, ch)) {  		if((unsigned)cl < NELEM(debug_class_str)) -			ret += fprintf(logfp, "%s:%s:%s:%d ", -                     debug_class_str[(unsigned)cl], ch, func, line); +			ret += fprintf(logfp, "%u %s:%s:%s:%d ", +                     gettid(), debug_class_str[(unsigned)cl], ch, func, line);  		if(fmt)  			ret += vfprintf(logfp, fmt, va);  	} @@ -110,6 +123,8 @@ int __debug_va(const char *func, const int line,  int __log(const char *func, const int line, const enum __debug_class cl,            const char *ch, const char *fmt, ...)  { +  MutexAutolock m(mutex); +    std::string logmsg;    char str[8]; @@ -120,7 +135,8 @@ int __log(const char *func, const int line, const enum __debug_class cl,  		if((unsigned)cl < NELEM(debug_class_str))  		if((unsigned)cl < NELEM(debug_class_str))  #ifdef WITH_DEBUG -			ret = fprintf(logfp, "%s:%s:%s:%d ", debug_class_str[(unsigned)cl], ch, func, line); +			ret = fprintf(logfp, "%u %s:%s:%s:%d ", +                    gettid(), debug_class_str[(unsigned)cl], ch, func, line);  #endif        sprintf(str, "%d", line);        logmsg = std::string(debug_class_str[(unsigned)cl]) + ":" + ch + ":" + func + ":" + str; @@ -149,6 +165,8 @@ int __log(const char *func, const int line, const enum __debug_class cl,  int __log_va(const char *func, const int line, const enum __debug_class cl,               const char *ch, const char *fmt, va_list va)  { +  MutexAutolock m(mutex); +    std::string logmsg;    char str[8];  #ifdef WITH_DEBUG @@ -157,7 +175,8 @@ int __log_va(const char *func, const int line, const enum __debug_class cl,  	if(__debug_enabled(cl, ch)) {  		if((unsigned)cl < NELEM(debug_class_str))  #ifdef WITH_DEBUG -			ret = fprintf(logfp, "%s:%s:%s:%d ", debug_class_str[(unsigned)cl], ch, func, line); +			ret = fprintf(logfp, "%u %s:%s:%s:%d ", +                    gettid(), debug_class_str[(unsigned)cl], ch, func, line);  #endif        sprintf(str, "%d", line);        logmsg = std::string(debug_class_str[(unsigned)cl]) + ":" + ch + ":" + func + ":" + str; @@ -181,9 +200,25 @@ int __log_va(const char *func, const int line, const enum __debug_class cl,  void debug_init(FILE *fp)  { +  mutex.name = "debug"; +  MutexAutolock m(mutex);    logfp = fp;  } +void debug_reinit(const char *logfile) +{ +  MutexAutolock m(mutex); + +  if(logfp != stderr) { +    fclose(logfp); +    logfp = fopen(logfile, "a"); +    if(!logfp) { +      fprintf(stderr, "Could not write to logfile: '%s'\n", logfile); +      logfp = stderr; +    } +  } +} +  /*   * fmt := [set[,set]*]* @@ -193,6 +228,7 @@ void debug_init(FILE *fp)   */  void debug_parse(const char *fmt)  { +  MutexAutolock m(mutex);  	char *s;  	char *next;  	char *opt; diff --git a/server/src/debug.h b/server/src/debug.h index a5f199d..838d83f 100644 --- a/server/src/debug.h +++ b/server/src/debug.h @@ -53,6 +53,7 @@  #endif/*HAVE_CONFIG*/  void debug_init(FILE *fp); +void debug_reinit(const char *file);  void debug_parse(const char *fmt);  enum __debug_class diff --git a/server/src/httpd.cc b/server/src/httpd.cc index 1c0575a..9349697 100644 --- a/server/src/httpd.cc +++ b/server/src/httpd.cc @@ -38,9 +38,15 @@  #include <string.h>  #include <microhttpd.h> +#include <time.h>  #include "debug.h" +// for inet_ntop +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +  typedef struct {    void *ptr;    bool firstrun; @@ -68,10 +74,22 @@ static int request_handler(void *cls,                             unsigned int *data_size,                             void **con_cls)  { +  time_t now = time(NULL); +  DEBUG(httpd, "Request time: %s", ctime(&now)); + +  void *so; +  so = MHD_get_connection_info(con, MHD_CONNECTION_INFO_CLIENT_ADDRESS)->client_addr; + +  char peeraddr[INET_ADDRSTRLEN]; +  inet_ntop(AF_INET, &((struct sockaddr_in*)so)->sin_addr, peeraddr, sizeof(peeraddr)); +  DEBUG(httpd, "peer: %s\n", peeraddr); +    DEBUG(httpd, "request_handler: cls(%p) con(%p) url(%s) method(%s)"          " version(%s) *con_cls(%p)\n",          cls, con, url, method, version, *con_cls); -  DEBUG(httpd, "request_handler: *data_size(%u) data:[%s]\n", *data_size, data); +  std::string datastr; datastr.append(data, *data_size); +  DEBUG(httpd, "request_handler: *data_size(%u) data:[%s]\n", +        *data_size, datastr.c_str());    int ret = MHD_YES; diff --git a/server/src/journal.cc b/server/src/journal.cc index fc4203c..04b1459 100644 --- a/server/src/journal.cc +++ b/server/src/journal.cc @@ -32,7 +32,8 @@  Journal::Journal() {}  void Journal::addEntry(Transaction &transaction, Commit &commit, -                       std::string resume, Template *templ) +                       std::string resume, Template *templ, +                       LUAOnCommit *oncommit)  {    size_t index = 0;    std::vector< Macro >::iterator i = templ->macros.begin(); @@ -70,16 +71,17 @@ void Journal::addEntry(Transaction &transaction, Commit &commit,    }  #endif -  addEntry(resume, commit.macro, transaction.user, index); +  addEntry(resume, commit.macro, transaction.user, index, oncommit);  }  void Journal::addEntry(std::string resume, std::string macro, -                       std::string user, int index) +                       std::string user, int index, LUAOnCommit *oncommit)  {    DEBUG(journal, "Add: %p %s %s - %s\n", this, macro.c_str(),          user.c_str(), resume.c_str());    ResumeEntry re; +  re.oncommit = oncommit;    re.resume = resume;    re.macro = macro;    re.user = user; @@ -148,6 +150,22 @@ std::string Journal::patientID()    return _patientid;  } +void Journal::runOnCommitScripts() throw(LUAScript::Exception) +{ +  std::map< int, ResumeEntry >::iterator i = entrylist.begin(); +  while(i != entrylist.end()) { +    if(i->second.oncommit != NULL) { +      try { +        i->second.oncommit->run(); +      } catch(LUAScript::Exception &e) { +        throw e; +      } +    } +    i++; +  } +} + +  #ifdef TEST_JOURNAL  //deps: debug.cc log.cc journal_uploadserver.cc journal_commit.cc  //cflags: -I.. diff --git a/server/src/journal.h b/server/src/journal.h index 573f252..d098756 100644 --- a/server/src/journal.h +++ b/server/src/journal.h @@ -33,22 +33,31 @@  #include "transaction.h"  #include "template.h" +#include "luaoncommit.h"  class SessionSerialiser;  class Journal {    friend class SessionSerialiser;  public: +  class Exception { +  public: +    Exception(std::string m) : msg(m) {} +    std::string msg; +  }; +    Journal();    virtual ~Journal() {}    void addEntry(Transaction &transaction, Commit &commit, -                std::string resume, Template *templ); +                std::string resume, Template *templ, LUAOnCommit *oncommit);    void addEntry(std::string resume, std::string macro, -                std::string user, int index); +                std::string user, int index, LUAOnCommit *oncommit); + +  void runOnCommitScripts() throw(LUAScript::Exception); -  virtual void commit() = 0; +  virtual void commit() throw(Exception) = 0;    std::string getEntry(std::string macro);    void removeEntry(std::string macro); @@ -65,6 +74,7 @@ protected:      std::string resume;      std::string macro;      std::string user; +    LUAOnCommit *oncommit;      bool dirty;    }; diff --git a/server/src/journal_commit.cc b/server/src/journal_commit.cc index 0c1a93d..d765ebb 100644 --- a/server/src/journal_commit.cc +++ b/server/src/journal_commit.cc @@ -132,6 +132,7 @@ int journal_commit(const char *cpr, const char *user,    // send body    if(sock != -1 && write(sock, resume.c_str(), resume.size()) != (ssize_t)resume.size()) {      ERR_LOG(journal, "write did not write all the bytes in the buffer.\n"); +    return -1;    }    DEBUG(journal, "%s\n", buf); diff --git a/server/src/journal_uploadserver.cc b/server/src/journal_uploadserver.cc index a1299ec..eac6cd5 100644 --- a/server/src/journal_uploadserver.cc +++ b/server/src/journal_uploadserver.cc @@ -163,7 +163,10 @@ JournalUploadServer::JournalUploadServer(std::string host,  }  void JournalUploadServer::commit() +  throw(Journal::Exception)  { +  int ret = 0; +  #ifdef USE_MULTIPLE_USERS    std::string resume;    std::string olduser; @@ -177,9 +180,12 @@ void JournalUploadServer::commit()      }      if(i->second.user != olduser && olduser != "" && resume != "") { -      journal_commit(patientID().c_str(), olduser.c_str(), -                     host.c_str(), port, -                     resume.c_str(), resume.size()); +      ret = journal_commit(patientID().c_str(), olduser.c_str(), +                           host.c_str(), port, +                           resume.c_str(), resume.size()); + +      if(ret == -1) throw Journal::Exception("Journal Commit error."); +        // FIXME - UGLY HACK: Avoid upload server spooling in the wrong order.        usleep(200000);        resume = ""; @@ -195,9 +201,9 @@ void JournalUploadServer::commit()    if(resume == "") return; -  journal_commit(patientID().c_str(), olduser.c_str(), -                 host.c_str(), port, -                 resume.c_str(), resume.size()); +  ret = journal_commit(patientID().c_str(), olduser.c_str(), +                       host.c_str(), port, +                       resume.c_str(), resume.size());  #else    std::string resume;    std::string user; @@ -222,10 +228,12 @@ void JournalUploadServer::commit()    if(resume == "") return;    // Connect to praxisuploadserver and commit all resumes in one bulk. -  journal_commit(patientID().c_str(), user.c_str(), -                 host.c_str(), port, -                 resume.c_str(), resume.size()); +  ret = journal_commit(patientID().c_str(), user.c_str(), +                       host.c_str(), port, +                       resume.c_str(), resume.size());  #endif/*USE_MULTIPLE_USERS*/ + +  if(ret == -1) throw Journal::Exception("Journal Commit error.");  } diff --git a/server/src/journal_uploadserver.h b/server/src/journal_uploadserver.h index 2393709..3e2fab3 100644 --- a/server/src/journal_uploadserver.h +++ b/server/src/journal_uploadserver.h @@ -33,7 +33,7 @@  class JournalUploadServer : public Journal {  public:    JournalUploadServer(std::string host, unsigned short int port); -  void commit(); +  void commit() throw(Journal::Exception);  private:    std::string host; diff --git a/server/src/luaoncommit.cc b/server/src/luaoncommit.cc new file mode 100644 index 0000000..8e96066 --- /dev/null +++ b/server/src/luaoncommit.cc @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + *            luaoncommit.cc + * + *  Thu Jan 12 08:38:02 CET 2012 + *  Copyright 2012 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 "luaoncommit.h" + +#include <lua.hpp> +#include <lauxlib.h> + +#include "luautil.h" +#include "luapraxisd.h" + +#include "debug.h" + +#include <stdio.h> + +LUAOnCommit::LUAOnCommit(Transaction &t, Commit &c) : LUAScript() +{ +  setEnv(LUAScript::ENV_PATIENTID, t.patientid); +  setEnv(LUAScript::ENV_TEMPLATE, c.templ); +  setEnv(LUAScript::ENV_MACRO, c.macro); +  setEnv(LUAScript::ENV_USER, t.user); + +  std::map<std::string, std::string>::iterator i = c.fields.begin(); +  while(i != c.fields.end()) { +    addValue(i->first, i->second); +    i++; +  } +} + +#ifdef TEST_LUAONCOMMIT +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_LUAONCOMMIT*/ diff --git a/server/src/resumeparser.h b/server/src/luaoncommit.h index 381e7c6..8db75f8 100644 --- a/server/src/resumeparser.h +++ b/server/src/luaoncommit.h @@ -1,9 +1,10 @@  /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */  /*************************************************************************** - *            resumeparser.h + *            luaoncommit.h   * - *  Mon Oct  1 11:17:35 CEST 2007 - *  Copyright 2007 Bent Bisballe Nyeng + *  Thu Jan 12 08:38:02 CET 2012 + *  Copyright 2012 Bent Bisballe Nyeng   *  deva@aasimon.org   ****************************************************************************/ @@ -24,13 +25,21 @@   *  along with Pracro; if not, write to the Free Software   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.   */ -#ifndef __PRACRO_RESUMEPARSER_H__ -#define __PRACRO_RESUMEPARSER_H__ +#ifndef __PRACRO_LUAONCOMMIT_H__ +#define __PRACRO_LUAONCOMMIT_H__ + +#include "luascript.h" -#include <string>  #include "transaction.h" -#include "template.h" +#include <string> + +class LUAOnCommit : public LUAScript { +public: +  LUAOnCommit() {} +  LUAOnCommit(Transaction &transaction, Commit &commit); +  virtual ~LUAOnCommit() {} -std::string resume_parser(Macro ¯o, Commit &commit); +  const char *name() { return "lua on commit"; } +}; -#endif/*__PRACRO_RESUMEPARSER_H__*/ +#endif/*__PRACRO_LUAONCOMMIT_H__*/ diff --git a/server/src/luapraxisd.cc b/server/src/luapraxisd.cc index 04a83db..da34d32 100644 --- a/server/src/luapraxisd.cc +++ b/server/src/luapraxisd.cc @@ -37,11 +37,14 @@  #define luaL_checkbool(L, i) \    (lua_isboolean(L,i) ? lua_toboolean(L,i) : luaL_checkint(L,i)) +#define error(L, m) \ +  ERR(luapraxisd, "%s\n", m); lua_pushstring(L, m); lua_error(L); +  typedef struct px_userdata {    Praxisd *px;  } px_userdata; -static int px_addcave(lua_State *L) +int px_addcave(lua_State *L)  {    px_userdata *pxu;    pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); @@ -49,64 +52,377 @@ static int px_addcave(lua_State *L)    const char *cpr = luaL_checkstring(L, 2);    const char *cave = luaL_checkstring(L, 3); +  const char *txt = luaL_checkstring(L, 4);    std::string sogeord;    std::string sogetxt; -  std::vector<Praxisd::cave_t> cavelist = pxu->px->diverse_get_cave(""); -  std::vector<Praxisd::cave_t>::iterator i = cavelist.begin(); -  while(i != cavelist.end()) { -    Praxisd::cave_t &c = *i; -    if(strcasecmp(cave, c.cave.c_str()) == 0) { -      pxu->px->add_sogeord(cpr, c.sogenr, ""); -      return 0; +  try { +    std::vector<Praxisd::cave_t> cavelist = pxu->px->diverse_get_cave(""); +    std::vector<Praxisd::cave_t>::iterator i = cavelist.begin(); +    while(i != cavelist.end()) { +      Praxisd::cave_t &c = *i; +      if(strcasecmp(cave, c.cave.c_str()) == 0) { +        pxu->px->add_sogeord(cpr, c.sogenr, txt); +        return 0; +      } +      i++;      } -    i++; + +    std::string text = cave; +    text += " "; +    text += txt; +    pxu->px->add_sogeord(cpr, "CA0003", text.c_str()); // CA0003 == 'ANDET' +  } catch(const char *msg) { +    error(L, msg);    } -  pxu->px->add_sogeord(cpr, "CA0003", cave); // CA0003 == 'ANDET' +  return 0; +} + +int px_addbehandling(lua_State *L) +{ +  px_userdata *pxu; +  pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); +  luaL_argcheck(L, pxu, 1, "Praxisd expected"); + +  const char *cpr = luaL_checkstring(L, 2); +  const char *behandling = luaL_checkstring(L, 3); +  const char *text = luaL_checkstring(L, 4); + +  std::string sogeord; +  std::string sogetxt; + +  try { +    std::vector<Praxisd::behandling_t> bhlst = +      pxu->px->diverse_get_behandling(""); +    std::vector<Praxisd::behandling_t>::iterator i = bhlst.begin(); +    while(i != bhlst.end()) { +      Praxisd::behandling_t &b = *i; +      if(strcasecmp(behandling, b.kode.c_str()) == 0) { +        pxu->px->add_sogeord(cpr, b.sogenr, text); +        return 0; +      } +      i++; +    } +  } catch(const char *msg) { +    error(L, msg); +  }    return 0;  } -static int px_getcave(lua_State *L) +int px_adddiagnose(lua_State *L)  {    px_userdata *pxu;    pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd");    luaL_argcheck(L, pxu, 1, "Praxisd expected");    const char *cpr = luaL_checkstring(L, 2); +  const char *diagnose = luaL_checkstring(L, 3); +  const char *text = luaL_checkstring(L, 4); -  std::vector<Praxisd::cave_t> cavelist; +  std::string sogeord; +  std::string sogetxt; + +  try { +    std::vector<Praxisd::diagnose_t> dialst  = +      pxu->px->diverse_get_diagnose(""); +    std::vector<Praxisd::diagnose_t>::iterator i = dialst.begin(); +    while(i != dialst.end()) { +      Praxisd::diagnose_t &d = *i; +      if(strcasecmp(diagnose, d.kode.c_str()) == 0) { +        pxu->px->add_sogeord(cpr, d.sogenr, text); +        return 0; +      } +      i++; +    } +  } catch(const char *msg) { +    error(L, msg); +  } +     +  return 0; +} + +class cavedata_t : public Praxisd::cave_t { +public: +  std::string sogetxt; +  std::string sogedato; +}; +int px_getcave(lua_State *L) +{ +  px_userdata *pxu; +  pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); +  luaL_argcheck(L, pxu, 1, "Praxisd expected"); + +  const char *cpr = luaL_checkstring(L, 2); + +  std::vector<cavedata_t> cavelist; + +  try { +    Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); +    std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); +    while(i != pat.sogeord.end()) { +      Praxisd::sogeord_t &s = *i; +      if(s.sogenr[0] == 'C') { +        std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); +        std::vector<Praxisd::cave_t> cl = pxu->px->diverse_get_cave(csogenr); +        if(cl.size() == 1) { +          cavedata_t cdata; +          cdata.cave = cl[0].cave; +          cdata.bemaerkning1 = cl[0].bemaerkning1; +          cdata.bemaerkning2 = cl[0].bemaerkning2; +          cdata.bemaerkning3 = cl[0].bemaerkning3; +          cdata.sogetxt = s.sogetxt; +          cdata.sogedato = s.sogedato; +          cavelist.push_back(cdata); +        } +      } +       +      i++; +    } +  } catch(const char *msg) { +    error(L, msg); +  } + +  int num = cavelist.size(); +  int sz = 6; // 4 fields in cave_t + 1 sogetxt and 1 sogedato +   +  lua_createtable(L, num, 0); +  int toptop = lua_gettop(L); + +  for(int i = 1; i <= num; i++) { +    lua_createtable(L, 0, sz); +    int top = lua_gettop(L); + +    for(int j = 1; j <= sz; j++) { +      if(j == 1) lua_pushstring(L, cavelist[i - 1].cave.c_str()); +      if(j == 2) lua_pushstring(L, cavelist[i - 1].bemaerkning1.c_str()); +      if(j == 3) lua_pushstring(L, cavelist[i - 1].bemaerkning2.c_str()); +      if(j == 4) lua_pushstring(L, cavelist[i - 1].bemaerkning3.c_str()); +      if(j == 5) lua_pushstring(L, cavelist[i - 1].sogetxt.c_str()); +      if(j == 6) lua_pushstring(L, cavelist[i - 1].sogedato.c_str()); +      lua_rawseti(L, top, j); +    } + +    lua_rawseti(L, toptop, i); +  } + +  return 1; +} + +class behandlingdata_t : public Praxisd::behandling_t { +public: +  std::string sogetxt; +  std::string sogedato; +}; +int px_getbehandling(lua_State *L) +{ +  px_userdata *pxu; +  pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); +  luaL_argcheck(L, pxu, 1, "Praxisd expected"); + +  const char *cpr = luaL_checkstring(L, 2); + +  std::vector<behandlingdata_t> behandlinglist; + +  try { +    Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); +    std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); +    while(i != pat.sogeord.end()) { +      Praxisd::sogeord_t &s = *i; +      if(s.sogenr[0] == 'B') { +        std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); +        std::vector<Praxisd::behandling_t> cl = pxu->px->diverse_get_behandling(csogenr); +        if(cl.size() == 1) { +          behandlingdata_t cdata; +          cdata.kode = cl[0].kode; +          cdata.behandling = cl[0].behandling; +          cdata.bemaerkning = cl[0].bemaerkning; +          cdata.udregning = cl[0].udregning; +          cdata.sogetxt = s.sogetxt; +          cdata.sogedato = s.sogedato; +          behandlinglist.push_back(cdata); +        } +      } +       +      i++; +    } +  } catch(const char *msg) { +    error(L, msg); +  } + +  int num = behandlinglist.size(); +  int sz = 6; // 4 fields in behandling_t + 1 sogetxt and 1 sogedato +   +  lua_createtable(L, num, 0); +  int toptop = lua_gettop(L); + +  for(int i = 1; i <= num; i++) { +    lua_createtable(L, 0, sz); +    int top = lua_gettop(L); + +    for(int j = 1; j <= sz; j++) { +      if(j == 1) lua_pushstring(L, behandlinglist[i - 1].kode.c_str()); +      if(j == 2) lua_pushstring(L, behandlinglist[i - 1].behandling.c_str()); +      if(j == 3) lua_pushstring(L, behandlinglist[i - 1].bemaerkning.c_str()); +      if(j == 4) lua_pushstring(L, behandlinglist[i - 1].udregning.c_str()); +      if(j == 5) lua_pushstring(L, behandlinglist[i - 1].sogetxt.c_str()); +      if(j == 6) lua_pushstring(L, behandlinglist[i - 1].sogedato.c_str()); +      lua_rawseti(L, top, j); +    } + +    lua_rawseti(L, toptop, i); +  } + +  return 1; +} + +class diagnosedata_t : public Praxisd::diagnose_t { +public: +  std::string sogetxt; +  std::string sogedato; +}; +int px_getdiagnose(lua_State *L) +{ +  px_userdata *pxu; +  pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); +  luaL_argcheck(L, pxu, 1, "Praxisd expected"); -  Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); -  std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); -  while(i != pat.sogeord.end()) { -    Praxisd::sogeord_t &s = *i; -    if(s.sogenr[0] == 'C') { -      std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); -      std::vector<Praxisd::cave_t> cl = pxu->px->diverse_get_cave(csogenr); -      if(cl.size() == 1) { -        if(cl[0].cave == "ANDET") cl[0].cave = s.sogetxt; -        cavelist.push_back(cl[0]); +  const char *cpr = luaL_checkstring(L, 2); + +  std::vector<diagnosedata_t> diagnoselist; + +  try { +    Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); +    std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); +    while(i != pat.sogeord.end()) { +      Praxisd::sogeord_t &s = *i; +      if(s.sogenr[0] == 'D') { +        std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); +        std::vector<Praxisd::diagnose_t> cl = pxu->px->diverse_get_diagnose(csogenr); +        if(cl.size() == 1) { +          diagnosedata_t cdata; +          cdata.kode = cl[0].kode; +          cdata.diagnose = cl[0].diagnose; +          cdata.bemaerkning = cl[0].bemaerkning; +          cdata.sogetxt = s.sogetxt; +          cdata.sogedato = s.sogedato; +          diagnoselist.push_back(cdata); +        }        } +       +      i++; +    } +  } catch(const char *msg) { +    error(L, msg); +  } + +  int num = diagnoselist.size(); +  int sz = 5; // 3 fields in diagnose_t + 1 sogetxt and 1 sogedato +   +  lua_createtable(L, num, 0); +  int toptop = lua_gettop(L); + +  for(int i = 1; i <= num; i++) { +    lua_createtable(L, 0, sz); +    int top = lua_gettop(L); + +    for(int j = 1; j <= sz; j++) { +      if(j == 1) lua_pushstring(L, diagnoselist[i - 1].kode.c_str()); +      if(j == 2) lua_pushstring(L, diagnoselist[i - 1].diagnose.c_str()); +      if(j == 3) lua_pushstring(L, diagnoselist[i - 1].bemaerkning.c_str()); +      if(j == 4) lua_pushstring(L, diagnoselist[i - 1].sogetxt.c_str()); +      if(j == 5) lua_pushstring(L, diagnoselist[i - 1].sogedato.c_str()); +      lua_rawseti(L, top, j); +    } + +    lua_rawseti(L, toptop, i); +  } + +  return 1; +} + +int px_cavelist(lua_State *L) +{ +  px_userdata *pxu; +  pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); +  luaL_argcheck(L, pxu, 1, "Praxisd expected"); + +  const char *sogenr = luaL_checkstring(L, 2); + +  std::vector<Praxisd::cave_t> cavelist; +  try { +    cavelist = pxu->px->diverse_get_cave(sogenr); +  } catch(const char *msg) { +    error(L, msg); +  } + + +  int num = cavelist.size(); +  int sz = 4; // 4 fields in cave_t +   +  lua_createtable(L, num, 0); +  int toptop = lua_gettop(L); + +  for(int i = 1; i <= num; i++) { +    lua_createtable(L, 0, sz); +    int top = lua_gettop(L); + +    for(int j = 1; j <= sz; j++) { +      if(j == 1) lua_pushstring(L, cavelist[i - 1].cave.c_str()); +      if(j == 2) lua_pushstring(L, cavelist[i - 1].bemaerkning1.c_str()); +      if(j == 3) lua_pushstring(L, cavelist[i - 1].bemaerkning2.c_str()); +      if(j == 4) lua_pushstring(L, cavelist[i - 1].bemaerkning3.c_str()); +      lua_rawseti(L, top, j);      } -    i++; +    lua_rawseti(L, toptop, i); +  } + +  return 1; +} + +int px_behandlinglist(lua_State *L) +{ +  px_userdata *pxu; +  pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); +  luaL_argcheck(L, pxu, 1, "Praxisd expected"); + +  const char *sogenr = luaL_checkstring(L, 2); + +  std::vector<Praxisd::behandling_t> behandlinglist; +  try { +    behandlinglist = pxu->px->diverse_get_behandling(sogenr); +  } catch(const char *msg) { +    error(L, msg);    } -  lua_createtable(L, 0, cavelist.size()); -  int top = lua_gettop(L); +  int num = behandlinglist.size(); +  int sz = 4; // 4 fields in behandling_t +   +  lua_createtable(L, num, 0); +  int toptop = lua_gettop(L); + +  for(int i = 1; i <= num; i++) { +    lua_createtable(L, 0, sz); +    int top = lua_gettop(L); + +    for(int j = 1; j <= sz; j++) { +      if(j == 1) lua_pushstring(L, behandlinglist[i - 1].kode.c_str()); +      if(j == 2) lua_pushstring(L, behandlinglist[i - 1].behandling.c_str()); +      if(j == 3) lua_pushstring(L, behandlinglist[i - 1].bemaerkning.c_str()); +      if(j == 4) lua_pushstring(L, behandlinglist[i - 1].udregning.c_str()); +      lua_rawseti(L, top, j); +    } -  for(size_t i = 0; i < cavelist.size(); i++) { -    lua_pushstring(L, cavelist[i].cave.c_str()); -    lua_rawseti(L, top, i); +    lua_rawseti(L, toptop, i);    }    return 1;  } -static int px_cavelist(lua_State *L) +int px_diagnoselist(lua_State *L)  {    px_userdata *pxu;    pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); @@ -114,20 +430,37 @@ static int px_cavelist(lua_State *L)    const char *sogenr = luaL_checkstring(L, 2); -  std::vector<Praxisd::cave_t> cavelist = pxu->px->diverse_get_cave(sogenr); +  std::vector<Praxisd::diagnose_t> diagnoselist; +  try { +    diagnoselist = pxu->px->diverse_get_diagnose(sogenr); +  } catch(const char *msg) { +    error(L, msg); +  } -  lua_createtable(L, 0, cavelist.size()); -  int top = lua_gettop(L); +  int num = diagnoselist.size(); +  int sz = 3; // 3 fields in diagnose_t +   +  lua_createtable(L, num, 0); +  int toptop = lua_gettop(L); + +  for(int i = 1; i <= num; i++) { +    lua_createtable(L, 0, sz); +    int top = lua_gettop(L); + +    for(int j = 1; j <= sz; j++) { +      if(j == 1) lua_pushstring(L, diagnoselist[i - 1].kode.c_str()); +      if(j == 2) lua_pushstring(L, diagnoselist[i - 1].diagnose.c_str()); +      if(j == 3) lua_pushstring(L, diagnoselist[i - 1].bemaerkning.c_str()); +      lua_rawseti(L, top, j); +    } -  for(size_t i = 0; i < cavelist.size(); i++) { -    lua_pushstring(L, cavelist[i].cave.c_str()); -    lua_rawseti(L, top, i); +    lua_rawseti(L, toptop, i);    }    return 1;  } -static int px_new(lua_State *L) +int px_new(lua_State *L)  {    const char *host = luaL_checkstring(L, 1);    int port = (int)luaL_checknumber(L, 2); @@ -138,12 +471,16 @@ static int px_new(lua_State *L)    luaL_getmetatable(L, "Praxisd");    lua_setmetatable(L, -2); -  pxu->px = new Praxisd(host, port); +  try { +    pxu->px = new Praxisd(host, port); +  } catch(const char *msg) { +    error(L, msg); +  }    return 1;  } -static int px_gc(lua_State *L) +int px_gc(lua_State *L)  {    px_userdata *pxu; @@ -155,19 +492,6 @@ static int px_gc(lua_State *L)    return 0;  } -static const struct luaL_Reg px_meths[] = { -  {"__gc" ,px_gc}, -  {"cavelist", px_cavelist}, -  {"getcave", px_getcave}, -  {"addcave", px_addcave}, -  {NULL, NULL} -}; - -static const struct luaL_reg px_funcs[] = { -  {"new", px_new}, -  {NULL, NULL} -}; -  void register_praxisd(lua_State *L)  {    luaL_newmetatable(L, "Praxisd"); @@ -179,7 +503,7 @@ void register_praxisd(lua_State *L)  }  #ifdef TEST_LUAPRAXISD -//deps: praxisd.cc debug.cc saxparser.cc log.cc +//deps: praxisd.cc debug.cc saxparser.cc log.cc mutex.cc  //cflags: -I.. $(LUA_CFLAGS) $(CURL_CFLAGS) $(EXPAT_CFLAGS)  //libs: $(LUA_LIBS) $(CURL_LIBS) $(EXPAT_LIBS)  #include "test.h" @@ -198,29 +522,36 @@ register_praxisd(L);  /////  std::string program =    "px = Praxisd.new('localhost', 10000)\n" -  "cl = px:cavelist('')\n" -  "for i=1,#cl do\n" -  "  print(cl[i])\n" -  "end\n" +  "--cl = px:cavelist('')\n" +  "--for i=1,#cl do\n" +  "--  print(cl[i])\n" +  "--end\n"    "\n" -  "--pcl = px:addcave('1505050505', 'LUMIGAN')\n" +  "pcl = px:addcave('1505050505', 'AZOPT', 'test')\n"    "\n" -  "pcl = px:addcave('1505050505', os.date('%H%M%S'))\n" +  "--pcl = px:addcave('1505050505', os.date('%H%M%S'))\n"    "\n" -  "pcl = px:getcave('1505050505')\n" -  "for i=1,#pcl do\n" -  "  print('p: ' .. pcl[i])\n" -  "end\n" +  "--pcl = px:getbehandling('1505050505')\n" +  "--print('#pcl: ' .. #pcl)\n" +  "--for i=1,#pcl do\n" +  "--  pc = pcl[i]\n" +  "--  print('#pc: ' .. #pc)\n" +  "--  for j=1,#pc do\n" +  "--    print('  pc: ' .. j .. ': ' .. pc[j])\n" +  "--  end\n" +  "--end\n"    ;  //int top = lua_gettop(L);  if(luaL_loadbuffer(L, program.c_str(), program.size(), "luapraxisd test")) {    ERR(luaresume, "loadbufer: %s\n", lua_tostring(L, lua_gettop(L))); +  printf("loadbuffer error: %s", lua_tostring(L, lua_gettop(L)));  }  if(lua_pcall(L, 0, LUA_MULTRET, 0)) {    ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); +  printf("pcall error: %s", lua_tostring(L, lua_gettop(L)));  }  /*  if(top != lua_gettop(L) - 1) { diff --git a/server/src/luapraxisd.h b/server/src/luapraxisd.h index 2ab9ab5..82445e1 100644 --- a/server/src/luapraxisd.h +++ b/server/src/luapraxisd.h @@ -30,6 +30,221 @@  #include <lua.hpp> +/*** + * Praxisd Class + * @class Praxisd + * @serverside + * @clientside + * The praxisd class connects and handles communication with the praxisd + * daemon process. + */ + +/*** + * @method object Praxisd.new(string host, int port) + * Create a new Praxisd connection object. + * @param host The hostname to connect to. + * @param port The port number to connect to. + * @return The newly created communication object. + * @example Create a new praxisd object: + * px = Praxisd.new('localhost', 10000) + */ +int px_new(lua_State *L); + +/*** + * @method stringlist-list praxisd:cavelist() + * Get server cave list. + * To retrieve cavelist for a patient see @see praxisd:getcave(). + * @return The complete list of known (possible) cave from the server. + * The returned stringlist are indexed as follows: 1: cave - 2: bemaerkning1 - + * 3: bemaerkning2 - 4: bemaerkning3 + * @example Create a new praxisd object, get and print cavelist: + * px = Praxisd.new('localhost', 10000) + * lst = px:cavelist() + * for i=0,#lst do + *   print('cave: ' .. lst[i][1]) + *   print('bemaerkning1: ' .. lst[i][2]) + *   print('bemaerkning2: ' .. lst[i][3]) + *   print('bemaerkning3: ' .. lst[i][4]) + * end + */ +int px_cavelist(lua_State *L); + +/*** + * @method stringlist-list praxisd:behandlinglist() + * Get server behandling list. + * To retrieve behandlinglist for a patient see @see praxisd:getbehandling(). + * @return The complete list of known (possible) behandling from the server. + * The returned stringlist are indexed as follows: 1: kode - 2: behandling - + * 3: bemaerkning - 4: udregning. + * @example Create a new praxisd object, get and print behandlinglist: + * px = Praxisd.new('localhost', 10000) + * lst = px:behandlinglist() + * for i=0,#lst do + *   print('kode: ' .. lst[i][1]) + *   print('behandling: ' .. lst[i][2]) + *   print('bemaerkning: ' .. lst[i][3]) + *   print('udregning: ' .. lst[i][4]) + * end + */ +int px_behandlinglist(lua_State *L); + +/*** + * @method stringlist-list praxisd:diagnoselist() + * Get server diagnose list. + * To retrieve diagnoselist for a patient see @see praxisd:getdiagnose(). + * @return The complete list of known (possible) diagnose from the server. + * The returned stringlist are indexed as follows: 1: kode - 2: diagnose - + * 3: bemaerkning. + * @example Create a new praxisd object, get and print diagnoselist: + * px = Praxisd.new('localhost', 10000) + * lst = px:diagnoselist() + * for i=0,#lst do + *   print('kode: ' .. lst[i][1]) + *   print('diagnose: ' .. lst[i][2]) + *   print('bemaerkning: ' .. lst[i][3]) + * end + */ +int px_diagnoselist(lua_State *L); + +/*** + * @method stringlist-list praxisd:getcave(string patientid) + * Get cave list from a patient. + * To retrieve cavelist from the server see @see praxisd:cavelist(). To add cave + * to a patient see @see praxisd:addcave(). + * @param patientid A string containing the patientid. + * @return The list cave registered with the patient. + * The returned stringlist are indexed as follows: 1: cave - 2: bemaerkning1 - + * 3: bemaerkning2 - 4: bemaerkning3 - 5: sogetxt - 6: sogedato. + * @example Create a new praxisd object, get and print cavelist: + * px = Praxisd.new('localhost', 10000) + * lst = px:getcave('1234567890') + * for i=0,#lst do + *   print('cave: ' .. lst[i][1]) + *   print('bemaerkning1: ' .. lst[i][2]) + *   print('bemaerkning2: ' .. lst[i][3]) + *   print('bemaerkning3: ' .. lst[i][4]) + *   print('sogetxt: ' .. lst[i][5]) + *   print('sogedato: ' .. lst[i][6]) + * end + */ +int px_getcave(lua_State *L); + +/*** + * @method stringlist-list praxisd:getbehandling(string patientid) + * Get behandling list from a patient. + * To retrieve behandlinglist from the server see @see praxisd:behandlinglist(). To add behandling + * to a patient see @see praxisd:addbehandling(). + * @param patientid A string containing the patientid. + * @return The list behandling registered with the patient. + * The returned stringlist are indexed as follows: 1: kode - 2: behandling - + * 3: bemaerkning - 4: udregning - 5: sogetxt - 6: sogedato. + * @example Create a new praxisd object, get and print behandlinglist: + * px = Praxisd.new('localhost', 10000) + * lst = px:getbehandling('1234567890') + * for i=0,#lst do + *   print('kode: ' .. lst[i][1]) + *   print('behandling: ' .. lst[i][2]) + *   print('bemaerkning: ' .. lst[i][3]) + *   print('udregning: ' .. lst[i][4]) + *   print('sogetxt: ' .. lst[i][5]) + *   print('sogedato: ' .. lst[i][6]) + * end + */ +int px_getbehandling(lua_State *L); + +/*** + * @method stringlist-list praxisd:getdiagnose(string patientid) + * Get diagnose list from a patient. + * To retrieve diagnoselist from the server see @see praxisd:diagnoselist(). To add diagnose + * to a patient see @see praxisd:adddiagnose(). + * @param patientid A string containing the patientid. + * @return The list diagnose registered with the patient. + * The returned stringlist are indexed as follows: 1: kode - 2: diagnose - + * 3: bemaerkning - 4: sogetxt - 5: sogedato. + * @example Create a new praxisd object, get and print diagnoselist: + * px = Praxisd.new('localhost', 10000) + * lst = px:getdiagnose('1234567890') + * for i=0,#lst do + *   print('kode: ' .. lst[i][1]) + *   print('diagnose: ' .. lst[i][2]) + *   print('bemaerkning: ' .. lst[i][3]) + *   print('sogetxt: ' .. lst[i][4]) + *   print('sogedato: ' .. lst[i][5]) + * end + */ +int px_getdiagnose(lua_State *L); + +/*** + * @method nil praxisd:addcave(string patientid, string cave, string text) + * Add a cave entry to a patient. To retrieve list of cave from a patient + * see @see praxisd:getcave(). + * NOTE: This function is only available on the server. + * @param patientid A string containing the patientid. + * @param diagnose The cave string. + * @param text A text to store with the cave entry. NOTE: This is not shown in + * PCPraxis! The string mat be up to 6 characters long. If longer it will be + * trunkated. + * @example Create a new praxisd object and add a cave entry: + * px = Praxisd.new('localhost', 10000) + * px:addcave('1234567890', 'AZOPT', '') + */ +int px_addcave(lua_State *L); + +/*** + * @method nil praxisd:addbehandling(string patientid, string behandling, string text) + * Add a behandling to a patient. To retrieve list of behandling from a patient + * see @see praxisd:getbehandling(). + * NOTE: This function is only available on the server. + * @param patientid A string containing the patientid. + * @param diagnose The behandling code. + * @param text A text to store with the behandling code. The string mat be up to 6 + * characters long. If longer it will be trunkated. + * @example Create a new praxisd object and add a behandling: + * px = Praxisd.new('localhost', 10000) + * px:addbehandling('1234567890', 'B0001', 'o.sin') + */ +int px_addbehandling(lua_State *L); + +/*** + * @method nil praxisd:adddiagnose(string patientid, string diagnose, string text) + * Add a diagnose to a patient. To retrieve list of diagnose from a patient + * see @see praxisd:getdiagnose(). + * NOTE: This function is only available on the server. + * @param patientid A string containing the patientid. + * @param diagnose The diagnose code. + * @param text A text to store with the diagnose code. The string mat be up to 6 + * characters long. If longer it will be trunkated. + * @example Create a new praxisd object and add a diagnose: + * px = Praxisd.new('localhost', 10000) + * px:adddiagnose('1234567890', 'C0001', 'o.dxt') + */ +int px_adddiagnose(lua_State *L); + +/*** + * @method nil __gc() + * Garbage collector. Closes connection and frees all allocated memory. + */ +int px_gc(lua_State *L); +  void register_praxisd(lua_State *L); +const struct luaL_Reg px_meths[] = { +  {"__gc", px_gc}, +  {"cavelist", px_cavelist}, +  {"behandliglist", px_behandlinglist}, +  {"diagnoselist", px_diagnoselist}, +  {"getcave", px_getcave}, +  {"getbehandling", px_getbehandling}, +  {"getdiagnose", px_getdiagnose}, +  {"addcave", px_addcave}, +  {"addbehandling", px_addbehandling}, +  {"adddiagnose", px_adddiagnose}, +  {NULL, NULL} +}; + +const struct luaL_reg px_funcs[] = { +  {"new", px_new}, +  {NULL, NULL} +}; +  #endif/*__PRACRO_LUAPRAXISD_H__*/ diff --git a/server/src/luaquerymapper.cc b/server/src/luaquerymapper.cc index 27dc21f..56ea1e9 100644 --- a/server/src/luaquerymapper.cc +++ b/server/src/luaquerymapper.cc @@ -109,9 +109,53 @@ LUAQueryMapper::~LUAQueryMapper()    if(L) lua_close(L);  } +static QueryResult splitgroups(QueryResult r) +{ +  QueryResult result; + +  result.timestamp = r.timestamp; +  result.source = r.source; + +  std::map< std::string, QueryResult >::iterator gi = r.groups.begin(); +  while(gi != r.groups.end()) { +    QueryResult child = splitgroups(gi->second); +    result.groups[gi->first] = child; +    gi++; +  } + +  std::map< std::string, std::string >::iterator vi = r.values.begin(); +  while(vi != r.values.end()) { +    std::string name = vi->first; +     +    // Also insert in table with name containing '.'. +    if(name.find(".") != std::string::npos) result.values[name] = vi->second; + +    QueryResult *ncurrent = &result; +    while(name.find(".") != std::string::npos) { +      DEBUG(splitgroups, "value name: %s\n", name.c_str()); +      QueryResult grp; +      grp.timestamp = ncurrent->timestamp; +      grp.source = ncurrent->source; +      std::string grpname = name.substr(0, name.find(".")); +      ncurrent->groups[grpname] = grp; +      ncurrent = &(ncurrent->groups[grpname]); +      name = name.substr(name.find(".") + 1); +    } +    ncurrent->values[name] = vi->second; + +    vi++; +  } +   +  return result; +} + +  void LUAQueryMapper::addQueryResult(QueryResult &result) throw(Exception)  { -  loadResult(L, result); +  // Check for '.' in names and further split up into groups. +  QueryResult splitted = splitgroups(result); + +  loadResult(L, splitted);    clean_top = lua_gettop(L);  } diff --git a/server/src/luaresume.cc b/server/src/luaresume.cc index d3597ad..1db4fb3 100644 --- a/server/src/luaresume.cc +++ b/server/src/luaresume.cc @@ -26,122 +26,27 @@   */  #include "luaresume.h" +#include <lua.hpp> +#include <lauxlib.h> +  #include "luautil.h" -#include "luapraxisd.h"  #include "debug.h"  #include <stdio.h> -#define GLOBAL_POINTER "_pracroGlobalLUAObjectPointerThisShouldBeANameThatIsNotAccidentallyOverwritten" - -static int _value(lua_State *L) -{ -  Pracro::checkParameters(L, -                          Pracro::T_STRING, -                          Pracro::T_END); - -  std::string name = lua_tostring(L, lua_gettop(L)); - -  lua_getglobal(L, GLOBAL_POINTER); -  LUAResume *lua = (LUAResume*)lua_touserdata(L, lua_gettop(L)); - -  if(!lua) { -    lua_pushstring(L, "No LUA pointer!"); -    lua_error(L); -    return 1; -  } - -  std::string value = lua->value(name); -  lua_pushstring(L, value.c_str()); - -  return 1; -} - -LUAResume::LUAResume(Commit &c) -  : commit(c) -{ -  L = luaL_newstate(); -  if(L == NULL) { -    ERR(luaresume, "Could not create LUA state.\n"); -    return; -  } - -  luaL_openlibs(L);                - -  lua_pushlightuserdata(L, this); // Push the pointer to 'this' instance -  lua_setglobal(L, GLOBAL_POINTER); // Assign it to a global lua var. - -  lua_register(L, "value", _value); - -  register_praxisd(L); -} - -LUAResume::~LUAResume() -{ -  lua_close(L); -} - -std::string LUAResume::value(std::string name) +LUAResume::LUAResume(Transaction &t, Commit &c) : LUAScript()  { -  if(commit.fields.find(name) == commit.fields.end()) { -    ERR(luaresume, "LUAResume: No such field '%s'\n", name.c_str()); -    return ""; +  setEnv(LUAScript::ENV_PATIENTID, t.patientid); +  setEnv(LUAScript::ENV_TEMPLATE, c.templ); +  setEnv(LUAScript::ENV_MACRO, c.macro); +  setEnv(LUAScript::ENV_USER, t.user); + +  std::map<std::string, std::string>::iterator i = c.fields.begin(); +  while(i != c.fields.end()) { +    addValue(i->first, i->second); +    i++;    } - -  return commit.fields[name]; -} - -std::string LUAResume::run(std::string program) -{ -  if(L == NULL) { -    ERR(luaresume, "LUA state not initialized!"); -    return false; -  } - -  DEBUG(luaresume, "Running %s\n", program.c_str()); - -  /* -  lua_pushstring(L, value.toStdString().c_str()); -  lua_setglobal(L, "value"); - -  lua_pushstring(L, name.toStdString().c_str()); -  lua_setglobal(L, "name"); -  */ - -  int top = lua_gettop(L); - -  if(luaL_loadbuffer(L, program.c_str(), program.size(), -                     "lua resume generator")) { -    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)) { -    ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); -    return false; -  } - -  if(top != lua_gettop(L) - 1) { -    ERR(luaresume, "Program did not return a single value.\n"); -    return false; -  } - -  if(lua_isstring(L, lua_gettop(L)) == false) { -    ERR(luaresume, "Program did not return a string value.\n"); -    return false; -  } - -  std::string res = lua_tostring(L, lua_gettop(L)); -  lua_pop(L, 1); - -  return res; -} - -void LUAResume::error(std::string message) -{ -  ERR(luaresume, "LUA ERROR: %s\n", message.c_str());  }  #ifdef TEST_LUARESUME diff --git a/server/src/luaresume.h b/server/src/luaresume.h index 1132f26..47a7652 100644 --- a/server/src/luaresume.h +++ b/server/src/luaresume.h @@ -27,26 +27,16 @@  #ifndef __PRACRO_LUARESUME_H__  #define __PRACRO_LUARESUME_H__ -#include <lua.hpp> -#include <lauxlib.h> +#include "luascript.h"  #include "transaction.h"  #include <string> -class LUAResume { +class LUAResume : public LUAScript {  public: -  LUAResume(Commit &commit); -  ~LUAResume(); -   -  std::string run(std::string program); +  LUAResume(Transaction &transaction, Commit &commit); -  std::string value(std::string name); - -  void error(std::string message); - -private: -  lua_State *L; -  Commit &commit; +  const char *name() { return "lua resume generator"; }  };  #endif/*__PRACRO_LUARESUME_H__*/ diff --git a/server/src/luascript.cc b/server/src/luascript.cc new file mode 100644 index 0000000..6ba52a9 --- /dev/null +++ b/server/src/luascript.cc @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + *            luascript.cc + * + *  Tue Jan 10 14:43:39 CET 2012 + *  Copyright 2012 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 "luascript.h" + +#include "configuration.h" +#include "debug.h" + +#include "luautil.h" +#include "luapraxisd.h" + +#define GLOBAL_POINTER "_pracroGlobalLUAObjectPointerThisShouldBeANameThatIsNotAccidentallyOverwritten" + +static int _debug(lua_State *L) +{ +  Pracro::checkParameters(L, +                          Pracro::T_STRING, +                          Pracro::T_END); + +  std::string msg = lua_tostring(L, lua_gettop(L)); + +  DEBUG(luascript, "%s\n", msg.c_str()); + +  return 0; +} + +static int _value(lua_State *L) +{ +  Pracro::checkParameters(L, +                          Pracro::T_STRING, +                          Pracro::T_END); + +  std::string name = lua_tostring(L, lua_gettop(L)); + +  lua_getglobal(L, GLOBAL_POINTER); +  LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + +  if(!lua) { +    lua_pushstring(L, "No LUA pointer!"); +    lua_error(L); +    return 1; +  } + +  if(lua->hasValue(name)) { +    lua_pushstring(L, lua->value(name).c_str()); +  } else { +    lua_pushnil(L); +  } + +  return 1; +} + +static int _patientid(lua_State *L) +{ +  Pracro::checkParameters(L, Pracro::T_END); + +  lua_getglobal(L, GLOBAL_POINTER); +  LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + +  if(!lua) { +    lua_pushstring(L, "No LUA pointer!"); +    lua_error(L); +    return 1; +  } + +  lua_pushstring(L, lua->env(LUAScript::ENV_PATIENTID).c_str()); + +  return 1; +} + +static int _template(lua_State *L) +{ +  Pracro::checkParameters(L, Pracro::T_END); + +  lua_getglobal(L, GLOBAL_POINTER); +  LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + +  if(!lua) { +    lua_pushstring(L, "No LUA pointer!"); +    lua_error(L); +    return 1; +  } + +  lua_pushstring(L, lua->env(LUAScript::ENV_TEMPLATE).c_str()); + +  return 1; +} + +static int _macro(lua_State *L) +{ +  Pracro::checkParameters(L, Pracro::T_END); + +  lua_getglobal(L, GLOBAL_POINTER); +  LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + +  if(!lua) { +    lua_pushstring(L, "No LUA pointer!"); +    lua_error(L); +    return 1; +  } + +  lua_pushstring(L, lua->env(LUAScript::ENV_MACRO).c_str()); + +  return 1; +} + +static int _user(lua_State *L) +{ +  Pracro::checkParameters(L, Pracro::T_END); + +  lua_getglobal(L, GLOBAL_POINTER); +  LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + +  if(!lua) { +    lua_pushstring(L, "No LUA pointer!"); +    lua_error(L); +    return 1; +  } + +  lua_pushstring(L, lua->env(LUAScript::ENV_USER).c_str()); + +  return 1; +} + +LUAScript::LUAScript() +{ +  L = NULL; +} + +void LUAScript::init() +  throw(Exception) +{ +  if(L) return; + +  L = luaL_newstate(); +  if(L == NULL) { +    ERR(luascript, "Could not create LUA state.\n"); +    throw Exception("Could not create LUA state."); +  } + +  luaL_openlibs(L);                + +  lua_pushlightuserdata(L, this); // Push the pointer to 'this' instance +  lua_setglobal(L, GLOBAL_POINTER); // Assign it to a global lua var. + +  lua_register(L, "value", _value); +  lua_register(L, "patientid", _patientid); +  lua_register(L, "template", _template); +  lua_register(L, "macro", _macro); +  lua_register(L, "user", _user); +  lua_register(L, "debug", _debug); + +  register_praxisd(L); +} + +void LUAScript::addFile(std::string src) +{ +  std::string file = +    Conf::xml_basedir + "/include/" + src; +  FILE *fp = fopen(file.c_str(), "r"); +  if(fp) { +    char buf[64]; +    size_t sz; +    std::string inc; +    while((sz = fread(buf, 1, sizeof(buf), fp)) != 0) { +      inc.append(buf, sz); +    } +    fclose(fp); +    addCode(inc, file); +  } +} + +void LUAScript::addCode(std::string c, std::string name) +{ +  scripts.push_back(std::make_pair(c, name)); +} + +void LUAScript::addValue(std::string name, const std::string &value) +{ +  values[name] = value; +} + +void LUAScript::addScripts(std::vector< Script > &scripts) +{ +  std::vector< Script >::iterator spi = scripts.begin(); +  while(spi != scripts.end()) { +    if(spi->attributes.find("src") != spi->attributes.end()) { +      std::string src = spi->attributes["src"]; +      addFile(src); +    } else { +      addCode(spi->code); +    } +    spi++; +  } +} + +void LUAScript::run() +  throw(Exception) +{ +  try { +    init(); +  } catch(Exception &e) { +    throw Exception(e.msg); +  } + +  if(L == NULL) { +    ERR(luascript, "LUA state not initialized!"); +    return; +  } + +  top = lua_gettop(L); + +  std::vector<std::pair<std::string, std::string> >::iterator i = +    scripts.begin(); +  while(i != scripts.end()) { +    std::string program = i->first; +    std::string codename = name(); +    if(i->second != "") codename += ": " + i->second; + +    DEBUG(luascript, "Running %s: %s\n", codename.c_str(), program.c_str()); + +    if(luaL_loadbuffer(L, program.c_str(), program.size(), codename.c_str())) { +      ERR(luascript, "loadbuffer: %s\n", lua_tostring(L, lua_gettop(L))); +      throw Exception(lua_tostring(L, lua_gettop(L))); +    } +     +    // Run the loaded code +    if(lua_pcall(L, 0, LUA_MULTRET, 0)) { +      ERR(luascript, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); +      throw Exception(lua_tostring(L, lua_gettop(L))); +    } + +    i++; +  } +} + +std::string LUAScript::resultString() throw(Exception) +{ +  if(top != lua_gettop(L) - 1) { +    ERR(luascript, "Program did not return a single value.\n"); +    throw Exception("Program did not return a single value."); +  } + +  if(lua_isstring(L, lua_gettop(L)) == false) { +    ERR(luascript, "Program did not return a string value.\n"); +    throw Exception("Program did not return a string value."); +  } + +  std::string res = lua_tostring(L, lua_gettop(L)); +  lua_pop(L, 1); + +  return res; +} + +bool LUAScript::hasValue(std::string name) +{ +  return values.find(name) != values.end(); +} + +std::string LUAScript::value(std::string name) +{ +  if(values.find(name) != values.end()) return values[name]; +  return ""; +} + +std::string LUAScript::env(LUAScript::env_t id) +{ +  if(_env.find(id) == _env.end()) return ""; +  return _env[id]; +} + +void LUAScript::setEnv(LUAScript::env_t id, std::string value) +{ +  _env[id] = value; +} + +#ifdef TEST_LUASCRIPT +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_LUASCRIPT*/ diff --git a/server/src/luascript.h b/server/src/luascript.h new file mode 100644 index 0000000..11fb77d --- /dev/null +++ b/server/src/luascript.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + *            luascript.h + * + *  Tue Jan 10 14:43:39 CET 2012 + *  Copyright 2012 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_LUASCRIPT_H__ +#define __PRACRO_LUASCRIPT_H__ + +#include <lua.hpp> +#include <lauxlib.h> + +#include <string> +#include <map> +#include <vector> + +#include "template.h" + +class LUAScript { +  friend class SessionSerialiser; +  friend class SessionParser; +public: +  typedef enum { +    ENV_PATIENTID, +    ENV_TEMPLATE, +    ENV_MACRO, +    ENV_USER +  } env_t; + +  class Exception { +  public: +    Exception(std::string m) : msg(m) {} +    std::string msg; +  }; + +  LUAScript(); +  virtual ~LUAScript() {} + +  virtual const char *name() { return ""; } + +  void init() throw(Exception); + +  void addFile(std::string file); +  void addCode(std::string code, std::string codename = ""); +  void addScripts(std::vector< Script > &scripts); + +  void addValue(std::string name, const std::string &value); + +  void run() throw(Exception); + +  bool hasValue(std::string name); +  std::string value(std::string value); + +  std::string env(env_t id); +  void setEnv(env_t id, std::string value); + +  std::string resultString() throw(Exception); + +protected: +  lua_State *L; + +  std::map<env_t, std::string> _env; + +private: +  std::vector<std::pair<std::string, std::string> > scripts; +  std::map<std::string, std::string> values; + +  int top; +}; + + +#endif/*__PRACRO_LUASCRIPT_H__*/ diff --git a/server/src/macroparser.cc b/server/src/macroparser.cc index d1604b2..be781aa 100644 --- a/server/src/macroparser.cc +++ b/server/src/macroparser.cc @@ -114,7 +114,12 @@ void MacroParser::characterData(std::string &data)    if(state == SCRIPT) {      assert(current_script); // No script present! -    current_script->attributes["code"].append(data); +    current_script->code.append(data); +  } + +  if(state == COMMIT_SCRIPT) { +    assert(current_commit_script); // No script present! +    current_commit_script->code.append(data);    }  } @@ -147,6 +152,18 @@ void MacroParser::startTag(std::string name, attributes_t &attr)      return;    } +  // Enable oncommit parsing +  if(name == "oncommit") { +    if(state != MACRO) error("oncommit found outside macro."); +    state = COMMIT_SCRIPTS; + +    m->resume.attributes = attr; + +    assert(m); // No macro is currently available, cannot create resume! + +    return; +  } +    // Enable Query parsing    if(name == "queries") {      if(state != MACRO) error("queries found outside macro."); @@ -232,8 +249,18 @@ void MacroParser::startTag(std::string name, attributes_t &attr)          current_resume_script = &(m->resume_scripts.back());        }        break; +    case COMMIT_SCRIPTS: +      { +        state = COMMIT_SCRIPT; + +        Script s; +        s.attributes = attr; +        m->commit_scripts.push_back(s); +        current_commit_script = &(m->commit_scripts.back()); +      } +      break;      default: -       error("<script> tag found outside <scripts> or <resume> tags."); +       error("<script> tag found outside <scripts>, <commitscripts> or <resume> tags.");         break;      }      return; @@ -290,6 +317,7 @@ void MacroParser::endTag(std::string name)      state = UNDEFINED;    }    if(name == "resume") state = MACRO; +  if(name == "oncommit") state = MACRO;    if(name == "queries") state = MACRO;    if(name == "query") state = QUERIES;    if(name == "maps") state = MACRO; @@ -310,6 +338,11 @@ void MacroParser::endTag(std::string name)        state = RESUME;        break; +    case COMMIT_SCRIPT: +      current_commit_script = NULL; +      state = COMMIT_SCRIPTS; +      break; +      default:        // tag mismatch?        break; diff --git a/server/src/macroparser.h b/server/src/macroparser.h index 71ef911..ab6fda5 100644 --- a/server/src/macroparser.h +++ b/server/src/macroparser.h @@ -42,7 +42,9 @@ class MacroParser : public SAXParser {      MAP,      WIDGETS,      SCRIPTS, -    SCRIPT +    SCRIPT, +    COMMIT_SCRIPTS, +    COMMIT_SCRIPT    } ParserState;  public: @@ -76,6 +78,7 @@ private:    Map *current_map;    Script *current_script;    Script *current_resume_script; +  Script *current_commit_script;    std::vector< Widget* > widgetstack;    // Error callback function. diff --git a/server/src/mutex.cc b/server/src/mutex.cc index ec0d0e8..9805591 100644 --- a/server/src/mutex.cc +++ b/server/src/mutex.cc @@ -27,6 +27,11 @@   */  #include "mutex.h" +#include "debug.h" + +#define MUTEX_DBG(x) +//#define MUTEX_DBG(x) x +  Mutex::Mutex()  {    pthread_mutex_init (&mutex, NULL); @@ -39,16 +44,23 @@ Mutex::~Mutex()  bool Mutex::trylock()  { -  return pthread_mutex_trylock(&mutex) == 0; +  bool ret = pthread_mutex_trylock(&mutex) == 0; + +  if(ret) MUTEX_DBG(printf("trylock succeeded on %s\n", name.c_str())); +  else MUTEX_DBG(printf("trylock failed on %s\n", name.c_str())); + +  return ret;  }  void Mutex::lock()  {    pthread_mutex_lock(&mutex); +  MUTEX_DBG(printf("lock on %s\n", name.c_str()));  }  void Mutex::unlock()  { +  MUTEX_DBG(printf("unlock on %s\n", name.c_str()));    pthread_mutex_unlock(&mutex);  } diff --git a/server/src/mutex.h b/server/src/mutex.h index cf052ad..87f18e1 100644 --- a/server/src/mutex.h +++ b/server/src/mutex.h @@ -30,6 +30,8 @@  #include <pthread.h> +#include <string> +  class Mutex {  public:    Mutex(); @@ -39,6 +41,8 @@ public:    void lock();    void unlock(); +  std::string name; +  private:    pthread_mutex_t mutex;  }; diff --git a/server/src/pracrod.cc b/server/src/pracrod.cc index 81acfbc..b68cf07 100644 --- a/server/src/pracrod.cc +++ b/server/src/pracrod.cc @@ -69,7 +69,7 @@ static const char version_str[] =  ;  static const char copyright_str[] = -"Copyright (C) 2006-2009 Bent Bisballe Nyeng - Aasimon.org.\n" +"Copyright (C) 2006-2012 Bent Bisballe Nyeng - Aasimon.org.\n"  "This is free software.  You may redistribute copies of it under the terms of\n"  "the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"  "There is NO WARRANTY, to the extent permitted by law.\n" @@ -116,18 +116,12 @@ void childwait(int)  }  static FILE *logfp = stderr; -static std::string logfile; +std::string logfile; +bool logfile_reload = false;  void reload(int)  { -  if(logfp != stderr) { -    fclose(logfp); -    logfp = fopen(logfile.c_str(), "a"); -    if(!logfp) { -      fprintf(stderr, "Could not write to logfile: '%s'\n", optarg); -      logfp = stderr; -    } -  } +  logfile_reload = true;  }  class PracroDaemon : public Daemon { diff --git a/server/src/pracrodaopgsql.cc b/server/src/pracrodaopgsql.cc index f9a773f..db0a9aa 100644 --- a/server/src/pracrodaopgsql.cc +++ b/server/src/pracrodaopgsql.cc @@ -228,6 +228,7 @@ void PracroDAOPgsql::commitTransaction(std::string sessionid,  } +//#define NEW  Values PracroDAOPgsql::getLatestValues(std::string sessionid,                                         std::string patientid,                                         Macro *macro, @@ -261,6 +262,70 @@ Values PracroDAOPgsql::getLatestValues(std::string sessionid,      }      pqxx::work W(*conn); + +#ifdef NEW + +    // Do not search for nothing... +    if(fieldnames.size() == 0) return values; + +    std::string names; +    std::vector< std::string >::iterator fni = fieldnames.begin(); +    while(fni != fieldnames.end()) { +      if(names != "") names += " OR "; +      names += "name='" + W.esc(*fni) + "'"; +      fni++; +    } + +    std::string macros; +    if(macro) { +      macros += " AND macro='" + macro->name + "'"; +      if(macro->version != "") +        macros += " AND t.version='" + macro->version + "'"; +    } + +    uncom = uncom; +    query = "SELECT uid FROM commits WHERE patientid='"+patientid+"' AND" +      " \"timestamp\">="+soldest.str()+" AND" +      " (status='committed' OR uid="+sessionid+");"; +    DEBUG(sql, "Query: %s\n", query.c_str()); +    pqxx::result commits = W.exec(query); +    pqxx::result::const_iterator ci = commits.begin(); +    while(ci != commits.end()) { +      std::string cid = (*ci)[0].c_str(); + +      query = "SELECT uid, \"timestamp\" FROM transactions WHERE cid="+cid+ +        macros+";"; +      DEBUG(sql, "Query: %s\n", query.c_str()); +      pqxx::result transactions = W.exec(query); +      pqxx::result::const_iterator ti = transactions.begin(); +      while(ti != transactions.end()) { +        std::string tid = (*ti)[0].c_str(); +        time_t timestamp = atol((*ti)[1].c_str()); + +        query = "SELECT name, value FROM fields WHERE" +          " transaction="+tid+" AND ("+ names +");"; +        DEBUG(sql, "Query: %s\n", query.c_str()); +        pqxx::result fields = W.exec(query); +        DEBUG(sql, "Results: %lu\n", fields.size()); +        pqxx::result::const_iterator fi = fields.begin(); +        while(fi != fields.end()) { +          std::string name = (*fi)[0].c_str(); +          if(values.find(name) == values.end() || +             values[name].timestamp <= timestamp) { +            Value v; +            v.value = (*fi)[1].c_str(); +            v.timestamp = timestamp; +            values[name] = v; +          } +          fi++; +        } + +        ti++; +      } + +      ci++; +    } +#else/*NEW*/      std::string namecond;      if(fieldnames.size() > 0) { @@ -318,6 +383,7 @@ Values PracroDAOPgsql::getLatestValues(std::string sessionid,        values[(*ri)[0].c_str()] = v;        ri++;      } +#endif/*NEW*/    } catch (std::exception &e) {      ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str());    } diff --git a/server/src/praxisd.cc b/server/src/praxisd.cc index 3ccdf0d..c198051 100644 --- a/server/src/praxisd.cc +++ b/server/src/praxisd.cc @@ -29,6 +29,13 @@  #include "saxparser.h" +static const char* str2err(std::string msg) +{ +  static char errbuf[1024]; +  sprintf(errbuf, "%s", msg.c_str()); +  return errbuf; +} +  static std::string strtime(bool with_sec = true)  {    std::string ret; @@ -59,12 +66,13 @@ static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)  }  Praxisd::Praxisd(std::string h, int port) +  throw(const char*)  {    ch = curl_easy_init();    host = h;    curl_easy_setopt(ch, CURLOPT_PORT, port); -  curl_easy_setopt(ch, CURLOPT_FAILONERROR, 1L); +  curl_easy_setopt(ch, CURLOPT_FAILONERROR, 0L/*1L*/);    curl_easy_setopt(ch, CURLOPT_TIMEOUT, 150L);    curl_easy_setopt(ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);    curl_easy_setopt(ch, CURLOPT_CONNECTTIMEOUT, 15L); @@ -82,6 +90,7 @@ Praxisd::~Praxisd()  // Get Journal By CPR  std::string Praxisd::journal_get_by_cpr(std::string cpr) +  throw(const char*)  {    std::string journal; @@ -93,16 +102,20 @@ std::string Praxisd::journal_get_by_cpr(std::string cpr)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum);    } -  time_t time; -  errornum = curl_easy_getinfo(ch, CURLINFO_FILETIME, &time); +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(journal); +  }    return journal;  }  time_t Praxisd::journal_last_changed(std::string cpr) +  throw(const char*)  {    std::string journal; @@ -114,17 +127,27 @@ time_t Praxisd::journal_last_changed(std::string cpr)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum);    }    time_t time;    errornum = curl_easy_getinfo(ch, CURLINFO_FILETIME, &time); +  if(errornum != CURLE_OK) { +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(journal); +  }    return time;  }  void Praxisd::journal_add(std::string cpr, std::string entry) +  throw(const char*)  {    std::string xml;    xml += "<praxisd version=\"1.0\">\n"; @@ -150,12 +173,19 @@ void Praxisd::journal_add(std::string cpr, std::string entry)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch: %d %s\n", errornum, reply.c_str()); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(reply);    }  }  void Praxisd::add_sogeord(std::string cpr, std::string sogeord,                            std::string sogetxt) +  throw(const char*)  {    std::string datestr = strtime(false); @@ -187,7 +217,13 @@ void Praxisd::add_sogeord(std::string cpr, std::string sogeord,    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch: %d %s\n", errornum, reply.c_str()); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(reply);    }  } @@ -259,6 +295,7 @@ private:  };  Praxisd::patient_t Praxisd::patient_get_by_cpr(std::string cpr) +  throw(const char*)  {    patient_t p; @@ -272,10 +309,14 @@ Praxisd::patient_t Praxisd::patient_get_by_cpr(std::string cpr)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum);    } -  //  printf("Get Patient: %d %s\n", xml.length(), xml.c_str()); fflush(stdout); +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(xml); +  }    PatientParser parser(p);    parser.parse(xml.data(), xml.length()); @@ -284,6 +325,7 @@ Praxisd::patient_t Praxisd::patient_get_by_cpr(std::string cpr)  }  std::string Praxisd::get_sogenr(std::string sogenr) +  throw(const char*)  {    std::string xml; @@ -296,7 +338,13 @@ std::string Praxisd::get_sogenr(std::string sogenr)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(xml);    }    return xml; @@ -331,6 +379,7 @@ private:  };  std::vector<Praxisd::adresse_t> Praxisd::diverse_get_adresse(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::adresse_t> lst;    std::string xml = get_sogenr("A"+sogenr); @@ -365,6 +414,7 @@ private:  std::vector<Praxisd::behandling_t>  Praxisd::diverse_get_behandling(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::behandling_t> lst;    std::string xml = get_sogenr("B"+sogenr); @@ -398,6 +448,7 @@ private:  };  std::vector<Praxisd::cave_t> Praxisd::diverse_get_cave(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::cave_t> lst;    std::string xml = get_sogenr("C"+sogenr); @@ -431,6 +482,7 @@ private:  std::vector<Praxisd::diagnose_t>  Praxisd::diverse_get_diagnose(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::diagnose_t> lst;    std::string xml = get_sogenr("D"+sogenr); @@ -464,6 +516,7 @@ private:  };  std::vector<Praxisd::frase_t> Praxisd::diverse_get_frase(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::frase_t> lst;    std::string xml = get_sogenr("F"+sogenr); @@ -495,6 +548,7 @@ private:  };  std::vector<Praxisd::grafik_t> Praxisd::diverse_get_grafik(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::grafik_t> lst;    std::string xml = get_sogenr("G"+sogenr); @@ -530,6 +584,7 @@ private:  std::vector<Praxisd::indholdstof_t>  Praxisd::diverse_get_indholdstof(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::indholdstof_t> lst;    std::string xml = get_sogenr("I"+sogenr); @@ -563,6 +618,7 @@ private:  std::vector<Praxisd::klage_t>  Praxisd::diverse_get_klage(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::klage_t> lst;    std::string xml = get_sogenr("K"+sogenr); @@ -595,6 +651,7 @@ private:  std::vector<Praxisd::oversigt_t>  Praxisd::diverse_get_oversigt(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::oversigt_t> lst;    std::string xml = get_sogenr("O"+sogenr); @@ -632,6 +689,7 @@ private:  std::vector<Praxisd::postnummer_t>  Praxisd::diverse_get_postnummer(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::postnummer_t> lst;    std::string xml = get_sogenr("P"+sogenr); @@ -666,6 +724,7 @@ private:  std::vector<Praxisd::type_t>  Praxisd::diverse_get_type(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::type_t> lst;    std::string xml = get_sogenr("T"+sogenr); @@ -699,6 +758,7 @@ private:  std::vector<Praxisd::undersoegelse_t>  Praxisd::diverse_get_undersoegelse(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::undersoegelse_t> lst;    std::string xml = get_sogenr("U"+sogenr); @@ -736,6 +796,7 @@ private:  std::vector<Praxisd::ydelse_t>  Praxisd::diverse_get_ydelse(std::string sogenr) +  throw(const char*)  {    std::vector<Praxisd::ydelse_t> lst;    std::string xml = get_sogenr("Y"+sogenr); @@ -755,7 +816,7 @@ public:      if(name == "aftale") {        Praxisd::aftale_t a;        if(attr.find("date") != attr.end()) a.date = attr["date"]; -      if(attr.find("calendar") != attr.end()) a.date = attr["calendar"]; +      if(attr.find("calendar") != attr.end()) a.calendar = attr["calendar"];        div.push_back(a);      }      DODIVTAG(cpr); @@ -775,8 +836,9 @@ static std::string i2s(int i) {    return buf;  }  std::vector<Praxisd::aftale_t> -Praxisd::aftale_get_all_by_date_and_calendar(int cal, int year, int month, -                                             int day) +Praxisd::aftale_get_all_by_date_and_calendar(int cal, +                                             int year, int month, int day) +  throw(const char*)  {    std::vector<Praxisd::aftale_t> aft; @@ -792,7 +854,13 @@ Praxisd::aftale_get_all_by_date_and_calendar(int cal, int year, int month,    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(xml);    }    AftaleParser parser(aft); @@ -802,6 +870,7 @@ Praxisd::aftale_get_all_by_date_and_calendar(int cal, int year, int month,  }  std::vector<Praxisd::aftale_t> Praxisd::aftale_get_all_by_cpr(std::string cpr) +  throw(const char*)  {    std::vector<Praxisd::aftale_t> aft; @@ -815,7 +884,13 @@ std::vector<Praxisd::aftale_t> Praxisd::aftale_get_all_by_cpr(std::string cpr)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(xml);    }    AftaleParser parser(aft); @@ -825,6 +900,7 @@ std::vector<Praxisd::aftale_t> Praxisd::aftale_get_all_by_cpr(std::string cpr)  }  bool Praxisd::authenticate(std::string user, std::string pass) +  throw(const char*)  {    std::string uri = host + "/praxisd/1.0/authenticate?user=" + user + "&pass=" +      pass; @@ -835,11 +911,14 @@ bool Praxisd::authenticate(std::string user, std::string pass)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    return false; +    throw curl_easy_strerror(errornum);    }    long code = 0;    errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(errornum != CURLE_OK) { +    throw curl_easy_strerror(errornum); +  }    if(code == 401) return false;    if(code == 200) return true; @@ -876,6 +955,7 @@ private:  };  std::vector<Praxisd::dokmenu_t> Praxisd::dokmenu_get_all_by_cpr(std::string cpr) +  throw(const char*)  {    std::vector<Praxisd::dokmenu_t> dokmenu;    std::string xml; @@ -888,7 +968,13 @@ std::vector<Praxisd::dokmenu_t> Praxisd::dokmenu_get_all_by_cpr(std::string cpr)    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(xml);    }    DokMenuParser parser(dokmenu); @@ -900,6 +986,7 @@ std::vector<Praxisd::dokmenu_t> Praxisd::dokmenu_get_all_by_cpr(std::string cpr)  // Get Dokmenu by Name and CPR  std::string Praxisd::dokmenu_get_by_cpr_and_name(std::string cpr,                                                   std::string name) +  throw(const char*)  {    std::string data; @@ -912,7 +999,13 @@ std::string Praxisd::dokmenu_get_by_cpr_and_name(std::string cpr,    CURLcode errornum = curl_easy_perform(ch);    if(errornum != CURLE_OK) { -    printf("Ouch %d\n", errornum); +    throw curl_easy_strerror(errornum); +  } + +  long code = 0; +  errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code);  +  if(code != 200) { +    throw str2err(data);    }    return data; diff --git a/server/src/praxisd.h b/server/src/praxisd.h index 84848b8..785cba1 100644 --- a/server/src/praxisd.h +++ b/server/src/praxisd.h @@ -37,13 +37,16 @@  class Praxisd {  public: -  Praxisd(std::string host, int port); +  Praxisd(std::string host, int port) +    throw(const char*);    ~Praxisd(); -  time_t journal_last_changed(std::string cpr); +  time_t journal_last_changed(std::string cpr) +    throw(const char*);    // Get Journal By CPR -  std::string journal_get_by_cpr(std::string cpr); +  std::string journal_get_by_cpr(std::string cpr) +    throw(const char*);    // Get Patient By CPR    typedef struct { @@ -89,7 +92,8 @@ public:      std::string unknown251;      std::string jtime;    } patient_t; -  patient_t patient_get_by_cpr(std::string cpr); +  patient_t patient_get_by_cpr(std::string cpr) +    throw(const char*);    // Get Diverse From Sogenr    typedef struct { @@ -102,7 +106,8 @@ public:      std::string tlf;      std::string fax;    } adresse_t; -  std::vector<adresse_t> diverse_get_adresse(std::string sogenr); +  std::vector<adresse_t> diverse_get_adresse(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -111,7 +116,8 @@ public:      std::string bemaerkning;      std::string udregning;    } behandling_t; -  std::vector<behandling_t> diverse_get_behandling(std::string sogenr); +  std::vector<behandling_t> diverse_get_behandling(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -120,7 +126,8 @@ public:      std::string bemaerkning2;      std::string bemaerkning3;    } cave_t; -  std::vector<cave_t> diverse_get_cave(std::string sogenr); +  std::vector<cave_t> diverse_get_cave(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -128,7 +135,8 @@ public:      std::string diagnose;      std::string bemaerkning;    } diagnose_t; -  std::vector<diagnose_t> diverse_get_diagnose(std::string sogenr); +  std::vector<diagnose_t> diverse_get_diagnose(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -137,14 +145,16 @@ public:      std::string frase3;      std::string frase4;    } frase_t; -  std::vector<frase_t> diverse_get_frase(std::string sogenr); +  std::vector<frase_t> diverse_get_frase(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr;      std::string navn;      std::string bemaerkning;    } grafik_t; -  std::vector<grafik_t> diverse_get_grafik(std::string sogenr); +  std::vector<grafik_t> diverse_get_grafik(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -154,7 +164,8 @@ public:      std::string form3;      std::string form4;    } indholdstof_t; -  std::vector<indholdstof_t> diverse_get_indholdstof(std::string sogenr); +  std::vector<indholdstof_t> diverse_get_indholdstof(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -162,14 +173,16 @@ public:      std::string klage;      std::string bemaerkning;    } klage_t; -  std::vector<klage_t> diverse_get_klage(std::string sogenr); +  std::vector<klage_t> diverse_get_klage(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr;      std::string navn;      std::string bemaerkning;    } oversigt_t; -  std::vector<oversigt_t> diverse_get_oversigt(std::string sogenr); +  std::vector<oversigt_t> diverse_get_oversigt(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -181,7 +194,8 @@ public:      std::string regionnavn;      std::string kommunenavn;    } postnummer_t; -  std::vector<postnummer_t> diverse_get_postnummer(std::string sogenr); +  std::vector<postnummer_t> diverse_get_postnummer(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -191,7 +205,8 @@ public:      std::string bemaerkning3;      std::string recept;    } type_t; -  std::vector<type_t> diverse_get_type(std::string sogenr); +  std::vector<type_t> diverse_get_type(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -200,7 +215,8 @@ public:      std::string bemaerkning;      std::string udregning;    } undersoegelse_t; -  std::vector<undersoegelse_t> diverse_get_undersoegelse(std::string sogenr); +  std::vector<undersoegelse_t> diverse_get_undersoegelse(std::string sogenr) +    throw(const char*);    typedef struct {      std::string sogenr; @@ -213,7 +229,8 @@ public:      std::string journal2;      std::string moms;    } ydelse_t; -  std::vector<ydelse_t> diverse_get_ydelse(std::string sogenr); +  std::vector<ydelse_t> diverse_get_ydelse(std::string sogenr) +    throw(const char*);    // Get Aftale All by Date and Calendar @@ -227,17 +244,21 @@ public:      std::string cres;    } aftale_t;    std::vector<aftale_t> aftale_get_all_by_date_and_calendar(int cal, int year, -                                                            int month, int day); +                                                            int month, int day) +    throw(const char*);    // Get Aftale All by CPR -  std::vector<aftale_t> aftale_get_all_by_cpr(std::string cpr); +  std::vector<aftale_t> aftale_get_all_by_cpr(std::string cpr) +    throw(const char*);    // Authenticate -  bool authenticate(std::string user, std::string pass); +  bool authenticate(std::string user, std::string pass) +    throw(const char*);  #if 0    // Get Name by UserID -  std::string user_get_name_by_id(std::string user); +  std::string user_get_name_by_id(std::string user) +    throw(const char*);  #endif    // Get All Dokmenu by CPR @@ -248,19 +269,23 @@ public:      size_t filesize;      std::string date;    } dokmenu_t; -  std::vector<dokmenu_t> dokmenu_get_all_by_cpr(std::string cpr); +  std::vector<dokmenu_t> dokmenu_get_all_by_cpr(std::string cpr) +    throw(const char*);    // Get Dokmenu by Name and CPR -  std::string dokmenu_get_by_cpr_and_name(std::string cpr, std::string name); +  std::string dokmenu_get_by_cpr_and_name(std::string cpr, std::string name) +    throw(const char*);    // POST:    // Add To Journal -  void journal_add(std::string cpr, std::string entry); +  void journal_add(std::string cpr, std::string entry) +    throw(const char*);    // Update Patient    // Add Sogeord to Patient -  void add_sogeord(std::string cpr, std::string sogeord, std::string sogetxt); +  void add_sogeord(std::string cpr, std::string sogeord, std::string sogetxt) +    throw(const char*);    // Update Aftale    // Add Aftale @@ -269,7 +294,8 @@ public:    // Delete File from Dokmenu  private: -  std::string get_sogenr(std::string sogenr); +  std::string get_sogenr(std::string sogenr) +    throw(const char*);    CURL *ch;    std::string host;  }; diff --git a/server/src/resumeparser.cc b/server/src/resumeparser.cc deleted file mode 100644 index 1c5335a..0000000 --- a/server/src/resumeparser.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - *            resumeparser.cc - * - *  Mon Oct  1 11:17:35 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 "resumeparser.h" - -#include <string.h> - -#include "luaresume.h" -#include "configuration.h" - -std::string resume_parser(Macro ¯o, Commit &commit) -{ -  LUAResume luaresume(commit); - -  std::string code; - -  std::vector< Script >::iterator spi = macro.resume_scripts.begin(); -  while(spi != macro.resume_scripts.end()) { -    if(spi->attributes.find("src") != spi->attributes.end()) { -      std::string src = spi->attributes["src"]; -      std::string file = -        Conf::xml_basedir + "/include/" + src; -      FILE *fp = fopen(file.c_str(), "r"); -      if(fp) { -        char buf[64]; -        size_t sz; -        std::string inc; -        while((sz = fread(buf, 1, sizeof(buf), fp)) != 0) { -          inc.append(buf, sz); -        } -        fclose(fp); -        code += "\n-- BEGIN INCLUDE: '" + src + "'\n"; -        code += inc; -        code += "\n-- END INCLUDE: '" + src + "'\n"; -      } -    } else { -      code += spi->code; -    } -    spi++; -  } - -  return luaresume.run(code); -} - -#ifdef TEST_RESUMEPARSER -//deps: luaresume.cc configuration.cc debug.cc log.cc luautil.cc -//cflags: -I.. $(LUA_CFLAGS) -//libs: $(LUA_LIBS) -#include <test.h> - -TEST_BEGIN; - -// TODO: Put some testcode here (see test.h for usable macros). -TEST_TRUE(false, "No tests yet!"); - -TEST_END; - -#endif/*TEST_RESUMEPARSER*/ diff --git a/server/src/saxparser.cc b/server/src/saxparser.cc index 95efffe..14f204c 100644 --- a/server/src/saxparser.cc +++ b/server/src/saxparser.cc @@ -130,6 +130,8 @@ bool SAXParser::parse(const char *data, size_t size)    xml.append(data, size);    DEBUG(sax, "parse %d bytes [%s]\n", size, xml.c_str()); +  if(data == NULL || size == 0) return done; +    bufferbytes = size;    totalbytes += bufferbytes; diff --git a/server/src/server.cc b/server/src/server.cc index e4d6474..838da32 100644 --- a/server/src/server.cc +++ b/server/src/server.cc @@ -37,6 +37,9 @@  #include "client_connection.h"  #include "admin_connection.h" +extern std::string logfile; +extern volatile bool logfile_reload; +  class PracroHttpd : public Httpd {  public:    PracroHttpd() {} @@ -57,6 +60,12 @@ public:                headers_t &getargs,                headers_t &headers)    { +    if(logfile_reload) { +      DEBUG(pracrod, "Reopen log file %s\n", logfile.c_str()); +      debug_reinit(logfile.c_str()); +      logfile_reload = false; +    } +      Connection *connection = NULL;      if(headers.find("User-Agent") != headers.end() && @@ -73,7 +82,7 @@ public:    {      if(ptr) {        Connection *connection = (Connection *)ptr; -      connection->handle(data, data_size); +      connection->data(data, data_size);      }      return true;    } @@ -84,7 +93,7 @@ public:        Connection *connection = (Connection *)ptr;        // Flush and do commit/discards -      connection->handle(NULL, 0); +      if(!connection->handle()) return false;        connection->getReply(reply);      } diff --git a/server/src/session.cc b/server/src/session.cc index fcd138a..ea4d11c 100644 --- a/server/src/session.cc +++ b/server/src/session.cc @@ -55,6 +55,8 @@ Session::Session(Environment *e,    patientid = pid;    templ = t; +  mutex.name = "session-" + sid; +    DEBUG(session, "[%p] new Session(sessionid: '%s', patientid: '%s',"          " template: '%s')\n", this, sid.c_str(), pid.c_str(), t.c_str()); @@ -82,10 +84,20 @@ std::string Session::id()  void Session::lock()  {    mutex.lock(); +  DEBUG(session, "lock() %p (%s)\n", this, sessionid.c_str()); +} + +bool Session::trylock() +{ +  bool r = mutex.trylock(); +  DEBUG(session, "trylock() %p (%s) == %s\n", +        this, sessionid.c_str(), r?"true":"false"); +  return r;  }  void Session::unlock()  { +  DEBUG(session, "unlock() %p (%s)\n", this, sessionid.c_str());    mutex.unlock();  } @@ -121,12 +133,21 @@ void Session::setIdle(bool idle)    }  } -void Session::commit() +void Session::commit() throw(LUAScript::Exception, Journal::Exception)  {    DEBUG(session, "[%p] commit(sessionid: '%s')\n", this, sessionid.c_str());    if(_journal != NULL) { -    _journal->commit(); +    try { +      _journal->runOnCommitScripts(); +    } catch(LUAScript::Exception &e) { +      throw e; +    } +    try { +      _journal->commit(); +    } catch(Journal::Exception &e) { +      throw e; +    }      delete _journal;      _journal = NULL;    } @@ -290,28 +311,76 @@ size_t Sessions::size()  void Sessions::store()  { +  Session *session = NULL; +  std::string sessionid; +  do { +    bool waitcont = false; +    session = NULL; +    { +      MutexAutolock lock(mutex); +      std::map<std::string, Session*>::iterator head = sessions.begin(); +      if(head != sessions.end()) { +        session = head->second; +        sessionid = head->first; +        if(session->trylock()) { +          sessions.erase(sessionid); +        } else { +          waitcont = true; +        } +      } +    } + +    if(waitcont) { +      usleep(200000); // sleep 200ms +      continue; +    } + +    if(session != NULL) { +      SessionSerialiser ser(env, Conf::session_path); +      ser.save(session); +      delete session; +    } +  } while(session != NULL); + +  /*    MutexAutolock lock(mutex);    std::map<std::string, Session*>::iterator i = sessions.begin();    while(i != sessions.end()) {      SessionSerialiser ser(env, Conf::session_path); -    ser.save(i->second); -    delete i->second; -    sessions.erase(i); +    Session *s = i->second; +    s->lock(); +    ser.save(s); +    delete s;      i++;    }    sessions.clear(); +  */  } -std::vector<std::string> Sessions::activeSessions() +std::vector<Sessions::SessionInfo> Sessions::activeSessions()  {    MutexAutolock lock(mutex); -  std::vector<std::string> act; +  std::vector<SessionInfo> act;    std::map<std::string, Session*>::iterator i = sessions.begin();    while(i != sessions.end()) { -    act.push_back(i->first); +    Session *s = i->second; +    SessionInfo si; +    si.id = i->first; +    si.templ = "LOCKED"; + +    if(s->trylock()) { +      // si.user = "simpson"; +      // si.course = s->course; +      si.patientid = s->patientid; +      si.templ = s->templ; +      si.idle = s->idle(); +      // si.ondisc = false; +      s->unlock(); +    } +    act.push_back(si);      i++;    } @@ -326,6 +395,7 @@ SessionAutounlock::SessionAutounlock(Session **s)  SessionAutounlock::~SessionAutounlock()  { +  DEBUG(session, "SessionAutounlock(%p)\n", *session);    if(*session) (*session)->unlock();  } diff --git a/server/src/session.h b/server/src/session.h index 4d1ed12..1cb3041 100644 --- a/server/src/session.h +++ b/server/src/session.h @@ -37,6 +37,9 @@  #include "transaction.h"  #include "template.h" +#include "luascript.h" +#include "journal.h" +  class Environment;  class Journal; @@ -50,9 +53,10 @@ public:    std::string id();    void lock(); +  bool trylock();    void unlock(); -  void commit(); +  void commit() throw(LUAScript::Exception, Journal::Exception);    void nocommit();    void discard(); @@ -128,7 +132,17 @@ public:    //    // Admin methods    // -  std::vector<std::string> activeSessions(); +  class SessionInfo { +  public: +    std::string id; +    std::string patientid; +    std::string user; +    std::string course; +    std::string templ; +    bool idle; +    bool ondisc; +  }; +  std::vector<SessionInfo> activeSessions();  private:    std::map<std::string, Session *> sessions; diff --git a/server/src/sessionheaderparser.cc b/server/src/sessionheaderparser.cc new file mode 100644 index 0000000..c9a8e67 --- /dev/null +++ b/server/src/sessionheaderparser.cc @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + *            sessionheaderparser.cc + * + *  Thu Aug  9 09:06:32 CEST 2012 + *  Copyright 2012 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 "sessionheaderparser.h" + +/* +<session timestamp="1234567890" +         status="readonly"" +         id="12345" +         template="amd_forunders" +         patientid="0000000000"> +   ... +</session> +*/ + +#include <stdio.h> + +// For assert +#include <assert.h> + +// For open and friends +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +// For vprintf and friends +#include <stdarg.h> + +#include <errno.h> +#include <string.h> + +#include "debug.h" +#include "configuration.h" +#include "exception.h" + +void SessionHeaderParser::error(const char* fmt, ...) +{ +  ERR_LOG(session, "Error in SessionHeaderParser: "); + +  { +    va_list argp; +    va_start(argp, fmt); +    ERR_LOG_VA(session, 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 SessionHeaderParser: " + std::string(p)); +      free(p); +    } +    va_end(argp); +  } + +} + +SessionHeaderParser::SessionHeaderParser(std::string sessionfile) +{ +  done = false; + +  file = sessionfile; + +  DEBUG(session, "Using session file: %s\n", sessionfile.c_str()); + +  fd = open(sessionfile.c_str(), O_RDONLY); +  if(fd == -1) error("Could not open file %s", sessionfile.c_str()); +} + +SessionHeaderParser::~SessionHeaderParser() +{ +  if(fd != -1) close(fd); +} + +void SessionHeaderParser::startTag(std::string name, attributes_t &attr) +{ +  if(done) return; + +  if(name == "session") { +    done = true; +    if(attr.find("patientid") != attr.end()) { +      header.patientid = attr["patientid"]; +    } + +    if(attr.find("template") != attr.end()) { +      header.templ = attr["template"]; +    } + +    if(attr.find("id") != attr.end()) { +      header.id = attr["id"]; +    } +  } else { +    throw Exception("Missing root tag 'session' - found '" + name + "'"); +  } +} + +int SessionHeaderParser::readData(char *data, size_t size) +{ +  if(done) return 0; // If done is true we already found what we were looking +                     //  for and can dismiss the rest of the document. + +  if(fd == -1) { +    ERR_LOG(session, "Invalid file descriptor.\n"); +    return 0; +  } +  ssize_t r = read(fd, data, size); +  if(r == -1) { +    ERR_LOG(session, "Could not read...%s\n", strerror(errno)); +    return 0; +  } +  return r; +} + +void SessionHeaderParser::parseError(const char *buf, size_t len, std::string error, int lineno) +{ +  if(done) return; // If done is true we already found what we were looking +                   //  for and can dismiss the rest of the document. + +  ERR_LOG(session, "SessionHeaderParser[%s] error at line %d: %s\n", +          file.c_str(), lineno, error.c_str()); +  ERR_LOG(session, "\tBuffer %u bytes: [", len); +  if(fwrite(buf, len, 1, stderr) != len) {} +  ERR_LOG(session, "]\n"); + +  char *slineno; +  if(asprintf(&slineno, " at line %d\n", lineno) != -1) { +    throw Exception(error + slineno); +    free(slineno); +  } +} + +SessionHeaderParser::Header SessionHeaderParser::getHeader() +{ +  return header; +} + +#ifdef TEST_SESSIONHEADERPARSER +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_SESSIONHEADERPARSER*/ diff --git a/server/src/sessionheaderparser.h b/server/src/sessionheaderparser.h new file mode 100644 index 0000000..5f2d3cf --- /dev/null +++ b/server/src/sessionheaderparser.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + *            sessionheaderparser.h + * + *  Thu Aug  9 09:06:32 CEST 2012 + *  Copyright 2012 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_SESSIONHEADERPARSER_H__ +#define __PRACRO_SESSIONHEADERPARSER_H__ + +#include "saxparser.h" + +/** + * Partial session parser. + * This class is used to parse only the first tag of a session xml file. + * The parser will run about 10 times faster than the one parsing the entire + * file (see the SessionParser class) and can be used to find a + * patientid/template match. + * This class inherits the SAXParser baseclass. + * Use the parse() method to run the parser, and collect the result via the + * getPatientID() and getTemplate() methods. + * If the file does not contain a session, or the file is not a valid xml file, + * an Exception is thrown. + */ +class SessionHeaderParser : public SAXParser { +public: +  class Header { +  public: +    std::string patientid; +    std::string templ; +    std::string id; +  }; + +  /** +   * Constructor. +   * @param sessionfile A std::string containing the name of the file to parse. +   */ +  SessionHeaderParser(std::string sessionfile); + +  /** +   * Destructor. +   */ +  ~SessionHeaderParser(); + +  /** +   * Overloaded parser callback method. +   */ +  void startTag(std::string name, attributes_t &attr); + +  /** +   * Overloaded parser callback method. +   */ +  void parseError(const char *buf, size_t len, std::string error, int lineno); + +  /** +   * Get a pointer to the parsed macro. +   * NOTE: The allocated memory for the macro is owned by the parser, and will be +   * freed upon parser deletion. +   * @return A pointer to the macro or NULL on error. +   */ +  Header getHeader(); + +protected: +  /** +   * Overloaded parser callback method. +   */ +  int readData(char *data, size_t size); + +private: +  int fd; + +  bool done; +  +  Header header; + +  std::string file; +  // Error callback function. +  void error(const char* fmt, ...); +}; + +#endif/*__PRACRO_SESSIONHEADERPARSER_H__*/ diff --git a/server/src/sessionparser.cc b/server/src/sessionparser.cc index 6b3653e..8913e3c 100644 --- a/server/src/sessionparser.cc +++ b/server/src/sessionparser.cc @@ -37,6 +37,9 @@ SessionParser::SessionParser()    totalbytes = 0;    inresume = false;    indatabase = false; +  invalue = false; +  inscript = false; +  inenv = false;  }  SessionParser::~SessionParser() @@ -49,6 +52,26 @@ void SessionParser::characterData(std::string &data)      entries[entries.size()-1].resume += data;    } +  if(inscript) { +    Entry &e = entries[entries.size() - 1]; +    LUAOnCommit *oncommit = e.oncommit; +    std::pair<std::string, std::string> &val = +      oncommit->scripts[oncommit->scripts.size() - 1]; +    val.first += data; +  } + +  if(invalue) { +    Entry &e = entries[entries.size() - 1]; +    LUAOnCommit *oncommit = e.oncommit; +    oncommit->values[valuename] += data; +  } + +  if(inenv) { +    Entry &e = entries[entries.size() - 1]; +    LUAOnCommit *oncommit = e.oncommit; +    oncommit->_env[envid] += data; +  } +    if(indatabase) {      database += data;    } @@ -80,12 +103,51 @@ void SessionParser::startTag(std::string name, attributes_t &attr)      e.index = atoi(attr["index"].c_str());      e.macro = attr["macro"];      e.user = attr["user"]; +    e.oncommit = NULL;      entries.push_back(e);    }    if(name == "resume") {      inresume = true;    } + +  if(name == "oncommit") { +    Entry &e = entries[entries.size() - 1]; +    if(e.oncommit != NULL) { +      ERR(sessionparser, "Multiple oncommit tags in journal!\n"); +      return; +    } +    e.oncommit = new LUAOnCommit(); +  } +  if(name == "envs") { } + +  if(name == "env") { +    if(attr["id"] == "ENV_PATIENTID") envid = LUAScript::ENV_PATIENTID; +    else if(attr["id"] == "ENV_TEMPLATE") envid = LUAScript::ENV_TEMPLATE; +    else if(attr["id"] == "ENV_MACRO") envid = LUAScript::ENV_MACRO; +    else if(attr["id"] == "ENV_USER") envid = LUAScript::ENV_USER; +    else { +      // Unknown env id +      return; +    } +    inenv = true; +  } + +  if(name == "values") { } + +  if(name == "value") { +    valuename = attr["name"]; +    invalue = true; +  } + +  if(name == "scripts") {} + +  if(name == "script") { +    Entry &e = entries[entries.size() - 1]; +    LUAOnCommit *oncommit = e.oncommit; +    oncommit->addCode("", attr["name"]); +    inscript = true; +  }  }  void SessionParser::endTag(std::string name) @@ -96,6 +158,15 @@ void SessionParser::endTag(std::string name)    if(name == "database") {      indatabase = false;    } +  if(name == "env") { +    inenv = false; +  } +  if(name == "value") { +    invalue = false; +  } +  if(name == "script") { +    inscript = false; +  }  }  void SessionParser::parseError(const char *buf, size_t len, diff --git a/server/src/sessionparser.h b/server/src/sessionparser.h index df32b06..8734fbd 100644 --- a/server/src/sessionparser.h +++ b/server/src/sessionparser.h @@ -30,6 +30,8 @@  #include "saxparser.h" +#include "luaoncommit.h" +  #include <string>  #include <vector> @@ -50,10 +52,11 @@ public:    std::string userid;    std::string database;    std::string dbtype; -   +    class Entry {    public:      int index; +    LUAOnCommit *oncommit;      std::string macro;      std::string resume;      std::string user; @@ -63,7 +66,12 @@ public:  private:    bool inresume; +  bool inscript; +  bool invalue; +  bool inenv; +  std::string valuename;    bool indatabase; +  LUAScript::env_t envid;  };  #endif/*__PRACRO_SESSIONPARSER_H__*/ diff --git a/server/src/sessionserialiser.cc b/server/src/sessionserialiser.cc index 36d0a0d..6288ddd 100644 --- a/server/src/sessionserialiser.cc +++ b/server/src/sessionserialiser.cc @@ -34,6 +34,7 @@  #include "journal.h"  #include "sessionparser.h" +#include "sessionheaderparser.h"  #include "database.h"  #include "xml_encode_decode.h" @@ -88,7 +89,8 @@ Session *SessionSerialiser::loadStr(const std::string &xml)    j->setPatientID(XDEC(parser.patientid));    std::vector<SessionParser::Entry>::iterator i = parser.entries.begin();    while(i != parser.entries.end()) { -    j->addEntry(XDEC(i->resume), XDEC(i->macro), XDEC(i->user), i->index); +    j->addEntry(XDEC(i->resume), XDEC(i->macro), XDEC(i->user), i->index, +                i->oncommit);      i++;    } @@ -122,6 +124,50 @@ std::string SessionSerialiser::saveStr(Session *session)        " macro=\"" + XENC(i->second.macro) + "\""        " user=\"" + XENC(i->second.user) + "\">\n";      xml += "      <resume>" + XENC(i->second.resume) + "</resume>\n"; +    LUAOnCommit *oncommit = i->second.oncommit; +    if(oncommit != NULL) { +      xml += "      <oncommit>\n"; + +      xml += "        <envs>\n"; +      std::map<LUAScript::env_t, std::string>::iterator ei = +        oncommit->_env.begin(); +      while(ei != oncommit->_env.end()) { +        std::string id; +        switch(ei->first) { +        case LUAScript::ENV_PATIENTID: id = "ENV_PATIENTID"; break; +        case LUAScript::ENV_TEMPLATE: id = "ENV_TEMPLATE"; break; +        case LUAScript::ENV_MACRO: id = "ENV_MACRO"; break; +        case LUAScript::ENV_USER: id = "ENV_USER"; break; +        } +         +        xml += "          <env id=\"" + XENC(id) + "\">"+ +          XENC(ei->second) + "</env>\n"; +        ei++; +      } +      xml += "        </envs>\n"; + +      xml += "        <values>\n"; +      std::map<std::string, std::string>::iterator vi = +        oncommit->values.begin(); +      while(vi != oncommit->values.end()) { +        xml += "          <value name=\"" + XENC(vi->first) + "\">"+ +          XENC(vi->second) + "</value>\n"; +        vi++; +      } +      xml += "        </values>\n"; + +      xml += "        <scripts>\n"; +      std::vector<std::pair<std::string, std::string> >::iterator si = +        oncommit->scripts.begin(); +      while(si != oncommit->scripts.end()) { +        xml += "          <script name=\"" + XENC(si->second) + "\">"+ +          XENC(si->first) + "</script>\n"; +        si++; +      } +      xml += "        </scripts>\n"; +       +      xml += "      </oncommit>\n"; +    }      xml += "    </entry>\n";      i++; @@ -170,6 +216,11 @@ void SessionSerialiser::save(Session *session)    // write xml to file    FILE *fp =  fopen(filename.c_str(), "w"); +  if(!fp) { +    ERR(sessionserialiser, "Could not write session to file %s\n", +        filename.c_str()); +    return; +  }    fwrite(xml.data(), xml.size(), 1, fp);    fclose(fp);  } @@ -210,25 +261,34 @@ Session *SessionSerialiser::findFromTupple(const std::string &patientid,        DEBUG(sessionserialiser, "Is xml file\n"); -      // Load session file -      FILE *fp =  fopen(filename.c_str(), "r"); -      std::string xml; -      while(!feof(fp)) { -        char str[64]; -        memset(str, 0, sizeof(str)); -        fread(str, sizeof(str) - 1, 1, fp); -        xml += str; +      SessionHeaderParser::Header header; + +      SessionHeaderParser p(filename); +      try { +        p.parse(); +        header = p.getHeader(); +      } catch( ... ) { +        continue;        } -      fclose(fp); -      Session *session = loadStr(xml); +      if(header.patientid == patientid && header.templ == templ) { +        // Load session file +        FILE *fp =  fopen(filename.c_str(), "r"); +        std::string xml; +        while(!feof(fp)) { +          char str[64]; +          memset(str, 0, sizeof(str)); +          fread(str, sizeof(str) - 1, 1, fp); +          xml += str; +        } +        fclose(fp); + +        Session *session = loadStr(xml); -      DEBUG(sessionserialiser, "PatientID %s - Template %s\n",  -            session->patientid.c_str(), -            session->templ.c_str()); +        DEBUG(sessionserialiser, "PatientID %s - Template %s\n",  +              session->patientid.c_str(), +              session->templ.c_str()); -      if(session->patientid == patientid && -         session->templ == templ) {          closedir(dir);          unlink(filename.c_str());          return session; @@ -241,6 +301,46 @@ Session *SessionSerialiser::findFromTupple(const std::string &patientid,    return NULL;  } +std::map<std::string, SessionHeaderParser::Header> +SessionSerialiser::sessionFiles() +{ +  std::map<std::string, SessionHeaderParser::Header> list; + +  DIR *dir = opendir(path.c_str()); +  if(!dir) { +    ERR(sessionserialiser, "Could not open directory: %s - %s\n", +        path.c_str(), strerror(errno)); +    return list; +  } + +  struct dirent *dirent; +  while( (dirent = readdir(dir)) != NULL ) { + +    std::string filename = path+"/"+dirent->d_name; +     +    if(isxmlfile(filename)) { + +      SessionHeaderParser::Header header; + +      SessionHeaderParser p(filename); +      try { +        p.parse(); +        header = p.getHeader(); +      } catch( ... ) { +        continue; +      } +       +      list[filename] = header; +    } +  } + +  closedir(dir); + +  return list; +   +} + +  #ifdef TEST_SESSIONSERIALISER  //deps: session.cc journal.cc debug.cc configuration.cc mutex.cc journal_commit.cc sessionparser.cc saxparser.cc xml_encode_decode.cc database.cc pracrodaopgsql.cc pracrodaotest.cc pracrodao.cc journal_uploadserver.cc log.cc environment.cc semaphore.cc artefact.cc macrolist.cc templatelist.cc entitylist.cc inotify.cc versionstr.cc exception.cc macroheaderparser.cc templateheaderparser.cc  //cflags: -I.. $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) $(PQXX_CFLAGS) -DWITHOUT_ARTEFACT diff --git a/server/src/sessionserialiser.h b/server/src/sessionserialiser.h index a85fc63..2c4e7ea 100644 --- a/server/src/sessionserialiser.h +++ b/server/src/sessionserialiser.h @@ -29,6 +29,9 @@  #define __PRACRO_SESSIONSERIALISER_H__  #include <string> +#include <map> + +#include "sessionheaderparser.h"  #include "session.h" @@ -47,6 +50,8 @@ public:    Session *load(const std::string &sessionid);    void save(Session *session); +  std::map<std::string, SessionHeaderParser::Header> sessionFiles(); +  private:    std::string path;    Environment *env; diff --git a/server/src/template.h b/server/src/template.h index a069cff..853db3d 100644 --- a/server/src/template.h +++ b/server/src/template.h @@ -67,6 +67,7 @@ public:    maps_t maps;    std::vector< Script > scripts;    std::vector< Script > resume_scripts; +  std::vector< Script > commit_scripts;    Widget widgets;    Resume resume; @@ -84,6 +85,8 @@ public:  class Template {  public: +  std::vector< Script > scripts; +    std::vector< Macro > macros;    std::string name; diff --git a/server/src/templateparser.cc b/server/src/templateparser.cc index b9c65f5..8fc3eff 100644 --- a/server/src/templateparser.cc +++ b/server/src/templateparser.cc @@ -81,6 +81,10 @@ TemplateParser::~TemplateParser()  void TemplateParser::characterData(std::string &data)  { +  if(state == SCRIPT) { +    assert(current_script); // No script present! +    current_script->code.append(data); +  }  }  void TemplateParser::startTag(std::string name, attributes_t &attr) @@ -127,6 +131,39 @@ void TemplateParser::startTag(std::string name, attributes_t &attr)      return;    } +  // Enable script parsing +  if(name == "scripts") { +    if(state != TEMPLATE) error("scripts found outside template."); +    state = SCRIPTS; + +    assert(t); // No template is currently available, cannot create maps! + +    return; +  } + +  // Create script +  if(name == "script") { + +    assert(t); // No template is currently available, cannot create script! + +    switch(state) { +    case SCRIPTS: +      { +        state = SCRIPT; + +        Script s; +        s.attributes = attr; +        t->scripts.push_back(s); +        current_script = &(t->scripts.back()); +      } +      break; +    default: +       error("<script> tag found outside <scripts> tag."); +       break; +    } +    return; +  } +    error("Unknown/illegal tag: %s", name.c_str());  } @@ -137,6 +174,18 @@ void TemplateParser::endTag(std::string name)      current_macro = NULL;      state = TEMPLATE;    } +  if(name == "scripts") state = TEMPLATE; +  if(name == "script") { +    switch(state) { +    case SCRIPT: +      current_script = NULL; +      state = SCRIPTS; +      break; +    default: +      // tag mismatch? +      break; +    } +  }  }  int TemplateParser::readData(char *data, size_t size) diff --git a/server/src/templateparser.h b/server/src/templateparser.h index 5b8302f..89f4917 100644 --- a/server/src/templateparser.h +++ b/server/src/templateparser.h @@ -34,6 +34,8 @@ typedef enum {    UNDEFINED,    TEMPLATE,    MACRO, +  SCRIPTS, +  SCRIPT  } ParserState;  class TemplateParser : public SAXParser { @@ -60,6 +62,7 @@ private:    ParserState state;    Template *t;    Macro *current_macro; +  Script *current_script;    std::vector< Widget* > widgetstack;    // Error callback function. diff --git a/server/src/transactionhandler.cc b/server/src/transactionhandler.cc index 5203ee2..6ab3d89 100644 --- a/server/src/transactionhandler.cc +++ b/server/src/transactionhandler.cc @@ -28,18 +28,27 @@  #include "transactionhandler.h"  #include "macroparser.h" -#include "resumeparser.h"  #include "templateparser.h"  #include "templateheaderparser.h"  #include "courseparser.h"  #include "configuration.h"  #include "luaquerymapper.h" +#include "luaresume.h" +#include "luaoncommit.h"  #include "queryhandlerpentominos.h"  #include "queryhandlerpracro.h"  #include "xml_encode_decode.h"  #include "widgetgenerator.h"  #include "journal.h" +#include "exception.h" + +class NotFoundException : public Exception { +public: +  NotFoundException(Request &r) +  : Exception("Macro " + r.macro + " not found in template " + r.templ) {} +}; +  static std::string error_box(std::string message)  {    std::string errorbox = @@ -52,34 +61,45 @@ static std::string error_box(std::string message)  static std::string handleCommits(Transaction &transaction, Environment &env,                                   Session &session) +  throw(LUAScript::Exception)  {    std::string answer; -  if(transaction.commits.size() > 0) { - -    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, commit); +  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; +    try { +      LUAResume luaresume(transaction, commit); +      luaresume.addScripts(macro->resume_scripts); +      luaresume.run(); +      resume = luaresume.resultString();        commit.fields["journal.resume"] = resume;        session.commitMacro(transaction, commit, *macro); +    } catch(LUAScript::Exception &e) { +      throw e; +    } -      if(resume != "") { -         -        TemplateParser tp(env.templatelist.getLatestVersion(commit.templ)); -        tp.parse(); -        Template *templ = tp.getTemplate(); -         -        session.journal()->addEntry(transaction, commit, resume, templ); -      } +    LUAOnCommit *oncommit = NULL; +    if(macro->commit_scripts.size() != 0) { +      oncommit = new LUAOnCommit(transaction, commit); +      oncommit->addScripts(macro->commit_scripts); +    } + +    if(resume != "" || oncommit != NULL) { +      TemplateParser tp(env.templatelist.getLatestVersion(commit.templ)); +      tp.parse(); +      Template *templ = tp.getTemplate(); -      i++; +      session.journal()->addEntry(transaction, commit, resume, templ, oncommit);      } + +    i++;    }    return answer; @@ -87,6 +107,7 @@ static std::string handleCommits(Transaction &transaction, Environment &env,  static std::string handleRequest(Request &request, Environment &env,                                   Session &session) +  throw(NotFoundException, Exception)  {    std::string answer; @@ -244,7 +265,7 @@ static std::string handleRequest(Request &request, Environment &env,                answer +="\n-- END INCLUDE: '"+spi->attributes["src"]+"'\n";              }            } else { -            answer += xml_encode(spi->attributes["code"]); +            answer += xml_encode(spi->code);            }            answer += "</script>\n";            spi++; @@ -316,9 +337,9 @@ std::string handleTransaction(Request &request,    try {      answer += handleCommits(transaction, env, session); -  } catch( std::exception &e ) { -    ERR(server, "Commit error: %s\n", e.what()); -    return error_box(xml_encode(e.what())); +  } catch( LUAScript::Exception &e ) { +    ERR(server, "Commit error: %s\n", e.msg.c_str()); +    return error_box(xml_encode(e.msg));    }    try { diff --git a/server/src/transactionhandler.h b/server/src/transactionhandler.h index 43ecf0b..914c9ad 100644 --- a/server/src/transactionhandler.h +++ b/server/src/transactionhandler.h @@ -28,18 +28,12 @@  #ifndef __PRACRO_TRANSACTIONHANDLER_H__  #define __PRACRO_TRANSACTIONHANDLER_H__ +#include <string> +  #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(Request &resuest,                                Transaction &transaction,                                Environment &env,  | 
