otsdaq_utilities  v2_04_01
LogbookSupervisor.cc
1 #include "otsdaq-utilities/Logbook/LogbookSupervisor.h"
2 //#include "otsdaq-core/MessageFacility/MessageFacility.h"
3 //#include "otsdaq-core/Macros/CoutMacros.h"
4 //#include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
5 //#include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
6 //#include "otsdaq-core/SOAPUtilities/SOAPUtilities.h"
7 //#include "otsdaq-core/SOAPUtilities/SOAPParameters.h"
8 //
9 //#include <xdaq/NamespaceURI.h>
10 //
11 //#include <iostream>
12 //#include <fstream>
13 //#include <string>
14 #include <dirent.h> //for DIR
15 #include <sys/stat.h> //for mkdir
16 //#include <thread> // std::thread
17 
18 using namespace ots;
19 
20 const std::string LOGBOOK_PATH = __ENV__("LOGBOOK_DATA_PATH") + std::string("/");
21 #define LOGBOOK_EXPERIMENT_LIST_PATH LOGBOOK_PATH + "experiment_list.xml"
22 #define LOGBOOK_EXPERIMENT_DIR_PREFACE "log_"
23 #define LOGBOOK_UPLOADS_PATH "uploads/" // within experiment directory
24 #define LOGBOOK_LOGBOOKS_PATH "logbooks/"
25 #define LOGBOOK_PREVIEWS_PATH "previews/"
26 #define LOGBOOK_FILE_PREFACE "entries_"
27 #define LOGBOOK_FILE_EXTENSION ".xml"
28 
29 #define ACTIVE_EXPERIMENT_PATH LOGBOOK_PATH + "active_experiment.txt"
30 #define REMOVE_EXPERIMENT_LOG_PATH LOGBOOK_PATH + "removed_experiments.log"
31 
32 #define XML_ADMIN_STATUS "logbook_admin_status"
33 #define XML_STATUS "logbook_status"
34 #define XML_MOST_RECENT_DAY "most_recent_day"
35 #define XML_EXPERIMENTS_ROOT "experiments"
36 #define XML_EXPERIMENT "experiment"
37 #define XML_ACTIVE_EXPERIMENT "active_experiment"
38 #define XML_EXPERIMENT_CREATE "create_time"
39 #define XML_EXPERIMENT_CREATOR "creator"
40 
41 #define XML_LOGBOOK_ENTRY "logbook_entry"
42 #define XML_LOGBOOK_ENTRY_SUBJECT "logbook_entry_subject"
43 #define XML_LOGBOOK_ENTRY_TEXT "logbook_entry_text"
44 #define XML_LOGBOOK_ENTRY_FILE "logbook_entry_file"
45 #define XML_LOGBOOK_ENTRY_TIME "logbook_entry_time"
46 #define XML_LOGBOOK_ENTRY_CREATOR "logbook_entry_creator"
47 #define XML_LOGBOOK_ENTRY_HIDDEN "logbook_entry_hidden"
48 #define XML_LOGBOOK_ENTRY_HIDER "logbook_entry_hider"
49 #define XML_LOGBOOK_ENTRY_HIDDEN_TIME "logbook_entry_hidden_time"
50 
51 #define XML_PREVIEW_INDEX "preview_index"
52 #define LOGBOOK_PREVIEW_FILE "preview.xml"
53 #define LOGBOOK_PREVIEW_UPLOAD_PREFACE "upload_"
54 
55 XDAQ_INSTANTIATOR_IMPL(LogbookSupervisor)
56 
57 #undef __MF_SUBJECT__
58 #define __MF_SUBJECT__ "Logbook"
59 
60 //========================================================================================================================
61 // sendmail ~~
62 // Helper function to send emails to the subscriber list of the active experiment
63 int sendmail(const char* to, const char* from, const char* subject, const char* message)
64 {
65  int retval = -1;
66  FILE* mailpipe = popen("/usr/lib/sendmail -t", "w");
67  if(mailpipe != NULL)
68  {
69  fprintf(mailpipe, "To: %s\n", to);
70  fprintf(mailpipe, "From: %s\n", from);
71  fprintf(mailpipe, "Subject: %s\n\n", subject);
72  fwrite(message, 1, strlen(message), mailpipe);
73  fwrite(".\n", 1, 2, mailpipe);
74  pclose(mailpipe);
75  retval = 0;
76  }
77  else
78  {
79  perror("Failed to invoke sendmail");
80  }
81  return retval;
82 }
83 
84 //========================================================================================================================
85 LogbookSupervisor::LogbookSupervisor(xdaq::ApplicationStub* stub)
86  : CoreSupervisorBase(stub)
87  , allowedFileUploadTypes_({"image/png",
88  "image/jpeg",
89  "image/gif",
90  "image/bmp",
91  "application/pdf",
92  "application/zip",
93  "text/plain"}) // init allowed file upload types
94  , matchingFileUploadTypes_({"png",
95  "jpeg",
96  "gif",
97  "bmp",
98  "pdf",
99  "zip",
100  "txt"}) // init allowed file upload types
101 {
102  INIT_MF("LogbookSupervisor");
103 
104  // xgi::bind (this, &LogbookSupervisor::Default, "Default" );
105  // xgi::bind (this, &LogbookSupervisor::Log, "Log" );
106  // xgi::bind (this, &LogbookSupervisor::LogImage, "LogImage" );
107  // xgi::bind (this, &LogbookSupervisor::LogReport, "LogReport" );
108 
109  xoap::bind(this,
110  &LogbookSupervisor::MakeSystemLogbookEntry,
111  "MakeSystemLogbookEntry",
112  XDAQ_NS_URI);
113 
114  init();
115 
116  // TODO allow admins to subscribe email addresses to the active experiment
117  // sendmail("rrivera@fnal.gov","ots-instance@otsdaq.fnal.gov","My
118  // Subject","Message\nHello!\n\nwhats up.");
119 }
120 
121 //========================================================================================================================
122 LogbookSupervisor::~LogbookSupervisor(void) { destroy(); }
123 //========================================================================================================================
124 void LogbookSupervisor::init(void)
125 {
126  // called by constructor
127  // allSupervisorInfo_.init(getApplicationContext());
128 
129  if(1) // check if LOGBOOK_PATH and subpaths event exist?! (if not, attempt to create)
130  {
131  std::string path = LOGBOOK_PATH;
132  DIR* dir = opendir(path.c_str());
133  if(dir)
134  closedir(dir);
135  else if(-1 == mkdir(path.c_str(), 0755))
136  {
137  // lets create the service folder (for first time)
138  std::stringstream ss;
139  ss << __COUT_HDR_FL__ << "Service directory creation failed: " << path
140  << std::endl;
141  __SS_THROW__;
142  }
143 
144  path = LOGBOOK_PATH + LOGBOOK_UPLOADS_PATH;
145  dir = opendir(path.c_str());
146  if(dir)
147  closedir(dir);
148  else if(-1 == mkdir((path).c_str(), 0755))
149  {
150  // lets create the service folder (for first time)
151  __SS__ << "Service directory creation failed: " << path << std::endl;
152  __SS_THROW__;
153  }
154 
155  path = LOGBOOK_PATH + LOGBOOK_LOGBOOKS_PATH;
156  dir = opendir(path.c_str());
157  if(dir)
158  closedir(dir);
159  else if(-1 == mkdir(path.c_str(), 0755))
160  {
161  // lets create the service folder (for first time)
162  __SS__ << "Service directory creation failed: " << path << std::endl;
163  __SS_THROW__;
164  }
165  }
166 
167  getActiveExperiment(); // init active experiment
168  __COUT__ << "Active Experiment is " << activeExperiment_ << std::endl;
169  mostRecentDayIndex_ = 0;
170 }
171 
172 //========================================================================================================================
173 void LogbookSupervisor::destroy(void)
174 {
175  // called by destructor
176 }
177 
178 //========================================================================================================================
179 void LogbookSupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
180 {
181  __COUT__ << " active experiment " << activeExperiment_ << std::endl;
182  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
183  "src='/WebPath/html/Logbook.html?urn="
184  << this->getApplicationDescriptor()->getLocalId()
185  << "&active_experiment=" << activeExperiment_ << "'></frameset></html>";
186 }
187 
188 //========================================================================================================================
189 // setSupervisorPropertyDefaults
190 // override to set defaults for supervisor property values (before user settings
191 // override)
192 void LogbookSupervisor::setSupervisorPropertyDefaults()
193 {
194  CorePropertySupervisorBase::setSupervisorProperty(
195  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
196  std::string() +
197  "*=1 | CreateExperiment=-1 | RemoveExperiment=-1 | GetExperimentListAdmin=-1 "
198  "| SetActiveExperiment=-1" +
199  " | AdminRemoveRestoreEntry=-1");
200 }
201 
202 //========================================================================================================================
203 // forceSupervisorPropertyValues
204 // override to force supervisor property values (and ignore user settings)
205 void LogbookSupervisor::forceSupervisorPropertyValues()
206 {
207  CorePropertySupervisorBase::setSupervisorProperty(
208  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
209  "RefreshLogbook");
210  CorePropertySupervisorBase::setSupervisorProperty(
211  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NonXMLRequestTypes,
212  "LogImage | LogReport");
213  // CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NeedUsernameRequestTypes,
214  // "CreateExperiment | RemoveExperiment | PreviewEntry |
215  // AdminRemoveRestoreEntry");
216 }
217 
218 //========================================================================================================================
219 // request
220 // Handles Web Interface requests to Logbook supervisor.
221 // Does not refresh cookie for automatic update checks.
222 void LogbookSupervisor::request(const std::string& requestType,
223  cgicc::Cgicc& cgiIn,
224  HttpXmlDocument& xmlOut,
225  const WebUsers::RequestUserInfo& userInfo)
226 {
227  // Commands
228  // CreateExperiment
229  // RemoveExperiment
230  // GetExperimentList
231  // SetActiveExperiment
232  // RefreshLogbook
233  // PreviewEntry
234  // ApproveEntry
235  // AdminRemoveRestoreEntr
236 
237  //
238  // cgicc::Cgicc cgiIn(in);
239  // std::string requestType;
240  // if((requestType = CgiDataUtilities::postData(cgiIn,"RequestType")) == "")
241  // requestType = cgiIn("RequestType"); //get command from form, if PreviewEntry
242  //
243  // __COUT__ << "requestType " << requestType << " files: " << cgiIn.getFiles().size()
244  //<< std::endl;
245  //
246  //
247  // HttpXmlDocument xmlOut;
248  // uint64_t activeSessionIndex;
249  // std::string user;
250  // uint8_t userPermissions;
251  //
252  // //**** start LOGIN GATEWAY CODE ***//
253  // {
254  // bool automaticCommand = requestType == "RefreshLogbook"; //automatic commands
255  // should not refresh cookie code.. only user initiated commands should! bool
256  // checkLock = true; bool getUser = (requestType == "CreateExperiment") ||
257  //(requestType == "RemoveExperiment") ||
258  // (requestType == "PreviewEntry") || (requestType ==
259  //"AdminRemoveRestoreEntry"); bool requireLock = false;
260  //
261  // if(!theRemoteWebUsers_.xmlRequestToGateway(
262  // cgiIn,
263  // out,
264  // &xmlOut,
265  // allSupervisorInfo_,
266  // &userPermissions, //acquire user's access level (optionally null
267  // pointer) !automaticCommand, //true/false refresh cookie
268  // code 1, //set access level requirement to pass gateway
269  // checkLock, //true/false enable check that system is
270  // unlocked or this user has the lock requireLock,
271  // //true/false requires this user has the lock to proceed
272  // 0,//&userWithLock,
274  // (getUser?&user:0) //acquire username of this user
275  //(optionally
276  // null pointer) ,0//,&displayName //acquire user's Display
277  // Name
278  // ,&activeSessionIndex //acquire user's session index associated
279  // with the cookieCode
280  // ))
281  // { //failure
282  // __COUT__ << "Failed Login Gateway: " <<
283  // out->str() << std::endl; //print out return string on failure
284  // return;
285  // }
286  // }
287  // //**** end LOGIN GATEWAY CODE ***//
288 
289  // to report to logbook admin status use
290  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,tempStr);
291 
292  if(requestType == "CreateExperiment")
293  {
294  // check that experiment directory does not exist, and it is not in xml list
295  // create experiment
296  // create directory
297  // add to experiments list
298  //
299  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
300  // {
301  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
302  // permissions."); goto CLEANUP;
303  // }
304  //
305  // //user is admin
306  //
307  // __COUT__ << "Admin" << std::endl;
308 
309  // get creator name
310  std::string creator = userInfo.username_;
311 
312  createExperiment(
313  CgiDataUtilities::postData(cgiIn, "Experiment"), creator, &xmlOut);
314 
315  __COUT__ << "Created" << std::endl;
316  }
317  else if(requestType == "RemoveExperiment")
318  {
319  // remove from xml list, but do not remove directory (requires manual delete so
320  // mistakes aren't made)
321  //
322  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
323  // {
324  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
325  // permissions."); goto CLEANUP;
326  // }
327 
328  // get remover name
329  std::string remover = userInfo.username_;
330  removeExperiment(
331  CgiDataUtilities::postData(cgiIn, "Experiment"), remover, &xmlOut);
332  }
333  else if(requestType == "GetExperimentList")
334  {
335  // remove from xml list, but do not remove directory (requires manual delete so
336  // mistakes aren't made)
337  if(userInfo.permissionLevel_ >=
338  CoreSupervisorBase::getSupervisorPropertyUserPermissionsThreshold(
339  "GetExperimentListAdmin"))
340  {
341  xmlOut.addTextElementToData("is_admin", "0"); // indicate not an admin
342  return;
343  }
344  // else
345 
346  xmlOut.addTextElementToData("is_admin", "1"); // indicate not an admin
347  getExperiments(&xmlOut);
348  }
349  else if(requestType == "SetActiveExperiment")
350  {
351  // check that experiment exists
352  // set active experiment
353 
354  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
355  // {
356  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
357  // permissions."); goto CLEANUP;
358  // }
359 
360  webUserSetActiveExperiment(CgiDataUtilities::postData(cgiIn, "Experiment"),
361  &xmlOut);
362  }
363  else if(requestType == "RefreshLogbook")
364  {
365  // returns logbook for currently active experiment based on date and duration
366  // parameters
367 
368  std::string Date = CgiDataUtilities::postData(cgiIn, "Date");
369  std::string Duration = CgiDataUtilities::postData(cgiIn, "Duration");
370 
371  time_t date;
372  unsigned char duration;
373  sscanf(Date.c_str(), "%li", &date); // scan for unsigned long
374  sscanf(Duration.c_str(), "%hhu", &duration); // scan for unsigned char
375 
376  __COUT__ << "date " << date << " duration " << (int)duration << std::endl;
377  std::stringstream str;
378  refreshLogbook(date, duration, &xmlOut, (std::ostringstream*)&str);
379  __COUT__ << str.str() << std::endl;
380  }
381  else if(requestType == "PreviewEntry")
382  {
383  // cleanup temporary folder
384  // NOTE: all input parameters for PreviewEntry will be attached to form
385  // so use cgiIn(xxx) to get values.
386  // increment number for each temporary preview, previewPostTempIndex_
387  // save entry and uploads to previewPath / previewPostTempIndex_ /.
388 
389  cleanUpPreviews();
390  std::string EntryText = cgiIn("EntryText");
391  __COUT__ << "EntryText " << EntryText << std::endl << std::endl;
392  std::string EntrySubject = cgiIn("EntrySubject");
393  __COUT__ << "EntrySubject " << EntrySubject << std::endl << std::endl;
394 
395  // get creator name
396  std::string creator = userInfo.username_;
397 
398  savePostPreview(EntrySubject, EntryText, cgiIn.getFiles(), creator, &xmlOut);
399  // else xmlOut.addTextElementToData(XML_STATUS,"Failed - could not get username
400  // info.");
401  }
402  else if(requestType == "ApproveEntry")
403  {
404  // If Approve = "1", then previewed Log entry specified by PreviewNumber
405  // is moved to logbook
406  // Else the specified Log entry is deleted.
407  std::string PreviewNumber = CgiDataUtilities::postData(cgiIn, "PreviewNumber");
408  std::string Approve = CgiDataUtilities::postData(cgiIn, "Approve");
409 
410  movePreviewEntry(PreviewNumber, Approve == "1", &xmlOut);
411  }
412  else if(requestType == "AdminRemoveRestoreEntry")
413  {
414  // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
415  // {
416  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
417  // permissions."); goto CLEANUP;
418  // }
419 
420  std::string EntryId = CgiDataUtilities::postData(cgiIn, "EntryId");
421  bool Hide = CgiDataUtilities::postData(cgiIn, "Hide") == "1" ? true : false;
422 
423  // get creator name
424  std::string hider = userInfo.username_;
425 
426  hideLogbookEntry(EntryId, Hide, hider);
427 
428  xmlOut.addTextElementToData(XML_ADMIN_STATUS, "1"); // success
429  }
430  else
431  __COUT__ << "requestType request not recognized." << std::endl;
432 }
433 
434 //========================================================================================================================
435 // request
436 // Handles Web Interface requests to Logbook supervisor.
437 // Does not refresh cookie for automatic update checks.
438 void LogbookSupervisor::nonXmlRequest(const std::string& requestType,
439  cgicc::Cgicc& cgiIn,
440  std::ostream& out,
441  const WebUsers::RequestUserInfo& userInfo)
442 {
443  // Commands
444  // LogImage
445  // LogReport
446 
447  if(requestType == "LogImage")
448  {
449  std::string src = CgiDataUtilities::getData(cgiIn, "src");
450  __COUT__ << " Get Log Image " << src << std::endl;
451 
452  out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
453  "src='/WebPath/html/LogbookImage.html?urn="
454  << this->getApplicationDescriptor()->getLocalId() << "&src=" << src
455  << "'></frameset></html>";
456  }
457  else if(requestType == "LogReport")
458  {
459  std::string activeExperiment =
460  CgiDataUtilities::getData(cgiIn, "activeExperiment");
461  __COUT__ << " Start Log Report for " << activeExperiment << std::endl;
462 
463  out << "<!DOCTYPE HTML><html lang='en'><header><title>ots Logbook "
464  "Reports</title></header><frameset col='100%' row='100%'><frame "
465  "src='/WebPath/html/LogbookReport.html?urn="
466  << this->getApplicationDescriptor()->getLocalId()
467  << "&activeExperiment=" << activeExperiment << "'></frameset></html>";
468  }
469  else
470  __COUT__ << "requestType request not recognized." << std::endl;
471 }
472 
473 //========================================================================================================================
474 // xoap::MakeSystemLogbookEntry
475 // make a system logbook entry into active experiment's logbook from Supervisor only
476 // TODO: (how to enforce?)
477 xoap::MessageReference LogbookSupervisor::MakeSystemLogbookEntry(
478  xoap::MessageReference msg)
479 {
480  SOAPParameters parameters("EntryText");
481  // SOAPParametersV parameters(1);
482  // parameters[0].setName("EntryText");
483  SOAPUtilities::receive(msg, parameters);
484  std::string EntryText = parameters.getValue("EntryText");
485 
486  __COUT__ << "Received External Supervisor System Entry " << EntryText << std::endl;
487  __COUT__ << "Active Experiment is " << activeExperiment_ << std::endl;
488 
489  std::string retStr = "Success";
490 
491  std::string logPath,
492  logDirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
493  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + activeExperiment_;
494 
495  char dayIndexStr[20];
496  HttpXmlDocument logXml;
497  char fileIndex[40];
498  xercesc::DOMElement* entryEl;
499  DIR* dir;
500 
501  if(activeExperiment_ == "")
502  {
503  retStr = "Warning - Currently, no Active Experiment.";
504  __COUT__ << retStr << std::endl;
505  goto XOAP_CLEANUP;
506  }
507 
508  // check that directory exists
509  dir = opendir(logDirPath.c_str());
510  if(!dir)
511  {
512  retStr = "Error - Active Experiment directory missing.";
513  __COUT__ << retStr << std::endl;
514  goto XOAP_CLEANUP;
515  }
516  closedir(dir);
517 
518  sprintf(dayIndexStr, "%6.6lu", time(0) / (60 * 60 * 24)); // get today's index
519 
520  logPath = logDirPath + "/" + LOGBOOK_FILE_PREFACE + activeExperiment_ + "_" +
521  (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
522  __COUT__ << "logPath " << logPath << std::endl;
523 
524  logXml.loadXmlDocument(logPath); // NOTE: on failure, no need to do anything
525  // because empty XML file is valid structure
526  // entry structure:
527  // <XML_LOGBOOK_ENTRY>
528  // <XML_LOGBOOK_ENTRY_TIME>
529  // <XML_LOGBOOK_ENTRY_CREATOR>
530  // <XML_LOGBOOK_ENTRY_TEXT>
531  // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
532  // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
533  // </XML_LOGBOOK_ENTRY>
534 
535  entryEl = logXml.addTextElementToData(XML_LOGBOOK_ENTRY);
536 
537  sprintf(fileIndex,
538  "%lu_%lu",
539  time(0),
540  clock()); // create unique time label for entry time(0)_clock()
541  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_TIME, fileIndex, entryEl);
542  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_CREATOR, "SYSTEM LOG", entryEl);
543  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_TEXT, EntryText, entryEl);
544  logXml.addTextElementToParent(XML_LOGBOOK_ENTRY_SUBJECT, "System Log", entryEl);
545 
546  logXml.saveXmlDocument(logPath);
547 
548 XOAP_CLEANUP:
549 
550  // fill return parameters
551  SOAPParameters retParameters("Status", retStr);
552  // SOAPParametersV retParameters(1);
553  // retParameters[0].setName("Status");
554  // retParameters[0].setValue(retStr);
555 
556  return SOAPUtilities::makeSOAPMessageReference("LogbookEntryStatusResponse",
557  retParameters);
558 }
559 
560 //
567 // void LogbookSupervisor::LogImage(xgi::Input * in, xgi::Output * out )
568 // throw (xgi::exception::Exception)
569 //{
570 // cgicc::Cgicc cgiIn(in);
571 // std::string src = CgiDataUtilities::getData(cgiIn,"src");
572 // __COUT__ << " Get Log Image " << src << std::endl;
573 // *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame
574 // src='/WebPath/html/LogbookImage.html?urn=" <<
575 // this->getApplicationDescriptor()->getLocalId() << "&src=" << src <<
576 //"'></frameset></html>";
577 //}
578 //
585 // void LogbookSupervisor::LogReport(xgi::Input * in, xgi::Output * out )
586 // throw (xgi::exception::Exception)
587 //{
588 // cgicc::Cgicc cgiIn(in);
589 // std::string activeExperiment = CgiDataUtilities::getData(cgiIn,"activeExperiment");
590 // __COUT__ << " Start Log Report for " << activeExperiment << std::endl;
591 // *out << "<!DOCTYPE HTML><html lang='en'><header><title>ots Logbook
592 // Reports</title></header><frameset col='100%' row='100%'><frame
593 // src='/WebPath/html/LogbookReport.html?urn=" <<
594 // this->getApplicationDescriptor()->getLocalId() << "&activeExperiment=" <<
595 // activeExperiment << "'></frameset></html>";
596 //}
597 
598 //========================================================================================================================
599 // getActiveExperiment
600 // load active experiment from txt file, must be first line in file
601 std::string LogbookSupervisor::getActiveExperiment()
602 {
603  FILE* fp = fopen(std::string((std::string)ACTIVE_EXPERIMENT_PATH).c_str(), "r");
604  if(!fp)
605  activeExperiment_ = "";
606  else
607  {
608  char line[100];
609  if(!fgets(line, 100, fp))
610  line[0] =
611  '\0'; // if null returned, file is empty and line is untouched, so touch.
612  fclose(fp);
613 
614  // remove \n \r
615  if(line[strlen(line) - 2] == '\r')
616  line[strlen(line) - 2] = '\0';
617  else if(line[strlen(line) - 1] == '\n')
618  line[strlen(line) - 1] = '\0';
619 
620  activeExperiment_ = line;
621  }
622 
623  return activeExperiment_;
624 }
625 
626 //========================================================================================================================
627 // setActiveExperiment
628 // "" means no experiment is active
629 void LogbookSupervisor::setActiveExperiment(std::string experiment)
630 {
631  FILE* fp = fopen(std::string((std::string)ACTIVE_EXPERIMENT_PATH).c_str(), "w");
632  if(!fp)
633  {
634  __COUT__ << "FATAL ERROR!!! - file write" << std::endl;
635  return;
636  }
637 
638  fprintf(fp, "%s", experiment.c_str());
639  fclose(fp);
640 
641  if(activeExperiment_ != "" &&
642  activeExperiment_ != experiment) // old active experiment is on its way out
643  theRemoteWebUsers_.makeSystemLogbookEntry(
644  allSupervisorInfo_.getGatewayDescriptor(),
645  "Experiment was made inactive."); // make system logbook entry
646 
647  bool entryNeeded = false;
648  if(experiment != "" &&
649  activeExperiment_ != experiment) // old active experiment is on its way out
650  entryNeeded = true;
651 
652  activeExperiment_ = experiment;
653  __COUT__ << "Active Experiment set to " << activeExperiment_ << std::endl;
654 
655  if(entryNeeded)
656  theRemoteWebUsers_.makeSystemLogbookEntry(
657  allSupervisorInfo_.getGatewayDescriptor(),
658  "Experiment was made active."); // make system logbook entry
659 }
660 
661 //========================================================================================================================
662 // validateExperimentName
663 // remove all chars that are not alphanumeric, dashes, or underscores
664 bool LogbookSupervisor::validateExperimentName(std::string& exp)
665 {
666  if(exp.length() < EXPERIMENT_NAME_MIN_LENTH ||
667  exp.length() > EXPERIMENT_NAME_MAX_LENTH)
668  return false;
669  for(int i = 0; i < (int)exp.length(); ++i)
670  if(!((exp[i] >= 'a' && exp[i] <= 'z') || (exp[i] >= 'A' && exp[i] <= 'Z') ||
671  (exp[i] >= '0' && exp[i] <= '9') || (exp[i] == '-' || exp[i] == '_')))
672  {
673  exp = exp.substr(0, i) + exp.substr(i + 1);
674  --i;
675  } // remove illegal chars and rewind i
676 
677  return true;
678 }
679 
680 //========================================================================================================================
681 // getExperiments
682 // if xmlOut, then output experiments to xml
683 // if out, then output to stream
684 void LogbookSupervisor::getExperiments(HttpXmlDocument* xmlOut, std::ostringstream* out)
685 {
686  // check that experiment listing doesn't already exist
687  HttpXmlDocument expXml;
688  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
689  {
690  __COUT__ << "Fatal Error - Experiment database." << std::endl;
691  __COUT__ << "Creating empty experiment database." << std::endl;
692 
693  expXml.addTextElementToData((std::string)XML_EXPERIMENTS_ROOT);
694  expXml.saveXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH);
695  return;
696  }
697 
698  std::vector<std::string> exps;
699  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
700 
701  if(xmlOut)
702  xmlOut->addTextElementToData(XML_ACTIVE_EXPERIMENT, activeExperiment_);
703 
704  for(unsigned int i = 0; i < exps.size(); ++i) // loop experiments
705  {
706  if(xmlOut)
707  xmlOut->addTextElementToData(XML_EXPERIMENT, exps[i]);
708  if(out)
709  *out << exps[i] << std::endl;
710  }
711 }
712 
713 //========================================================================================================================
714 // createExperiment
715 void LogbookSupervisor::createExperiment(std::string experiment,
716  std::string creator,
717  HttpXmlDocument* xmlOut)
718 {
719  if(!validateExperimentName(experiment))
720  {
721  if(xmlOut)
722  xmlOut->addTextElementToData(
723  XML_ADMIN_STATUS, "Error - Experiment name must be 3-25 characters.");
724  return;
725  }
726 
727  __COUT__ << "experiment " << experiment << std::endl;
728 
729  // check that directory doesn't already exist
730  std::string dirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
731  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + experiment;
732 
733  __COUT__ << "dirPath " << dirPath << std::endl;
734 
735  bool directoryExists = false;
736  DIR* dir = opendir(dirPath.c_str());
737  if(dir)
738  {
739  closedir(dir);
740  directoryExists = true;
741  }
742 
743  // check that experiment listing doesn't already exist
744  HttpXmlDocument expXml;
745  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
746  {
747  if(xmlOut)
748  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
749  "Fatal Error - Experiment database.");
750  return;
751  }
752 
753  std::vector<std::string> exps;
754  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
755 
756  for(unsigned int i = 0; i < exps.size(); ++i)
757  if(experiment == exps[i])
758  {
759  if(xmlOut)
760  xmlOut->addTextElementToData(
761  XML_ADMIN_STATUS,
762  "Failed - Experiment, " + experiment + ", already exists.");
763  return;
764  }
765  __COUT__ << "experiments count: " << exps.size() << std::endl;
766 
767  // everything checks out, add experiment!
768  // add to experiments xml doc and save
769  // <experiments>
770  // ...
771  // <experiment_name = "xx">
772  // <create_time = "##"> <who_created = "aa">
773  xercesc::DOMElement* expEl =
774  expXml.addTextElementToParent(XML_EXPERIMENT, experiment, XML_EXPERIMENTS_ROOT);
775  char createTime[20];
776  sprintf(createTime, "%lu", time(0));
777  expXml.addTextElementToParent(XML_EXPERIMENT_CREATE, createTime, expEl);
778  expXml.addTextElementToParent(XML_EXPERIMENT_CREATOR, creator, expEl);
779  expXml.saveXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH);
780 
781  // create directory only if doesn't already exist
782  if(directoryExists)
783  {
784  // check uploads folder
785  dirPath += "/" + (std::string)LOGBOOK_UPLOADS_PATH;
786  __COUT__ << "Checking uploads directory" << std::endl;
787 
788  directoryExists = false;
789  dir = opendir(dirPath.c_str());
790  if(!dir) // check if uploads directory exists within experiment directory
791  {
792  __COUT__ << "Creating uploads directory" << std::endl;
793  if(-1 == mkdir(dirPath.c_str(), 0755)) // make uploads directory
794  {
795  if(xmlOut)
796  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
797  "Failed - uploads directory for " +
798  experiment + " was not created.");
799  __COUT__ << "Uploads directory failure." << std::endl;
800  return;
801  }
802  }
803  else
804  closedir(dir);
805 
806  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
807  "Directory already exists for " + experiment +
808  ", re-added to list of experiments.");
809  return;
810  }
811  __COUT__ << "Creating experiment and uploads directory at: " << dirPath << std::endl;
812  if(-1 == mkdir(dirPath.c_str(), 0755) ||
813  -1 == mkdir((dirPath + "/" + (std::string)LOGBOOK_UPLOADS_PATH).c_str(), 0755))
814  {
815  if(xmlOut)
816  xmlOut->addTextElementToData(
817  XML_ADMIN_STATUS,
818  "Failed - directory, " + experiment + ", could not be created.");
819  return;
820  }
821 
822  if(xmlOut)
823  xmlOut->addTextElementToData(
824  XML_ADMIN_STATUS, "Experiment, " + experiment + ", successfully created.");
825 }
826 
827 //========================================================================================================================
828 // webUserSetActiveExperiment
829 // if experiment exists, set as active
830 // to clear active experiment set to ""
831 void LogbookSupervisor::webUserSetActiveExperiment(std::string experiment,
832  HttpXmlDocument* xmlOut)
833 {
834  if(experiment == "") // clear active experiment
835  {
836  setActiveExperiment(experiment);
837  if(xmlOut)
838  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
839  "Active experiment cleared successfully.");
840  }
841 
842  // check that experiment listing exists
843  HttpXmlDocument expXml;
844  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
845  {
846  if(xmlOut)
847  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
848  "Fatal Error - Experiment database.");
849  return;
850  }
851  std::vector<std::string> exps;
852  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
853 
854  unsigned int i;
855  for(i = 0; i < exps.size(); ++i)
856  if(experiment == exps[i])
857  break;
858 
859  if(i == exps.size()) // not found
860  {
861  if(xmlOut)
862  xmlOut->addTextElementToData(
863  XML_ADMIN_STATUS, "Failed - Experiment, " + experiment + ", not found.");
864  return;
865  }
866 
867  // found!
868  setActiveExperiment(experiment);
869  if(xmlOut)
870  xmlOut->addTextElementToData(
871  XML_ADMIN_STATUS,
872  "Active experiment set to " + experiment + " successfully.");
873 }
874 
875 //========================================================================================================================
876 // removeExperiment
877 // remove experiment from listing only (do NOT remove logbook data directory)
878 // record remover in log file REMOVE_EXPERIMENT_LOG_PATH
879 void LogbookSupervisor::removeExperiment(std::string experiment,
880  std::string remover,
881  HttpXmlDocument* xmlOut)
882 {
883  __COUT__ << "experiment " << experiment << std::endl;
884 
885  // check that experiment listing exists
886  HttpXmlDocument expXml;
887  if(!expXml.loadXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH))
888  {
889  if(xmlOut)
890  xmlOut->addTextElementToData(XML_ADMIN_STATUS,
891  "Fatal Error - Experiment database.");
892  return;
893  }
894  std::vector<std::string> exps;
895  expXml.getAllMatchingValues(XML_EXPERIMENT, exps);
896 
897  unsigned int i;
898  for(i = 0; i < exps.size(); ++i)
899  if(experiment == exps[i])
900  break;
901 
902  if(i == exps.size()) // not found
903  {
904  if(xmlOut)
905  xmlOut->addTextElementToData(
906  XML_ADMIN_STATUS, "Failed - Experiment, " + experiment + ", not found.");
907  return;
908  }
909 
910  // found!
911 
912  // remove experiment from xml
913  xercesc::DOMElement* parent = expXml.getMatchingElement(XML_EXPERIMENTS_ROOT);
914  xercesc::DOMElement* child = expXml.getMatchingElement(XML_EXPERIMENT, i);
915  __COUT__ << "experiments original count: " << expXml.getChildrenCount(parent)
916  << std::endl;
917  expXml.recursiveRemoveChild(child, parent);
918  __COUT__ << "experiments new count: " << expXml.getChildrenCount(parent) << std::endl;
919 
920  // update removed experiments log
921  FILE* fp = fopen(((std::string)REMOVE_EXPERIMENT_LOG_PATH).c_str(), "a");
922  if(!fp)
923  {
924  if(xmlOut)
925  xmlOut->addTextElementToData(XML_ADMIN_STATUS, "Fatal Error - Remove log.");
926  return;
927  }
928  fprintf(fp,
929  "%s -- %s Experiment removed by %s.\n",
930  asctime(localtime(&((time_t const&)(time(0))))),
931  experiment.c_str(),
932  remover.c_str());
933  fclose(fp);
934 
935  expXml.saveXmlDocument((std::string)LOGBOOK_EXPERIMENT_LIST_PATH); // save database
936 
937  // unset from activeExperiment_ if is active experiment
938  if(activeExperiment_ == experiment)
939  setActiveExperiment(); // clear active experiment
940 
941  if(xmlOut)
942  xmlOut->addTextElementToData(
943  XML_ADMIN_STATUS, "Experiment, " + experiment + ", successfully removed.");
944 }
945 
946 //========================================================================================================================
947 // refreshLogbook
948 // returns all the logbook data for active experiment from starting date and back in
949 // time for duration total number of days.
950 // e.g. data = today, and duration = 1 returns logbook for today from active
951 // experiment The entries are returns from oldest to newest
952 void LogbookSupervisor::refreshLogbook(time_t date,
953  unsigned char duration,
954  HttpXmlDocument* xmlOut,
955  std::ostringstream* out,
956  std::string experiment)
957 {
958  if(experiment == "")
959  experiment = activeExperiment_; // default to active experiment
960  if(xmlOut)
961  xmlOut->addTextElementToData(XML_ACTIVE_EXPERIMENT, experiment); // for success
962 
963  // check that directory exists
964  std::string dirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
965  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + experiment;
966 
967  if(out)
968  *out << __COUT_HDR_FL__ << "dirPath " << dirPath << std::endl;
969 
970  DIR* dir = opendir(dirPath.c_str());
971  if(!dir)
972  {
973  if(xmlOut)
974  xmlOut->addTextElementToData(
975  XML_STATUS,
976  "Error - Directory for experiment, " + experiment + ", missing.");
977  if(out)
978  *out << __COUT_HDR_FL__ << "Error - Directory missing" << std::endl;
979  return;
980  }
981 
982  unsigned int baseDay;
983 
984  if(!date) // if date is 0 take most recent day and update it
985  {
986  struct dirent* drnt;
987  unsigned int extractedDay;
988  int start, finish; // always find number after last _ and before last .
989 
990  mostRecentDayIndex_ = 0;
991  while((drnt = readdir(dir)))
992  {
993  // if(out) *out << __COUT_HDR_FL__ << "dirContents " << drnt->d_name <<
994  // std::endl;
995 
996  if(strcmp(&(drnt->d_name[strlen(drnt->d_name) - 4]), ".xml"))
997  continue; // skip non logbook files
998 
999  for(finish = strlen(drnt->d_name) - 1; finish > 0; --finish)
1000  if(drnt->d_name[finish] == '.')
1001  break;
1002  if(finish == 0)
1003  {
1004  if(out)
1005  *out << __COUT_HDR_FL__ << "failed to find day index finish "
1006  << std::endl;
1007  return;
1008  }
1009  for(start = finish - 1; start > 0; --start)
1010  if(drnt->d_name[start - 1] == '_')
1011  break;
1012  if(start == 0)
1013  {
1014  if(out)
1015  *out << __COUT_HDR_FL__ << "failed to find day index start "
1016  << std::endl;
1017  return;
1018  }
1019  drnt->d_name[finish] = '\0';
1020  extractedDay = atoi((char*)(&(drnt->d_name[start])));
1021  // if(out) *out << __COUT_HDR_FL__ << "dirContents " << (char
1022  // *)(&(drnt->d_name[start])) << " " << extractedDay << std::endl;
1023  if(!mostRecentDayIndex_ || mostRecentDayIndex_ < extractedDay)
1024  mostRecentDayIndex_ = extractedDay;
1025  }
1026  if(out)
1027  *out << __COUT_HDR_FL__
1028  << "dirContents done, found most recent day: " << mostRecentDayIndex_
1029  << std::endl;
1030 
1031  baseDay = mostRecentDayIndex_;
1032  }
1033  else
1034  baseDay = (date / (60 * 60 * 24));
1035  closedir(dir);
1036 
1037  std::string entryPath;
1038  char dayIndexStr[20];
1039  FILE* fp;
1040 
1041  // read all days selected out
1042  // entries are in file as oldest at top, newest at bottom
1043  // so read oldest files first to have global ordering of old to new
1044  for(unsigned char i = duration; i != 0; --i)
1045  {
1046  sprintf(dayIndexStr, "%6.6u", baseDay - i + 1); // get day index, back in time
1047  entryPath = dirPath + "/" + LOGBOOK_FILE_PREFACE + experiment + "_" +
1048  (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
1049 
1050  if(out)
1051  *out << __COUT_HDR_FL__ << "Directory Entry " << entryPath << std::endl;
1052 
1053  fp = fopen(entryPath.c_str(), "r");
1054  if(!fp)
1055  {
1056  if(out)
1057  *out << __COUT_HDR_FL__ << "File not found" << std::endl;
1058  continue;
1059  }
1060  fclose(fp);
1061 
1062  // file found! read file out
1063 
1064  HttpXmlDocument logXml;
1065  if(!logXml.loadXmlDocument(entryPath))
1066  {
1067  if(xmlOut)
1068  xmlOut->addTextElementToData(
1069  XML_STATUS, "Critical Failure - log did not load. Notify admins.");
1070  if(out)
1071  *out << __COUT_HDR_FL__ << "Failure - log XML did not load" << std::endl;
1072  return;
1073  }
1074 
1075  if(xmlOut)
1076  xmlOut->copyDataChildren(logXml); // copy file to output xml
1077  }
1078 
1079  if(xmlOut)
1080  xmlOut->addTextElementToData(XML_STATUS, "1"); // for success
1081  if(out)
1082  *out << __COUT_HDR_FL__ << "Today: " << time(0) / (60 * 60 * 24) << std::endl;
1083 
1084  sprintf(dayIndexStr, "%lu", time(0) / (60 * 60 * 24) - mostRecentDayIndex_);
1085  if(xmlOut)
1086  xmlOut->addTextElementToData(XML_MOST_RECENT_DAY,
1087  dayIndexStr); // send most recent day index
1088 }
1089 
1090 //========================================================================================================================
1091 // cleanUpPreviews
1092 // cleanup logbook preview directory
1093 // all names have time_t creation time + "_" + incremented index
1094 void LogbookSupervisor::cleanUpPreviews()
1095 {
1096  std::string previewPath =
1097  (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_PREVIEWS_PATH;
1098 
1099  DIR* dir = opendir(previewPath.c_str());
1100  if(!dir)
1101  {
1102  __COUT__ << "Error - Previews directory missing: " << previewPath << std::endl;
1103  return;
1104  }
1105 
1106  struct dirent* entry;
1107  time_t dirCreateTime;
1108  unsigned int i;
1109 
1110  while(
1111  (entry = readdir(
1112  dir))) // loop through all entries in directory and remove anything expired
1113  {
1114  if(strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0 &&
1115  strcmp(entry->d_name, ".svn") != 0)
1116  {
1117  // replace _ with space so sscanf works
1118  for(i = 0; i < strlen(entry->d_name); ++i)
1119  if(entry->d_name[i] == '_')
1120  {
1121  entry->d_name[i] = ' ';
1122  break;
1123  }
1124  sscanf(entry->d_name, "%li", &dirCreateTime);
1125 
1126  if((time(0) - dirCreateTime) > LOGBOOK_PREVIEW_EXPIRATION_TIME)
1127  {
1128  __COUT__ << "Expired" << std::endl;
1129 
1130  entry->d_name[i] = '_'; // put _ back
1131 
1132  __COUT__ << "rm -rf " << previewPath + (std::string)entry->d_name
1133  << std::endl
1134  << std::endl;
1135  system(
1136  ((std::string)("rm -rf " + previewPath + (std::string)entry->d_name))
1137  .c_str());
1138  }
1139  }
1140  }
1141 
1142  closedir(dir);
1143 }
1144 
1145 //========================================================================================================================
1146 // savePostPreview
1147 // save post to preview directory named with time and incremented index
1148 void LogbookSupervisor::savePostPreview(std::string& subject,
1149  std::string& text,
1150  const std::vector<cgicc::FormFile>& files,
1151  std::string creator,
1152  HttpXmlDocument* xmlOut)
1153 {
1154  if(activeExperiment_ == "") // no active experiment!
1155  {
1156  if(xmlOut)
1157  xmlOut->addTextElementToData(XML_STATUS,
1158  "Failed - no active experiment currently!");
1159  return;
1160  }
1161 
1162  char fileIndex[40];
1163  sprintf(fileIndex,
1164  "%lu_%lu",
1165  time(0),
1166  clock()); // create unique time label for entry time(0)_clock()
1167  std::string previewPath = (std::string)LOGBOOK_PATH +
1168  (std::string)LOGBOOK_PREVIEWS_PATH + (std::string)fileIndex;
1169 
1170  __COUT__ << "previewPath " << previewPath << std::endl;
1171  if(-1 == mkdir(previewPath.c_str(), 0755))
1172  {
1173  if(xmlOut)
1174  xmlOut->addTextElementToData(XML_STATUS,
1175  "Failed - preview could not be generated.");
1176  return;
1177  }
1178 
1179  // new directory created successfully, save text and files
1180  // entry structure:
1181  // <XML_LOGBOOK_ENTRY>
1182  // <XML_LOGBOOK_ENTRY_TIME>
1183  // <XML_LOGBOOK_ENTRY_CREATOR>
1184  // <XML_LOGBOOK_ENTRY_SUBJECT>
1185  // <XML_LOGBOOK_ENTRY_TEXT>
1186  // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
1187  // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
1188  // </XML_LOGBOOK_ENTRY>
1189 
1190  escapeLogbookEntry(text);
1191  escapeLogbookEntry(subject);
1192  __COUT__ << "~~subject " << subject << std::endl
1193  << "~~text " << text << std::endl
1194  << std::endl;
1195 
1196  HttpXmlDocument previewXml;
1197 
1198  previewXml.addTextElementToData(XML_LOGBOOK_ENTRY);
1199  previewXml.addTextElementToParent(
1200  XML_LOGBOOK_ENTRY_TIME, fileIndex, XML_LOGBOOK_ENTRY);
1201  if(xmlOut)
1202  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_TIME, fileIndex); // return time
1203  previewXml.addTextElementToParent(
1204  XML_LOGBOOK_ENTRY_CREATOR, creator, XML_LOGBOOK_ENTRY);
1205  if(xmlOut)
1206  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_CREATOR,
1207  creator); // return creator
1208  previewXml.addTextElementToParent(XML_LOGBOOK_ENTRY_TEXT, text, XML_LOGBOOK_ENTRY);
1209  if(xmlOut)
1210  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_TEXT, text); // return text
1211  previewXml.addTextElementToParent(
1212  XML_LOGBOOK_ENTRY_SUBJECT, subject, XML_LOGBOOK_ENTRY);
1213  if(xmlOut)
1214  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_SUBJECT,
1215  subject); // return subject
1216 
1217  __COUT__ << "file size " << files.size() << std::endl;
1218 
1219  std::string filename;
1220  std::ofstream myfile;
1221  for(unsigned int i = 0; i < files.size(); ++i)
1222  {
1223  previewXml.addTextElementToParent(
1224  XML_LOGBOOK_ENTRY_FILE, files[i].getDataType(), XML_LOGBOOK_ENTRY);
1225  if(xmlOut)
1226  xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY_FILE,
1227  files[i].getDataType()); // return file type
1228 
1229  if((filename = validateUploadFileType(files[i].getDataType())) ==
1230  "") // invalid file type
1231  {
1232  if(xmlOut)
1233  xmlOut->addTextElementToData(
1234  XML_STATUS,
1235  "Failed - invalid file type, " + files[i].getDataType() + ".");
1236  return;
1237  }
1238 
1239  // file validated, so save upload to temp directory
1240  sprintf(fileIndex, "%d", i);
1241  filename = previewPath + "/" + (std::string)LOGBOOK_PREVIEW_UPLOAD_PREFACE +
1242  (std::string)fileIndex + "." + filename;
1243 
1244  __COUT__ << "file " << i << " - " << filename << std::endl;
1245  myfile.open(filename.c_str());
1246  if(myfile.is_open())
1247  {
1248  files[i].writeToStream(myfile);
1249  myfile.close();
1250  }
1251  }
1252 
1253  // save xml doc for preview entry
1254  previewXml.saveXmlDocument(previewPath + "/" + (std::string)LOGBOOK_PREVIEW_FILE);
1255 
1256  if(xmlOut)
1257  xmlOut->addTextElementToData(XML_STATUS, "1"); // 1 indicates success!
1258  if(xmlOut)
1259  xmlOut->addTextElementToData(XML_PREVIEW_INDEX,
1260  "1"); // 1 indicates is a preview post
1261 }
1262 
1263 //========================================================================================================================
1264 // movePreviewEntry
1265 // if approve
1266 // move entry to current active logbook
1267 // if not approve
1268 // delete directory
1269 void LogbookSupervisor::movePreviewEntry(std::string previewNumber,
1270  bool approve,
1271  HttpXmlDocument* xmlOut)
1272 {
1273  __COUT__ << "previewNumber " << previewNumber
1274  << (approve ? " Accepted" : " Cancelled") << std::endl;
1275 
1276  std::string sysCmd, previewPath = (std::string)LOGBOOK_PATH +
1277  (std::string)LOGBOOK_PREVIEWS_PATH + previewNumber;
1278 
1279  if(approve)
1280  {
1281  // move from preview to logbook
1282 
1283  HttpXmlDocument previewXml;
1284  previewXml.loadXmlDocument(previewPath + "/" + (std::string)LOGBOOK_PREVIEW_FILE);
1285 
1286  std::string logPath,
1287  logDirPath = (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
1288  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + activeExperiment_;
1289 
1290  // check that directory exists
1291  DIR* dir = opendir(logDirPath.c_str());
1292  if(!dir)
1293  {
1294  __COUT__ << "Error - Active Experiment directory missing: " << logPath
1295  << std::endl;
1296  return;
1297  }
1298  closedir(dir);
1299 
1300  char dayIndexStr[20];
1301  sprintf(dayIndexStr, "%6.6lu", time(0) / (60 * 60 * 24)); // get today's index
1302 
1303  logPath = logDirPath + "/" + LOGBOOK_FILE_PREFACE + activeExperiment_ + "_" +
1304  (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
1305  __COUT__ << "logPath " << logPath << std::endl;
1306 
1307  HttpXmlDocument logXml;
1308  logXml.loadXmlDocument(logPath); // NOTE: on failure, no need to do anything
1309  // because empty XML file is valid structure
1310  // entry structure:
1311  // <XML_LOGBOOK_ENTRY>
1312  // <XML_LOGBOOK_ENTRY_TIME>
1313  // <XML_LOGBOOK_ENTRY_CREATOR>
1314  // <XML_LOGBOOK_ENTRY_TEXT>
1315  // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
1316  // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
1317  // </XML_LOGBOOK_ENTRY>
1318 
1319  logXml.copyDataChildren(previewXml); // Copy from previewXML to logXML
1320  logXml.saveXmlDocument(logPath);
1321 
1322  // Move upload files
1323  std::vector<std::string> fileTypes;
1324  previewXml.getAllMatchingValues(XML_LOGBOOK_ENTRY_FILE, fileTypes);
1325  std::string entryTimeLabel = previewXml.getMatchingValue(XML_LOGBOOK_ENTRY_TIME);
1326  std::string fileExtension, previewFilename, logFilename;
1327  char fileIndex[10];
1328  for(unsigned int i = 0; i < fileTypes.size(); ++i)
1329  {
1330  if((fileExtension = validateUploadFileType(fileTypes[i])) ==
1331  "") // invalid file type
1332  {
1333  __COUT__ << "Failed - invalid file type: " << fileTypes[i] << std::endl;
1334  continue;
1335  }
1336 
1337  // file validated, so save upload to temp directory
1338  sprintf(fileIndex, "%d", i);
1339  previewFilename = (std::string)LOGBOOK_PREVIEW_UPLOAD_PREFACE +
1340  (std::string)fileIndex + "." + fileExtension;
1341  logFilename = (std::string)LOGBOOK_PREVIEW_UPLOAD_PREFACE + entryTimeLabel +
1342  "_" + (std::string)fileIndex + "." + fileExtension;
1343 
1344  sysCmd = "mv " + (previewPath + "/" + previewFilename) + " " +
1345  (logDirPath + "/" + (std::string)LOGBOOK_UPLOADS_PATH + logFilename);
1346  __COUT__ << sysCmd << std::endl;
1347  system(sysCmd.c_str());
1348  }
1349  }
1350 
1351  // remove preview directory
1352  sysCmd = "rm -rf " + previewPath;
1353  __COUT__ << sysCmd << std::endl << std::endl;
1354  system(sysCmd.c_str());
1355 }
1356 
1357 //========================================================================================================================
1358 // validateUploadFileType
1359 // returns "" if file type is invalide, else returns file extension to use
1360 std::string LogbookSupervisor::validateUploadFileType(const std::string fileType)
1361 {
1362  for(unsigned int i = 0; i < allowedFileUploadTypes_.size(); ++i)
1363  if(allowedFileUploadTypes_[i] == fileType)
1364  return matchingFileUploadTypes_[i]; // found and done
1365 
1366  return ""; // not valid, return ""
1367 }
1368 
1369 //========================================================================================================================
1370 // escapeLogbookEntry
1371 // replace html/xhtml reserved characters with equivalent.
1372 // reserved: ", ', &, <, >, \n, double-space
1373 void LogbookSupervisor::escapeLogbookEntry(std::string& entry)
1374 {
1375  // NOTE: should already be taken care of by web gui javascript! do we care to check?
1376 }
1377 
1378 //========================================================================================================================
1379 // hideLogbookEntry
1380 // NOTE: does not actually delete entry, just marks as hidden
1381 // removes/restores logbook entry. Requires admin priveleges
1382 // Locates the entry within the active experiment and if hide
1383 // appends xml fields:
1384 // XML_LOGBOOK_ENTRY_HIDDEN
1385 // XML_LOGBOOK_ENTRY_HIDER
1386 // XML_LOGBOOK_ENTRY_HIDDEN_TIME
1387 void LogbookSupervisor::hideLogbookEntry(const std::string& entryId,
1388  bool hide,
1389  const std::string& hider)
1390 {
1391  __COUT__ << "Hide=" << hide << " for entryid " << entryId << std::endl;
1392 
1393  // get path to entries file for entry at entryId
1394  char dayIndexStr[20];
1395  unsigned int i;
1396  for(i = 0; i < entryId.length(); ++i)
1397  if(entryId[i] == '_')
1398  {
1399  dayIndexStr[i] = '\0';
1400  break;
1401  }
1402  else
1403  dayIndexStr[i] = entryId[i];
1404  time_t days;
1405  sscanf(dayIndexStr, "%li", &days); // get seconds
1406  days /= 60 * 60 * 24; // get days
1407  sprintf(dayIndexStr, "%6.6lu", days);
1408 
1409  std::string logDirPath =
1410  (std::string)LOGBOOK_PATH + (std::string)LOGBOOK_LOGBOOKS_PATH +
1411  (std::string)LOGBOOK_EXPERIMENT_DIR_PREFACE + activeExperiment_;
1412  std::string logPath = logDirPath + "/" + LOGBOOK_FILE_PREFACE + activeExperiment_ +
1413  "_" + (std::string)dayIndexStr + LOGBOOK_FILE_EXTENSION;
1414 
1415  __COUT__ << "logPath=" << logPath << std::endl;
1416 
1417  // locate entry
1418  HttpXmlDocument logXml;
1419  if(!logXml.loadXmlDocument(logPath))
1420  {
1421  __COUT__ << "Failure - log XML did not load" << std::endl;
1422  return;
1423  }
1424 
1425  std::vector<std::string> allEntryIds;
1426  logXml.getAllMatchingValues(XML_LOGBOOK_ENTRY_TIME, allEntryIds);
1427  for(i = 0; i < allEntryIds.size(); ++i)
1428  if(allEntryIds[i] == entryId)
1429  break;
1430  if(i == allEntryIds.size())
1431  {
1432  __COUT__ << "Failure - entry not found" << std::endl;
1433  return;
1434  }
1435 
1436  __COUT__ << "found " << logXml.getMatchingValue(XML_LOGBOOK_ENTRY_TEXT, i)
1437  << std::endl;
1438 
1439  xercesc::DOMElement *hiddenParentEl, *entryParentEl = logXml.getMatchingElement(
1440  XML_LOGBOOK_ENTRY, i); // get entry element
1441 
1442  // check if already hidden
1443  hiddenParentEl =
1444  logXml.getMatchingElementInSubtree(entryParentEl, XML_LOGBOOK_ENTRY_HIDDEN);
1445 
1446  if(hide) // remove entry
1447  {
1448  if(hiddenParentEl)
1449  {
1450  __COUT__ << "Hidden tag already applied to entry." << std::endl;
1451  return;
1452  }
1453  hiddenParentEl = logXml.addTextElementToParent(
1454  XML_LOGBOOK_ENTRY_HIDDEN,
1455  "1",
1456  entryParentEl); // add hidden parent with value "1"
1457  logXml.addTextElementToParent(
1458  XML_LOGBOOK_ENTRY_HIDER, hider, hiddenParentEl); // hider
1459  sprintf(dayIndexStr, "%lu", time(0));
1460  logXml.addTextElementToParent(
1461  XML_LOGBOOK_ENTRY_HIDDEN_TIME, dayIndexStr, hiddenParentEl); // hide time
1462  }
1463  else // restore entry
1464  {
1465  if(!hiddenParentEl)
1466  {
1467  __COUT__ << "Entry already was not hidden." << std::endl;
1468  return;
1469  }
1470 
1471  logXml.recursiveRemoveChild(hiddenParentEl,
1472  entryParentEl); // remove hidden parent
1473  }
1474  logXml.saveXmlDocument(logPath);
1475  __COUT__ << "Success." << std::endl;
1476 }