summaryrefslogtreecommitdiffstats
path: root/task1/ehrclient.cpp
diff options
context:
space:
mode:
authormanuel <manuel@nc8430.lan>2009-10-31 16:11:26 +0100
committermanuel <manuel@nc8430.lan>2009-10-31 16:11:26 +0100
commit1d8445b8461f558987067d870f0f11cdc84b4f35 (patch)
tree146d7d02dddf1a403392ac4b86c19bca4a3c6f75 /task1/ehrclient.cpp
downloadselinux-1d8445b8461f558987067d870f0f11cdc84b4f35.tar.gz
selinux-1d8445b8461f558987067d870f0f11cdc84b4f35.tar.bz2
selinux-1d8445b8461f558987067d870f0f11cdc84b4f35.zip
pushing task1 to repo
Diffstat (limited to 'task1/ehrclient.cpp')
-rw-r--r--task1/ehrclient.cpp496
1 files changed, 496 insertions, 0 deletions
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 @@
1/**
2 * $Id: ehrclient.cpp 2 2009-10-31 02:48:23Z l0728348 $
3 *
4 * Copyright 2009
5 *
6 * @author Manuel Mausz (0728348)
7 * @brief implements the main routines of the console application
8 */
9
10#include <iostream>
11#include <iomanip>
12#include <fstream>
13#include <Ice/Plugin.h>
14#include <time.h>
15#include <sys/time.h>
16#include "ehrclient.h"
17#include "getoptwrapper.h"
18#include "cairodocument.h"
19#include "utils.h"
20
21using namespace std;
22using namespace Ehr;
23
24int EhrClient::run(int argc, char *argv[])
25{
26 /* setup commandline option */
27 CommandOption optnode("node", 0, "The object identifier string used to connect to the EHR provider node", CommandOption::needArg);
28 CommandOption optreq("requestor", 0, "The PEM file containing the requestors certifcate and private key for signing", CommandOption::needArg);
29 CommandOption optowner("owner", 0, "The PEM file containing the patients certifcate and private key for signing", CommandOption::needArg);
30 CommandOption optlist("list", 0, "Display a list of all documents available in the patient's EHR", CommandOption::noArg);
31 CommandOption optdoc("document", 0, "The ID of the document selected for exporting to PDF", CommandOption::needArg);
32 CommandOption optout("output", 0, "The output file", CommandOption::needArg);
33 CommandOption opthelp("help", 0, "Display this help and exit", CommandOption::noArg);
34 CommandOptionParse opts(argc, argv, "Allowed options");
35 opts.addSynopsis("--node <providernode> --requestor <infile> --owner <infile> --list");
36 opts.addSynopsis("--node <providernode> --requestor <infile> --owner <infile> --document <id> --output <outfile>");
37
38 /* error during parsing or help-option set */
39 if (!opts.parse() || opts["help"])
40 {
41 cerr << opts;
42 return EXIT_FAILURE;
43 }
44
45 /* check commandline option combinations */
46 try
47 {
48 /* missing mandatory options */
49 if (!opts["node"] || !opts["requestor"] || !opts["owner"]
50 || (!opts["list"] && !opts["document"]))
51 throw runtime_error("missing mandatory commandline options");
52
53 /* list-option needs to be alone (and vica-versa) */
54 if (opts["list"] && (opts["document"] || opts["output"]))
55 throw runtime_error("unable to combine these commandline options");
56
57 /* document-option needs output-option */
58 if (opts["document"] && !opts["output"])
59 throw runtime_error("--document needs --output");
60
61 /* don't allow options to occur multiple times */
62 if (opts["node"] > 1 || opts["requestor"] > 1 || opts["owner"] > 1
63 || opts["list"] > 1 || opts["document"] > 1 || opts["output"] > 1)
64 throw runtime_error("commandline options may occur only once");
65 }
66 catch(runtime_error& ex)
67 {
68 cerr << appName() << ": " << ex.what() << endl
69 << opts;
70 return EXIT_FAILURE;
71 }
72
73 /* save for later */
74 m_ownercert = opts["owner"].value();
75 m_requestorcert = opts["requestor"].value();
76
77 /* set some properties */
78 Ice::PropertiesPtr props = communicator()->getProperties();
79 if (props->getProperty("IceSSL.CertAuthFile").empty())
80 props->setProperty("IceSSL.CertAuthFile", "./certs/rootca.pem");
81 props->setProperty("IceSSL.CertFile", m_requestorcert);
82
83 /* load plugins */
84 Ice::PluginManagerPtr pluginmgr = communicator()->getPluginManager();
85 pluginmgr->initializePlugins();
86
87 /* get security instance */
88 m_security = &Security::instance();
89
90 /* get provider proxy */
91 Ice::ObjectPrx base = communicator()->stringToProxy(opts["node"]);
92 if (!(m_provider = ProviderPrx::checkedCast(base)))
93 throw runtime_error("error - invalid node or doesn't exist");
94
95 try
96 {
97 if (opts["list"])
98 listDocuments();
99 else if (opts["document"] && opts["output"])
100 {
101 long docid;
102 try
103 {
104 docid = Utils::lexical_cast<long, string>(opts["document"].value());
105 }
106 catch(Utils::bad_lexical_cast& ex)
107 {
108 throw runtime_error("error - invalid document-parameter - not a number");
109 }
110 if (docid <= 0)
111 throw runtime_error("error - invalid document-parameter - only positive numbers");
112 saveDocument(docid, opts["output"]);
113 }
114 }
115 catch(EhrException& e)
116 {
117 throw runtime_error("error - " + e.what);
118 }
119
120 return 0;
121}
122
123/*----------------------------------------------------------------------------*/
124
125#include <memory>
126Ehr::DocumentList EhrClient::getDocumentList()
127{
128 /* fill in request */
129 ListDocumentsRequestPtr request = new ListDocumentsRequest();
130 pair<string, string> cnowner = splitCN(m_ownercert);
131 pair<string, string> cnrequestor = splitCN(m_requestorcert);
132 request->owner.user = cnowner.first;
133 request->owner.provider = cnowner.second;
134 request->requestor.user = cnrequestor.first;
135 request->requestor.provider = cnrequestor.second;
136 request->when.msecs = getTime();
137
138 /* objects containing the signature */
139 Signature requestor;
140 Signature owner;
141
142 /* sign the request */
143 vector<Ice::Byte> data = convertToByteStream(request);
144 m_security->sign(m_requestorcert, data, requestor.data);
145 m_security->sign(m_ownercert, data, owner.data);
146
147 /* execute */
148 return m_provider->listDocuments(request, requestor, owner);
149}
150
151/*----------------------------------------------------------------------------*/
152
153Ehr::DocumentList EhrClient::searchDocument(const bool hasFrom, const long from,
154 const bool hasTill, const long till,
155 const Ehr::DocumentType doctype, const long docid)
156{
157 /* fill in request */
158 FindDocumentsRequestPtr request = new FindDocumentsRequest();
159 pair<string, string> cnowner = splitCN(m_ownercert);
160 pair<string, string> cnrequestor = splitCN(m_requestorcert);
161 request->owner.user = cnowner.first;
162 request->owner.provider = cnowner.second;
163 request->requestor.user = cnrequestor.first;
164 request->requestor.provider = cnrequestor.second;
165 request->when.msecs = getTime();
166 request->hasFrom = hasFrom;
167 request->from.msecs = from;
168 request->hasTill = hasTill;
169 request->from.msecs = till;
170 request->type = doctype;
171 request->documentId = docid;
172
173 /* objects containing the signature */
174 Signature requestor;
175 Signature owner;
176
177 /* sign the request */
178 vector<Ice::Byte> data = convertToByteStream(request);
179 m_security->sign(m_requestorcert, data, requestor.data);
180 m_security->sign(m_ownercert, data, owner.data);
181
182 /* execute */
183 return m_provider->findDocuments(request, requestor, owner);
184}
185
186/*----------------------------------------------------------------------------*/
187
188void EhrClient::listDocuments()
189{
190 /* get document list */
191 DocumentList doclist = getDocumentList();
192 DocumentPtr doc = new Document();
193
194 /* print the list */
195 cout << "Documentlist for " << m_security->getCommonName(m_ownercert) << ":" << endl;
196 if (doclist.size() != 0)
197 {
198 for(unsigned i = 0; i < doclist.size(); ++i)
199 {
200 /* decrypt our document */
201 vector<Ice::Byte> data;
202 m_security->decryptPrivate(m_ownercert, doclist[i].document.encryptedData,
203 doclist[i].document.initialVector, doclist[i].document.encryptedKey, data);
204 Ice::InputStreamPtr in = Ice::createInputStream(communicator(), data);
205 ice_readDocument(in, doc);
206 in->readPendingObjects();
207
208 cout << " ID#" << doclist[i].id
209 << " " << getDate(doclist[i].created.msecs)
210 << endl;
211 if (doc->type == DOCPRESCRIPTION || doc->type == DOCCONSUMEDPRESCRIPTION)
212 {
213 PrescriptionPtr presc = PrescriptionPtr::dynamicCast(doc);
214 cout << " Orginator: " << presc->originatorName << ", " << presc->originatorProfession << endl
215 << " Parmaceutical: " << presc->drugDescription << endl
216 << " Valid: " << getDate(presc->validFrom) << " - " << getDate(presc->expires) << endl;
217 }
218
219 cout << endl;
220 }
221 }
222 else
223 cout << "There are no documents available" << endl;
224}
225
226/*----------------------------------------------------------------------------*/
227
228void EhrClient::saveDocument(const long docid, const std::string& output)
229{
230 /* first check if file already exist */
231 ifstream ifile(output.c_str());
232 if (ifile)
233 throw runtime_error("error - output file already exist");
234 ifile.close();
235
236 /* search for document */
237 DocumentList doclist = searchDocument(docid);
238 if (doclist.size() == 0)
239 throw runtime_error("error - document not found");
240 else if (doclist.size() > 1)
241 throw runtime_error("error - document exists multiple times");
242
243 /* decrypt our document */
244 DocumentPtr doc = new Document();
245 vector<Ice::Byte> data;
246 m_security->decryptPrivate(m_ownercert, doclist[0].document.encryptedData,
247 doclist[0].document.initialVector, doclist[0].document.encryptedKey, data);
248 Ice::InputStreamPtr in = Ice::createInputStream(communicator(), data);
249 ice_readDocument(in, doc);
250 in->readPendingObjects();
251
252 if (doc->type == DOCPRESCRIPTION || doc->type == DOCCONSUMEDPRESCRIPTION)
253 {
254 PrescriptionPtr presc = PrescriptionPtr::dynamicCast(doc);
255
256 /* create our pdf document */
257 //CairoDocument cairodoc(210, 148); // A6 lanscape
258 CairoDocument cairodoc(148, 105); // A5 landscape
259 CairoColor colblack(0, 0, 0);
260 CairoColor colgrey(0.5, 0.5, 0.5);
261 CairoFont fontsmall(7, "sans-serif");
262 CairoFont fontbig(10, "sans-serif");
263
264 /* set default */
265 cairodoc.add(fontbig).add(colblack);
266
267 /* our current position */
268 double docleft = 0;
269 double doctop = 0;
270 double docwidth = cairodoc.width();
271 double docheight = cairodoc.height();
272 double padding = 3;
273
274 /* add document border */
275 CairoRectangle docborder(docleft += padding, doctop += padding,
276 docwidth -= 2 * padding, docheight -= 2 * padding, 0.2);
277 cairodoc.add(docborder);
278
279 /* add inner padding */
280 docleft += padding;
281 doctop += padding;
282 docwidth -= 2 * padding;
283 docheight -= 2 * padding;
284
285 /* add consumer name + label */
286 double boxleftwith = (docwidth - docleft) * 3 / 4;
287 double boxleftwithin = boxleftwith - padding;
288 CairoTextBox consumerlabel(docleft, doctop, boxleftwithin, fontsmall.height());
289 consumerlabel << "Consumer name:";
290 cairodoc.add(consumerlabel.add(colgrey).add(fontsmall));
291 doctop += consumerlabel.height();
292
293 CairoTextBox consumername(docleft, doctop, boxleftwithin, fontbig.height());
294 consumername << presc->consumerName;
295 cairodoc.add(consumername);
296 doctop += consumername.height();
297
298 /* add consumer birth + label */
299 doctop += 1;
300 CairoTextBox consumerdatelabel(docleft, doctop, boxleftwithin, fontsmall.height(), 0);
301 consumerdatelabel << "Date of birth:";
302 cairodoc.add(consumerdatelabel.add(colgrey).add(fontsmall));
303 doctop += consumerdatelabel.height();
304
305 CairoTextBox consumerdate(docleft, doctop, boxleftwithin, fontbig.height());
306 consumerdate << getDate(presc->consumerDateOfBirth);
307 cairodoc.add(consumerdate);
308 doctop += consumerdate.height();
309
310 /* put that in a box */
311 CairoRectangle consumerborder(docborder.x(), docborder.y(),
312 docleft - docborder.x() + boxleftwith, doctop - docborder.y() + padding);
313 cairodoc.add(consumerborder);
314 doctop += 2 * padding;
315
316 /* add originator */
317 CairoTextBox originatorlabel(docleft, doctop, boxleftwithin, fontsmall.height());
318 originatorlabel << "Originator:";
319 cairodoc.add(originatorlabel.add(colgrey).add(fontsmall));
320 doctop += originatorlabel.height();
321
322 CairoTextBox originator(docleft, doctop, boxleftwithin, fontbig.height() * 2);
323 originator << presc->originatorName << ", " << presc->originatorProfession
324 << "\n" << presc->originatorAddress;
325 cairodoc.add(originator);
326 doctop += originator.height();
327
328 /* put that in a box */
329 CairoRectangle originatorborder(docborder.x(), docborder.y(),
330 docleft - docborder.x() + boxleftwith, originator.y() + originator.height());
331 cairodoc.add(originatorborder);
332 doctop += 2 * padding;
333
334 /* add left box */
335 double boxrightx = consumerlabel.x() + boxleftwith;
336 double boxrighty = docborder.y();
337 double boxrightwidth = docborder.x() + docborder.width() - boxrightx;
338 CairoRectangle datesborder(boxrightx, boxrighty, boxrightwidth, originator.y() + originator.height());
339 cairodoc.add(datesborder);
340
341 /* add inner padding */
342 double boxrightxin = boxrightx + padding;
343 boxrighty += padding;
344 double boxrightwidthin = boxrightwidth - 2 * padding;
345
346 /* add creation date */
347 CairoTextBox createdlabel(boxrightxin, boxrighty, boxrightwidthin, fontsmall.height());
348 createdlabel << "Created on:";
349 cairodoc.add(createdlabel.add(colgrey).add(fontsmall));
350 boxrighty += consumerlabel.height();
351
352 CairoTextBox created(boxrightxin, boxrighty, boxrightwidthin, fontbig.height());
353 created << getDate(presc->creationDate);
354 cairodoc.add(created);
355 boxrighty += created.height();
356 boxrighty += 1;
357
358 /* add valid from date */
359 CairoTextBox validfromlabel(boxrightxin, boxrighty, boxrightwidthin, fontsmall.height());
360 validfromlabel << "Valid from:";
361 cairodoc.add(validfromlabel.add(colgrey).add(fontsmall));
362 boxrighty += validfromlabel.height();
363
364 CairoTextBox valid(boxrightxin, boxrighty, boxrightwidthin, fontbig.height());
365 valid << getDate(presc->validFrom);
366 cairodoc.add(valid);
367 boxrighty += valid.height();
368 boxrighty += 1;
369
370 /* add expire date */
371 CairoTextBox expirelabel(boxrightxin, boxrighty, boxrightwidthin, fontsmall.height());
372 expirelabel << "Expire on:";
373 cairodoc.add(expirelabel.add(colgrey).add(fontsmall));
374 boxrighty += expirelabel.height();
375
376 CairoTextBox expire(boxrightxin, boxrighty, boxrightwidthin, fontbig.height());
377 expire << getDate(presc->expires);
378 cairodoc.add(expire);
379
380 /* add drug */
381 CairoTextBox druglabel(docleft, doctop, docwidth, fontsmall.height());
382 druglabel << "Pharmaceutical:";
383 cairodoc.add(druglabel.add(colgrey).add(fontsmall));
384 doctop += druglabel.height();
385
386 CairoTextBox drug(docleft, doctop, docwidth, fontbig.height());
387 drug << presc->drugDescription
388 << "\n" << presc->dosage << " " << presc->measuringUnit
389 << ", " << presc->form;
390 cairodoc.add(drug);
391
392 if (doc->type == DOCCONSUMEDPRESCRIPTION)
393 {
394 ConsumedPrescriptionPtr presc2 = ConsumedPrescriptionPtr::dynamicCast(doc);
395
396 /* add consumed prescreption */
397 double boxbottomy = docborder.y() + docborder.height() -
398 (2 * padding + 2 * fontbig.height() + fontsmall.height());
399 CairoRectangle consumedborder(docborder.x(), boxbottomy, docleft - docborder.x() + boxleftwith,
400 docborder.height() - boxbottomy + docborder.x());
401 cairodoc.add(consumedborder);
402
403 double boxbottomyin = boxbottomy + padding;
404 CairoTextBox consumedlabel(docleft, boxbottomyin, boxleftwithin, fontsmall.height());
405 consumedlabel << "Consumed by:";
406 cairodoc.add(consumedlabel.add(colgrey).add(fontsmall));
407
408 CairoTextBox consumeddatelabel(boxrightxin, boxbottomyin, boxrightwidth, fontsmall.height());
409 consumeddatelabel << "Consumed on:";
410 cairodoc.add(consumeddatelabel.add(colgrey).add(fontsmall));
411 boxbottomyin += consumedlabel.height();
412
413 CairoTextBox consumed(docleft, boxbottomyin, boxleftwithin, fontbig.height() * 2);
414 consumed << presc2->dispenserName << ", " << presc2->dispenserProfession
415 << "\n" << presc2->dispenserAddress;
416 cairodoc.add(consumed);
417
418 CairoTextBox consumedon(boxrightxin, boxbottomyin, boxrightwidth, fontbig.height());
419 consumedon << getDate(presc2->dispensingDate);
420 cairodoc.add(consumedon);
421
422 /* add consumed date border */
423 CairoRectangle consumedborder2(boxrightx, boxbottomy, boxrightwidth,
424 docborder.height() - boxbottomy + docborder.x());
425 cairodoc.add(consumedborder2);
426 }
427
428 cairodoc.save(output, CairoDocument::PDF);
429 }
430 else
431 throw runtime_error("error - unsupported document type");
432
433 return;
434}
435
436/*----------------------------------------------------------------------------*/
437
438long EhrClient::getTime()
439{
440 /* get time in milliseconds */
441 struct timeval tv;
442 gettimeofday(&tv, NULL);
443 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
444}
445
446/*----------------------------------------------------------------------------*/
447
448std::string EhrClient::getDate(long msecs)
449{
450 struct tm doctime;
451 const time_t timep = msecs / 1000;
452 localtime_r(&timep, &doctime);
453 stringstream out("");
454 out << doctime.tm_mday << "." << doctime.tm_mon << "." << doctime.tm_year + 1900
455 << " " << doctime.tm_hour << ':' << doctime.tm_min << ':' << doctime.tm_sec;
456 return out.str();
457}
458
459/*----------------------------------------------------------------------------*/
460
461std::string EhrClient::getDate(const Ehr::Date& date)
462{
463 stringstream out("");
464 out << static_cast<unsigned>(date.day) << "." << static_cast<unsigned>(date.month)
465 << "." << date.year;
466 return out.str();
467}
468
469/*----------------------------------------------------------------------------*/
470
471vector<Ice::Byte> EhrClient::convertToByteStream(const Ice::ObjectPtr& request)
472{
473 /* in order to sign our request, we need to convert
474 * the request to a bytestream first
475 */
476 Ice::OutputStreamPtr out = Ice::createOutputStream(communicator());
477 vector<Ice::Byte> data;
478 out->writeObject(request);
479 out->writePendingObjects();
480 out->finished(data);
481 return data;
482}
483
484/*----------------------------------------------------------------------------*/
485
486std::pair<std::string, std::string> EhrClient::splitCN(const std::string& cert)
487{
488 /* parse requestor details from cert */
489 string commonname = m_security->getCommonName(cert);
490 size_t pos = commonname.find_first_of('@');
491 if (pos == string::npos)
492 throw runtime_error("error - commonname of cert \"" + cert + "\" doesn't have user@provider syntax");
493 return pair<string, string>(commonname.substr(0, pos), commonname.substr(pos + 1));
494}
495
496/* vim: set et sw=2 ts=2: */