From 1d8445b8461f558987067d870f0f11cdc84b4f35 Mon Sep 17 00:00:00 2001 From: manuel Date: Sat, 31 Oct 2009 16:11:26 +0100 Subject: pushing task1 to repo --- task1/ehrclient.cpp | 496 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 task1/ehrclient.cpp (limited to 'task1/ehrclient.cpp') diff --git a/task1/ehrclient.cpp b/task1/ehrclient.cpp new file mode 100644 index 0000000..ec3c2c1 --- /dev/null +++ b/task1/ehrclient.cpp @@ -0,0 +1,496 @@ +/** + * $Id: ehrclient.cpp 2 2009-10-31 02:48:23Z l0728348 $ + * + * Copyright 2009 + * + * @author Manuel Mausz (0728348) + * @brief implements the main routines of the console application + */ + +#include +#include +#include +#include +#include +#include +#include "ehrclient.h" +#include "getoptwrapper.h" +#include "cairodocument.h" +#include "utils.h" + +using namespace std; +using namespace Ehr; + +int EhrClient::run(int argc, char *argv[]) +{ + /* setup commandline option */ + CommandOption optnode("node", 0, "The object identifier string used to connect to the EHR provider node", CommandOption::needArg); + CommandOption optreq("requestor", 0, "The PEM file containing the requestors certifcate and private key for signing", CommandOption::needArg); + CommandOption optowner("owner", 0, "The PEM file containing the patients certifcate and private key for signing", CommandOption::needArg); + CommandOption optlist("list", 0, "Display a list of all documents available in the patient's EHR", CommandOption::noArg); + CommandOption optdoc("document", 0, "The ID of the document selected for exporting to PDF", CommandOption::needArg); + CommandOption optout("output", 0, "The output file", CommandOption::needArg); + CommandOption opthelp("help", 0, "Display this help and exit", CommandOption::noArg); + CommandOptionParse opts(argc, argv, "Allowed options"); + opts.addSynopsis("--node --requestor --owner --list"); + opts.addSynopsis("--node --requestor --owner --document --output "); + + /* error during parsing or help-option set */ + if (!opts.parse() || opts["help"]) + { + cerr << opts; + return EXIT_FAILURE; + } + + /* check commandline option combinations */ + try + { + /* missing mandatory options */ + if (!opts["node"] || !opts["requestor"] || !opts["owner"] + || (!opts["list"] && !opts["document"])) + throw runtime_error("missing mandatory commandline options"); + + /* list-option needs to be alone (and vica-versa) */ + if (opts["list"] && (opts["document"] || opts["output"])) + throw runtime_error("unable to combine these commandline options"); + + /* document-option needs output-option */ + if (opts["document"] && !opts["output"]) + throw runtime_error("--document needs --output"); + + /* don't allow options to occur multiple times */ + if (opts["node"] > 1 || opts["requestor"] > 1 || opts["owner"] > 1 + || opts["list"] > 1 || opts["document"] > 1 || opts["output"] > 1) + throw runtime_error("commandline options may occur only once"); + } + catch(runtime_error& ex) + { + cerr << appName() << ": " << ex.what() << endl + << opts; + return EXIT_FAILURE; + } + + /* save for later */ + m_ownercert = opts["owner"].value(); + m_requestorcert = opts["requestor"].value(); + + /* set some properties */ + Ice::PropertiesPtr props = communicator()->getProperties(); + if (props->getProperty("IceSSL.CertAuthFile").empty()) + props->setProperty("IceSSL.CertAuthFile", "./certs/rootca.pem"); + props->setProperty("IceSSL.CertFile", m_requestorcert); + + /* load plugins */ + Ice::PluginManagerPtr pluginmgr = communicator()->getPluginManager(); + pluginmgr->initializePlugins(); + + /* get security instance */ + m_security = &Security::instance(); + + /* get provider proxy */ + Ice::ObjectPrx base = communicator()->stringToProxy(opts["node"]); + if (!(m_provider = ProviderPrx::checkedCast(base))) + throw runtime_error("error - invalid node or doesn't exist"); + + try + { + if (opts["list"]) + listDocuments(); + else if (opts["document"] && opts["output"]) + { + long docid; + try + { + docid = Utils::lexical_cast(opts["document"].value()); + } + catch(Utils::bad_lexical_cast& ex) + { + throw runtime_error("error - invalid document-parameter - not a number"); + } + if (docid <= 0) + throw runtime_error("error - invalid document-parameter - only positive numbers"); + saveDocument(docid, opts["output"]); + } + } + catch(EhrException& e) + { + throw runtime_error("error - " + e.what); + } + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +#include +Ehr::DocumentList EhrClient::getDocumentList() +{ + /* fill in request */ + ListDocumentsRequestPtr request = new ListDocumentsRequest(); + pair cnowner = splitCN(m_ownercert); + pair cnrequestor = splitCN(m_requestorcert); + request->owner.user = cnowner.first; + request->owner.provider = cnowner.second; + request->requestor.user = cnrequestor.first; + request->requestor.provider = cnrequestor.second; + request->when.msecs = getTime(); + + /* objects containing the signature */ + Signature requestor; + Signature owner; + + /* sign the request */ + vector data = convertToByteStream(request); + m_security->sign(m_requestorcert, data, requestor.data); + m_security->sign(m_ownercert, data, owner.data); + + /* execute */ + return m_provider->listDocuments(request, requestor, owner); +} + +/*----------------------------------------------------------------------------*/ + +Ehr::DocumentList EhrClient::searchDocument(const bool hasFrom, const long from, + const bool hasTill, const long till, + const Ehr::DocumentType doctype, const long docid) +{ + /* fill in request */ + FindDocumentsRequestPtr request = new FindDocumentsRequest(); + pair cnowner = splitCN(m_ownercert); + pair cnrequestor = splitCN(m_requestorcert); + request->owner.user = cnowner.first; + request->owner.provider = cnowner.second; + request->requestor.user = cnrequestor.first; + request->requestor.provider = cnrequestor.second; + request->when.msecs = getTime(); + request->hasFrom = hasFrom; + request->from.msecs = from; + request->hasTill = hasTill; + request->from.msecs = till; + request->type = doctype; + request->documentId = docid; + + /* objects containing the signature */ + Signature requestor; + Signature owner; + + /* sign the request */ + vector data = convertToByteStream(request); + m_security->sign(m_requestorcert, data, requestor.data); + m_security->sign(m_ownercert, data, owner.data); + + /* execute */ + return m_provider->findDocuments(request, requestor, owner); +} + +/*----------------------------------------------------------------------------*/ + +void EhrClient::listDocuments() +{ + /* get document list */ + DocumentList doclist = getDocumentList(); + DocumentPtr doc = new Document(); + + /* print the list */ + cout << "Documentlist for " << m_security->getCommonName(m_ownercert) << ":" << endl; + if (doclist.size() != 0) + { + for(unsigned i = 0; i < doclist.size(); ++i) + { + /* decrypt our document */ + vector data; + m_security->decryptPrivate(m_ownercert, doclist[i].document.encryptedData, + doclist[i].document.initialVector, doclist[i].document.encryptedKey, data); + Ice::InputStreamPtr in = Ice::createInputStream(communicator(), data); + ice_readDocument(in, doc); + in->readPendingObjects(); + + cout << " ID#" << doclist[i].id + << " " << getDate(doclist[i].created.msecs) + << endl; + if (doc->type == DOCPRESCRIPTION || doc->type == DOCCONSUMEDPRESCRIPTION) + { + PrescriptionPtr presc = PrescriptionPtr::dynamicCast(doc); + cout << " Orginator: " << presc->originatorName << ", " << presc->originatorProfession << endl + << " Parmaceutical: " << presc->drugDescription << endl + << " Valid: " << getDate(presc->validFrom) << " - " << getDate(presc->expires) << endl; + } + + cout << endl; + } + } + else + cout << "There are no documents available" << endl; +} + +/*----------------------------------------------------------------------------*/ + +void EhrClient::saveDocument(const long docid, const std::string& output) +{ + /* first check if file already exist */ + ifstream ifile(output.c_str()); + if (ifile) + throw runtime_error("error - output file already exist"); + ifile.close(); + + /* search for document */ + DocumentList doclist = searchDocument(docid); + if (doclist.size() == 0) + throw runtime_error("error - document not found"); + else if (doclist.size() > 1) + throw runtime_error("error - document exists multiple times"); + + /* decrypt our document */ + DocumentPtr doc = new Document(); + vector data; + m_security->decryptPrivate(m_ownercert, doclist[0].document.encryptedData, + doclist[0].document.initialVector, doclist[0].document.encryptedKey, data); + Ice::InputStreamPtr in = Ice::createInputStream(communicator(), data); + ice_readDocument(in, doc); + in->readPendingObjects(); + + if (doc->type == DOCPRESCRIPTION || doc->type == DOCCONSUMEDPRESCRIPTION) + { + PrescriptionPtr presc = PrescriptionPtr::dynamicCast(doc); + + /* create our pdf document */ + //CairoDocument cairodoc(210, 148); // A6 lanscape + CairoDocument cairodoc(148, 105); // A5 landscape + CairoColor colblack(0, 0, 0); + CairoColor colgrey(0.5, 0.5, 0.5); + CairoFont fontsmall(7, "sans-serif"); + CairoFont fontbig(10, "sans-serif"); + + /* set default */ + cairodoc.add(fontbig).add(colblack); + + /* our current position */ + double docleft = 0; + double doctop = 0; + double docwidth = cairodoc.width(); + double docheight = cairodoc.height(); + double padding = 3; + + /* add document border */ + CairoRectangle docborder(docleft += padding, doctop += padding, + docwidth -= 2 * padding, docheight -= 2 * padding, 0.2); + cairodoc.add(docborder); + + /* add inner padding */ + docleft += padding; + doctop += padding; + docwidth -= 2 * padding; + docheight -= 2 * padding; + + /* add consumer name + label */ + double boxleftwith = (docwidth - docleft) * 3 / 4; + double boxleftwithin = boxleftwith - padding; + CairoTextBox consumerlabel(docleft, doctop, boxleftwithin, fontsmall.height()); + consumerlabel << "Consumer name:"; + cairodoc.add(consumerlabel.add(colgrey).add(fontsmall)); + doctop += consumerlabel.height(); + + CairoTextBox consumername(docleft, doctop, boxleftwithin, fontbig.height()); + consumername << presc->consumerName; + cairodoc.add(consumername); + doctop += consumername.height(); + + /* add consumer birth + label */ + doctop += 1; + CairoTextBox consumerdatelabel(docleft, doctop, boxleftwithin, fontsmall.height(), 0); + consumerdatelabel << "Date of birth:"; + cairodoc.add(consumerdatelabel.add(colgrey).add(fontsmall)); + doctop += consumerdatelabel.height(); + + CairoTextBox consumerdate(docleft, doctop, boxleftwithin, fontbig.height()); + consumerdate << getDate(presc->consumerDateOfBirth); + cairodoc.add(consumerdate); + doctop += consumerdate.height(); + + /* put that in a box */ + CairoRectangle consumerborder(docborder.x(), docborder.y(), + docleft - docborder.x() + boxleftwith, doctop - docborder.y() + padding); + cairodoc.add(consumerborder); + doctop += 2 * padding; + + /* add originator */ + CairoTextBox originatorlabel(docleft, doctop, boxleftwithin, fontsmall.height()); + originatorlabel << "Originator:"; + cairodoc.add(originatorlabel.add(colgrey).add(fontsmall)); + doctop += originatorlabel.height(); + + CairoTextBox originator(docleft, doctop, boxleftwithin, fontbig.height() * 2); + originator << presc->originatorName << ", " << presc->originatorProfession + << "\n" << presc->originatorAddress; + cairodoc.add(originator); + doctop += originator.height(); + + /* put that in a box */ + CairoRectangle originatorborder(docborder.x(), docborder.y(), + docleft - docborder.x() + boxleftwith, originator.y() + originator.height()); + cairodoc.add(originatorborder); + doctop += 2 * padding; + + /* add left box */ + double boxrightx = consumerlabel.x() + boxleftwith; + double boxrighty = docborder.y(); + double boxrightwidth = docborder.x() + docborder.width() - boxrightx; + CairoRectangle datesborder(boxrightx, boxrighty, boxrightwidth, originator.y() + originator.height()); + cairodoc.add(datesborder); + + /* add inner padding */ + double boxrightxin = boxrightx + padding; + boxrighty += padding; + double boxrightwidthin = boxrightwidth - 2 * padding; + + /* add creation date */ + CairoTextBox createdlabel(boxrightxin, boxrighty, boxrightwidthin, fontsmall.height()); + createdlabel << "Created on:"; + cairodoc.add(createdlabel.add(colgrey).add(fontsmall)); + boxrighty += consumerlabel.height(); + + CairoTextBox created(boxrightxin, boxrighty, boxrightwidthin, fontbig.height()); + created << getDate(presc->creationDate); + cairodoc.add(created); + boxrighty += created.height(); + boxrighty += 1; + + /* add valid from date */ + CairoTextBox validfromlabel(boxrightxin, boxrighty, boxrightwidthin, fontsmall.height()); + validfromlabel << "Valid from:"; + cairodoc.add(validfromlabel.add(colgrey).add(fontsmall)); + boxrighty += validfromlabel.height(); + + CairoTextBox valid(boxrightxin, boxrighty, boxrightwidthin, fontbig.height()); + valid << getDate(presc->validFrom); + cairodoc.add(valid); + boxrighty += valid.height(); + boxrighty += 1; + + /* add expire date */ + CairoTextBox expirelabel(boxrightxin, boxrighty, boxrightwidthin, fontsmall.height()); + expirelabel << "Expire on:"; + cairodoc.add(expirelabel.add(colgrey).add(fontsmall)); + boxrighty += expirelabel.height(); + + CairoTextBox expire(boxrightxin, boxrighty, boxrightwidthin, fontbig.height()); + expire << getDate(presc->expires); + cairodoc.add(expire); + + /* add drug */ + CairoTextBox druglabel(docleft, doctop, docwidth, fontsmall.height()); + druglabel << "Pharmaceutical:"; + cairodoc.add(druglabel.add(colgrey).add(fontsmall)); + doctop += druglabel.height(); + + CairoTextBox drug(docleft, doctop, docwidth, fontbig.height()); + drug << presc->drugDescription + << "\n" << presc->dosage << " " << presc->measuringUnit + << ", " << presc->form; + cairodoc.add(drug); + + if (doc->type == DOCCONSUMEDPRESCRIPTION) + { + ConsumedPrescriptionPtr presc2 = ConsumedPrescriptionPtr::dynamicCast(doc); + + /* add consumed prescreption */ + double boxbottomy = docborder.y() + docborder.height() - + (2 * padding + 2 * fontbig.height() + fontsmall.height()); + CairoRectangle consumedborder(docborder.x(), boxbottomy, docleft - docborder.x() + boxleftwith, + docborder.height() - boxbottomy + docborder.x()); + cairodoc.add(consumedborder); + + double boxbottomyin = boxbottomy + padding; + CairoTextBox consumedlabel(docleft, boxbottomyin, boxleftwithin, fontsmall.height()); + consumedlabel << "Consumed by:"; + cairodoc.add(consumedlabel.add(colgrey).add(fontsmall)); + + CairoTextBox consumeddatelabel(boxrightxin, boxbottomyin, boxrightwidth, fontsmall.height()); + consumeddatelabel << "Consumed on:"; + cairodoc.add(consumeddatelabel.add(colgrey).add(fontsmall)); + boxbottomyin += consumedlabel.height(); + + CairoTextBox consumed(docleft, boxbottomyin, boxleftwithin, fontbig.height() * 2); + consumed << presc2->dispenserName << ", " << presc2->dispenserProfession + << "\n" << presc2->dispenserAddress; + cairodoc.add(consumed); + + CairoTextBox consumedon(boxrightxin, boxbottomyin, boxrightwidth, fontbig.height()); + consumedon << getDate(presc2->dispensingDate); + cairodoc.add(consumedon); + + /* add consumed date border */ + CairoRectangle consumedborder2(boxrightx, boxbottomy, boxrightwidth, + docborder.height() - boxbottomy + docborder.x()); + cairodoc.add(consumedborder2); + } + + cairodoc.save(output, CairoDocument::PDF); + } + else + throw runtime_error("error - unsupported document type"); + + return; +} + +/*----------------------------------------------------------------------------*/ + +long EhrClient::getTime() +{ + /* get time in milliseconds */ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +/*----------------------------------------------------------------------------*/ + +std::string EhrClient::getDate(long msecs) +{ + struct tm doctime; + const time_t timep = msecs / 1000; + localtime_r(&timep, &doctime); + stringstream out(""); + out << doctime.tm_mday << "." << doctime.tm_mon << "." << doctime.tm_year + 1900 + << " " << doctime.tm_hour << ':' << doctime.tm_min << ':' << doctime.tm_sec; + return out.str(); +} + +/*----------------------------------------------------------------------------*/ + +std::string EhrClient::getDate(const Ehr::Date& date) +{ + stringstream out(""); + out << static_cast(date.day) << "." << static_cast(date.month) + << "." << date.year; + return out.str(); +} + +/*----------------------------------------------------------------------------*/ + +vector EhrClient::convertToByteStream(const Ice::ObjectPtr& request) +{ + /* in order to sign our request, we need to convert + * the request to a bytestream first + */ + Ice::OutputStreamPtr out = Ice::createOutputStream(communicator()); + vector data; + out->writeObject(request); + out->writePendingObjects(); + out->finished(data); + return data; +} + +/*----------------------------------------------------------------------------*/ + +std::pair EhrClient::splitCN(const std::string& cert) +{ + /* parse requestor details from cert */ + string commonname = m_security->getCommonName(cert); + size_t pos = commonname.find_first_of('@'); + if (pos == string::npos) + throw runtime_error("error - commonname of cert \"" + cert + "\" doesn't have user@provider syntax"); + return pair(commonname.substr(0, pos), commonname.substr(pos + 1)); +} + +/* vim: set et sw=2 ts=2: */ -- cgit v1.2.3