LCOV - code coverage report
Current view: top level - progs - mhttpd.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 8934 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 198 0

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         mhttpd.cxx
       4              :   Created by:   Stefan Ritt
       5              : 
       6              :   Contents:     Web server program for midas RPC calls
       7              : 
       8              : \********************************************************************/
       9              : 
      10              : #undef NDEBUG // midas required assert() to be always enabled
      11              : 
      12              : #include <stdio.h>
      13              : #include <math.h> // fabs()
      14              : #include <assert.h>
      15              : #include <algorithm> // std::sort()
      16              : #include <thread> // std::thread
      17              : #include <deque>  // std::deque
      18              : #include <mutex>  // std::mutex
      19              : #include <condition_variable>  // std::condition_variable
      20              : #include <atomic> // std::atomic<>
      21              : 
      22              : #include "midas.h"
      23              : #include "msystem.h"
      24              : #include "mxml.h"
      25              : #include "odbxx.h"
      26              : #include "mstrlcpy.h"
      27              : 
      28              : #include "mgd.h"
      29              : #include "history.h"
      30              : 
      31              : #ifdef HAVE_MSCB
      32              : #include "mscb.h"
      33              : #endif
      34              : 
      35              : #include "mjsonrpc.h"
      36              : #include "mvodb.h"
      37              : 
      38              : /* refresh times in seconds */
      39              : #define DEFAULT_REFRESH 60
      40              : 
      41              : #ifdef HAVE_MONGOOSE6
      42              : static MUTEX_T* request_mutex = NULL;
      43              : #endif
      44              : 
      45              : #define OBSOLETE 1 // obsolete code scheduled for removal. K.O.
      46              : 
      47              : static std::mutex gMutex;
      48              : static MVOdb* gOdb = NULL;
      49              : 
      50              : /*------------------------------------------------------------------*/
      51              : 
      52              : #define MAX_GROUPS    32
      53              : #define MAX_VARS     100
      54              : 
      55              : /*------------------------------------------------------------------*/
      56              : 
      57            0 : static std::string toString(int i)
      58              : {
      59              :    char buf[256];
      60            0 :    sprintf(buf, "%d", i);
      61            0 :    return buf;
      62              : }
      63              : 
      64              : /*------------------------------------------------------------------*/
      65              : 
      66              : class Attachment
      67              : {
      68              : public:
      69              :    char*  attachment_buffer[3];
      70              :    size_t attachment_size[3];
      71              : public:
      72            0 :    Attachment() // ctor
      73            0 :    {
      74            0 :       for (int i=0; i<3; i++) {
      75            0 :          attachment_buffer[i] = NULL;
      76            0 :          attachment_size[i] = 0;
      77              :       }
      78            0 :    }
      79            0 :    ~Attachment() // dtor
      80              :    {
      81            0 :       for (int i=0; i<3; i++) {
      82            0 :          clear(i);
      83              :       }
      84            0 :    }
      85            0 :    void clear(int i)
      86              :    {
      87            0 :       if (attachment_size[i]) {
      88            0 :          attachment_size[i] = 0;
      89            0 :          free(attachment_buffer[i]);
      90            0 :          attachment_buffer[i] = NULL;
      91              :       }
      92            0 :    }
      93              : };
      94              : static BOOL elog_mode = FALSE;
      95              : static BOOL history_mode = FALSE;
      96              : static BOOL verbose = FALSE;
      97              : 
      98              : // month name from midas.c
      99              : extern const char *mname[];
     100              : 
     101              : static const char default_type_list[20][NAME_LENGTH] = {
     102              :    "Routine",
     103              :    "Shift summary",
     104              :    "Minor error",
     105              :    "Severe error",
     106              :    "Fix",
     107              :    "Question",
     108              :    "Info",
     109              :    "Modification",
     110              :    "Reply",
     111              :    "Alarm",
     112              :    "Test",
     113              :    "Other"
     114              : };
     115              : 
     116              : static const char default_system_list[20][NAME_LENGTH] = {
     117              :    "General",
     118              :    "DAQ",
     119              :    "Detector",
     120              :    "Electronics",
     121              :    "Target",
     122              :    "Beamline"
     123              : };
     124              : 
     125              : struct MimetypeTableEntry {
     126              :    std::string ext;
     127              :    std::string mimetype;
     128              : };
     129              : 
     130              : const MimetypeTableEntry gMimetypeTable[] = {
     131              :    { ".ASC",   "text/plain" },
     132              :    { ".CSS",   "text/css"   },
     133              :    { ".CSV",   "text/csv"   },
     134              :    { ".HTM",   "text/html"  },
     135              :    { ".HTML",  "text/html"  },
     136              :    { ".TXT",   "text/plain" },
     137              : 
     138              :    { ".BMP",   "image/bmp"     },
     139              :    { ".GIF",   "image/gif"     },
     140              :    { ".ICO",   "image/x-icon"  },
     141              :    { ".JPEG",  "image/jpeg"    },
     142              :    { ".JPG",   "image/jpeg"    },
     143              :    { ".PNG",   "image/png"     },
     144              :    { ".SVG",   "image/svg+xml" },
     145              :    { ".TIF",   "image/tiff"    },
     146              :    { ".TIFF",  "image/tiff"    },
     147              : 
     148              :    { ".MID",   "audio/midi"    },
     149              :    { ".MP3",   "audio/mpeg"    },
     150              :    { ".OGA",   "audio/ogg"     },
     151              :    { ".OGG",   "audio/ogg"     },
     152              :    { ".WAV",   "audio/wav"     },
     153              : 
     154              :    { ".BIN",   "application/octet-stream" },
     155              :    { ".BZ",    "application/x-bzip"       },
     156              :    { ".BZ2",   "application/x-bzip2"      },
     157              :    { ".DOC",   "application/msword"       },
     158              :    { ".EPS",   "application/postscript"   },
     159              :    { ".GZ",    "application/gzip"         },
     160              :    { ".JS",    "application/javascript"   },
     161              :    { ".JSON",  "application/json"         },
     162              :    { ".MJS",   "application/javascript"   },
     163              :    { ".PDF",   "application/pdf"          },
     164              :    { ".PHP",   "application/x-httpd-php"  },
     165              :    { ".RTF",   "application/rtf"          },
     166              :    { ".PS",    "application/postscript"   },
     167              :    { ".ROOT",  "application/octet-stream" },
     168              :    { ".XLS",   "application/x-msexcel"    },
     169              :    { ".XML",   "application/xml"          },
     170              :    { ".ZIP",   "application/zip"          },
     171              : 
     172              :    { ".ODP",   "application/vnd.oasis.opendocument.presentation" },
     173              :    { ".ODS",   "application/vnd.oasis.opendocument.spreadsheet"  },
     174              :    { ".ODT",   "application/vnd.oasis.opendocument.text"         },
     175              : 
     176              :    { "", "" }
     177              : };
     178              : 
     179              : static MVOdb* gMimeTypesOdb = NULL;
     180              : 
     181            0 : static std::string GetMimetype(const std::string& ext)
     182              : {
     183            0 :    if (gMimeTypesOdb) {
     184            0 :       std::string mimetype;
     185            0 :       gMimeTypesOdb->RS(ext.c_str(), &mimetype);
     186            0 :       if (mimetype.length() > 0) {
     187              :          //printf("GetMimetype: %s -> %s from ODB\n", ext.c_str(), mimetype.c_str());
     188            0 :          return mimetype;
     189              :       }
     190            0 :    }
     191              : 
     192            0 :    for (int i=0; gMimetypeTable[i].ext[0]; i++) {
     193            0 :       if (ext == gMimetypeTable[i].ext) {
     194              :          //printf("GetMimetype: %s -> %s from built-in table\n", ext.c_str(), gMimetypeTable[i].mimetype.c_str());
     195            0 :          return gMimetypeTable[i].mimetype;
     196              :       }
     197              :    }
     198              : 
     199              :    //printf("GetMimetype: %s -> not found\n", ext.c_str());
     200            0 :    return "";
     201              : }
     202              : 
     203            0 : static void SaveMimetypes(MVOdb* odb)
     204              : {
     205            0 :    gMimeTypesOdb = odb;
     206              : 
     207            0 :    for (int i=0; gMimetypeTable[i].ext.length() > 0; i++) {
     208            0 :       std::string tmp = gMimetypeTable[i].mimetype;
     209            0 :       gMimeTypesOdb->RS(gMimetypeTable[i].ext.c_str(), &tmp, true);
     210            0 :    }
     211            0 : }
     212              : 
     213              : #define HTTP_ENCODING "UTF-8"
     214              : 
     215              : /*------------------------------------------------------------------*/
     216              : 
     217              : const unsigned char favicon_png[] = {
     218              :    0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
     219              :    0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
     220              :    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
     221              :    0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x91, 0x68,
     222              :    0x36, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D,
     223              :    0x45, 0x07, 0xD4, 0x0B, 0x1A, 0x08, 0x37, 0x07,
     224              :    0x0D, 0x7F, 0x16, 0x5C, 0x00, 0x00, 0x00, 0x09,
     225              :    0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x2E, 0x23,
     226              :    0x00, 0x00, 0x2E, 0x23, 0x01, 0x78, 0xA5, 0x3F,
     227              :    0x76, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4D,
     228              :    0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61,
     229              :    0x05, 0x00, 0x00, 0x01, 0x7D, 0x49, 0x44, 0x41,
     230              :    0x54, 0x78, 0xDA, 0x63, 0xFC, 0xFF, 0xFF, 0x3F,
     231              :    0x03, 0x29, 0x80, 0x09, 0xAB, 0xE8, 0xD2, 0x65,
     232              :    0x77, 0x36, 0x6F, 0x7E, 0x8A, 0x5D, 0xC7, 0x7F,
     233              :    0x0C, 0x30, 0x67, 0xEE, 0x0D, 0x56, 0xCE, 0xCD,
     234              :    0x5C, 0xBC, 0x3B, 0xB6, 0x6D, 0x7F, 0x81, 0x29,
     235              :    0xCB, 0x88, 0xE6, 0x24, 0x20, 0x57, 0x50, 0x7C,
     236              :    0xDD, 0xCF, 0x1F, 0x6C, 0x40, 0xCB, 0xB5, 0xB5,
     237              :    0x05, 0xCF, 0x1C, 0xB7, 0x42, 0xB3, 0x80, 0x05,
     238              :    0x8D, 0xCF, 0xC8, 0xC8, 0x58, 0x5A, 0x2A, 0xFB,
     239              :    0xF6, 0x4D, 0x37, 0x1B, 0xAB, 0xA0, 0xB4, 0x4C,
     240              :    0x0A, 0x51, 0x4E, 0x02, 0x82, 0x85, 0xCB, 0x12,
     241              :    0x0E, 0x1D, 0xAB, 0xC7, 0x2A, 0xC5, 0x82, 0x69,
     242              :    0xC4, 0xAF, 0x5F, 0x7F, 0x1E, 0x3F, 0xF8, 0xCD,
     243              :    0xCB, 0xF1, 0xF5, 0xEF, 0xDF, 0x7F, 0xCC, 0xCC,
     244              :    0x4C, 0x84, 0x6D, 0x98, 0x59, 0xD5, 0xEB, 0xCF,
     245              :    0xA5, 0x16, 0xC4, 0xAB, 0x71, 0x72, 0xCB, 0x21,
     246              :    0x4C, 0x59, 0x74, 0x03, 0x5E, 0x3F, 0x7F, 0xB3,
     247              :    0x6B, 0xD6, 0x22, 0x46, 0xA6, 0x7F, 0x0C, 0x0C,
     248              :    0x7F, 0xD7, 0x75, 0x4D, 0xFB, 0xF1, 0xFD, 0x27,
     249              :    0x81, 0x78, 0xB8, 0x7D, 0xE9, 0x0A, 0xCB, 0xFF,
     250              :    0xDF, 0x4C, 0x8C, 0x8C, 0x40, 0xF6, 0xAD, 0x4B,
     251              :    0x67, 0x1F, 0xDE, 0xBD, 0x8B, 0x45, 0x03, 0x3C,
     252              :    0x60, 0x8F, 0x9D, 0xD8, 0xB3, 0xEB, 0x74, 0xB5,
     253              :    0x90, 0x26, 0x07, 0x03, 0x48, 0xE4, 0x3F, 0x8F,
     254              :    0xF6, 0xFF, 0x1B, 0x0F, 0x9A, 0x1E, 0x3E, 0x3A,
     255              :    0xFB, 0xF3, 0xDB, 0x8F, 0xB7, 0x0F, 0x9E, 0x43,
     256              :    0x83, 0xF1, 0xCF, 0xDF, 0x3F, 0x8A, 0x29, 0xCE,
     257              :    0x3F, 0x7F, 0xFD, 0xFC, 0xCF, 0xF0, 0xDF, 0x98,
     258              :    0xE9, 0xB5, 0x8F, 0xBD, 0x8A, 0x3C, 0x6F, 0xEC,
     259              :    0xB9, 0x2D, 0x47, 0xFE, 0xFC, 0xFF, 0x6F, 0x16,
     260              :    0x6C, 0xF3, 0xEC, 0xD3, 0x1C, 0x2E, 0x96, 0xEF,
     261              :    0xBF, 0xAB, 0x7E, 0x32, 0x7D, 0xE2, 0x10, 0xCE,
     262              :    0x88, 0xF4, 0x69, 0x2B, 0x60, 0xFC, 0xF4, 0xF5,
     263              :    0x97, 0x78, 0x8A, 0x36, 0xD8, 0x44, 0x86, 0x18,
     264              :    0x0D, 0xD7, 0x29, 0x95, 0x13, 0xD8, 0xD9, 0x58,
     265              :    0xE1, 0x0E, 0xF8, 0xF1, 0xF3, 0xDB, 0xC6, 0xD6,
     266              :    0xEC, 0x5F, 0x53, 0x8E, 0xBF, 0xFE, 0xC3, 0x70,
     267              :    0x93, 0x8D, 0x6D, 0xDA, 0xCB, 0x0B, 0x4C, 0x3F,
     268              :    0xFF, 0xFC, 0xFA, 0xCF, 0x0C, 0xB4, 0x09, 0x84,
     269              :    0x54, 0xD5, 0x74, 0x91, 0x55, 0x03, 0x01, 0x07,
     270              :    0x3B, 0x97, 0x96, 0x6E, 0xC8, 0x17, 0xFE, 0x7F,
     271              :    0x4F, 0xF8, 0xFE, 0xBC, 0x95, 0x16, 0x60, 0x62,
     272              :    0x62, 0x64, 0xE1, 0xE6, 0x60, 0x73, 0xD1, 0xB2,
     273              :    0x7A, 0xFA, 0xE2, 0xF1, 0xDF, 0x3F, 0xFF, 0xC4,
     274              :    0x78, 0x44, 0x31, 0xA3, 0x45, 0x2B, 0xD0, 0xE3,
     275              :    0xF6, 0xD9, 0xE3, 0x2F, 0x2E, 0x9D, 0x29, 0xA9,
     276              :    0xAC, 0x07, 0xA6, 0x03, 0xF4, 0xB4, 0x44, 0x10,
     277              :    0x00, 0x00, 0x75, 0x65, 0x12, 0xB0, 0x49, 0xFF,
     278              :    0x3F, 0x68, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
     279              :    0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
     280              : };
     281              : 
     282              : const unsigned char favicon_ico[] = {
     283              :    0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10,
     284              :    0x10, 0x00, 0x01, 0x00, 0x04, 0x00, 0x28, 0x01,
     285              :    0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
     286              :    0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00,
     287              :    0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
     288              :    0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
     289              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     290              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x0F,
     291              :    0x0A, 0x00, 0x5C, 0x86, 0x4C, 0x00, 0x2F, 0x5E,
     292              :    0x1A, 0x00, 0xBF, 0xD3, 0xD7, 0x00, 0x29, 0x17,
     293              :    0x8D, 0x00, 0x50, 0xA7, 0xA4, 0x00, 0x59, 0x57,
     294              :    0x7F, 0x00, 0xC6, 0xA3, 0xAC, 0x00, 0xFC, 0xFE,
     295              :    0xFC, 0x00, 0x28, 0x12, 0x53, 0x00, 0x58, 0x7D,
     296              :    0x72, 0x00, 0xC4, 0x3A, 0x34, 0x00, 0x3C, 0x3D,
     297              :    0x69, 0x00, 0xC5, 0xB6, 0xB9, 0x00, 0x94, 0x92,
     298              :    0x87, 0x00, 0x7E, 0x7A, 0xAA, 0x00, 0x88, 0x88,
     299              :    0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x81, 0x22,
     300              :    0xD8, 0x88, 0x88, 0x88, 0xF6, 0xD8, 0x82, 0x22,
     301              :    0xE8, 0x88, 0x88, 0x8D, 0x44, 0x98, 0x82, 0x22,
     302              :    0xA8, 0x88, 0x88, 0x8F, 0x44, 0x48, 0x82, 0x22,
     303              :    0x25, 0x76, 0x67, 0x55, 0x44, 0xF8, 0x88, 0x88,
     304              :    0x3A, 0xC9, 0x9C, 0x53, 0x83, 0x88, 0x88, 0x88,
     305              :    0x8D, 0x99, 0x99, 0x38, 0x88, 0x88, 0x88, 0x88,
     306              :    0x88, 0x99, 0x9C, 0x88, 0x88, 0x88, 0x88, 0x88,
     307              :    0x88, 0xF9, 0x9D, 0x88, 0x88, 0x88, 0x88, 0x88,
     308              :    0x88, 0x8A, 0x58, 0x88, 0x88, 0x88, 0x88, 0x88,
     309              :    0x88, 0x85, 0xD8, 0x88, 0x88, 0x88, 0x88, 0x88,
     310              :    0x88, 0xEA, 0xAE, 0x88, 0x88, 0x88, 0x88, 0x88,
     311              :    0x88, 0x00, 0x0B, 0x88, 0x88, 0x88, 0x88, 0x88,
     312              :    0x88, 0x70, 0x0D, 0x88, 0x88, 0x88, 0x88, 0x88,
     313              :    0x88, 0x87, 0xD8, 0x88, 0x88, 0x88, 0x88, 0x88,
     314              :    0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
     315              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     316              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     317              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     318              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     319              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     320              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     321              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     322              :    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
     323              : };
     324              : 
     325              : /*------------------------------------------------------------------*/
     326              : 
     327              : class Param;
     328              : class Return;
     329              : void show_hist_page(MVOdb* odb, Param* p, Return* r, const char *dec_path, char *buffer, int *buffer_size, int refresh);
     330              : int vaxis(gdImagePtr im, gdFont * font, int col, int gcol, int x1, int y1, int width,
     331              :           int minor, int major, int text, int label, int grid, double ymin, double ymax,
     332              :           BOOL logaxis);
     333              : void haxis(gdImagePtr im, gdFont * font, int col, int gcol, int x1, int y1, int width,
     334              :            int minor, int major, int text, int label, int grid, double xmin, double xmax);
     335              : void get_elog_url(char *url, int len);
     336              : void show_header(Return* r, const char *title, const char *method, const char *path, int refresh);
     337              : void show_navigation_bar(Return* r, const char *cur_page);
     338              : 
     339              : /*------------------------------------------------------------------*/
     340              : 
     341            0 : char *stristr(const char *str, const char *pattern)
     342              : {
     343              :    char c1, c2, *ps, *pp;
     344              : 
     345            0 :    if (str == NULL || pattern == NULL)
     346            0 :       return NULL;
     347              : 
     348            0 :    while (*str) {
     349            0 :       ps = (char *) str;
     350            0 :       pp = (char *) pattern;
     351            0 :       c1 = *ps;
     352            0 :       c2 = *pp;
     353            0 :       if (toupper(c1) == toupper(c2)) {
     354            0 :          while (*pp) {
     355            0 :             c1 = *ps;
     356            0 :             c2 = *pp;
     357              : 
     358            0 :             if (toupper(c1) != toupper(c2))
     359            0 :                break;
     360              : 
     361            0 :             ps++;
     362            0 :             pp++;
     363              :          }
     364              : 
     365            0 :          if (!*pp)
     366            0 :             return (char *) str;
     367              :       }
     368            0 :       str++;
     369              :    }
     370              : 
     371            0 :    return NULL;
     372              : }
     373              : 
     374              : /*------------------------------------------------------------------*/
     375              : 
     376            0 : static double GetTimeSec()
     377              : {
     378              :    struct timeval tv;
     379            0 :    gettimeofday(&tv, NULL);
     380            0 :    return tv.tv_sec*1.0 + tv.tv_usec/1000000.0;
     381              : }
     382              : 
     383              : /*------------------------------------------------------------------*/
     384              : 
     385              : class RequestTrace
     386              : {
     387              : public:
     388              :    double fTimeReceived; // time request received
     389              :    double fTimeLocked; // time lock is taken
     390              :    double fTimeUnlocked; // time lock is released
     391              :    double fTimeProcessed; // time processing of request is done
     392              :    double fTimeSent; // time sending the request is done
     393              :    bool fCompleted; // flag the request completed, it's RequestTrace is safe to delete
     394              :    std::string fMethod; // request HTTP method
     395              :    std::string fUri; // request URL/URI
     396              :    std::string fQuery; // request query string
     397              :    std::string fRPC; // request RPC
     398              :    std::string fResource; // request file resource, with full path
     399              :    bool fAuthOk; // password check passed
     400              : 
     401              : public:
     402            0 :    RequestTrace() // ctor
     403            0 :    {
     404            0 :       fTimeReceived = 0;
     405            0 :       fTimeLocked = 0;
     406            0 :       fTimeUnlocked = 0;
     407            0 :       fTimeProcessed = 0;
     408            0 :       fTimeSent = 0;
     409            0 :       fCompleted = false;
     410            0 :       fAuthOk = false;
     411            0 :    }
     412              : 
     413            0 :    void PrintTrace0() const
     414              :    {
     415            0 :       printf("%.3f ", fTimeReceived);
     416            0 :       printf("%.3f ", fTimeLocked-fTimeReceived);
     417            0 :       printf("%.3f ", fTimeUnlocked-fTimeLocked);
     418            0 :       printf("%.3f ", fTimeProcessed-fTimeUnlocked);
     419            0 :       printf("%.3f ", fTimeSent-fTimeProcessed);
     420            0 :       printf("A ");
     421            0 :       printf("%d ", fAuthOk);
     422            0 :       printf("T ");
     423            0 :       printf("%.3f ", fTimeSent-fTimeReceived);
     424            0 :       printf("%.3f ", fTimeLocked-fTimeReceived);
     425            0 :       printf("%.3f ", fTimeProcessed-fTimeLocked);
     426            0 :       printf("M %s ", fMethod.c_str());
     427            0 :       printf("URL %s ", fUri.c_str());
     428            0 :       if (fRPC.length() > 0) {
     429            0 :          printf("RPC %s ", fRPC.c_str());
     430              :       }
     431            0 :       printf("\n");
     432            0 :    };
     433              : };
     434              : 
     435              : static int http_trace = 0;
     436              : 
     437              : class RequestTraceBuf
     438              : {
     439              : public:
     440              :    MUTEX_T* fMutex;
     441              :    std::vector<RequestTrace*> fBuf;
     442              : 
     443              : public:
     444            0 :    RequestTraceBuf() // ctor
     445            0 :    {
     446              :       int status;
     447            0 :       status = ss_mutex_create(&fMutex, FALSE);
     448            0 :       assert(status==SS_SUCCESS || status==SS_CREATED);
     449            0 :    }
     450              : 
     451              :    //RequestTrace* NewTrace() // RequestTrace factory
     452              :      //{
     453              :      // RequestTrace* t = new RequestTrace;
     454              :      // fBuf.push_back(t);
     455              :      // return t;
     456              :    //}
     457              : 
     458              :    void AddTrace(RequestTrace* t)
     459              :    {
     460              :       fBuf.push_back(t);
     461              :    }
     462              : 
     463            0 :    void AddTraceMTS(RequestTrace* t)
     464              :    {
     465            0 :       ss_mutex_wait_for(fMutex, 0);
     466            0 :       if (http_trace) {
     467            0 :          t->PrintTrace0();
     468              :       }
     469            0 :       delete t;
     470              :       //AddTrace(t);
     471            0 :       ss_mutex_release(fMutex);
     472            0 :    }
     473              : 
     474              :    void Clear() // clear all completed requests
     475              :    {
     476              :       // delete all completed requests
     477              :       for (unsigned i=0; i<fBuf.size(); i++) {
     478              :          if (fBuf[i] && fBuf[i]->fCompleted) {
     479              :             delete fBuf[i];
     480              :             fBuf[i] = NULL;
     481              :          }
     482              :       }
     483              : 
     484              :       // compact all non-completed requests
     485              :       unsigned k = 0;
     486              :       for (unsigned i=0; i<fBuf.size(); i++) {
     487              :          if (fBuf[i]) {
     488              :             if (fBuf[k] != NULL) {
     489              :                for (; k<i; k++) {
     490              :                   if (fBuf[k] == NULL) {
     491              :                      break;
     492              :                   }
     493              :                }
     494              :             }
     495              :             // if we found an empty spot between "k" and "i" move "i" there
     496              :             // if there is no empty spot, then "i" does not need to be moved
     497              :             if (fBuf[k] == NULL) {
     498              :                fBuf[k] = fBuf[i];
     499              :                fBuf[i] = NULL;
     500              :             }
     501              :          }
     502              :       }
     503              :    }
     504              : };
     505              : 
     506              : static RequestTraceBuf* gTraceBuf = NULL;
     507              : 
     508              : /*------------------------------------------------------------------*/
     509              : 
     510              : /* size of buffer for incoming data, must fit sum of all attachments */
     511              : #define WEB_BUFFER_SIZE (6*1024*1024)
     512              : 
     513              : class Return
     514              : {
     515              : public:
     516              : 
     517              :    size_t return_size;
     518              :    char *return_buffer;
     519              : 
     520              :    int strlen_retbuf;
     521              :    int return_length;
     522              : 
     523              : public:
     524            0 :    Return() // ctor
     525            0 :    {
     526            0 :       return_size = WEB_BUFFER_SIZE;
     527            0 :       return_buffer = (char*)malloc(return_size);
     528            0 :       assert(return_buffer != NULL);
     529              : 
     530            0 :       strlen_retbuf = 0;
     531            0 :       return_length = 0;
     532            0 :    }
     533              : 
     534            0 :    ~Return() // dtor
     535              :    {
     536            0 :       if (return_buffer)
     537            0 :          free(return_buffer);
     538            0 :       return_buffer = NULL;
     539            0 :       return_size = 0;
     540            0 :       strlen_retbuf = 0;
     541            0 :       return_length = 0;
     542            0 :    }
     543              : 
     544            0 :    void reset()
     545              :    {
     546            0 :       strlen_retbuf = 0;
     547            0 :    }
     548              : 
     549            0 :    void zero()
     550              :    {
     551            0 :       memset(return_buffer, 0, return_size);
     552            0 :       strlen_retbuf = 0;
     553            0 :       return_length = 0;
     554            0 :    }
     555              : 
     556            0 : int return_grow(size_t len)
     557              : {
     558              :    //printf("size %d, grow %d, room %d\n", return_size, len, return_size - strlen_retbuf);
     559              : 
     560            0 :    for (int i=0; i<1000; i++) { // infinite loop with protection against infinite looping
     561            0 :       if (strlen_retbuf + len < return_size-40)
     562            0 :          return SUCCESS;
     563              : 
     564            0 :       return_size *= 2;
     565            0 :       return_buffer = (char*)realloc(return_buffer, return_size);
     566              : 
     567            0 :       assert(return_buffer);
     568              : 
     569              :       //printf("new size %d\n", return_size);
     570              :    }
     571              : 
     572            0 :    assert(!"Cannot happen!"); // does not return
     573              :    return 0;
     574              : }
     575              : 
     576              : /*------------------------------------------------------------------*/
     577              : 
     578            0 : void rmemcpy(const void *buf, int len)
     579              : {
     580            0 :    return_grow(len);
     581            0 :    memcpy(return_buffer + strlen_retbuf, buf, len);
     582            0 :    strlen_retbuf += len;
     583            0 :    return_length = strlen_retbuf;
     584            0 : }
     585              : 
     586              : /*------------------------------------------------------------------*/
     587              : 
     588            0 : void rread(const char* filename, int fh, int len)
     589              : {
     590            0 :    return_grow(len);
     591            0 :    int rd = read(fh, return_buffer + strlen_retbuf, len);
     592            0 :    if (rd != len) {
     593            0 :       cm_msg(MERROR, "rread", "Cannot read file \'%s\', read of %d returned %d, errno %d (%s)", filename, len, rd, errno, strerror(errno));
     594            0 :       memset(return_buffer + strlen_retbuf, 0, len);
     595              :    }
     596            0 :    strlen_retbuf += len;
     597            0 :    return_length = strlen_retbuf;
     598            0 : }
     599              : 
     600              : /*------------------------------------------------------------------*/
     601              : 
     602            0 : void rsputs(const char *str)
     603              : {
     604            0 :    size_t len = strlen(str);
     605              : 
     606            0 :    return_grow(len);
     607              : 
     608            0 :    if (strlen_retbuf + len > return_size-40) {
     609            0 :       strcpy(return_buffer, "<H1>Error: return buffer too small</H1>");
     610            0 :       strlen_retbuf = strlen(return_buffer);
     611              :    } else {
     612            0 :       strcpy(return_buffer + strlen_retbuf, str);
     613            0 :       strlen_retbuf += len;
     614              :    }
     615              : 
     616            0 :    return_length = strlen_retbuf;
     617            0 : }
     618              : 
     619              : /*------------------------------------------------------------------*/
     620              : 
     621              : void rsputs2(const char *str)
     622              : {
     623              :    size_t len = strlen(str);
     624              : 
     625              :    return_grow(len);
     626              : 
     627              :    if (strlen_retbuf + len > return_size) {
     628              :       mstrlcpy(return_buffer, "<H1>Error: return buffer too small</H1>", return_size);
     629              :       strlen_retbuf = strlen(return_buffer);
     630              :    } else {
     631              :       int j = strlen_retbuf;
     632              :       for (size_t i = 0; i < len; i++) {
     633              :          if (strncmp(str + i, "http://", 7) == 0) {
     634              :             int k;
     635              :             char link[256];
     636              :             char* p = (char *) (str + i + 7);
     637              : 
     638              :             i += 7;
     639              :             for (k = 0; *p && *p != ' ' && *p != '\n'; k++, i++)
     640              :                link[k] = *p++;
     641              :             link[k] = 0;
     642              : 
     643              :             sprintf(return_buffer + j, "<a href=\"http://%s\">http://%s</a>", link, link);
     644              :             j += strlen(return_buffer + j);
     645              :          } else
     646              :             switch (str[i]) {
     647              :             case '<':
     648              :                mstrlcat(return_buffer, "&lt;", return_size);
     649              :                j += 4;
     650              :                break;
     651              :             case '>':
     652              :                mstrlcat(return_buffer, "&gt;", return_size);
     653              :                j += 4;
     654              :                break;
     655              :             default:
     656              :                return_buffer[j++] = str[i];
     657              :             }
     658              :       }
     659              : 
     660              :       return_buffer[j] = 0;
     661              :       strlen_retbuf = j;
     662              :    }
     663              : 
     664              :    return_length = strlen_retbuf;
     665              : }
     666              : 
     667              : /*------------------------------------------------------------------*/
     668              : 
     669            0 : void rsprintf(const char *format, ...) MATTRPRINTF(2,3)
     670              : {
     671              :    va_list argptr;
     672              :    char str[10000];
     673              : 
     674            0 :    va_start(argptr, format);
     675            0 :    vsprintf(str, (char *) format, argptr);
     676            0 :    va_end(argptr);
     677              : 
     678              :    // catch array overrun. better too late than never...
     679            0 :    assert(strlen(str) < sizeof(str));
     680              : 
     681            0 :    return_grow(strlen(str));
     682              : 
     683            0 :    if (strlen_retbuf + strlen(str) > return_size)
     684            0 :       strcpy(return_buffer, "<H1>Error: return buffer too small</H1>");
     685              :    else
     686            0 :       strcpy(return_buffer + strlen_retbuf, str);
     687              : 
     688            0 :    strlen_retbuf += strlen(str);
     689            0 :    return_length = strlen_retbuf;
     690            0 : }
     691              : };
     692              : 
     693              : /*------------------------------------------------------------------*/
     694              : 
     695              : /* Parameter handling functions similar to setenv/getenv */
     696              : 
     697              : #define MAX_PARAM    500
     698              : #define PARAM_LENGTH 256
     699              : #define TEXT_SIZE  50000
     700              : 
     701              : class Param
     702              : {
     703              : public:
     704              :    char _param[MAX_PARAM][PARAM_LENGTH];
     705              :    char *_value[MAX_PARAM];
     706              :    char _text[TEXT_SIZE];
     707              : 
     708              : public:
     709            0 :    Param() // ctor
     710            0 :    {
     711            0 :       initparam();
     712            0 :    }
     713              : 
     714            0 :    ~Param() // dtor
     715              :    {
     716            0 :       freeparam();
     717            0 :    }
     718              : 
     719            0 :    void initparam()
     720              :    {
     721            0 :       memset(_param, 0, sizeof(_param));
     722            0 :       memset(_value, 0, sizeof(_value));
     723            0 :       _text[0] = 0;
     724            0 :    }
     725              : 
     726            0 :    void setparam(const char *param, const char *value)
     727              :    {
     728              :       int i;
     729              : 
     730            0 :       if (equal_ustring(param, "text")) {
     731            0 :          if (strlen(value) >= TEXT_SIZE)
     732            0 :             printf("Error: parameter value too big\n");
     733              : 
     734            0 :          mstrlcpy(_text, value, TEXT_SIZE);
     735            0 :          _text[TEXT_SIZE - 1] = 0;
     736            0 :          return;
     737              :       }
     738              : 
     739            0 :       for (i = 0; i < MAX_PARAM; i++)
     740            0 :          if (_param[i][0] == 0)
     741            0 :             break;
     742              : 
     743            0 :       if (i < MAX_PARAM) {
     744            0 :          mstrlcpy(_param[i], param, PARAM_LENGTH);
     745              : 
     746            0 :          int size = strlen(value)+1;
     747            0 :          _value[i] = (char*)malloc(size);
     748            0 :          mstrlcpy(_value[i], value, size);
     749            0 :          _value[i][strlen(value)] = 0;
     750              : 
     751              :       } else {
     752            0 :          printf("Error: parameter array too small\n");
     753              :       }
     754              :    }
     755              : 
     756            0 :    void freeparam()
     757              :    {
     758              :       int i;
     759              : 
     760            0 :       for (i=0 ; i<MAX_PARAM ; i++)
     761            0 :          if (_value[i] != NULL) {
     762            0 :             free(_value[i]);
     763            0 :             _value[i] = NULL;
     764              :          }
     765            0 :    }
     766              : 
     767              :    void printparam()
     768              :    {
     769              :       int i;
     770              : 
     771              :       for (i = 0; i < MAX_PARAM && _param[i][0]; i++) {
     772              :          printf("param %d name [%s] value [%s]\n", i, _param[i], _value[i]);;
     773              :       }
     774              :    }
     775              : 
     776            0 :    const char *getparam(const char *param)
     777              :    {
     778              :       int i;
     779              : 
     780            0 :       if (equal_ustring(param, "text"))
     781            0 :          return _text;
     782              : 
     783            0 :       for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
     784            0 :          if (equal_ustring(param, _param[i]))
     785            0 :             break;
     786              : 
     787            0 :       if (i == MAX_PARAM)
     788            0 :          return NULL;
     789              : 
     790            0 :       if (_value[i] == NULL)
     791            0 :          return "";
     792              : 
     793            0 :       return _value[i];
     794              :    }
     795              : 
     796            0 :    std::string xgetparam(const char *param)
     797              :    {
     798            0 :       const char* s = getparam(param);
     799            0 :       if (s)
     800            0 :          return s;
     801              :       else
     802            0 :          return "";
     803              :    }
     804              : 
     805            0 :    BOOL isparam(const char *param)
     806              :    {
     807              :       int i;
     808              : 
     809            0 :       for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
     810            0 :          if (equal_ustring(param, _param[i]))
     811            0 :             break;
     812              : 
     813            0 :       if (i < MAX_PARAM && _param[i][0])
     814            0 :          return TRUE;
     815              : 
     816            0 :       return FALSE;
     817              :    }
     818              : 
     819            0 :    void unsetparam(const char *param)
     820              :    {
     821              :       int i;
     822              : 
     823            0 :       for (i = 0; i < MAX_PARAM; i++)
     824            0 :          if (equal_ustring(param, _param[i]))
     825            0 :             break;
     826              : 
     827            0 :       if (i < MAX_PARAM) {
     828            0 :          _param[i][0] = 0;
     829            0 :          _value[i][0] = 0;
     830              :       }
     831            0 :    }
     832              : };
     833              : 
     834              : /*------------------------------------------------------------------*/
     835              : 
     836            0 : const char *mhttpd_revision(void)
     837              : {
     838            0 :    return cm_get_revision();
     839              : }
     840              : 
     841              : /*------------------------------------------------------------------*/
     842              : 
     843            0 : static std::string UrlDecode(const char* p)
     844              : /********************************************************************\
     845              :    Decode the given string in-place by expanding %XX escapes
     846              : \********************************************************************/
     847              : {
     848            0 :    std::string s;
     849              : 
     850              :    //printf("URL decode: [%s] --> ", p);
     851              : 
     852            0 :    while (*p) {
     853            0 :       if (*p == '%') {
     854              :          /* Escape: next 2 chars are hex representation of the actual character */
     855            0 :          p++;
     856            0 :          if (isxdigit(p[0]) && isxdigit(p[1])) {
     857            0 :             int i = 0;
     858              :             char str[3];
     859            0 :             str[0] = p[0];
     860            0 :             str[1] = p[1];
     861            0 :             str[2] = 0;
     862            0 :             sscanf(str, "%02X", &i);
     863              : 
     864            0 :             s += (char) i;
     865            0 :             p += 2;
     866            0 :          } else
     867            0 :             s += '%';
     868            0 :       } else if (*p == '+') {
     869              :          /* convert '+' to ' ' */
     870            0 :          s += ' ';
     871            0 :          p++;
     872              :       } else {
     873            0 :          s += *p++;
     874              :       }
     875              :    }
     876              : 
     877              :    //printf("[%s]\n", s.c_str());
     878              : 
     879            0 :    return s;
     880            0 : }
     881              : 
     882            0 : static void urlDecode(char *p)
     883              : /********************************************************************\
     884              :    Decode the given string in-place by expanding %XX escapes
     885              : \********************************************************************/
     886              : {
     887              :    //char *px = p;
     888              :    char *pD, str[3];
     889              :    int i;
     890              : 
     891              :    //printf("URL decode: [%s] --> ", p);
     892              : 
     893            0 :    pD = p;
     894            0 :    while (*p) {
     895            0 :       if (*p == '%') {
     896              :          /* Escape: next 2 chars are hex representation of the actual character */
     897            0 :          p++;
     898            0 :          if (isxdigit(p[0]) && isxdigit(p[1])) {
     899            0 :             str[0] = p[0];
     900            0 :             str[1] = p[1];
     901            0 :             str[2] = 0;
     902            0 :             sscanf(str, "%02X", &i);
     903              : 
     904            0 :             *pD++ = (char) i;
     905            0 :             p += 2;
     906              :          } else
     907            0 :             *pD++ = '%';
     908            0 :       } else if (*p == '+') {
     909              :          /* convert '+' to ' ' */
     910            0 :          *pD++ = ' ';
     911            0 :          p++;
     912              :       } else {
     913            0 :          *pD++ = *p++;
     914              :       }
     915              :    }
     916            0 :    *pD = '\0';
     917              : 
     918              :    //printf("[%s]\n", px);
     919            0 : }
     920              : 
     921            0 : static void urlEncode(char *ps, int ps_size)
     922              : /*
     923              :    Encode mhttpd ODB path for embedding into HTML <a href="?cmd=odb&odb_path=xxx"> elements.
     924              :    Encoding is intended to be compatible with RFC 3986 section 2 (adding of %XX escapes)
     925              : */
     926              : {
     927              :    char *pd, *p;
     928            0 :    int len = strlen(ps);
     929            0 :    char *str = (char*)malloc(len*3 + 10); // at worst, each input character is expanded into 3 output characters
     930              : 
     931            0 :    pd = str;
     932            0 :    p = ps;
     933            0 :    while (*p) {
     934            0 :       if (isalnum(*p)) {
     935            0 :          *pd++ = *p++;
     936              :       } else {
     937            0 :          sprintf(pd, "%%%02X", (*p)&0xFF);
     938            0 :          pd += 3;
     939            0 :          p++;
     940              :       }
     941              :    }
     942            0 :    *pd = '\0';
     943              : 
     944              :    if (/* DISABLES CODE */ (0)) {
     945              :       printf("urlEncode [");
     946              :       for (p=ps; *p!=0; p++)
     947              :          printf("0x%02x ", (*p)&0xFF);
     948              :       printf("]\n");
     949              : 
     950              :       printf("urlEncode [%s] -> [%s]\n", ps, str);
     951              :    }
     952              : 
     953            0 :    mstrlcpy(ps, str, ps_size);
     954            0 :    free(str);
     955            0 : }
     956              : 
     957            0 : static std::string urlEncode(const char *text)
     958              : /*
     959              :    Encode mhttpd ODB path for embedding into HTML <a href="?cmd=odb&odb_path=xxx"> elements.
     960              :    Encoding is intended to be compatible with RFC 3986 section 2 (adding of %XX escapes)
     961              : */
     962              : {
     963            0 :    std::string encoded;
     964              : 
     965            0 :    const char* p = text;
     966            0 :    while (*p) {
     967            0 :       if (isalnum(*p)) {
     968            0 :          encoded += *p++;
     969              :       } else {
     970              :          char buf[16];
     971            0 :          sprintf(buf, "%%%02X", (*p)&0xFF);
     972            0 :          encoded += buf;
     973            0 :          p++;
     974              :       }
     975              :    }
     976              : 
     977              :    if (/* DISABLES CODE */ (0)) {
     978              :       printf("urlEncode [");
     979              :       for (p=text; *p!=0; p++)
     980              :          printf("0x%02x ", (*p)&0xFF);
     981              :       printf("]\n");
     982              : 
     983              :       printf("urlEncode [%s] -> [%s]\n", text, encoded.c_str());
     984              :    }
     985              : 
     986            0 :    return encoded;
     987            0 : }
     988              : 
     989              : /*------------------------------------------------------------------*/
     990              : 
     991            0 : std::vector<std::string> get_resource_paths()
     992              : {
     993              :    HNDLE hDB;
     994              :    int status;
     995              : 
     996            0 :    cm_get_experiment_database(&hDB, NULL);
     997              : 
     998            0 :    std::vector<std::string> paths;
     999              : 
    1000              :    // add /Experiment/Resources
    1001            0 :    std::string buf;
    1002            0 :    status = db_get_value_string(hDB, 0, "/Experiment/Resources", 0, &buf, TRUE);
    1003            0 :    if (status == DB_SUCCESS && buf.length() > 0)
    1004            0 :       paths.push_back(buf);
    1005              : 
    1006              :    // add  "/Logger/History/IMAGE/History dir"
    1007            0 :    paths.push_back(cm_get_history_path("IMAGE"));
    1008              : 
    1009              :    // add /Logger/Data dir
    1010            0 :    status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &buf, TRUE);
    1011            0 :    if (status == DB_SUCCESS && buf.length() > 0)
    1012            0 :       paths.push_back(buf);
    1013              : 
    1014            0 :    std::string cwd = ss_getcwd();
    1015            0 :    if (!cwd.empty()) {
    1016            0 :       paths.push_back(cwd + "/");
    1017            0 :       paths.push_back(cwd + "/resources/");
    1018              :    }
    1019            0 :    paths.push_back(cm_get_path());
    1020            0 :    paths.push_back(cm_get_path() + "resources/");
    1021            0 :    char *m = getenv("MIDASSYS");
    1022            0 :    if (m) {
    1023            0 :       paths.push_back(std::string(m) + "/resources/");
    1024              :    }
    1025              : 
    1026            0 :    return paths;
    1027            0 : }
    1028              : 
    1029              : /*------------------------------------------------------------------*/
    1030              : 
    1031            0 : bool open_resource_file(const char *filename, std::string* ppath, FILE** pfp)
    1032              : {
    1033              :    // resource file names should not start with a directory separator "/"
    1034              :    // or contain ".." as this will allow them to escape the mhttpd filename "jail"
    1035              :    // by asking file files names like "../../etc/passwd", etc.
    1036              : 
    1037            0 :    if (strlen(filename) < 1) {
    1038            0 :       cm_msg(MERROR, "open_resource_file", "Invalid resource file name \'%s\' is too short",
    1039              :               filename);
    1040            0 :       return false;
    1041              :    }
    1042              : 
    1043            0 :    if (filename[0] == DIR_SEPARATOR) {
    1044            0 :       cm_msg(MERROR, "open_resource_file", "Invalid resource file name \'%s\' starting with \'%c\' which is not allowed",
    1045              :               filename, DIR_SEPARATOR);
    1046            0 :       return false;
    1047              :    }
    1048              : 
    1049            0 :    if (strstr(filename, "..") != NULL) {
    1050            0 :       cm_msg(MERROR, "open_resource_file", "Invalid resource file name \'%s\' containing \'..\' which is not allowed",
    1051              :              filename);
    1052            0 :       return false;
    1053              :    }
    1054              : 
    1055            0 :    std::vector<std::string> paths = get_resource_paths();
    1056              : 
    1057            0 :    std::vector<std::string> paths_not_found;
    1058              : 
    1059            0 :    for (unsigned i=0; i<paths.size(); i++) {
    1060            0 :       std::string path = paths[i];
    1061            0 :       if (path.length() < 1)
    1062            0 :          continue;
    1063            0 :       if (path[0] == '#')
    1064            0 :          continue;
    1065              : 
    1066              :       // expand env.variables before we add the filename.
    1067              :       // the filename comes from the URL and if the URL
    1068              :       // has '$' characters we will try to expand them
    1069              :       // as an env.variable and maybe escape the file jail.
    1070              : 
    1071            0 :       std::string xpath = cm_expand_env(path.c_str());
    1072              : 
    1073            0 :       if (xpath[xpath.length()-1] != DIR_SEPARATOR)
    1074            0 :          xpath += DIR_SEPARATOR_STR;
    1075            0 :       xpath += filename;
    1076              : 
    1077              :       //printf("path [%s] [%s] [%s]\n", paths[i].c_str(), path.c_str(), xpath.c_str());
    1078              : 
    1079            0 :       FILE* fp = fopen(xpath.c_str(), "r");
    1080            0 :       if (fp) {
    1081              :          struct stat statbuf;
    1082            0 :          int status = fstat(fileno(fp), &statbuf);
    1083            0 :          if (status != 0) {
    1084            0 :             cm_msg(MERROR, "open_resource_file", "Cannot fstat() file \'%s\', error %d (%s)", xpath.c_str(), errno, strerror(errno));
    1085            0 :             fclose(fp);
    1086            0 :             fp = NULL;
    1087              :          }
    1088              : 
    1089            0 :          if (statbuf.st_mode & S_IFREG) {
    1090              :             // good, normal file
    1091              :             //printf("%s: regular!\n", xpath.c_str());
    1092              :          //} else if (statbuf.st_mode & S_IFLNK) {
    1093              :             // symlink
    1094              :             //printf("%s: symlink!\n", xpath.c_str());
    1095            0 :          } else if (statbuf.st_mode & S_IFDIR) {
    1096            0 :             cm_msg(MERROR, "open_resource_file", "File \'%s\' for resource \'%s\' is a directory", xpath.c_str(), filename);
    1097            0 :             fclose(fp);
    1098            0 :             fp = NULL;
    1099              :          } else {
    1100            0 :             cm_msg(MERROR, "open_resource_file", "File \'%s\' for resource \'%s\' is not a regular file, st_mode is 0x%08x", xpath.c_str(), filename, statbuf.st_mode);
    1101            0 :             fclose(fp);
    1102            0 :             fp = NULL;
    1103              :          }
    1104              : 
    1105            0 :          if (fp) {
    1106            0 :             if (ppath)
    1107            0 :                *ppath = xpath;
    1108            0 :             if (pfp) {
    1109            0 :                *pfp = fp;
    1110              :             } else {
    1111            0 :                fclose(fp);
    1112            0 :                fp = NULL;
    1113              :             }
    1114              :             //cm_msg(MINFO, "open_resource_file", "Resource file \'%s\' is \'%s\'", filename, xpath.c_str());
    1115            0 :             return true;
    1116              :          }
    1117              :       }
    1118              : 
    1119            0 :       paths_not_found.push_back(xpath);
    1120            0 :    }
    1121              : 
    1122            0 :    std::string s;
    1123            0 :    for (unsigned i=0; i<paths_not_found.size(); i++) {
    1124            0 :       if (i>0)
    1125            0 :          s += ", ";
    1126            0 :       s += paths_not_found[i];
    1127              :    }
    1128              : 
    1129            0 :    cm_msg(MERROR, "open_resource_file", "Cannot find resource file \'%s\', tried %s", filename, s.c_str());
    1130            0 :    return false;
    1131            0 : }
    1132              : 
    1133              : /*------------------------------------------------------------------*/
    1134              : 
    1135            0 : std::string get_content_type(const char* filename)
    1136              : {
    1137            0 :    std::string ext_upper;
    1138            0 :    const char* p = filename;
    1139            0 :    const char* last_dot = NULL;
    1140            0 :    for (; *p; p++) {
    1141            0 :       if (*p == '.')
    1142            0 :          last_dot = p;
    1143            0 :       if (*p == DIR_SEPARATOR)
    1144            0 :          last_dot = NULL;
    1145              :    }
    1146              : 
    1147            0 :    if (last_dot) {
    1148            0 :       p = last_dot;
    1149            0 :       for (; *p; p++)
    1150            0 :          ext_upper += toupper(*p);
    1151              :    }
    1152              : 
    1153              :    //printf("filename: [%s], ext [%s]\n", filename, ext_upper.c_str());
    1154              : 
    1155            0 :    std::string type = GetMimetype(ext_upper);
    1156            0 :    if (type.length() > 0)
    1157            0 :       return type;
    1158              : 
    1159            0 :    cm_msg(MERROR, "get_content_type", "Unknown HTTP Content-Type for resource file \'%s\', file extension \'%s\'", filename, ext_upper.c_str());
    1160              : 
    1161            0 :    return "text/plain";
    1162            0 : }
    1163              : 
    1164              : /*------------------------------------------------------------------*/
    1165              : 
    1166            0 : bool send_fp(Return* r, const std::string& path, FILE* fp)
    1167              : {
    1168            0 :    assert(fp != NULL);
    1169              : 
    1170              :    // send HTTP headers
    1171              : 
    1172            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    1173            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1174            0 :    r->rsprintf("Accept-Ranges: bytes\r\n");
    1175              : 
    1176              :    // send HTTP cache control headers
    1177              : 
    1178            0 :    time_t now = time(NULL);
    1179            0 :    now += (int) (3600 * 24);
    1180              :    struct tm gmt_tms;
    1181            0 :    gmtime_r(&now, &gmt_tms);
    1182            0 :    const char* format = "%A, %d-%b-%y %H:%M:%S GMT";
    1183              : 
    1184              :    char str[256];
    1185            0 :    strftime(str, sizeof(str), format, &gmt_tms);
    1186            0 :    r->rsprintf("Expires: %s\r\n", str);
    1187              : 
    1188              :    // send Content-Type header
    1189              : 
    1190            0 :    r->rsprintf("Content-Type: %s\r\n", get_content_type(path.c_str()).c_str());
    1191              : 
    1192              :    // send Content-Length header
    1193              : 
    1194              :    struct stat stat_buf;
    1195            0 :    fstat(fileno(fp), &stat_buf);
    1196            0 :    int length = stat_buf.st_size;
    1197            0 :    r->rsprintf("Content-Length: %d\r\n", length);
    1198              : 
    1199              :    // send end of headers
    1200              : 
    1201            0 :    r->rsprintf("\r\n");
    1202              : 
    1203              :    // send file data
    1204              : 
    1205            0 :    r->rread(path.c_str(), fileno(fp), length);
    1206              : 
    1207            0 :    fclose(fp);
    1208              : 
    1209            0 :    return true;
    1210              : }
    1211              : 
    1212            0 : bool send_file(Return* r, const std::string& path, bool generate_404 = true)
    1213              : {
    1214            0 :    FILE *fp = fopen(path.c_str(), "rb");
    1215              : 
    1216            0 :    if (!fp) {
    1217            0 :       if (generate_404) {
    1218              :          /* header */
    1219            0 :          r->rsprintf("HTTP/1.1 404 Not Found\r\n");
    1220            0 :          r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1221            0 :          r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
    1222            0 :          r->rsprintf("\r\n");
    1223            0 :          r->rsprintf("Error: Cannot read \"%s\", fopen() errno %d (%s)\n", path.c_str(), errno, strerror(errno));
    1224              :       }
    1225            0 :       return false;
    1226              :    }
    1227              : 
    1228            0 :    return send_fp(r, path, fp);
    1229              : }
    1230              : 
    1231            0 : bool send_resource(Return* r, const std::string& name, bool generate_404 = true)
    1232              : {
    1233            0 :    std::string path;
    1234            0 :    FILE *fp = NULL;
    1235              : 
    1236            0 :    bool found = open_resource_file(name.c_str(), &path, &fp);
    1237              : 
    1238            0 :    if (!found) {
    1239            0 :       if (generate_404) {
    1240              :          /* header */
    1241            0 :          r->rsprintf("HTTP/1.1 404 Not Found\r\n");
    1242            0 :          r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1243            0 :          r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
    1244            0 :          r->rsprintf("\r\n");
    1245            0 :          r->rsprintf("Error: resource file \"%s\" not found, see messages\n", name.c_str());
    1246              :       }
    1247            0 :       return false;
    1248              :    }
    1249              : 
    1250            0 :    return send_fp(r, path, fp);
    1251            0 : }
    1252              : 
    1253              : /*------------------------------------------------------------------*/
    1254              : 
    1255            0 : INT sendmail(const char* from_host, const char *smtp_host, const char *from, const char *to, const char *subject, const char *text)
    1256              : {
    1257              :    struct sockaddr_in bind_addr;
    1258              :    struct hostent *phe;
    1259              :    int i, s, strsize, offset;
    1260              :    char *str, buf[256];
    1261              : 
    1262            0 :    if (verbose)
    1263            0 :       printf("\n\nEmail from %s to %s, SMTP host %s:\n", from, to, smtp_host);
    1264              : 
    1265              :    /* create a new socket for connecting to remote server */
    1266            0 :    s = socket(AF_INET, SOCK_STREAM, 0);
    1267            0 :    if (s == -1)
    1268            0 :       return -1;
    1269              : 
    1270              :    /* connect to remote node port 25 */
    1271            0 :    memset(&bind_addr, 0, sizeof(bind_addr));
    1272            0 :    bind_addr.sin_family = AF_INET;
    1273            0 :    bind_addr.sin_port = htons((short) 25);
    1274              : 
    1275            0 :    phe = gethostbyname(smtp_host);
    1276            0 :    if (phe == NULL)
    1277            0 :       return -1;
    1278            0 :    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
    1279              : 
    1280            0 :    if (connect(s, (const sockaddr*)&bind_addr, sizeof(bind_addr)) < 0) {
    1281            0 :       closesocket(s);
    1282            0 :       return -1;
    1283              :    }
    1284              : 
    1285            0 :    strsize = TEXT_SIZE + 1000;
    1286            0 :    str = (char*)malloc(strsize);
    1287              : 
    1288            0 :    recv_string(s, str, strsize, 3000);
    1289            0 :    if (verbose)
    1290            0 :       puts(str);
    1291              : 
    1292              :    /* drain server messages */
    1293              :    do {
    1294            0 :       str[0] = 0;
    1295            0 :       recv_string(s, str, strsize, 300);
    1296            0 :       if (verbose)
    1297            0 :          puts(str);
    1298            0 :    } while (str[0]);
    1299              : 
    1300            0 :    sprintf(str, "HELO %s\r\n", from_host);
    1301            0 :    send(s, str, strlen(str), 0);
    1302            0 :    if (verbose)
    1303            0 :       puts(str);
    1304            0 :    recv_string(s, str, strsize, 3000);
    1305            0 :    if (verbose)
    1306            0 :       puts(str);
    1307              : 
    1308            0 :    if (strchr(from, '<')) {
    1309            0 :       mstrlcpy(buf, strchr(from, '<') + 1, sizeof(buf));
    1310            0 :       if (strchr(buf, '>'))
    1311            0 :          *strchr(buf, '>') = 0;
    1312              :    } else
    1313            0 :       mstrlcpy(buf, from, sizeof(buf));
    1314              : 
    1315            0 :    sprintf(str, "MAIL FROM: %s\n", buf);
    1316            0 :    send(s, str, strlen(str), 0);
    1317            0 :    if (verbose)
    1318            0 :       puts(str);
    1319            0 :    recv_string(s, str, strsize, 3000);
    1320            0 :    if (verbose)
    1321            0 :       puts(str);
    1322              : 
    1323            0 :    sprintf(str, "RCPT TO: <%s>\r\n", to);
    1324            0 :    send(s, str, strlen(str), 0);
    1325            0 :    if (verbose)
    1326            0 :       puts(str);
    1327            0 :    recv_string(s, str, strsize, 3000);
    1328            0 :    if (verbose)
    1329            0 :       puts(str);
    1330              : 
    1331            0 :    sprintf(str, "DATA\r\n");
    1332            0 :    send(s, str, strlen(str), 0);
    1333            0 :    if (verbose)
    1334            0 :       puts(str);
    1335            0 :    recv_string(s, str, strsize, 3000);
    1336            0 :    if (verbose)
    1337            0 :       puts(str);
    1338              : 
    1339            0 :    sprintf(str, "To: %s\r\nFrom: %s\r\nSubject: %s\r\n", to, from, subject);
    1340            0 :    send(s, str, strlen(str), 0);
    1341            0 :    if (verbose)
    1342            0 :       puts(str);
    1343              : 
    1344            0 :    sprintf(str, "X-Mailer: mhttpd revision %s\r\n", mhttpd_revision());
    1345            0 :    send(s, str, strlen(str), 0);
    1346            0 :    if (verbose)
    1347            0 :       puts(str);
    1348              : 
    1349            0 :    ss_tzset(); // required for localtime_r()
    1350              :    time_t now;
    1351            0 :    time(&now);
    1352              :    struct tm tms;
    1353            0 :    localtime_r(&now, &tms);
    1354            0 :    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tms);
    1355            0 :    offset = (-(int) timezone);
    1356            0 :    if (tms.tm_isdst)
    1357            0 :       offset += 3600;
    1358            0 :    sprintf(str, "Date: %s %+03d%02d\r\n", buf, (int) (offset / 3600),
    1359            0 :            (int) ((abs((int) offset) / 60) % 60));
    1360            0 :    send(s, str, strlen(str), 0);
    1361            0 :    if (verbose)
    1362            0 :       puts(str);
    1363              : 
    1364            0 :    sprintf(str, "Content-Type: TEXT/PLAIN; charset=US-ASCII\r\n\r\n");
    1365            0 :    send(s, str, strlen(str), 0);
    1366            0 :    if (verbose)
    1367            0 :       puts(str);
    1368              : 
    1369              :    /* analyze text for "." at beginning of line */
    1370            0 :    const char* p = text;
    1371            0 :    str[0] = 0;
    1372            0 :    while (strstr(p, "\r\n.\r\n")) {
    1373            0 :       i = (POINTER_T) strstr(p, "\r\n.\r\n") - (POINTER_T) p + 1;
    1374            0 :       mstrlcat(str, p, i);
    1375            0 :       p += i + 4;
    1376            0 :       mstrlcat(str, "\r\n..\r\n", strsize);
    1377              :    }
    1378            0 :    mstrlcat(str, p, strsize);
    1379            0 :    mstrlcat(str, "\r\n", strsize);
    1380            0 :    send(s, str, strlen(str), 0);
    1381            0 :    if (verbose)
    1382            0 :       puts(str);
    1383              : 
    1384              :    /* send ".<CR>" to signal end of message */
    1385            0 :    sprintf(str, ".\r\n");
    1386            0 :    send(s, str, strlen(str), 0);
    1387            0 :    if (verbose)
    1388            0 :       puts(str);
    1389            0 :    recv_string(s, str, strsize, 3000);
    1390            0 :    if (verbose)
    1391            0 :       puts(str);
    1392              : 
    1393            0 :    sprintf(str, "QUIT\n");
    1394            0 :    send(s, str, strlen(str), 0);
    1395            0 :    if (verbose)
    1396            0 :       puts(str);
    1397            0 :    recv_string(s, str, strsize, 3000);
    1398            0 :    if (verbose)
    1399            0 :       puts(str);
    1400              : 
    1401            0 :    closesocket(s);
    1402            0 :    free(str);
    1403              : 
    1404            0 :    return 1;
    1405              : }
    1406              : 
    1407              : /*------------------------------------------------------------------*/
    1408              : 
    1409            0 : void redirect(Return *r, const char *path)
    1410              : {
    1411              :    char str[256];
    1412              : 
    1413              :    //printf("redirect to [%s]\n", path);
    1414              : 
    1415            0 :    mstrlcpy(str, path, sizeof(str));
    1416            0 :    if (str[0] == 0)
    1417            0 :       strcpy(str, "./");
    1418              : 
    1419              :    /* redirect */
    1420            0 :    r->rsprintf("HTTP/1.1 302 Found\r\n");
    1421            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1422            0 :    r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
    1423              : 
    1424            0 :    if (strncmp(path, "http:", 5) == 0)
    1425            0 :       r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
    1426            0 :    else if (strncmp(path, "https:", 6) == 0)
    1427            0 :       r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
    1428              :    else {
    1429            0 :       r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
    1430              :    }
    1431            0 : }
    1432              : 
    1433            0 : void redirect_307(Return *r, const char *path)
    1434              : {
    1435              :    //printf("redirect_307 to [%s]\n", path);
    1436              : 
    1437              :    /* redirect */
    1438            0 :    r->rsprintf("HTTP/1.1 307 Temporary Redirect\r\n");
    1439            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1440            0 :    r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
    1441            0 :    r->rsprintf("Location: %s\r\n", path);
    1442            0 :    r->rsprintf("\r\n");
    1443            0 :    r->rsprintf("<html>redirect to %s</html>\r\n", path);
    1444            0 : }
    1445              : 
    1446            0 : void redirect2(Return* r, const char *path)
    1447              : {
    1448            0 :    redirect(r, path);
    1449            0 : }
    1450              : 
    1451              : /*------------------------------------------------------------------*/
    1452              : 
    1453              : struct search_data
    1454              : {
    1455              :    Return* r;
    1456              :    const char* search_name;
    1457              : };
    1458              : 
    1459            0 : INT search_callback(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *info)
    1460              : {
    1461            0 :    search_data* sinfo = (search_data*)info;
    1462              :    int i;
    1463              :    INT size, status;
    1464              : 
    1465            0 :    Return* r = sinfo->r;
    1466            0 :    const char* search_name = sinfo->search_name;
    1467              : 
    1468              :    /* convert strings to uppercase */
    1469              : 
    1470              :    char xstr1[MAX_ODB_PATH];
    1471            0 :    for (i = 0; key->name[i]; i++)
    1472            0 :       xstr1[i] = toupper(key->name[i]);
    1473            0 :    xstr1[i] = 0;
    1474              : 
    1475              :    char str2[MAX_ODB_PATH];
    1476            0 :    for (i = 0; search_name[i] ; i++)
    1477            0 :       str2[i] = toupper(search_name[i]);
    1478            0 :    str2[i] = 0;
    1479              : 
    1480            0 :    if (strstr(xstr1, str2) != NULL) {
    1481              :       char data[10000];
    1482            0 :       std::string path = db_get_path(hDB, hKey).substr(1);
    1483            0 :       std::string path_encoded = urlEncode(path.c_str());
    1484              : 
    1485            0 :       if (key->type == TID_KEY || key->type == TID_LINK) {
    1486              :          /* for keys, don't display data value */
    1487            0 :          r->rsprintf("<tr><td class=\"ODBkey\"><a href=\"?cmd=odb&odb_path=/%s\">/%s</a></tr>\n", path_encoded.c_str(), path.c_str());
    1488              :       } else {
    1489              :          /* strip variable name from path */
    1490            0 :          char* p = const_cast<char *>(path.data() + path.length() - 1);
    1491            0 :          while (*p && *p != '/')
    1492            0 :             *p-- = 0;
    1493            0 :          if (*p == '/')
    1494            0 :             *p = 0;
    1495              : 
    1496              :          /* display single value */
    1497            0 :          if (key->num_values == 1) {
    1498            0 :             size = sizeof(data);
    1499            0 :             status = db_get_data(hDB, hKey, data, &size, key->type);
    1500            0 :             std::string data_str;
    1501            0 :             if (status == DB_NO_ACCESS)
    1502            0 :                data_str = "<no read access>";
    1503              :             else
    1504            0 :                data_str = db_sprintf(data, key->item_size, 0, key->type);
    1505              : 
    1506            0 :             r->rsprintf("<tr><td class=\"ODBkey\">");
    1507            0 :             r->rsprintf("<a href=\"?cmd=odb&odb_path=/%s\">/%s/%s</a></td>", path_encoded.c_str(), path.c_str(), key->name);
    1508            0 :             r->rsprintf("<td class=\"ODBvalue\">%s</td></tr>\n", data_str.c_str());
    1509            0 :          } else {
    1510              :             /* display first value */
    1511            0 :             i = key->num_values;
    1512            0 :             if (i > 10)
    1513            0 :                i = 11;
    1514            0 :             r->rsprintf("<tr><td rowspan=%d class=\"ODBkey\">", i);
    1515            0 :             r->rsprintf("<a href=\"?cmd=odb&odb_path=/%s\">/%s/%s\n", path_encoded.c_str(), path.c_str(), key->name);
    1516              : 
    1517            0 :             for (int i = 0; i < key->num_values; i++) {
    1518            0 :                size = sizeof(data);
    1519            0 :                db_get_data_index(hDB, hKey, data, &size, i, key->type);
    1520              : 
    1521            0 :                std::string data_str = db_sprintf(data, key->item_size, 0, key->type);
    1522              : 
    1523            0 :                if (i > 0)
    1524            0 :                   r->rsprintf("<tr>");
    1525              : 
    1526            0 :                r->rsprintf("<td class=\"ODBvalue\">[%d] %s</td></tr>\n", i, data_str.c_str());
    1527              : 
    1528            0 :                if (i > 8) {
    1529            0 :                   r->rsprintf("<tr><td class=\"ODBvalue\">... [%d] values ...</td></tr>\n", key->num_values - i - 1);
    1530            0 :                   break;
    1531              :                }
    1532            0 :             }
    1533              :          }
    1534              :       }
    1535            0 :    }
    1536              : 
    1537            0 :    return SUCCESS;
    1538              : }
    1539              : 
    1540              : /*------------------------------------------------------------------*/
    1541              : 
    1542            0 : void show_help_page(Return* r, const char* dec_path)
    1543              : {
    1544              :    const char *s;
    1545              :    char str[256];
    1546              :    int status;
    1547              : 
    1548            0 :    show_header(r, "Help", "", "./", 0);
    1549            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
    1550            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
    1551            0 :    show_navigation_bar(r, "Help");
    1552              : 
    1553            0 :    r->rsprintf("<table class=\"mtable\" style=\"width: 95%%\">\n");
    1554            0 :    r->rsprintf("  <tr>\n");
    1555            0 :    r->rsprintf("    <td class=\"mtableheader\">MIDAS Help Page</td>\n");
    1556            0 :    r->rsprintf("  </tr>\n");
    1557            0 :    r->rsprintf("  <tr>\n");
    1558            0 :    r->rsprintf("    <td>\n");
    1559            0 :    r->rsprintf("      <table>\n");
    1560              : 
    1561            0 :    r->rsprintf("        <tr>\n");
    1562            0 :    r->rsprintf("          <td style=\"text-align:right;\">Documentation:</td>\n");
    1563            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"https://midas.triumf.ca\">https://midas.triumf.ca</a></td>\n");
    1564            0 :    r->rsprintf("        </tr>\n");
    1565            0 :    r->rsprintf("        <tr>\n");
    1566            0 :    r->rsprintf("          <td style=\"text-align:right;\">Discussion Forum:</td>\n");
    1567            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"https://midas.triumf.ca/forum/\">https://midas.triumf.ca/forum/</a></td>\n");
    1568            0 :    r->rsprintf("        </tr>\n");
    1569            0 :    r->rsprintf("        <tr>\n");
    1570            0 :    r->rsprintf("          <td style=\"text-align:right;\">Code:</td>\n");
    1571            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"https://bitbucket.org/tmidas/midas/\">https://bitbucket.org/tmidas/midas/</a></td>\n");
    1572            0 :    r->rsprintf("        </tr>\n");
    1573            0 :    r->rsprintf("        <tr>\n");
    1574            0 :    r->rsprintf("          <td style=\"text-align:right;\">Report a bug:</td>\n");
    1575            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"https://bitbucket.org/tmidas/midas/issues/\">https://bitbucket.org/tmidas/midas/issues/</a></td>\n");
    1576            0 :    r->rsprintf("        </tr>\n");
    1577              : 
    1578            0 :    r->rsprintf("        <tr>\n");
    1579            0 :    r->rsprintf("          <td style=\"text-align:right;\">Version:</td>\n");
    1580            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", cm_get_version());
    1581            0 :    r->rsprintf("        </tr>\n");
    1582            0 :    r->rsprintf("        <tr>\n");
    1583            0 :    r->rsprintf("          <td style=\"text-align:right;\">Revision:</td>\n");
    1584            0 :    std::string rev = cm_get_revision();
    1585            0 :    std::string url = "https://bitbucket.org/tmidas/midas/commits/";
    1586              :    // rev format looks like this:
    1587              :    // Fri Nov 24 10:15:54 2017 -0800 - midas-2017-07-c-171-gb8928d5c-dirty on branch develop
    1588              :    // -gXXX is the commit hash
    1589              :    // -dirty should be removed from the hash url, if present
    1590              :    // " " before "on branch" should be removed from the hash url
    1591            0 :    std::string::size_type pos = rev.find("-g");
    1592            0 :    if (pos != std::string::npos) {
    1593            0 :       std::string hash = rev.substr(pos+2);
    1594            0 :       pos = hash.find("-dirty");
    1595            0 :       if (pos != std::string::npos) {
    1596            0 :          hash = hash.substr(0, pos);
    1597              :       }
    1598            0 :       pos = hash.find(" ");
    1599            0 :       if (pos != std::string::npos) {
    1600            0 :          hash = hash.substr(0, pos);
    1601              :       }
    1602            0 :       url += hash;
    1603            0 :       r->rsprintf("          <td style=\"text-align:left;\"><a href=\"%s\">%s</a></td>\n", url.c_str(), rev.c_str());
    1604            0 :    } else {
    1605            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", rev.c_str());
    1606              :    }
    1607            0 :    r->rsprintf("        </tr>\n");
    1608              : 
    1609            0 :    r->rsprintf("        <tr>\n");
    1610            0 :    r->rsprintf("          <td style=\"text-align:right;\">MIDASSYS:</td>\n");
    1611            0 :    s = getenv("MIDASSYS");
    1612            0 :    if (!s) s = "(unset)";
    1613            0 :    mstrlcpy(str, s, sizeof(str));
    1614            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", str);
    1615            0 :    r->rsprintf("        </tr>\n");
    1616              : 
    1617            0 :    r->rsprintf("        <tr>\n");
    1618            0 :    r->rsprintf("          <td style=\"text-align:right;\">mhttpd current directory:</td>\n");
    1619            0 :    std::string cwd = ss_getcwd();
    1620            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", cwd.c_str());
    1621            0 :    r->rsprintf("        </tr>\n");
    1622              : 
    1623            0 :    r->rsprintf("        <tr>\n");
    1624            0 :    r->rsprintf("          <td style=\"text-align:right;\">Exptab file:</td>\n");
    1625            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", cm_get_exptab_filename().c_str());
    1626            0 :    r->rsprintf("        </tr>\n");
    1627              : 
    1628            0 :    r->rsprintf("        <tr>\n");
    1629            0 :    r->rsprintf("          <td style=\"text-align:right;\">Experiment:</td>\n");
    1630            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", cm_get_experiment_name().c_str());
    1631            0 :    r->rsprintf("        </tr>\n");
    1632              : 
    1633            0 :    r->rsprintf("        <tr>\n");
    1634            0 :    r->rsprintf("          <td style=\"text-align:right;\">Experiment directory:</td>\n");
    1635            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", cm_get_path().c_str());
    1636            0 :    r->rsprintf("        </tr>\n");
    1637              : 
    1638            0 :    STRING_LIST list;
    1639            0 :    status = cm_msg_facilities(&list);
    1640              : 
    1641            0 :    if (status == CM_SUCCESS) {
    1642            0 :       if (list.size() == 1) {
    1643            0 :          r->rsprintf("        <tr>\n");
    1644            0 :          r->rsprintf("          <td style=\"text-align:right;\">System logfile:</td>\n");
    1645            0 :          std::string s;
    1646            0 :          cm_msg_get_logfile("midas", 0, &s, NULL, NULL);
    1647            0 :          r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", s.c_str());
    1648            0 :          r->rsprintf("        </tr>\n");
    1649            0 :       } else {
    1650            0 :          r->rsprintf("        <tr>\n");
    1651            0 :          r->rsprintf("          <td style=\"text-align:right;\">Logfiles:</td>\n");
    1652            0 :          r->rsprintf("          <td style=\"text-align:left;\">\n");
    1653            0 :          for (unsigned i=0 ; i<list.size() ; i++) {
    1654            0 :             if (i>0)
    1655            0 :                r->rsputs("<br />\n");
    1656            0 :             std::string s;
    1657            0 :             cm_msg_get_logfile(list[i].c_str(), 0, &s, NULL, NULL);
    1658            0 :             r->rsputs(s.c_str());
    1659            0 :          }
    1660            0 :          r->rsprintf("\n          </td>\n");
    1661            0 :          r->rsprintf("        </tr>\n");
    1662              :       }
    1663              :    }
    1664              : 
    1665            0 :    r->rsprintf("        <tr>\n");
    1666            0 :    r->rsprintf("          <td style=\"text-align:right;\">Image history:</td>\n");
    1667            0 :    r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", cm_get_history_path("IMAGE").c_str());
    1668            0 :    r->rsprintf("        </tr>\n");
    1669              : 
    1670            0 :    r->rsprintf("        <tr>\n");
    1671            0 :    r->rsprintf("          <td style=\"text-align:right;\">Resource paths:</td>\n");
    1672            0 :    r->rsprintf("          <td style=\"text-align:left;\">");
    1673            0 :    std::vector<std::string> resource_paths = get_resource_paths();
    1674            0 :    for (unsigned i=0; i<resource_paths.size(); i++) {
    1675            0 :       if (i>0)
    1676            0 :          r->rsputs("<br>");
    1677            0 :       r->rsputs(resource_paths[i].c_str());
    1678            0 :       std::string exp = cm_expand_env(resource_paths[i].c_str());
    1679              :       //printf("%d %d [%s] [%s]\n", resource_paths[i].length(), exp.length(), resource_paths[i].c_str(), exp.c_str());
    1680            0 :       if (exp != resource_paths[i]) {
    1681            0 :          r->rsputs(" (");
    1682            0 :          r->rsputs(exp.c_str());
    1683            0 :          r->rsputs(")");
    1684              :       }
    1685            0 :    }
    1686            0 :    r->rsprintf("          </td>\n");
    1687            0 :    r->rsprintf("        </tr>\n");
    1688              : 
    1689            0 :    std::string path;
    1690              : 
    1691            0 :    r->rsprintf("        <tr>\n");
    1692            0 :    r->rsprintf("          <td style=\"text-align:right;\">midas.css:</td>\n");
    1693            0 :    if (open_resource_file("midas.css", &path, NULL))
    1694            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", path.c_str());
    1695              :    else
    1696            0 :       r->rsprintf("          <td style=\"text-align:left;\">NOT FOUND</td>\n");
    1697            0 :    r->rsprintf("        </tr>\n");
    1698              : 
    1699            0 :    r->rsprintf("        <tr>\n");
    1700            0 :    r->rsprintf("          <td style=\"text-align:right;\">midas.js:</td>\n");
    1701            0 :    if (open_resource_file("midas.js", &path, NULL))
    1702            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", path.c_str());
    1703              :    else
    1704            0 :       r->rsprintf("          <td style=\"text-align:left;\">NOT FOUND</td>\n");
    1705            0 :    r->rsprintf("        </tr>\n");
    1706              : 
    1707            0 :    r->rsprintf("        <tr>\n");
    1708            0 :    r->rsprintf("          <td style=\"text-align:right;\">controls.js:</td>\n");
    1709            0 :    if (open_resource_file("controls.js", &path, NULL))
    1710            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", path.c_str());
    1711              :    else
    1712            0 :       r->rsprintf("          <td style=\"text-align:left;\">NOT FOUND</td>\n");
    1713            0 :    r->rsprintf("        </tr>\n");
    1714              : 
    1715            0 :    r->rsprintf("        <tr>\n");
    1716            0 :    r->rsprintf("          <td style=\"text-align:right;\">mhttpd.js:</td>\n");
    1717            0 :    if (open_resource_file("mhttpd.js", &path, NULL))
    1718            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", path.c_str());
    1719              :    else
    1720            0 :       r->rsprintf("          <td style=\"text-align:left;\">NOT FOUND</td>\n");
    1721            0 :    r->rsprintf("        </tr>\n");
    1722              : 
    1723            0 :    r->rsprintf("        <tr>\n");
    1724            0 :    r->rsprintf("          <td style=\"text-align:right;\">obsolete.js:</td>\n");
    1725            0 :    if (open_resource_file("obsolete.js", &path, NULL))
    1726            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", path.c_str());
    1727              :    else
    1728            0 :       r->rsprintf("          <td style=\"text-align:left;\">NOT FOUND</td>\n");
    1729            0 :    r->rsprintf("        </tr>\n");
    1730              : 
    1731            0 :    r->rsprintf("        <tr>\n");
    1732            0 :    r->rsprintf("          <td style=\"text-align:right;\">Obsolete mhttpd.css:</td>\n");
    1733            0 :    if (open_resource_file("mhttpd.css", &path, NULL))
    1734            0 :       r->rsprintf("          <td style=\"text-align:left;\">%s</td>\n", path.c_str());
    1735              :    else
    1736            0 :       r->rsprintf("          <td style=\"text-align:left;\">NOT FOUND</td>\n");
    1737            0 :    r->rsprintf("        </tr>\n");
    1738              : 
    1739            0 :    r->rsprintf("        <tr>\n");
    1740            0 :    r->rsprintf("          <td style=\"text-align:right;\">JSON-RPC schema:</td>\n");
    1741            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"?mjsonrpc_schema\">json format</a> or <a href=\"?mjsonrpc_schema_text\">text table format</a></td>\n");
    1742            0 :    r->rsprintf("        </tr>\n");
    1743              : 
    1744            0 :    r->rsprintf("        <tr>\n");
    1745            0 :    r->rsprintf("          <td style=\"text-align:right;\">JavaScript examples:</td>\n");
    1746            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"?cmd=example\">example.html</a></td>\n");
    1747            0 :    r->rsprintf("        </tr>\n");
    1748              : 
    1749            0 :    r->rsprintf("        <tr>\n");
    1750            0 :    r->rsprintf("          <td style=\"text-align:right;\">Custom page example:</td>\n");
    1751            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"?cmd=custom_example\">custom_example.html</a></td>\n");
    1752            0 :    r->rsprintf("        </tr>\n");
    1753              : 
    1754            0 :    r->rsprintf("        <tr>\n");
    1755            0 :    r->rsprintf("          <td style=\"text-align:right;\">MPlot custom plot examples:</td>\n");
    1756            0 :    r->rsprintf("          <td style=\"text-align:left;\"><a href=\"?cmd=plot_example\">plot_example.html</a></td>\n");
    1757            0 :    r->rsprintf("        </tr>\n");
    1758              : 
    1759            0 :    r->rsprintf("      </table>\n");
    1760            0 :    r->rsprintf("    </td>\n");
    1761            0 :    r->rsprintf("  </tr>\n");
    1762            0 :    r->rsprintf("</table>\n");
    1763              : 
    1764            0 :    r->rsprintf("<table class=\"mtable\" style=\"width: 95%%\">\n");
    1765            0 :    r->rsprintf("  <tr>\n");
    1766            0 :    r->rsprintf("    <td class=\"mtableheader\">Contributions</td>\n");
    1767            0 :    r->rsprintf("  </tr>\n");
    1768            0 :    r->rsprintf("  <tr>\n");
    1769            0 :    r->rsprintf("    <td>\n");
    1770            0 :    r->rsprintf("Pierre-Andre&nbsp;Amaudruz - Sergio&nbsp;Ballestrero - Suzannah&nbsp;Daviel - Peter&nbsp;Green - Qing&nbsp;Gu - Greg&nbsp;Hackman - Gertjan&nbsp;Hofman - Paul&nbsp;Knowles - Exaos&nbsp;Lee - Thomas&nbsp;Lindner - Shuoyi&nbsp;Ma - Rudi&nbsp;Meier - Bill&nbsp;Mills - Glenn&nbsp;Moloney - Dave&nbsp;Morris - John&nbsp;M&nbsp;O'Donnell - Konstantin&nbsp;Olchanski - Chris&nbsp;Pearson - Renee&nbsp;Poutissou - Stefan&nbsp;Ritt - Zaher&nbsp;Salman - Ryu&nbsp;Sawada - Tamsen&nbsp;Schurman - Ben&nbsp;Smith - Andreas&nbsp;Suter - Jan&nbsp;M.&nbsp;Wouters - Piotr&nbsp;Adam&nbsp;Zolnierczuk\n");
    1771            0 :    r->rsprintf("    </td>\n");
    1772            0 :    r->rsprintf("  </tr>\n");
    1773            0 :    r->rsprintf("</table>\n");
    1774              : 
    1775            0 :    r->rsprintf("</div></form>\n");
    1776            0 :    r->rsprintf("</body></html>\r\n");
    1777            0 : }
    1778              : 
    1779              : /*------------------------------------------------------------------*/
    1780              : 
    1781            0 : void show_header(Return* r, const char *title, const char *method, const char *path, int refresh)
    1782              : {
    1783              :    HNDLE hDB;
    1784              :    time_t now;
    1785              :    char str[256];
    1786              : 
    1787            0 :    cm_get_experiment_database(&hDB, NULL);
    1788              : 
    1789              :    /* header */
    1790            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    1791            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1792            0 :    r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
    1793            0 :    r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
    1794            0 :    r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
    1795              : 
    1796            0 :    r->rsprintf("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n");
    1797            0 :    r->rsprintf("<html><head>\n");
    1798              : 
    1799              :    /* style sheet */
    1800            0 :    r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
    1801            0 :    r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
    1802            0 :    r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
    1803              : 
    1804              :    /* auto refresh */
    1805            0 :    if (refresh > 0)
    1806            0 :       r->rsprintf("<meta http-equiv=\"Refresh\" content=\"%02d\">\n", refresh);
    1807              : 
    1808            0 :    r->rsprintf("<title>%s</title></head>\n", title);
    1809              : 
    1810            0 :    mstrlcpy(str, path, sizeof(str));
    1811            0 :    urlEncode(str, sizeof(str));
    1812              : 
    1813            0 :    if (equal_ustring(method, "POST"))
    1814              :       r->rsprintf
    1815            0 :           ("<body><form name=\"form1\" method=\"POST\" action=\"%s\" enctype=\"multipart/form-data\">\n\n",
    1816              :            str);
    1817            0 :    else if (equal_ustring(method, "GET"))
    1818            0 :       r->rsprintf("<body><form name=\"form1\" method=\"GET\" action=\"%s\">\n\n", str);
    1819              : 
    1820              :    /* title row */
    1821              : 
    1822            0 :    std::string exptname;
    1823            0 :    db_get_value_string(hDB, 0, "/Experiment/Name", 0, &exptname, TRUE);
    1824            0 :    time(&now);
    1825            0 : }
    1826              : 
    1827              : /*------------------------------------------------------------------*/
    1828              : 
    1829            0 : void show_text_header(Return* r)
    1830              : {
    1831            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    1832            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1833            0 :    r->rsprintf("Access-Control-Allow-Origin: *\r\n");
    1834            0 :    r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
    1835            0 :    r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
    1836            0 :    r->rsprintf("Content-Type: text/plain; charset=%s\r\n\r\n", HTTP_ENCODING);
    1837            0 : }
    1838              : 
    1839              : /*------------------------------------------------------------------*/
    1840              : 
    1841            0 : void show_error(Return* r, const char *error)
    1842              : {
    1843              :    /* header */
    1844            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    1845            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1846            0 :    r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
    1847              : 
    1848            0 :    r->rsprintf("<html><head>\n");
    1849            0 :    r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
    1850            0 :    r->rsprintf("<title>MIDAS error</title></head>\n");
    1851            0 :    r->rsprintf("<body><H1>%s</H1></body></html>\n", error);
    1852            0 : }
    1853              : 
    1854              : /*------------------------------------------------------------------*/
    1855              : 
    1856            0 : void show_error_404(Return* r, const char *error)
    1857              : {
    1858              :    /* header */
    1859            0 :    r->rsprintf("HTTP/1.1 404 Not Found\r\n");
    1860            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    1861            0 :    r->rsprintf("Content-Type: text/plain\r\n");
    1862            0 :    r->rsprintf("\r\n");
    1863              : 
    1864            0 :    r->rsprintf("MIDAS error: %s\n", error);
    1865            0 : }
    1866              : 
    1867              : /*------------------------------------------------------------------*/
    1868              : 
    1869            0 : void show_navigation_bar(Return* r, const char *cur_page)
    1870              : {
    1871            0 :    r->rsprintf("<script>\n");
    1872            0 :    r->rsprintf("window.addEventListener(\"load\", function(e) { mhttpd_init('%s', 1000); });\n", cur_page);
    1873            0 :    r->rsprintf("</script>\n");
    1874              : 
    1875            0 :    r->rsprintf("<!-- header and side navigation will be filled in mhttpd_init -->\n");
    1876            0 :    r->rsprintf("<div id=\"mheader\"></div>\n");
    1877            0 :    r->rsprintf("<div id=\"msidenav\"></div>\n");
    1878            0 :    r->rsprintf("<div id=\"mmain\">\n");
    1879            0 : }
    1880              : 
    1881              : /*------------------------------------------------------------------*/
    1882              : 
    1883            0 : void check_obsolete_odb(HNDLE hDB, const char* odb_path)
    1884              : {
    1885              :    HNDLE hKey;
    1886            0 :    int status = db_find_key(hDB, 0, odb_path, &hKey);
    1887            0 :    if (status == DB_SUCCESS) {
    1888            0 :       cm_msg(MERROR, "check_obsolete_odb", "ODB \"%s\" is obsolete, please delete it.", odb_path);
    1889              :    }
    1890            0 : }
    1891              : 
    1892            0 : void init_menu_buttons(MVOdb* odb)
    1893              : {
    1894              :    HNDLE hDB;
    1895            0 :    BOOL true_value = TRUE;
    1896            0 :    BOOL false_value = FALSE;
    1897            0 :    int size = sizeof(true_value);
    1898            0 :    cm_get_experiment_database(&hDB, NULL);
    1899            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Status",     &true_value,  &size, TID_BOOL, TRUE);
    1900            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Start",      &false_value, &size, TID_BOOL, TRUE);
    1901            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Transition", &true_value,  &size, TID_BOOL, TRUE);
    1902            0 :    db_get_value(hDB, 0, "/Experiment/Menu/ODB",        &true_value,  &size, TID_BOOL, TRUE);
    1903            0 :    db_get_value(hDB, 0, "/Experiment/Menu/OldODB",     &true_value,  &size, TID_BOOL, TRUE);
    1904            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Messages",   &true_value,  &size, TID_BOOL, TRUE);
    1905            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Chat",       &true_value,  &size, TID_BOOL, TRUE);
    1906            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Elog",       &true_value,  &size, TID_BOOL, TRUE);
    1907            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Alarms",     &true_value,  &size, TID_BOOL, TRUE);
    1908            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Programs",   &true_value,  &size, TID_BOOL, TRUE);
    1909            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Buffers",    &true_value,  &size, TID_BOOL, TRUE);
    1910            0 :    db_get_value(hDB, 0, "/Experiment/Menu/History",    &true_value,  &size, TID_BOOL, TRUE);
    1911            0 :    db_get_value(hDB, 0, "/Experiment/Menu/OldHistory", &true_value,  &size, TID_BOOL, TRUE);
    1912            0 :    db_get_value(hDB, 0, "/Experiment/Menu/MSCB",       &true_value,  &size, TID_BOOL, TRUE);
    1913            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Sequencer",  &true_value,  &size, TID_BOOL, TRUE);
    1914            0 :    db_get_value(hDB, 0, "/Experiment/Menu/PySequencer",&true_value,  &size, TID_BOOL, TRUE);
    1915            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Event Dump", &true_value,  &size, TID_BOOL, TRUE);
    1916            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Config",     &true_value,  &size, TID_BOOL, TRUE);
    1917            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Example",    &false_value, &size, TID_BOOL, TRUE);
    1918            0 :    db_get_value(hDB, 0, "/Experiment/Menu/Help",       &true_value,  &size, TID_BOOL, TRUE);
    1919              : 
    1920              :    //std::string buf;
    1921              :    //status = db_get_value_string(hDB, 0, "/Experiment/Menu buttons", 0, &buf, FALSE);
    1922              :    //if (status == DB_SUCCESS) {
    1923              :    //   cm_msg(MERROR, "init_menu_buttons", "ODB \"/Experiment/Menu buttons\" is obsolete, please delete it.");
    1924              :    //}
    1925              : 
    1926            0 :    check_obsolete_odb(hDB, "/Experiment/Menu buttons");
    1927            0 :    check_obsolete_odb(hDB, "/Experiment/Menu/OldSequencer");
    1928            0 :    check_obsolete_odb(hDB, "/Experiment/Menu/NewSequencer");
    1929            0 : }
    1930              : 
    1931              : /*------------------------------------------------------------------*/
    1932              : 
    1933            0 : void init_mhttpd_odb(MVOdb* odb)
    1934              : {
    1935              :    HNDLE hDB;
    1936              :    HNDLE hKey;
    1937              :    int status;
    1938            0 :    std::string s;
    1939            0 :    cm_get_experiment_database(&hDB, NULL);
    1940              : 
    1941            0 :    status = db_find_key(hDB, 0, "/Experiment/Base URL", &hKey);
    1942            0 :    if (status == DB_SUCCESS) {
    1943            0 :       cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/Base URL\" is obsolete, please delete it.");
    1944              :    }
    1945              : 
    1946            0 :    status = db_find_key(hDB, 0, "/Experiment/CSS File", &hKey);
    1947            0 :    if (status == DB_SUCCESS) {
    1948            0 :       cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/CSS File\" is obsolete, please delete it.");
    1949              :    }
    1950              : 
    1951            0 :    status = db_find_key(hDB, 0, "/Experiment/JS File", &hKey);
    1952            0 :    if (status == DB_SUCCESS) {
    1953            0 :       cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/JS File\" is obsolete, please delete it.");
    1954              :    }
    1955              : 
    1956            0 :    status = db_find_key(hDB, 0, "/Experiment/Start-Stop Buttons", &hKey);
    1957            0 :    if (status == DB_SUCCESS) {
    1958            0 :       cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/Start-Stop Buttons\" is obsolete, please delete it.");
    1959              :    }
    1960              : 
    1961            0 :    bool xdefault = true;
    1962            0 :    odb->RB("Experiment/Pause-Resume Buttons", &xdefault, true);
    1963              : 
    1964              : #ifdef HAVE_MONGOOSE616
    1965            0 :    check_obsolete_odb(hDB, "/Experiment/midas http port");
    1966            0 :    check_obsolete_odb(hDB, "/Experiment/midas https port");
    1967            0 :    check_obsolete_odb(hDB, "/Experiment/http redirect to https");
    1968            0 :    check_obsolete_odb(hDB, "/Experiment/Security/mhttpd hosts");
    1969              : #endif
    1970              : 
    1971            0 :    status = db_find_key(hDB, 0, "/Logger/Message file", &hKey);
    1972            0 :    if (status == DB_SUCCESS) {
    1973            0 :       cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Logger/Message file\" is obsolete, please delete it and use \"/Logger/Message dir\" and \"/Logger/message file date format\" instead.");
    1974              :    }
    1975              : 
    1976            0 :    check_obsolete_odb(hDB, "/Logger/Watchdog timeout");
    1977            0 : }
    1978              : 
    1979              : /*------------------------------------------------------------------*/
    1980              : 
    1981            0 : void init_elog_odb()
    1982              : {
    1983              :    HNDLE hDB;
    1984              :    int size;
    1985              :    HNDLE hkey;
    1986            0 :    cm_get_experiment_database(&hDB, NULL);
    1987              : 
    1988            0 :    BOOL external_elog = FALSE;
    1989            0 :    std::string external_elog_url;
    1990              : 
    1991            0 :    size = sizeof(external_elog);
    1992            0 :    db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, TRUE);
    1993            0 :    db_get_value_string(hDB, 0, "/Elog/URL", 0, &external_elog_url, TRUE);
    1994              : 
    1995            0 :    BOOL allow_delete = FALSE;
    1996            0 :    BOOL allow_edit = FALSE;
    1997            0 :    size = sizeof(BOOL);
    1998            0 :    db_get_value(hDB, 0, "/Elog/Allow delete", &allow_delete, &size, TID_BOOL, TRUE);
    1999            0 :    db_get_value(hDB, 0, "/Elog/Allow edit", &allow_edit, &size, TID_BOOL, TRUE);
    2000              :    //db_get_value(hDB, 0, "/Elog/Display run number", &display_run_number, &size, TID_BOOL, TRUE);
    2001              : 
    2002            0 :    if (db_find_key(hDB, 0, "/Elog/Buttons", &hkey) != DB_SUCCESS) {
    2003            0 :       const char def_button[][NAME_LENGTH] = { "8h", "24h", "7d" };
    2004            0 :       db_set_value(hDB, 0, "/Elog/Buttons", def_button, NAME_LENGTH*3, 3, TID_STRING);
    2005              :    }
    2006              : 
    2007              : 
    2008              :    /* get type list from ODB */
    2009            0 :    size = 20 * NAME_LENGTH;
    2010            0 :    if (db_find_key(hDB, 0, "/Elog/Types", &hkey) != DB_SUCCESS) {
    2011            0 :       db_set_value(hDB, 0, "/Elog/Types", default_type_list, NAME_LENGTH * 20, 20, TID_STRING);
    2012              :    }
    2013              : 
    2014              :    /* get system list from ODB */
    2015            0 :    size = 20 * NAME_LENGTH;
    2016            0 :    if (db_find_key(hDB, 0, "/Elog/Systems", &hkey) != DB_SUCCESS)
    2017            0 :       db_set_value(hDB, 0, "/Elog/Systems", default_system_list, NAME_LENGTH * 20, 20, TID_STRING);
    2018            0 : }
    2019              : 
    2020              : /*------------------------------------------------------------------*/
    2021              : 
    2022            0 : void strencode(Return* r, const char *text)
    2023              : {
    2024            0 :    size_t len = strlen(text);
    2025            0 :    for (size_t i = 0; i < len; i++) {
    2026            0 :       switch (text[i]) {
    2027            0 :       case '\n':
    2028            0 :          r->rsprintf("<br>\n");
    2029            0 :          break;
    2030            0 :       case '<':
    2031            0 :          r->rsprintf("&lt;");
    2032            0 :          break;
    2033            0 :       case '>':
    2034            0 :          r->rsprintf("&gt;");
    2035            0 :          break;
    2036            0 :       case '&':
    2037            0 :          r->rsprintf("&amp;");
    2038            0 :          break;
    2039            0 :       case '\"':
    2040            0 :          r->rsprintf("&quot;");
    2041            0 :          break;
    2042            0 :       default:
    2043            0 :          r->rsprintf("%c", text[i]);
    2044              :       }
    2045              :    }
    2046            0 : }
    2047              : 
    2048              : /*------------------------------------------------------------------*/
    2049              : 
    2050            0 : std::string strencode2(const char *text)
    2051              : {
    2052            0 :    std::string b;
    2053            0 :    size_t len = strlen(text);
    2054            0 :    for (size_t i = 0; i < len; i++) {
    2055            0 :       switch (text[i]) {
    2056            0 :       case '\n':
    2057            0 :          b += "<br>\n";
    2058            0 :          break;
    2059            0 :       case '<':
    2060            0 :          b += "&lt;";
    2061            0 :          break;
    2062            0 :       case '>':
    2063            0 :          b += "&gt;";
    2064            0 :          break;
    2065            0 :       case '&':
    2066            0 :          b += "&amp;";
    2067            0 :          break;
    2068            0 :       case '\"':
    2069            0 :          b += "&quot;";
    2070            0 :          break;
    2071            0 :       default:
    2072            0 :          b += text[i];
    2073            0 :          break;
    2074              :       }
    2075              :    }
    2076            0 :    return b;
    2077            0 : }
    2078              : 
    2079              : /*------------------------------------------------------------------*/
    2080              : 
    2081            0 : void strencode3(Return* r, const char *text)
    2082              : {
    2083            0 :    size_t len = strlen(text);
    2084            0 :    for (size_t i = 0; i < len; i++) {
    2085            0 :       switch (text[i]) {
    2086            0 :       case '<':
    2087            0 :          r->rsprintf("&lt;");
    2088            0 :          break;
    2089            0 :       case '>':
    2090            0 :          r->rsprintf("&gt;");
    2091            0 :          break;
    2092            0 :       case '&':
    2093            0 :          r->rsprintf("&amp;");
    2094            0 :          break;
    2095            0 :       case '\"':
    2096            0 :          r->rsprintf("&quot;");
    2097            0 :          break;
    2098            0 :       default:
    2099            0 :          r->rsprintf("%c", text[i]);
    2100              :       }
    2101              :    }
    2102            0 : }
    2103              : 
    2104              : /*------------------------------------------------------------------*/
    2105              : 
    2106            0 : void strencode4(Return* r, const char *text)
    2107              : {
    2108            0 :    size_t len = strlen(text);
    2109            0 :    for (size_t i = 0; i < len; i++) {
    2110            0 :       switch (text[i]) {
    2111            0 :          case '\n':
    2112            0 :             r->rsprintf("<br>\n");
    2113            0 :             break;
    2114            0 :          case '<':
    2115            0 :             r->rsprintf("&lt;");
    2116            0 :             break;
    2117            0 :          case '>':
    2118            0 :             r->rsprintf("&gt;");
    2119            0 :             break;
    2120            0 :          case '&':
    2121            0 :             r->rsprintf("&amp;");
    2122            0 :             break;
    2123            0 :          case '\"':
    2124            0 :             r->rsprintf("&quot;");
    2125            0 :             break;
    2126            0 :          case ' ':
    2127            0 :             r->rsprintf("&nbsp;");
    2128            0 :             break;
    2129            0 :          default:
    2130            0 :             r->rsprintf("%c", text[i]);
    2131              :       }
    2132              :    }
    2133            0 : }
    2134              : 
    2135              : /*------------------------------------------------------------------*/
    2136              : 
    2137            0 : void gen_odb_attachment(Return* r, const char *path, std::string& bout)
    2138              : {
    2139              :    HNDLE hDB, hkeyroot, hkey;
    2140              :    KEY key;
    2141              :    INT i, j, size;
    2142              :    char data[1024];
    2143              :    time_t now;
    2144              : 
    2145            0 :    cm_get_experiment_database(&hDB, NULL);
    2146            0 :    db_find_key(hDB, 0, path, &hkeyroot);
    2147            0 :    assert(hkeyroot);
    2148              : 
    2149              :    /* title row */
    2150              :    //size = sizeof(str);
    2151              :    //str[0] = 0;
    2152              :    //db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
    2153            0 :    time(&now);
    2154              : 
    2155            0 :    bout += "<table border=3 cellpadding=1 class=\"dialogTable\">\n";
    2156              :    char ctimebuf[32];
    2157            0 :    ctime_r(&now, ctimebuf);
    2158            0 :    bout += msprintf("<tr><th colspan=2>%s</tr>\n", ctimebuf);
    2159            0 :    bout += msprintf("<tr><th colspan=2>%s</tr>\n", path);
    2160              : 
    2161              :    /* enumerate subkeys */
    2162            0 :    for (i = 0;; i++) {
    2163            0 :       db_enum_link(hDB, hkeyroot, i, &hkey);
    2164            0 :       if (!hkey)
    2165            0 :          break;
    2166            0 :       db_get_key(hDB, hkey, &key);
    2167              : 
    2168              :       /* resolve links */
    2169            0 :       if (key.type == TID_LINK) {
    2170            0 :          db_enum_key(hDB, hkeyroot, i, &hkey);
    2171            0 :          db_get_key(hDB, hkey, &key);
    2172              :       }
    2173              : 
    2174            0 :       if (key.type == TID_KEY) {
    2175              :          /* for keys, don't display data value */
    2176            0 :          bout += msprintf("<tr><td colspan=2>%s</td></tr>\n", key.name);
    2177              :       } else {
    2178              :          /* display single value */
    2179            0 :          if (key.num_values == 1) {
    2180            0 :             size = sizeof(data);
    2181            0 :             db_get_data(hDB, hkey, data, &size, key.type);
    2182              :             //printf("data size %d [%s]\n", size, data);
    2183            0 :             std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
    2184            0 :             std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
    2185              : 
    2186            0 :             if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
    2187            0 :                data_str = "(empty)";
    2188            0 :                hex_str  = "";
    2189              :             }
    2190              : 
    2191            0 :             if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
    2192              :                //sprintf(b, "<tr><td>%s</td><td>%s (%s)</td></tr>\n", key.name, data_str, hex_str);
    2193            0 :                bout += "<tr><td>";
    2194            0 :                bout += key.name;
    2195            0 :                bout += "</td><td>";
    2196            0 :                bout += data_str;
    2197            0 :                bout += " (";
    2198            0 :                bout += hex_str;
    2199            0 :                bout += ")</td></tr>\n";
    2200              :             } else {
    2201            0 :                bout += msprintf("<tr><td>%s</td><td>", key.name);
    2202            0 :                bout += strencode2(data_str.c_str());
    2203            0 :                bout += "</td></tr>\n";
    2204              :             }
    2205            0 :          } else {
    2206              :             /* display first value */
    2207            0 :             bout += msprintf("<tr><td rowspan=%d>%s</td>\n", key.num_values, key.name);
    2208              : 
    2209            0 :             for (j = 0; j < key.num_values; j++) {
    2210            0 :                size = sizeof(data);
    2211            0 :                db_get_data_index(hDB, hkey, data, &size, j, key.type);
    2212            0 :                std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
    2213            0 :                std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
    2214              : 
    2215            0 :                if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
    2216            0 :                   data_str = "(empty)";
    2217            0 :                   hex_str  = "";
    2218              :                }
    2219              : 
    2220            0 :                if (j > 0) {
    2221            0 :                   bout += "<tr>";
    2222              :                }
    2223              : 
    2224            0 :                if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
    2225              :                   //sprintf(b, "<td>[%d] %s (%s)<br></td></tr>\n", j, data_str, hex_str);
    2226            0 :                   bout += "<td>[";
    2227            0 :                   bout += toString(j);
    2228            0 :                   bout += "] ";
    2229            0 :                   bout += data_str;
    2230            0 :                   bout += " (";
    2231            0 :                   bout += hex_str;
    2232            0 :                   bout += ")<br></td></tr>\n";
    2233              :                } else {
    2234              :                   //sprintf(b, "<td>[%d] %s<br></td></tr>\n", j, data_str);
    2235            0 :                   bout += "<td>[";
    2236            0 :                   bout += toString(j);
    2237            0 :                   bout += "] ";
    2238            0 :                   bout += data_str;
    2239            0 :                   bout += "<br></td></tr>\n";
    2240              :                }
    2241            0 :             }
    2242              :          }
    2243              :       }
    2244            0 :    }
    2245              : 
    2246            0 :    bout += "</table>\n";
    2247            0 : }
    2248              : 
    2249              : /*------------------------------------------------------------------*/
    2250              : 
    2251            0 : void submit_elog(MVOdb* odb, Param* pp, Return* r, Attachment* a)
    2252              : {
    2253              :    char path[256], path1[256];
    2254              :    char mail_to[256], mail_from[256], mail_list[256],
    2255              :        smtp_host[256], tag[80], mail_param[1000];
    2256              :    char *p, *pitem;
    2257              :    HNDLE hDB, hkey;
    2258              :    char att_file[3][256];
    2259              :    int fh, size, n_mail;
    2260              :    char mhttpd_full_url[256];
    2261              : 
    2262            0 :    cm_get_experiment_database(&hDB, NULL);
    2263            0 :    mstrlcpy(att_file[0], pp->getparam("attachment0"), sizeof(att_file[0]));
    2264            0 :    mstrlcpy(att_file[1], pp->getparam("attachment1"), sizeof(att_file[1]));
    2265            0 :    mstrlcpy(att_file[2], pp->getparam("attachment2"), sizeof(att_file[2]));
    2266              : 
    2267              :    /* check for valid attachment files */
    2268            0 :    for (int i = 0; i < 3; i++) {
    2269              :       char str[256];
    2270            0 :       sprintf(str, "attachment%d", i);
    2271              :       //printf("submit_elog: att %d, [%s] param [%s], size %d\n", i, str, pp->getparam(str), a->_attachment_size[i]);
    2272            0 :       if (pp->getparam(str) && *pp->getparam(str) && a->attachment_size[i] == 0) {
    2273              :          /* replace '\' by '/' */
    2274            0 :          mstrlcpy(path, pp->getparam(str), sizeof(path));
    2275            0 :          mstrlcpy(path1, path, sizeof(path1));
    2276            0 :          while (strchr(path, '\\'))
    2277            0 :             *strchr(path, '\\') = '/';
    2278              : 
    2279              :          /* check if valid ODB tree */
    2280            0 :          if (db_find_key(hDB, 0, path, &hkey) == DB_SUCCESS) {
    2281            0 :             std::string bout;
    2282            0 :             gen_odb_attachment(r, path, bout);
    2283            0 :             int bufsize = bout.length()+1;
    2284            0 :             char* buf = (char*)M_MALLOC(bufsize);
    2285            0 :             memcpy(buf, bout.c_str(), bufsize);
    2286            0 :             mstrlcpy(att_file[i], path, sizeof(att_file[0]));
    2287            0 :             mstrlcat(att_file[i], ".html", sizeof(att_file[0]));
    2288            0 :             a->attachment_buffer[i] = buf;
    2289            0 :             a->attachment_size[i] = bufsize;
    2290            0 :          }
    2291              :          /* check if local file */
    2292            0 :          else if ((fh = open(path1, O_RDONLY | O_BINARY)) >= 0) {
    2293            0 :             size = lseek(fh, 0, SEEK_END);
    2294            0 :             char* buf = (char*)M_MALLOC(size);
    2295            0 :             lseek(fh, 0, SEEK_SET);
    2296            0 :             int rd = read(fh, buf, size);
    2297            0 :             if (rd < 0)
    2298            0 :                rd = 0;
    2299            0 :             close(fh);
    2300            0 :             mstrlcpy(att_file[i], path, sizeof(att_file[0]));
    2301            0 :             a->attachment_buffer[i] = buf;
    2302            0 :             a->attachment_size[i] = rd;
    2303            0 :          } else if (strncmp(path, "/HS/", 4) == 0) {
    2304            0 :             char* buf = (char*)M_MALLOC(100000);
    2305            0 :             size = 100000;
    2306            0 :             mstrlcpy(str, path + 4, sizeof(str));
    2307            0 :             if (strchr(str, '?')) {
    2308            0 :                p = strchr(str, '?') + 1;
    2309            0 :                p = strtok(p, "&");
    2310            0 :                while (p != NULL) {
    2311            0 :                   pitem = p;
    2312            0 :                   p = strchr(p, '=');
    2313            0 :                   if (p != NULL) {
    2314            0 :                      *p++ = 0;
    2315            0 :                      urlDecode(pitem); // parameter name
    2316            0 :                      urlDecode(p); // parameter value
    2317              : 
    2318            0 :                      pp->setparam(pitem, p);
    2319              : 
    2320            0 :                      p = strtok(NULL, "&");
    2321              :                   }
    2322              :                }
    2323            0 :                *strchr(str, '?') = 0;
    2324              :             }
    2325            0 :             show_hist_page(odb, pp, r, "image.gif", buf, &size, 0);
    2326            0 :             mstrlcpy(att_file[i], str, sizeof(att_file[0]));
    2327            0 :             a->attachment_buffer[i] = buf;
    2328            0 :             a->attachment_size[i] = size;
    2329            0 :             pp->unsetparam("scale");
    2330            0 :             pp->unsetparam("offset");
    2331            0 :             pp->unsetparam("width");
    2332            0 :             pp->unsetparam("index");
    2333              :          } else {
    2334            0 :             r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    2335            0 :             r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    2336            0 :             r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
    2337              : 
    2338            0 :             r->rsprintf("<html><head>\n");
    2339            0 :             r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
    2340            0 :             r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
    2341            0 :             r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
    2342            0 :             r->rsprintf("<title>ELog Error</title></head>\n");
    2343            0 :             r->rsprintf("<i>Error: Attachment file <i>%s</i> not valid.</i><p>\n", pp->getparam(str));
    2344            0 :             r->rsprintf("Please go back and enter a proper filename (use the <b>Browse</b> button).\n");
    2345            0 :             r->rsprintf("<body></body></html>\n");
    2346            0 :             return;
    2347              :          }
    2348              :       }
    2349              :    }
    2350              : 
    2351            0 :    int edit = atoi(pp->getparam("edit"));
    2352              :    //printf("submit_elog: edit [%s] %d, orig [%s]\n", pp->getparam("edit"), edit, pp->getparam("orig"));
    2353              : 
    2354            0 :    tag[0] = 0;
    2355            0 :    if (edit) {
    2356            0 :       mstrlcpy(tag, pp->getparam("orig"), sizeof(tag));
    2357              :    }
    2358              : 
    2359            0 :    int status = el_submit(atoi(pp->getparam("run")),
    2360              :                           pp->getparam("author"),
    2361              :                           pp->getparam("type"),
    2362              :                           pp->getparam("system"),
    2363              :                           pp->getparam("subject"),
    2364              :                           pp->getparam("text"),
    2365              :                           pp->getparam("orig"),
    2366            0 :                           *pp->getparam("html") ? "HTML" : "plain",
    2367            0 :                           att_file[0], a->attachment_buffer[0], a->attachment_size[0],
    2368            0 :                           att_file[1], a->attachment_buffer[1], a->attachment_size[1],
    2369            0 :                           att_file[2], a->attachment_buffer[2], a->attachment_size[2],
    2370              :                           tag, sizeof(tag));
    2371              : 
    2372              :    //printf("el_submit status %d, tag [%s]\n", status, tag);
    2373              : 
    2374            0 :    if (status != EL_SUCCESS) {
    2375            0 :       cm_msg(MERROR, "submit_elog", "el_submit() returned status %d", status);
    2376              :    }
    2377              : 
    2378              :    /* supersede host name with "/Elog/Host name" */
    2379            0 :    std::string elog_host_name;
    2380            0 :    db_get_value_string(hDB, 0, "/Elog/Host name", 0, &elog_host_name, TRUE);
    2381              : 
    2382              :    // K.O. FIXME: we cannot guess the Elog URL like this because
    2383              :    // we do not know if access is through a proxy or redirect
    2384              :    // we do not know if it's http: or https:, etc. Better
    2385              :    // to read the whole "mhttpd_full_url" string from ODB.
    2386            0 :    sprintf(mhttpd_full_url, "http://%s/", elog_host_name.c_str());
    2387              : 
    2388              :    /* check for mail submissions */
    2389            0 :    mail_param[0] = 0;
    2390            0 :    n_mail = 0;
    2391              : 
    2392            0 :    for (int index = 0; index <= 1; index++) {
    2393            0 :       std::string str;
    2394            0 :       str += "/Elog/Email ";
    2395            0 :       if (index == 0)
    2396            0 :          str += pp->getparam("type");
    2397              :       else
    2398            0 :          str += pp->getparam("system");
    2399              : 
    2400            0 :       if (db_find_key(hDB, 0, str.c_str(), &hkey) == DB_SUCCESS) {
    2401            0 :          size = sizeof(mail_list);
    2402            0 :          db_get_data(hDB, hkey, mail_list, &size, TID_STRING);
    2403              : 
    2404            0 :          if (db_find_key(hDB, 0, "/Elog/SMTP host", &hkey) != DB_SUCCESS) {
    2405            0 :             show_error(r, "No SMTP host defined under /Elog/SMTP host");
    2406            0 :             return;
    2407              :          }
    2408            0 :          size = sizeof(smtp_host);
    2409            0 :          db_get_data(hDB, hkey, smtp_host, &size, TID_STRING);
    2410              : 
    2411            0 :          p = strtok(mail_list, ",");
    2412              :          while (1) {
    2413            0 :             mstrlcpy(mail_to, p, sizeof(mail_to));
    2414              : 
    2415            0 :             std::string exptname;
    2416            0 :             db_get_value_string(hDB, 0, "/Experiment/Name", 0, &exptname, TRUE);
    2417              : 
    2418            0 :             sprintf(mail_from, "MIDAS %s <MIDAS@%s>", exptname.c_str(), elog_host_name.c_str());
    2419              : 
    2420            0 :             std::string mail_text;
    2421            0 :             mail_text += "A new entry has been submitted by ";
    2422            0 :             mail_text += pp->getparam("author");
    2423            0 :             mail_text += "\n";
    2424            0 :             mail_text += "\n";
    2425              : 
    2426            0 :             mail_text += "Experiment : ";
    2427            0 :             mail_text += exptname.c_str();
    2428            0 :             mail_text += "\n";
    2429              : 
    2430            0 :             mail_text += "Type       : ";
    2431            0 :             mail_text += pp->getparam("type");
    2432            0 :             mail_text += "\n";
    2433              : 
    2434            0 :             mail_text += "System     : ";
    2435            0 :             mail_text += pp->getparam("system");
    2436            0 :             mail_text += "\n";
    2437              : 
    2438            0 :             mail_text += "Subject    : ";
    2439            0 :             mail_text += pp->getparam("subject");
    2440            0 :             mail_text += "\n";
    2441              : 
    2442            0 :             mail_text += "Link       : ";
    2443            0 :             mail_text += mhttpd_full_url;
    2444            0 :             mail_text += "/EL/";
    2445            0 :             mail_text += tag;
    2446            0 :             mail_text += "\n";
    2447              : 
    2448            0 :             mail_text += "\n";
    2449              : 
    2450            0 :             mail_text += pp->getparam("text");
    2451            0 :             mail_text += "\n";
    2452              : 
    2453            0 :             sendmail(elog_host_name.c_str(), smtp_host, mail_from, mail_to, pp->getparam("type"), mail_text.c_str());
    2454              : 
    2455            0 :             if (mail_param[0] == 0)
    2456            0 :                mstrlcpy(mail_param, "?", sizeof(mail_param));
    2457              :             else
    2458            0 :                mstrlcat(mail_param, "&", sizeof(mail_param));
    2459            0 :             sprintf(mail_param + strlen(mail_param), "mail%d=%s", n_mail++, mail_to);
    2460              : 
    2461            0 :             p = strtok(NULL, ",");
    2462            0 :             if (!p)
    2463            0 :                break;
    2464            0 :             while (*p == ' ')
    2465            0 :                p++;
    2466            0 :          }
    2467              :       }
    2468            0 :    }
    2469              : 
    2470            0 :    r->rsprintf("HTTP/1.1 302 Found\r\n");
    2471            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    2472              : 
    2473              :    //if (mail_param[0])
    2474              :    //   r->rsprintf("Location: ../EL/%s?%s\n\n<html>redir</html>\r\n", tag, mail_param + 1);
    2475              :    //else
    2476              :    //   r->rsprintf("Location: ../EL/%s\n\n<html>redir</html>\r\n", tag);
    2477              : 
    2478            0 :    if (mail_param[0])
    2479            0 :       r->rsprintf("Location: ?cmd=Show+elog&tag=%s&%s\n\n<html>redir</html>\r\n", tag, mail_param + 1);
    2480              :    else
    2481            0 :       r->rsprintf("Location: ?cmd=Show+elog&tag=%s\n\n<html>redir</html>\r\n", tag);
    2482            0 : }
    2483              : 
    2484              : /*------------------------------------------------------------------*/
    2485              : 
    2486            0 : void show_elog_attachment(Param* p, Return* r, const char* path)
    2487              : {
    2488              :    HNDLE hDB;
    2489              :    int size;
    2490              :    int status;
    2491              :    char file_name[256];
    2492              : 
    2493            0 :    cm_get_experiment_database(&hDB, NULL);
    2494            0 :    file_name[0] = 0;
    2495            0 :    if (hDB > 0) {
    2496            0 :       size = sizeof(file_name);
    2497            0 :       memset(file_name, 0, size);
    2498              : 
    2499            0 :       status = db_get_value(hDB, 0, "/Logger/Elog dir", file_name, &size, TID_STRING, FALSE);
    2500            0 :       if (status != DB_SUCCESS)
    2501            0 :          db_get_value(hDB, 0, "/Logger/Data dir", file_name, &size, TID_STRING, TRUE);
    2502              : 
    2503            0 :       if (file_name[0] != 0)
    2504            0 :          if (file_name[strlen(file_name) - 1] != DIR_SEPARATOR)
    2505            0 :             mstrlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
    2506              :    }
    2507            0 :    mstrlcat(file_name, path, sizeof(file_name));
    2508              : 
    2509            0 :    int fh = open(file_name, O_RDONLY | O_BINARY);
    2510            0 :    if (fh > 0) {
    2511            0 :       lseek(fh, 0, SEEK_END);
    2512            0 :       int length = TELL(fh);
    2513            0 :       lseek(fh, 0, SEEK_SET);
    2514              : 
    2515            0 :       r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    2516            0 :       r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    2517            0 :       r->rsprintf("Accept-Ranges: bytes\r\n");
    2518              :       //r->rsprintf("Content-disposition: attachment; filename=%s\r\n", path);
    2519              : 
    2520            0 :       r->rsprintf("Content-Type: %s\r\n", get_content_type(file_name).c_str());
    2521              : 
    2522            0 :       r->rsprintf("Content-Length: %d\r\n\r\n", length);
    2523              : 
    2524            0 :       r->rread(file_name, fh, length);
    2525              : 
    2526            0 :       close(fh);
    2527              :    }
    2528              : 
    2529            0 :    return;
    2530              : }
    2531              : 
    2532              : /*------------------------------------------------------------------*/
    2533              : 
    2534            0 : BOOL is_editable(char *eq_name, char *var_name)
    2535              : {
    2536              :    HNDLE hDB, hkey;
    2537              :    KEY key;
    2538              :    char str[256];
    2539              :    int i, size;
    2540              : 
    2541            0 :    cm_get_experiment_database(&hDB, NULL);
    2542            0 :    sprintf(str, "/Equipment/%s/Settings/Editable", eq_name);
    2543            0 :    db_find_key(hDB, 0, str, &hkey);
    2544              : 
    2545              :    /* if no editable entry found, use default */
    2546            0 :    if (!hkey) {
    2547            0 :       return (equal_ustring(var_name, "Demand") ||
    2548            0 :               equal_ustring(var_name, "Output") || strncmp(var_name, "D_", 2) == 0);
    2549              :    }
    2550              : 
    2551            0 :    db_get_key(hDB, hkey, &key);
    2552            0 :    for (i = 0; i < key.num_values; i++) {
    2553            0 :       size = sizeof(str);
    2554            0 :       db_get_data_index(hDB, hkey, str, &size, i, TID_STRING);
    2555            0 :       if (equal_ustring(var_name, str))
    2556            0 :          return TRUE;
    2557              :    }
    2558            0 :    return FALSE;
    2559              : }
    2560              : 
    2561              : #ifdef OBSOLETE
    2562            0 : void show_eqtable_page(Param* pp, Return* r, int refresh)
    2563              : {
    2564              :    int i, j, k, colspan, size, n_var, i_edit, i_set, line;
    2565              :    char eq_name[32], group[32];
    2566              :    char group_name[MAX_GROUPS][32], data[256], style[80];
    2567              :    HNDLE hDB;
    2568              :    char odb_path[256];
    2569              : 
    2570            0 :    cm_get_experiment_database(&hDB, NULL);
    2571              : 
    2572              :    /* check if variable to edit */
    2573            0 :    i_edit = -1;
    2574            0 :    if (equal_ustring(pp->getparam("cmd"), "Edit"))
    2575            0 :       i_edit = atoi(pp->getparam("index"));
    2576              : 
    2577              :    /* check if variable to set */
    2578            0 :    i_set = -1;
    2579            0 :    if (equal_ustring(pp->getparam("cmd"), "Set"))
    2580            0 :       i_set = atoi(pp->getparam("index"));
    2581              : 
    2582              :    /* get equipment and group */
    2583            0 :    if (pp->getparam("eq"))
    2584            0 :       mstrlcpy(eq_name, pp->getparam("eq"), sizeof(eq_name));
    2585            0 :    mstrlcpy(group, "All", sizeof(group));
    2586            0 :    if (pp->getparam("group") && *pp->getparam("group"))
    2587            0 :       mstrlcpy(group, pp->getparam("group"), sizeof(group));
    2588              : 
    2589              : #if 0
    2590              :    /* check for "names" in settings */
    2591              :    if (eq_name[0]) {
    2592              :       sprintf(str, "/Equipment/%s/Settings", eq_name);
    2593              :       HNDLE hkeyset;
    2594              :       db_find_key(hDB, 0, str, &hkeyset);
    2595              :       HNDLE hkeynames = 0;
    2596              :       if (hkeyset) {
    2597              :          for (i = 0;; i++) {
    2598              :             db_enum_link(hDB, hkeyset, i, &hkeynames);
    2599              : 
    2600              :             if (!hkeynames)
    2601              :                break;
    2602              : 
    2603              :             KEY key;
    2604              :             db_get_key(hDB, hkeynames, &key);
    2605              : 
    2606              :             if (strncmp(key.name, "Names", 5) == 0)
    2607              :                break;
    2608              :          }
    2609              :       }
    2610              : 
    2611              :       /* redirect if no names found */
    2612              :       if (!hkeyset || !hkeynames) {
    2613              :          /* redirect */
    2614              :          sprintf(str, "?cmd=odb&odb_path=/Equipment/%s/Variables", eq_name);
    2615              :          redirect(r, str);
    2616              :          return;
    2617              :       }
    2618              :    }
    2619              : #endif
    2620              : 
    2621            0 :    show_header(r, "MIDAS slow control", "", group, i_edit == -1 ? refresh : 0);
    2622            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
    2623            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
    2624            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"obsolete.js\"></script>\n");
    2625            0 :    show_navigation_bar(r, "SC");
    2626              : 
    2627              :    /*---- menu buttons ----*/
    2628              : 
    2629            0 :    r->rsprintf("<tr><td colspan=15>\n");
    2630              : 
    2631            0 :    if (equal_ustring(pp->getparam("cmd"), "Edit"))
    2632            0 :       r->rsprintf("<input type=submit name=cmd value=Set>\n");
    2633              : 
    2634            0 :    r->rsprintf("</tr>\n\n");
    2635            0 :    r->rsprintf("</table>");  //end header table
    2636              : 
    2637            0 :    r->rsprintf("<table class=\"ODBtable\" style=\"max-width:700px;\">");  //body table
    2638              : 
    2639              :    /*---- enumerate SC equipment ----*/
    2640              : 
    2641            0 :    r->rsprintf("<tr><td class=\"subStatusTitle\" colspan=15><i>Equipment:</i> &nbsp;&nbsp;\n");
    2642              : 
    2643              :    HNDLE hkeyeqroot;
    2644            0 :    db_find_key(hDB, 0, "/Equipment", &hkeyeqroot);
    2645            0 :    if (hkeyeqroot)
    2646            0 :       for (i = 0;; i++) {
    2647              :          HNDLE hkeyeq;
    2648            0 :          db_enum_link(hDB, hkeyeqroot, i, &hkeyeq);
    2649              : 
    2650            0 :          if (!hkeyeq)
    2651            0 :             break;
    2652              : 
    2653              :          KEY eqkey;
    2654            0 :          db_get_key(hDB, hkeyeq, &eqkey);
    2655              : 
    2656              :          HNDLE hkeyset;
    2657            0 :          db_find_key(hDB, hkeyeq, "Settings", &hkeyset);
    2658            0 :          if (hkeyset) {
    2659            0 :             for (j = 0;; j++) {
    2660              :                HNDLE hkeynames;
    2661            0 :                db_enum_link(hDB, hkeyset, j, &hkeynames);
    2662              : 
    2663            0 :                if (!hkeynames)
    2664            0 :                   break;
    2665              : 
    2666              :                KEY key;
    2667            0 :                db_get_key(hDB, hkeynames, &key);
    2668              : 
    2669            0 :                if (strncmp(key.name, "Names", 5) == 0) {
    2670            0 :                   if (equal_ustring(eq_name, eqkey.name))
    2671            0 :                      r->rsprintf("<b>%s</b> &nbsp;&nbsp;", eqkey.name);
    2672              :                   else {
    2673            0 :                      r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eqkey.name).c_str(), eqkey.name);
    2674              :                   }
    2675            0 :                   break;
    2676              :                }
    2677            0 :             }
    2678              :          }
    2679            0 :       }
    2680            0 :    r->rsprintf("</tr>\n");
    2681              : 
    2682            0 :    if (!eq_name[0]) {
    2683            0 :       r->rsprintf("</table>");
    2684            0 :       return;
    2685              :    }
    2686              : 
    2687              :    /*---- display SC ----*/
    2688              : 
    2689            0 :    n_var = 0;
    2690            0 :    std::string names_path = msprintf("/Equipment/%s/Settings/Names", eq_name);
    2691              :    HNDLE hkeyeqnames;
    2692            0 :    db_find_key(hDB, 0, names_path.c_str(), &hkeyeqnames);
    2693              : 
    2694            0 :    if (hkeyeqnames) {
    2695              : 
    2696              :       /*---- single name array ----*/
    2697            0 :       r->rsprintf("<tr><td colspan=15><i>Groups:</i> &nbsp;&nbsp;");
    2698              : 
    2699              :       /* "all" group */
    2700            0 :       if (equal_ustring(group, "All"))
    2701            0 :          r->rsprintf("<b>All</b> &nbsp;&nbsp;");
    2702              :       else
    2703            0 :          r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">All</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2704              : 
    2705              :       /* collect groups */
    2706              : 
    2707            0 :       memset(group_name, 0, sizeof(group_name));
    2708              :       KEY key;
    2709            0 :       db_get_key(hDB, hkeyeqnames, &key);
    2710              : 
    2711            0 :       for (int level = 0; ; level++) {
    2712            0 :          bool next_level = false;
    2713            0 :          for (i = 0; i < key.num_values; i++) {
    2714              :             char name_str[256];
    2715            0 :             size = sizeof(name_str);
    2716            0 :             db_get_data_index(hDB, hkeyeqnames, name_str, &size, i, TID_STRING);
    2717              : 
    2718            0 :             char *s = strchr(name_str, '%');
    2719            0 :             for (int k=0; s && k<level; k++)
    2720            0 :                s = strchr(s+1, '%');
    2721              : 
    2722            0 :             if (s) {
    2723            0 :                *s = 0;
    2724            0 :                if (strchr(s+1, '%'))
    2725            0 :                    next_level = true;
    2726              : 
    2727              :                //printf("try group [%s] name [%s], level %d, %d\n", name_str, s+1, level, next_level);
    2728              : 
    2729            0 :                for (j = 0; j < MAX_GROUPS; j++) {
    2730            0 :                   if (equal_ustring(group_name[j], name_str) || group_name[j][0] == 0)
    2731            0 :                      break;
    2732              :                }
    2733            0 :                if ((j < MAX_GROUPS) && (group_name[j][0] == 0))
    2734            0 :                   mstrlcpy(group_name[j], name_str, sizeof(group_name[0]));
    2735              :             }
    2736              :          }
    2737              : 
    2738            0 :          if (!next_level)
    2739            0 :             break;
    2740            0 :       }
    2741              : 
    2742            0 :       for (i = 0; i < MAX_GROUPS && group_name[i][0]; i++) {
    2743            0 :          if (equal_ustring(group_name[i], group))
    2744            0 :             r->rsprintf("<b>%s</b> &nbsp;&nbsp;", group_name[i]);
    2745              :          else {
    2746            0 :             r->rsprintf("<a href=\"?cmd=eqtable&eq=%s&group=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str(), urlEncode(group_name[i]).c_str(), group_name[i]);
    2747              :          }
    2748              :       }
    2749              : 
    2750            0 :       r->rsprintf("<i>ODB:</i> &nbsp;&nbsp;");
    2751            0 :       r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Common\">Common</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2752            0 :       r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Settings\">Settings</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2753            0 :       r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Variables\">Variables</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2754            0 :       r->rsprintf("</tr>\n");
    2755              : 
    2756              :       /* count variables */
    2757            0 :       std::string vars_path = msprintf("/Equipment/%s/Variables", eq_name);
    2758              :       HNDLE hkeyvar;
    2759            0 :       db_find_key(hDB, 0, vars_path.c_str(), &hkeyvar);
    2760            0 :       if (!hkeyvar) {
    2761            0 :          r->rsprintf("</table>");
    2762            0 :          return;
    2763              :       }
    2764            0 :       for (i = 0;; i++) {
    2765              :          HNDLE hkey;
    2766            0 :          db_enum_link(hDB, hkeyvar, i, &hkey);
    2767            0 :          if (!hkey)
    2768            0 :             break;
    2769            0 :       }
    2770              : 
    2771            0 :       if (i == 0 || i > 15) {
    2772            0 :          r->rsprintf("</table>");
    2773            0 :          return;
    2774              :       }
    2775              : 
    2776              :       /* title row */
    2777            0 :       colspan = 15 - i;
    2778            0 :       r->rsprintf("<tr class=\"subStatusTitle\"><th colspan=%d>Names", colspan);
    2779              : 
    2780              :       /* display entries for this group */
    2781            0 :       for (int i = 0;; i++) {
    2782              :          HNDLE hkey;
    2783            0 :          db_enum_link(hDB, hkeyvar, i, &hkey);
    2784              : 
    2785            0 :          if (!hkey)
    2786            0 :             break;
    2787              : 
    2788              :          KEY key;
    2789            0 :          db_get_key(hDB, hkey, &key);
    2790            0 :          r->rsprintf("<th>%s", key.name);
    2791            0 :       }
    2792              : 
    2793            0 :       r->rsprintf("</tr>\n");
    2794              : 
    2795              :       /* data for current group */
    2796            0 :       std::string names_path = msprintf("/Equipment/%s/Settings/Names", eq_name);
    2797            0 :       int num_values = 0;
    2798              :       HNDLE hnames_key;
    2799            0 :       db_find_key(hDB, 0, names_path.c_str(), &hnames_key);
    2800            0 :       if (hnames_key) {
    2801              :          KEY names_key;
    2802            0 :          db_get_key(hDB, hnames_key, &names_key);
    2803            0 :          num_values = names_key.num_values;
    2804              :       }
    2805            0 :       for (int i = 0; i < num_values; i++) {
    2806              :          char names_str[256];
    2807            0 :          size = sizeof(names_str);
    2808            0 :          db_get_data_index(hDB, hnames_key, names_str, &size, i, TID_STRING);
    2809              : 
    2810              :          char name[NAME_LENGTH+32];
    2811            0 :          mstrlcpy(name, names_str, sizeof(name));
    2812              : 
    2813              :          //printf("group [%s], name [%s], str [%s]\n", group, name, names_str);
    2814              : 
    2815            0 :          if (!equal_ustring(group, "All")) {
    2816              :             // check if name starts with the name of the group we want to display
    2817            0 :             char *s = strstr(name, group);
    2818            0 :             if (s != name)
    2819            0 :                continue;
    2820            0 :             if (name[strlen(group)] != '%')
    2821            0 :                continue;
    2822              :          }
    2823              : 
    2824            0 :          if (strlen(name) < 1)
    2825            0 :             sprintf(name, "[%d]", i);
    2826              : 
    2827            0 :          if (i % 2 == 0)
    2828            0 :             r->rsprintf("<tr class=\"ODBtableEven\"><td colspan=%d><nobr>%s</nobr>", colspan, name);
    2829              :          else
    2830            0 :             r->rsprintf("<tr class=\"ODBtableOdd\"><td colspan=%d><nobr>%s</nobr>", colspan, name);
    2831              : 
    2832            0 :          for (int j = 0;; j++) {
    2833              :             HNDLE hkey;
    2834            0 :             db_enum_link(hDB, hkeyvar, j, &hkey);
    2835            0 :             if (!hkey)
    2836            0 :                break;
    2837              : 
    2838              :             KEY varkey;
    2839            0 :             db_get_key(hDB, hkey, &varkey);
    2840              : 
    2841              :             /* check if "variables" array is shorter than the "names" array */
    2842            0 :             if (i >= varkey.num_values)
    2843            0 :                continue;
    2844              : 
    2845            0 :             size = sizeof(data);
    2846            0 :             db_get_data_index(hDB, hkey, data, &size, i, varkey.type);
    2847            0 :             std::string data_str = db_sprintf(data, varkey.item_size, 0, varkey.type);
    2848              : 
    2849            0 :             if (is_editable(eq_name, varkey.name)) {
    2850            0 :                if (n_var == i_set) {
    2851              :                   /* set value */
    2852              :                   char str[256];
    2853            0 :                   mstrlcpy(str, pp->getparam("value"), sizeof(str));
    2854            0 :                   db_sscanf(str, data, &size, 0, varkey.type);
    2855            0 :                   db_set_data_index(hDB, hkey, data, size, i, varkey.type);
    2856              : 
    2857              :                   /* redirect (so that 'reload' does not reset value) */
    2858            0 :                   r->reset();
    2859            0 :                   redirect(r, group);
    2860            0 :                   return;
    2861              :                }
    2862            0 :                if (n_var == i_edit) {
    2863            0 :                   r->rsprintf("<td align=center>");
    2864            0 :                   r->rsprintf("<input type=text size=10 maxlenth=80 name=value value=\"%s\">\n", data_str.c_str());
    2865            0 :                   r->rsprintf("<input type=submit size=20 name=cmd value=Set>\n");
    2866            0 :                   r->rsprintf("<input type=hidden name=index value=%d>\n", i_edit);
    2867            0 :                   n_var++;
    2868              :                } else {
    2869            0 :                   sprintf(odb_path, "Equipment/%s/Variables/%s[%d]", eq_name, varkey.name, i);
    2870            0 :                   r->rsprintf("<td align=center>");
    2871            0 :                   r->rsprintf("<a href=\"#\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\', 0);return false;\" >%s</a>", odb_path, data_str.c_str());
    2872            0 :                   n_var++;
    2873              :                }
    2874              :             } else
    2875            0 :                r->rsprintf("<td align=center>%s", data_str.c_str());
    2876            0 :          }
    2877              : 
    2878            0 :          r->rsprintf("</tr>\n");
    2879              :       }
    2880            0 :    } else {
    2881              :       /*---- multiple name arrays ----*/
    2882            0 :       r->rsprintf("<tr><td colspan=15><i>Groups:</i> ");
    2883              : 
    2884              :       /* "all" group */
    2885            0 :       if (equal_ustring(group, "All"))
    2886            0 :          r->rsprintf("<b>All</b> &nbsp;&nbsp;");
    2887              :       else
    2888            0 :          r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">All</a> &nbsp;&nbsp;", eq_name);
    2889              : 
    2890              :       /* groups from Variables tree */
    2891              : 
    2892            0 :       std::string vars_path = msprintf("/Equipment/%s/Variables", eq_name);
    2893              :       HNDLE hkeyvar;
    2894            0 :       db_find_key(hDB, 0, vars_path.c_str(), &hkeyvar);
    2895              : 
    2896            0 :       if (hkeyvar) {
    2897            0 :          for (int i = 0;; i++) {
    2898              :             HNDLE hkey;
    2899            0 :             db_enum_link(hDB, hkeyvar, i, &hkey);
    2900              : 
    2901            0 :             if (!hkey)
    2902            0 :                break;
    2903              : 
    2904              :             KEY key;
    2905            0 :             db_get_key(hDB, hkey, &key);
    2906              : 
    2907            0 :             if (equal_ustring(key.name, group)) {
    2908            0 :                r->rsprintf("<b>%s</b> &nbsp;&nbsp;", key.name);
    2909              :             } else {
    2910            0 :                r->rsprintf("<a href=\"?cmd=eqtable&eq=%s&group=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str(), urlEncode(key.name).c_str(), key.name);
    2911              :             }
    2912            0 :          }
    2913              :       }
    2914              : 
    2915            0 :       r->rsprintf("<i>ODB:</i> &nbsp;&nbsp;");
    2916            0 :       r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Common\">Common</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2917            0 :       r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Settings\">Settings</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2918            0 :       r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Variables\">Variables</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
    2919            0 :       r->rsprintf("</tr>\n");
    2920              : 
    2921              :       /* enumerate variable arrays */
    2922            0 :       line = 0;
    2923            0 :       for (i = 0;; i++) {
    2924              :          HNDLE hkey;
    2925            0 :          db_enum_link(hDB, hkeyvar, i, &hkey);
    2926              : 
    2927            0 :          if (line % 2 == 0)
    2928            0 :             mstrlcpy(style, "ODBtableEven", sizeof(style));
    2929              :          else
    2930            0 :             mstrlcpy(style, "ODBtableOdd", sizeof(style));
    2931              : 
    2932            0 :          if (!hkey)
    2933            0 :             break;
    2934              : 
    2935              :          KEY varkey;
    2936            0 :          db_get_key(hDB, hkey, &varkey);
    2937              : 
    2938            0 :          if (!equal_ustring(group, "All") && !equal_ustring(varkey.name, group))
    2939            0 :             continue;
    2940              : 
    2941              :          /* title row */
    2942            0 :          r->rsprintf("<tr class=\"subStatusTitle\"><th colspan=9>Names<th>%s</tr>\n", varkey.name);
    2943              : 
    2944            0 :          if (varkey.type == TID_KEY) {
    2945            0 :             HNDLE hkeyroot = hkey;
    2946              : 
    2947              :             /* enumerate subkeys */
    2948            0 :             for (j = 0;; j++) {
    2949            0 :                db_enum_key(hDB, hkeyroot, j, &hkey);
    2950            0 :                if (!hkey)
    2951            0 :                   break;
    2952              : 
    2953              :                KEY key;
    2954            0 :                db_get_key(hDB, hkey, &key);
    2955              : 
    2956            0 :                if (key.type == TID_KEY) {
    2957              :                   /* for keys, don't display data value */
    2958            0 :                   r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<br></tr>\n", style, key.name);
    2959              :                } else {
    2960              :                   /* display single value */
    2961            0 :                   if (key.num_values == 1) {
    2962            0 :                      size = sizeof(data);
    2963            0 :                      db_get_data(hDB, hkey, data, &size, key.type);
    2964              : 
    2965            0 :                      std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
    2966            0 :                      std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
    2967              : 
    2968            0 :                      if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
    2969            0 :                         data_str = "(empty)";
    2970            0 :                         hex_str  = "";
    2971              :                      }
    2972              : 
    2973            0 :                      if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
    2974              :                         r->rsprintf
    2975            0 :                             ("<tr class=\"%s\" ><td colspan=9>%s<td align=center>%s (%s)<br></tr>\n",
    2976              :                              style, key.name, data_str.c_str(), hex_str.c_str());
    2977              :                      else
    2978            0 :                         r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<td align=center>%s<br></tr>\n",
    2979              :                                     style, key.name, data_str.c_str());
    2980            0 :                      line++;
    2981            0 :                   } else {
    2982              :                      /* display first value */
    2983            0 :                      r->rsprintf("<tr class=\"%s\"><td colspan=9 rowspan=%d>%s\n", style, key.num_values,
    2984              :                               key.name);
    2985              : 
    2986            0 :                      for (k = 0; k < key.num_values; k++) {
    2987            0 :                         size = sizeof(data);
    2988            0 :                         db_get_data_index(hDB, hkey, data, &size, k, key.type);
    2989            0 :                         std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
    2990            0 :                         std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
    2991              : 
    2992            0 :                         if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
    2993            0 :                            data_str = "(empty)";
    2994            0 :                            hex_str  = "";
    2995              :                         }
    2996              : 
    2997            0 :                         if (k > 0)
    2998            0 :                            r->rsprintf("<tr>");
    2999              : 
    3000            0 :                         if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
    3001            0 :                            r->rsprintf("<td>[%d] %s (%s)<br></tr>\n", k, data_str.c_str(), hex_str.c_str());
    3002              :                         else
    3003            0 :                            r->rsprintf("<td>[%d] %s<br></tr>\n", k, data_str.c_str());
    3004            0 :                         line++;
    3005            0 :                      }
    3006              :                   }
    3007              :                }
    3008            0 :             }
    3009              :          } else {
    3010              :             /* data for current group */
    3011            0 :             std::string names_path = msprintf("/Equipment/%s/Settings/Names %s", eq_name, varkey.name);
    3012              :             HNDLE hkeyset;
    3013            0 :             db_find_key(hDB, 0, names_path.c_str(), &hkeyset);
    3014              :             KEY key;
    3015            0 :             if (hkeyset)
    3016            0 :                db_get_key(hDB, hkeyset, &key);
    3017              : 
    3018            0 :             if (varkey.num_values > 1000)
    3019            0 :                r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<td align=center><i>... %d values ...</i>",
    3020              :                         style, varkey.name, varkey.num_values);
    3021              :             else {
    3022            0 :                for (j = 0; j < varkey.num_values; j++) {
    3023              : 
    3024            0 :                   if (line % 2 == 0)
    3025            0 :                      mstrlcpy(style, "ODBtableEven", sizeof(style));
    3026              :                   else
    3027            0 :                      mstrlcpy(style, "ODBtableOdd", sizeof(style));
    3028              : 
    3029              :                   char name[NAME_LENGTH+32];
    3030            0 :                   if (hkeyset && j<key.num_values) {
    3031            0 :                      size = sizeof(name);
    3032            0 :                      db_get_data_index(hDB, hkeyset, name, &size, j, TID_STRING);
    3033              :                   } else {
    3034            0 :                      sprintf(name, "%s[%d]", varkey.name, j);
    3035              :                   }
    3036              : 
    3037            0 :                   if (strlen(name) < 1) {
    3038            0 :                      sprintf(name, "%s[%d]", varkey.name, j);
    3039              :                   }
    3040              : 
    3041            0 :                   r->rsprintf("<tr class=\"%s\"><td colspan=9>%s", style, name);
    3042              : 
    3043            0 :                   size = sizeof(data);
    3044            0 :                   db_get_data_index(hDB, hkey, data, &size, j, varkey.type);
    3045            0 :                   std::string data_str = db_sprintf(data, varkey.item_size, 0, varkey.type);
    3046              : 
    3047            0 :                   if (is_editable(eq_name, varkey.name)) {
    3048            0 :                      if (n_var == i_set) {
    3049              :                         /* set value */
    3050              :                         char str[256];
    3051            0 :                         mstrlcpy(str, pp->getparam("value"), sizeof(str));
    3052            0 :                         db_sscanf(str, data, &size, 0, varkey.type);
    3053            0 :                         db_set_data_index(hDB, hkey, data, size, j, varkey.type);
    3054              : 
    3055              :                         /* redirect (so that 'reload' does not reset value) */
    3056            0 :                         r->reset();
    3057            0 :                         sprintf(str, "%s", group);
    3058            0 :                         redirect(r, str);
    3059            0 :                         return;
    3060              :                      }
    3061            0 :                      if (n_var == i_edit) {
    3062            0 :                         r->rsprintf("<td align=center><input type=text size=10 maxlenth=80 name=value value=\"%s\">\n", data_str.c_str());
    3063            0 :                         r->rsprintf("<input type=submit size=20 name=cmd value=Set></tr>\n");
    3064            0 :                         r->rsprintf("<input type=hidden name=index value=%d>\n", i_edit);
    3065            0 :                         r->rsprintf("<input type=hidden name=cmd value=Set>\n");
    3066            0 :                         n_var++;
    3067              :                      } else {
    3068            0 :                         sprintf(odb_path, "Equipment/%s/Variables/%s[%d]", eq_name, varkey.name, j);
    3069              : 
    3070            0 :                         r->rsprintf("<td align=cernter>");
    3071            0 :                         r->rsprintf("<a href=\"#\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\', 0);return false;\" >%s</a>", odb_path, data_str.c_str());
    3072            0 :                         n_var++;
    3073              :                      }
    3074              : 
    3075              :                   } else
    3076            0 :                      r->rsprintf("<td align=center>%s\n", data_str.c_str());
    3077            0 :                   r->rsprintf("</tr>\n");
    3078            0 :                   line++;
    3079            0 :                }
    3080              :             }
    3081              : 
    3082            0 :             r->rsprintf("</tr>\n");
    3083            0 :          }
    3084            0 :       }
    3085            0 :    }
    3086              : 
    3087            0 :    r->rsprintf("</table>\n");
    3088            0 :    r->rsprintf("</div>\n"); // closing for <div id="mmain">
    3089            0 :    r->rsprintf("</form>\n");
    3090            0 :    r->rsprintf("</body></html>\r\n");
    3091            0 : }
    3092              : #endif
    3093              : 
    3094              : /*------------------------------------------------------------------*/
    3095              : 
    3096            0 : char *find_odb_tag(char *p, char *path, char *format, int *edit, char *type, char *pwd, char *tail)
    3097              : {
    3098              :    char str[256], *ps, *pt;
    3099              :    BOOL in_script;
    3100              : 
    3101            0 :    *edit = 0;
    3102            0 :    *tail = 0;
    3103            0 :    *format = 0;
    3104            0 :    pwd[0] = 0;
    3105            0 :    in_script = FALSE;
    3106            0 :    strcpy(type, "text");
    3107              :    do {
    3108            0 :       while (*p && *p != '<')
    3109            0 :          p++;
    3110              : 
    3111              :       /* return if end of string reached */
    3112            0 :       if (!*p)
    3113            0 :          return NULL;
    3114              : 
    3115            0 :       p++;
    3116            0 :       while (*p && ((*p == ' ') || iscntrl(*p)))
    3117            0 :          p++;
    3118              : 
    3119            0 :       strncpy(str, p, 6);
    3120            0 :       str[6] = 0;
    3121            0 :       if (equal_ustring(str, "script"))
    3122            0 :          in_script = TRUE;
    3123              : 
    3124            0 :       strncpy(str, p, 7);
    3125            0 :       str[7] = 0;
    3126            0 :       if (equal_ustring(str, "/script"))
    3127            0 :          in_script = FALSE;
    3128              : 
    3129            0 :       strncpy(str, p, 4);
    3130            0 :       str[4] = 0;
    3131            0 :       if (equal_ustring(str, "odb ")) {
    3132            0 :          ps = p - 1;
    3133            0 :          p += 4;
    3134            0 :          while (*p && ((*p == ' ') || iscntrl(*p)))
    3135            0 :             p++;
    3136              : 
    3137              :          do {
    3138            0 :             strncpy(str, p, 7);
    3139            0 :             str[7] = 0;
    3140            0 :             if (equal_ustring(str, "format=")) {
    3141            0 :                p += 7;
    3142            0 :                if (*p == '\"') {
    3143            0 :                   p++;
    3144            0 :                   while (*p && *p != '\"')
    3145            0 :                      *format++ = *p++;
    3146            0 :                   *format = 0;
    3147            0 :                   if (*p == '\"')
    3148            0 :                     p++;
    3149              :                } else {
    3150            0 :                   while (*p && *p != ' ' && *p != '>')
    3151            0 :                      *format++ = *p++;
    3152            0 :                   *format = 0;
    3153              :                }
    3154              : 
    3155              :             } else {
    3156              : 
    3157            0 :             strncpy(str, p, 4);
    3158            0 :             str[4] = 0;
    3159            0 :             if (equal_ustring(str, "src=")) {
    3160            0 :                p += 4;
    3161            0 :                if (*p == '\"') {
    3162            0 :                   p++;
    3163            0 :                   while (*p && *p != '\"')
    3164            0 :                      *path++ = *p++;
    3165            0 :                   *path = 0;
    3166            0 :                   if (*p == '\"')
    3167            0 :                     p++;
    3168              :                } else {
    3169            0 :                   while (*p && *p != ' ' && *p != '>')
    3170            0 :                      *path++ = *p++;
    3171            0 :                   *path = 0;
    3172              :                }
    3173              :             } else {
    3174              : 
    3175            0 :                if (in_script)
    3176            0 :                   break;
    3177              : 
    3178            0 :                strncpy(str, p, 5);
    3179            0 :                str[5] = 0;
    3180            0 :                if (equal_ustring(str, "edit=")) {
    3181            0 :                   p += 5;
    3182              : 
    3183            0 :                   if (*p == '\"') {
    3184            0 :                      p++;
    3185            0 :                      *edit = atoi(p);
    3186            0 :                      if (*p == '\"')
    3187            0 :                        p++;
    3188              :                   } else {
    3189            0 :                      *edit = atoi(p);
    3190            0 :                      while (*p && *p != ' ' && *p != '>')
    3191            0 :                         p++;
    3192              :                   }
    3193              : 
    3194              :                } else {
    3195              : 
    3196            0 :                   strncpy(str, p, 5);
    3197            0 :                   str[5] = 0;
    3198            0 :                   if (equal_ustring(str, "type=")) {
    3199            0 :                      p += 5;
    3200            0 :                      if (*p == '\"') {
    3201            0 :                         p++;
    3202            0 :                         while (*p && *p != '\"')
    3203            0 :                            *type++ = *p++;
    3204            0 :                         *type = 0;
    3205            0 :                         if (*p == '\"')
    3206            0 :                           p++;
    3207              :                      } else {
    3208            0 :                         while (*p && *p != ' ' && *p != '>')
    3209            0 :                            *type++ = *p++;
    3210            0 :                         *type = 0;
    3211              :                      }
    3212              :                   } else {
    3213            0 :                      strncpy(str, p, 4);
    3214            0 :                      str[4] = 0;
    3215            0 :                      if (equal_ustring(str, "pwd=")) {
    3216            0 :                         p += 4;
    3217            0 :                         if (*p == '\"') {
    3218            0 :                            p++;
    3219            0 :                            while (*p && *p != '\"')
    3220            0 :                               *pwd++ = *p++;
    3221            0 :                            *pwd = 0;
    3222            0 :                            if (*p == '\"')
    3223            0 :                              p++;
    3224              :                         } else {
    3225            0 :                            while (*p && *p != ' ' && *p != '>')
    3226            0 :                               *pwd++ = *p++;
    3227            0 :                            *pwd = 0;
    3228              :                         }
    3229              :                      } else {
    3230            0 :                         if (strchr(p, '=')) {
    3231            0 :                            mstrlcpy(str, p, sizeof(str));
    3232            0 :                            pt = strchr(str, '=')+1;
    3233            0 :                            if (*pt == '\"') {
    3234            0 :                               pt++;
    3235            0 :                               while (*pt && *pt != '\"')
    3236            0 :                                  pt++;
    3237            0 :                               if (*pt == '\"')
    3238            0 :                                  pt++;
    3239            0 :                               *pt = 0;
    3240              :                            } else {
    3241            0 :                               while (*pt && *pt != ' ' && *pt != '>')
    3242            0 :                                  pt++;
    3243            0 :                               *pt = 0;
    3244              :                            }
    3245            0 :                            if (tail[0]) {
    3246            0 :                               mstrlcat(tail, " ", 256);
    3247            0 :                               mstrlcat(tail, str, 256);
    3248              :                            } else {
    3249            0 :                               mstrlcat(tail, str, 256);
    3250              :                            }
    3251            0 :                            p += strlen(str);
    3252              :                         }
    3253              :                      }
    3254              :                   }
    3255              :                }
    3256              :             }
    3257              :             }
    3258              : 
    3259            0 :             while (*p && ((*p == ' ') || iscntrl(*p)))
    3260            0 :                p++;
    3261              : 
    3262            0 :             if (*p == '<') {
    3263            0 :                cm_msg(MERROR, "find_odb_tag", "Invalid odb tag '%s'", ps);
    3264            0 :                return NULL;
    3265              :             }
    3266            0 :          } while (*p != '>');
    3267              : 
    3268            0 :          return ps;
    3269              :       }
    3270              : 
    3271            0 :       while (*p && *p != '>')
    3272            0 :          p++;
    3273              : 
    3274              :    } while (1);
    3275              : 
    3276              : }
    3277              : 
    3278              : /*------------------------------------------------------------------*/
    3279              : 
    3280            0 : void show_odb_tag(Param* pp, Return* r, const char *path, const char *keypath1, const char *format, int n_var, int edit, char *type, char *pwd, char *tail)
    3281              : {
    3282              :    int size, index, i_edit, i_set;
    3283              :    char data[TEXT_SIZE], full_keypath[256], keypath[256], *p;
    3284              :    HNDLE hDB, hkey;
    3285              :    KEY key;
    3286              : 
    3287              :    /* check if variable to edit */
    3288            0 :    i_edit = -1;
    3289            0 :    if (equal_ustring(pp->getparam("cmd"), "Edit"))
    3290            0 :       i_edit = atoi(pp->getparam("index"));
    3291              : 
    3292              :    /* check if variable to set */
    3293            0 :    i_set = -1;
    3294            0 :    if (equal_ustring(pp->getparam("cmd"), "Set"))
    3295            0 :       i_set = atoi(pp->getparam("index"));
    3296              : 
    3297              :    /* check if path contains index */
    3298            0 :    mstrlcpy(full_keypath, keypath1, sizeof(full_keypath));
    3299            0 :    mstrlcpy(keypath, keypath1, sizeof(keypath));
    3300            0 :    index = 0;
    3301              : 
    3302            0 :    if (strchr(keypath, '[') && strchr(keypath, ']')) {
    3303            0 :       for (p = strchr(keypath, '[') + 1; *p && *p != ']'; p++)
    3304            0 :          if (!isdigit(*p))
    3305            0 :             break;
    3306              : 
    3307            0 :       if (*p && *p == ']') {
    3308            0 :          index = atoi(strchr(keypath, '[') + 1);
    3309            0 :          *strchr(keypath, '[') = 0;
    3310              :       }
    3311              :    }
    3312              : 
    3313            0 :    cm_get_experiment_database(&hDB, NULL);
    3314            0 :    db_find_key(hDB, 0, keypath, &hkey);
    3315            0 :    if (!hkey)
    3316            0 :       r->rsprintf("<b>Key \"%s\" not found in ODB</b>\n", keypath);
    3317              :    else {
    3318            0 :       db_get_key(hDB, hkey, &key);
    3319            0 :       size = sizeof(data);
    3320            0 :       db_get_data_index(hDB, hkey, data, &size, index, key.type);
    3321              : 
    3322            0 :       std::string data_str;
    3323            0 :       if (format && strlen(format)>0)
    3324            0 :          data_str = db_sprintff(format, data, key.item_size, 0, key.type);
    3325              :       else
    3326            0 :          data_str= db_sprintf(data, key.item_size, 0, key.type);
    3327              : 
    3328            0 :       if (equal_ustring(type, "checkbox")) {
    3329              : 
    3330            0 :          if (pp->isparam("cbi"))
    3331            0 :             i_set = atoi(pp->getparam("cbi"));
    3332            0 :          if (n_var == i_set) {
    3333              :             /* toggle state */
    3334            0 :             if (key.type == TID_BOOL) {
    3335            0 :                if (data_str[0] == 'y')
    3336            0 :                   data_str = "n";
    3337              :                else
    3338            0 :                   data_str = "y";
    3339              :             } else {
    3340            0 :                if (atoi(data_str.c_str()) > 0)
    3341            0 :                   data_str = "0";
    3342              :                else
    3343            0 :                   data_str = "1";
    3344              :             }
    3345              : 
    3346            0 :             db_sscanf(data_str.c_str(), data, &size, 0, key.type);
    3347            0 :             db_set_data_index(hDB, hkey, data, size, index, key.type);
    3348              :          }
    3349              : 
    3350            0 :          std::string options;
    3351            0 :          if (data_str[0] == 'y' || atoi(data_str.c_str()) > 0)
    3352            0 :             options += "checked ";
    3353            0 :          if (!edit)
    3354            0 :             options += "disabled ";
    3355              :          else {
    3356            0 :             if (edit == 1) {
    3357            0 :                options += "onClick=\"o=document.createElement('input');o.type='hidden';o.name='cbi';o.value='";
    3358            0 :                options += msprintf("%d", n_var);
    3359            0 :                options += "';document.form1.appendChild(o);";
    3360            0 :                options += "document.form1.submit();\" ";
    3361              :             }
    3362              :          }
    3363              : 
    3364            0 :          if (tail[0])
    3365            0 :             options += tail;
    3366              : 
    3367            0 :          r->rsprintf("<input type=\"checkbox\" %s>\n", options.c_str());
    3368              : 
    3369            0 :       } else { // checkbox
    3370              : 
    3371            0 :          if (edit == 1) {
    3372            0 :             if (n_var == i_set) {
    3373              :                /* set value */
    3374              :                char str[256];
    3375            0 :                mstrlcpy(str, pp->getparam("value"), sizeof(str));
    3376            0 :                db_sscanf(str, data, &size, 0, key.type);
    3377            0 :                db_set_data_index(hDB, hkey, data, size, index, key.type);
    3378              : 
    3379              :                /* read back value */
    3380            0 :                size = sizeof(data);
    3381            0 :                db_get_data_index(hDB, hkey, data, &size, index, key.type);
    3382            0 :                data_str = db_sprintf(data, key.item_size, 0, key.type);
    3383              :             }
    3384              : 
    3385            0 :             if (n_var == i_edit) {
    3386            0 :                r->rsprintf("<input type=text size=10 maxlength=80 name=value value=\"%s\">\n", data_str.c_str());
    3387            0 :                r->rsprintf("<input type=submit size=20 name=cmd value=Set>\n");
    3388            0 :                r->rsprintf("<input type=hidden name=index value=%d>\n", n_var);
    3389            0 :                r->rsprintf("<input type=hidden name=cmd value=Set>\n");
    3390              :             } else {
    3391            0 :                if (edit == 2) {
    3392              :                   /* edit handling through user supplied JavaScript */
    3393            0 :                   r->rsprintf("<a href=\"#\" %s>", tail);
    3394              :                } else {
    3395              :                   /* edit handling through form submission */
    3396            0 :                   if (pwd[0]) {
    3397            0 :                      r->rsprintf("<a onClick=\"promptpwd('%s?cmd=Edit&index=%d&pnam=%s')\" href=\"#\">", path, n_var, pwd);
    3398              :                   } else {
    3399            0 :                      r->rsprintf("<a href=\"%s?cmd=Edit&index=%d\" %s>", path, n_var, tail);
    3400              :                   }
    3401              :                }
    3402              : 
    3403            0 :                r->rsputs(data_str.c_str());
    3404            0 :                r->rsprintf("</a>");
    3405              :             }
    3406            0 :          } else if (edit == 2) {
    3407            0 :             r->rsprintf("<a href=\"#\" onclick=\"ODBEdit('%s')\">\n", full_keypath);
    3408            0 :             r->rsputs(data_str.c_str());
    3409            0 :             r->rsprintf("</a>");
    3410              :          }
    3411              :            else
    3412            0 :               r->rsputs(data_str.c_str());
    3413              :       }
    3414            0 :    }
    3415            0 : }
    3416              : 
    3417              : /*------------------------------------------------------------------*/
    3418              : 
    3419              : /* add labels using following syntax under /Custom/Images/<name.gif>/Labels/<name>:
    3420              : 
    3421              :    [Name]    [Description]                       [Example]
    3422              : 
    3423              :    Src       ODB path for vairable to display    /Equipment/Environment/Variables/Input[0]
    3424              :    Format    Formt for float/double              %1.2f Deg. C
    3425              :    Font      Font to use                         small | medium | giant
    3426              :    X         X-position in pixel                 90
    3427              :    Y         Y-position from top                 67
    3428              :    Align     horizontal align left/center/right  left
    3429              :    FGColor   Foreground color RRGGBB             000000
    3430              :    BGColor   Background color RRGGBB             FFFFFF
    3431              : */
    3432              : 
    3433              : static const char *cgif_label_str[] = {
    3434              :    "Src = STRING : [256] ",
    3435              :    "Format = STRING : [32] %1.1f",
    3436              :    "Font = STRING : [32] Medium",
    3437              :    "X = INT : 0",
    3438              :    "Y = INT : 0",
    3439              :    "Align = INT : 0",
    3440              :    "FGColor = STRING : [8] 000000",
    3441              :    "BGColor = STRING : [8] FFFFFF",
    3442              :    NULL
    3443              : };
    3444              : 
    3445              : typedef struct {
    3446              :    char src[256];
    3447              :    char format[32];
    3448              :    char font[32];
    3449              :    int x, y, align;
    3450              :    char fgcolor[8];
    3451              :    char bgcolor[8];
    3452              : } CGIF_LABEL;
    3453              : 
    3454              : /* add labels using following syntax under /Custom/Images/<name.gif>/Bars/<name>:
    3455              : 
    3456              :    [Name]    [Description]                       [Example]
    3457              : 
    3458              :    Src       ODB path for vairable to display    /Equipment/Environment/Variables/Input[0]
    3459              :    X         X-position in pixel                 90
    3460              :    Y         Y-position from top                 67
    3461              :    Width     Width in pixel                      20
    3462              :    Height    Height in pixel                     100
    3463              :    Direction 0(vertical)/1(horiz.)               0
    3464              :    Axis      Draw axis 0(none)/1(left)/2(right)  1
    3465              :    Logscale  Draw logarithmic axis               n
    3466              :    Min       Min value for axis                  0
    3467              :    Max       Max value for axis                  10
    3468              :    FGColor   Foreground color RRGGBB             000000
    3469              :    BGColor   Background color RRGGBB             FFFFFF
    3470              :    BDColor   Border color RRGGBB                 808080
    3471              : */
    3472              : 
    3473              : static const char *cgif_bar_str[] = {
    3474              :    "Src = STRING : [256] ",
    3475              :    "X = INT : 0",
    3476              :    "Y = INT : 0",
    3477              :    "Width = INT : 10",
    3478              :    "Height = INT : 100",
    3479              :    "Direction = INT : 0",
    3480              :    "Axis = INT : 1",
    3481              :    "Logscale = BOOL : n",
    3482              :    "Min = DOUBLE : 0",
    3483              :    "Max = DOUBLE : 10",
    3484              :    "FGColor = STRING : [8] 000000",
    3485              :    "BGColor = STRING : [8] FFFFFF",
    3486              :    "BDColor = STRING : [8] 808080",
    3487              :    NULL
    3488              : };
    3489              : 
    3490              : typedef struct {
    3491              :    char src[256];
    3492              :    int x, y, width, height, direction, axis;
    3493              :    BOOL logscale;
    3494              :    double min, max;
    3495              :    char fgcolor[8];
    3496              :    char bgcolor[8];
    3497              :    char bdcolor[8];
    3498              : } CGIF_BAR;
    3499              : 
    3500              : /*------------------------------------------------------------------*/
    3501              : 
    3502            0 : int evaluate_src(char *key, char *src, double *fvalue)
    3503              : {
    3504              :    HNDLE hDB, hkeyval;
    3505              :    KEY vkey;
    3506              :    int i, n, size, ivalue;
    3507              :    char str[256], data[256];
    3508              : 
    3509            0 :    cm_get_experiment_database(&hDB, NULL);
    3510              : 
    3511              :    /* separate source from operators */
    3512            0 :    for (i=0 ; i<(int)strlen(src) ; i++)
    3513            0 :       if (src[i] == '>' || src[i] == '&')
    3514              :          break;
    3515            0 :    strncpy(str, src, i);
    3516            0 :    str[i] = 0;
    3517              : 
    3518              :    /* strip trailing blanks */
    3519            0 :    while (strlen(str) > 0 && str[strlen(str)-1] == ' ')
    3520            0 :       str[strlen(str)-1] = 0;
    3521              : 
    3522            0 :    db_find_key(hDB, 0, str, &hkeyval);
    3523            0 :    if (!hkeyval) {
    3524            0 :       cm_msg(MERROR, "evaluate_src", "Invalid Src key \"%s\" for Fill \"%s\"",
    3525              :              src, key);
    3526            0 :       return 0;
    3527              :    }
    3528              : 
    3529            0 :    db_get_key(hDB, hkeyval, &vkey);
    3530            0 :    size = sizeof(data);
    3531            0 :    db_get_value(hDB, 0, src, data, &size, vkey.type, FALSE);
    3532            0 :    std::string value = db_sprintf(data, size, 0, vkey.type);
    3533            0 :    if (equal_ustring(value.c_str(), "NAN"))
    3534            0 :       return 0;
    3535              : 
    3536            0 :    if (vkey.type == TID_BOOL) {
    3537            0 :       *fvalue = (value[0] == 'y');
    3538              :    } else
    3539            0 :       *fvalue = atof(value.c_str());
    3540              : 
    3541              :    /* evaluate possible operators */
    3542              :    do {
    3543            0 :       if (src[i] == '>' && src[i+1] == '>') {
    3544            0 :          i+=2;
    3545            0 :          n = atoi(src+i);
    3546            0 :          while (src[i] == ' ' || isdigit(src[i]))
    3547            0 :             i++;
    3548            0 :          ivalue = (int)*fvalue;
    3549            0 :          ivalue >>= n;
    3550            0 :          *fvalue = ivalue;
    3551              :       }
    3552              : 
    3553            0 :       if (src[i] == '&') {
    3554            0 :          i+=1;
    3555            0 :          while (src[i] == ' ')
    3556            0 :             i++;
    3557            0 :          if (src[i] == '0' && src[i+1] == 'x')
    3558            0 :             sscanf(src+2+i, "%x", &n);
    3559              :          else
    3560            0 :             n = atoi(src+i);
    3561            0 :          while (src[i] == ' ' || isxdigit(src[i]) || src[i] == 'x')
    3562            0 :             i++;
    3563            0 :          ivalue = (int)*fvalue;
    3564            0 :          ivalue &= n;
    3565            0 :          *fvalue = ivalue;
    3566              :       }
    3567              : 
    3568            0 :    } while (src[i]);
    3569              : 
    3570            0 :    return 1;
    3571            0 : }
    3572              : 
    3573              : /*------------------------------------------------------------------*/
    3574              : 
    3575            0 : std::string add_custom_path(const std::string& filename)
    3576              : {
    3577              :    // do not append custom path to absolute filenames
    3578              : 
    3579            0 :    if (filename[0] == '/')
    3580            0 :       return filename;
    3581            0 :    if (filename[0] == DIR_SEPARATOR)
    3582            0 :       return filename;
    3583              : 
    3584              :    HNDLE hDB;
    3585            0 :    cm_get_experiment_database(&hDB, NULL);
    3586              : 
    3587            0 :    std::string custom_path = "";
    3588              : 
    3589            0 :    int status = db_get_value_string(hDB, 0, "/Custom/Path", 0, &custom_path, TRUE);
    3590              : 
    3591            0 :    if (status != DB_SUCCESS)
    3592            0 :       return filename;
    3593              : 
    3594            0 :    if (custom_path.length() < 1)
    3595            0 :       return filename;
    3596              : 
    3597            0 :    if ((custom_path == DIR_SEPARATOR_STR) || !strchr(custom_path.c_str(), DIR_SEPARATOR)) {
    3598            0 :       cm_msg(MERROR, "add_custom_path", "ODB /Custom/Path has a forbidden value \"%s\", please change it", custom_path.c_str());
    3599            0 :       return filename;
    3600              :    }
    3601              : 
    3602            0 :    custom_path = ss_replace_env_variables(custom_path);
    3603              : 
    3604            0 :    std::string full_filename = custom_path;
    3605            0 :    if (full_filename[full_filename.length()-1] != DIR_SEPARATOR)
    3606            0 :       full_filename += DIR_SEPARATOR_STR;
    3607            0 :    full_filename += filename;
    3608              : 
    3609            0 :    return full_filename;
    3610            0 : }
    3611              : 
    3612              : /*------------------------------------------------------------------*/
    3613              : 
    3614            0 : void show_custom_file(Return* r, const char *name)
    3615              : {
    3616              :    char str[256];
    3617            0 :    std::string filename;
    3618              :    HNDLE hDB;
    3619              : 
    3620            0 :    cm_get_experiment_database(&hDB, NULL);
    3621              : 
    3622              :    HNDLE hkey;
    3623            0 :    sprintf(str, "/Custom/%s", name);
    3624            0 :    db_find_key(hDB, 0, str, &hkey);
    3625              : 
    3626            0 :    if (!hkey) {
    3627            0 :       sprintf(str, "/Custom/%s&", name);
    3628            0 :       db_find_key(hDB, 0, str, &hkey);
    3629            0 :       if (!hkey) {
    3630            0 :          sprintf(str, "/Custom/%s!", name);
    3631            0 :          db_find_key(hDB, 0, str, &hkey);
    3632              :       }
    3633              :    }
    3634              : 
    3635            0 :    if(!hkey){
    3636            0 :       sprintf(str,"show_custom_file: Invalid custom page: \"/Custom/%s\" not found in ODB", name);
    3637            0 :       show_error_404(r, str);
    3638            0 :       return;
    3639              :    }
    3640              : 
    3641              :    int status;
    3642              :    KEY key;
    3643              : 
    3644            0 :    status = db_get_key(hDB, hkey, &key);
    3645              : 
    3646            0 :    if (status != DB_SUCCESS) {
    3647              :       char errtext[512];
    3648            0 :       sprintf(errtext, "show_custom_file: Error: db_get_key() for \"%s\" status %d", str, status);
    3649            0 :       show_error_404(r, errtext);
    3650            0 :       return;
    3651              :    }
    3652              : 
    3653            0 :    int size = key.total_size;
    3654            0 :    char* ctext = (char*)malloc(size);
    3655              : 
    3656            0 :    status = db_get_data(hDB, hkey, ctext, &size, TID_STRING);
    3657              : 
    3658            0 :    if (status != DB_SUCCESS) {
    3659              :       char errtext[512];
    3660            0 :       sprintf(errtext, "show_custom_file: Error: db_get_data() for \"%s\" status %d", str, status);
    3661            0 :       show_error_404(r, errtext);
    3662            0 :       free(ctext);
    3663            0 :       return;
    3664              :    }
    3665              : 
    3666            0 :    filename = add_custom_path(ctext);
    3667              : 
    3668            0 :    free(ctext);
    3669              : 
    3670            0 :    send_file(r, filename, true);
    3671              : 
    3672            0 :    return;
    3673            0 : }
    3674              : 
    3675              : /*------------------------------------------------------------------*/
    3676              : 
    3677            0 : void show_custom_gif(Return* rr, const char *name)
    3678              : {
    3679              :    char str[256], data[256], src[256];
    3680              :    int i, index, length, status, size, width, height, bgcol, fgcol, bdcol, r, g, b, x, y;
    3681              :    HNDLE hDB, hkeygif, hkeyroot, hkey, hkeyval;
    3682              :    double fvalue, ratio;
    3683              :    KEY key, vkey;
    3684              :    gdImagePtr im;
    3685              :    gdGifBuffer gb;
    3686              :    gdFontPtr pfont;
    3687              :    FILE *f;
    3688              :    CGIF_LABEL label;
    3689              :    CGIF_BAR bar;
    3690              : 
    3691            0 :    cm_get_experiment_database(&hDB, NULL);
    3692              : 
    3693              :    /* find image description in ODB */
    3694            0 :    sprintf(str, "/Custom/Images/%s", name);
    3695            0 :    db_find_key(hDB, 0, str, &hkeygif);
    3696            0 :    if (!hkeygif) {
    3697              : 
    3698              :       // If we don't have Images directory,
    3699              :       // then just treat this like any other custom file.
    3700            0 :       show_custom_file(rr, name);
    3701            0 :       return;
    3702              :    }
    3703              : 
    3704              :    /* load background image */
    3705            0 :    std::string filename;
    3706            0 :    db_get_value_string(hDB, hkeygif, "Background", 0, &filename, FALSE);
    3707              : 
    3708            0 :    std::string full_filename = add_custom_path(filename);
    3709              : 
    3710            0 :    f = fopen(full_filename.c_str(), "rb");
    3711            0 :    if (f == NULL) {
    3712            0 :       sprintf(str, "show_custom_gif: Cannot open file \"%s\"", full_filename.c_str());
    3713            0 :       show_error_404(rr, str);
    3714            0 :       return;
    3715              :    }
    3716              : 
    3717            0 :    im = gdImageCreateFromGif(f);
    3718            0 :    fclose(f);
    3719              : 
    3720            0 :    if (im == NULL) {
    3721            0 :       sprintf(str, "show_custom_gif: File \"%s\" is not a GIF image", filename.c_str());
    3722            0 :       show_error_404(rr, str);
    3723            0 :       return;
    3724              :    }
    3725              : 
    3726            0 :    cm_get_experiment_database(&hDB, NULL);
    3727              : 
    3728              :    /*---- draw labels ----------------------------------------------*/
    3729              : 
    3730            0 :    db_find_key(hDB, hkeygif, "Labels", &hkeyroot);
    3731            0 :    if (hkeyroot) {
    3732            0 :       for (index = 0;; index++) {
    3733            0 :          db_enum_key(hDB, hkeyroot, index, &hkey);
    3734            0 :          if (!hkey)
    3735            0 :             break;
    3736            0 :          db_get_key(hDB, hkey, &key);
    3737              : 
    3738            0 :          size = sizeof(label);
    3739            0 :          status = db_get_record1(hDB, hkey, &label, &size, 0, strcomb1(cgif_label_str).c_str());
    3740            0 :          if (status != DB_SUCCESS) {
    3741            0 :             cm_msg(MERROR, "show_custom_gif", "Cannot open data record for label \"%s\"",
    3742              :                    key.name);
    3743            0 :             continue;
    3744              :          }
    3745              : 
    3746            0 :          if (label.src[0] == 0) {
    3747            0 :             cm_msg(MERROR, "show_custom_gif", "Empty Src key for label \"%s\"", key.name);
    3748            0 :             continue;
    3749              :          }
    3750              : 
    3751            0 :          db_find_key(hDB, 0, label.src, &hkeyval);
    3752            0 :          if (!hkeyval) {
    3753            0 :             cm_msg(MERROR, "show_custom_gif", "Invalid Src key \"%s\" for label \"%s\"",
    3754              :                    label.src, key.name);
    3755            0 :             continue;
    3756              :          }
    3757              : 
    3758            0 :          db_get_key(hDB, hkeyval, &vkey);
    3759            0 :          size = sizeof(data);
    3760            0 :          status = db_get_value(hDB, 0, label.src, data, &size, vkey.type, FALSE);
    3761              : 
    3762            0 :          std::string value;
    3763              : 
    3764            0 :          if (label.format[0]) {
    3765            0 :             if (vkey.type == TID_FLOAT)
    3766            0 :                value = msprintf(label.format, *(((float *) data)));
    3767            0 :             else if (vkey.type == TID_DOUBLE)
    3768            0 :                value = msprintf(label.format, *(((double *) data)));
    3769            0 :             else if (vkey.type == TID_INT)
    3770            0 :                value = msprintf(label.format, *(((INT *) data)));
    3771            0 :             else if (vkey.type == TID_BOOL) {
    3772            0 :                if (strstr(label.format, "%c"))
    3773            0 :                   value = msprintf(label.format, *(((INT *) data)) ? 'y' : 'n');
    3774              :                else
    3775            0 :                   value = msprintf(label.format, *(((INT *) data)));
    3776              :             } else
    3777            0 :                value = db_sprintf(data, size, 0, vkey.type);
    3778              :          } else
    3779            0 :             value = db_sprintf(data, size, 0, vkey.type);
    3780              : 
    3781            0 :          sscanf(label.fgcolor, "%02x%02x%02x", &r, &g, &b);
    3782            0 :          fgcol = gdImageColorAllocate(im, r, g, b);
    3783            0 :          if (fgcol == -1)
    3784            0 :             fgcol = gdImageColorClosest(im, r, g, b);
    3785              : 
    3786            0 :          sscanf(label.bgcolor, "%02x%02x%02x", &r, &g, &b);
    3787            0 :          bgcol = gdImageColorAllocate(im, r, g, b);
    3788            0 :          if (bgcol == -1)
    3789            0 :             bgcol = gdImageColorClosest(im, r, g, b);
    3790              : 
    3791              :          /* select font */
    3792            0 :          if (equal_ustring(label.font, "Small"))
    3793            0 :             pfont = gdFontSmall;
    3794            0 :          else if (equal_ustring(label.font, "Medium"))
    3795            0 :             pfont = gdFontMediumBold;
    3796            0 :          else if (equal_ustring(label.font, "Giant"))
    3797            0 :             pfont = gdFontGiant;
    3798              :          else
    3799            0 :             pfont = gdFontMediumBold;
    3800              : 
    3801            0 :          width = value.length() * pfont->w + 5 + 5;
    3802            0 :          height = pfont->h + 2 + 2;
    3803              : 
    3804            0 :          if (label.align == 0) {
    3805              :             /* left */
    3806            0 :             gdImageFilledRectangle(im, label.x, label.y, label.x + width,
    3807            0 :                                    label.y + height, bgcol);
    3808            0 :             gdImageRectangle(im, label.x, label.y, label.x + width, label.y + height,
    3809              :                              fgcol);
    3810            0 :             gdImageString(im, pfont, label.x + 5, label.y + 2, value.c_str(), fgcol);
    3811            0 :          } else if (label.align == 1) {
    3812              :             /* center */
    3813            0 :             gdImageFilledRectangle(im, label.x - width / 2, label.y, label.x + width / 2,
    3814            0 :                                    label.y + height, bgcol);
    3815            0 :             gdImageRectangle(im, label.x - width / 2, label.y, label.x + width / 2,
    3816            0 :                              label.y + height, fgcol);
    3817            0 :             gdImageString(im, pfont, label.x + 5 - width / 2, label.y + 2, value.c_str(), fgcol);
    3818              :          } else {
    3819              :             /* right */
    3820            0 :             gdImageFilledRectangle(im, label.x - width, label.y, label.x,
    3821            0 :                                    label.y + height, bgcol);
    3822            0 :             gdImageRectangle(im, label.x - width, label.y, label.x, label.y + height,
    3823              :                              fgcol);
    3824            0 :             gdImageString(im, pfont, label.x - width + 5, label.y + 2, value.c_str(), fgcol);
    3825              :          }
    3826            0 :       }
    3827              :    }
    3828              : 
    3829              :    /*---- draw bars ------------------------------------------------*/
    3830              : 
    3831            0 :    db_find_key(hDB, hkeygif, "Bars", &hkeyroot);
    3832            0 :    if (hkeyroot) {
    3833            0 :       for (index = 0;; index++) {
    3834            0 :          db_enum_key(hDB, hkeyroot, index, &hkey);
    3835            0 :          if (!hkey)
    3836            0 :             break;
    3837            0 :          db_get_key(hDB, hkey, &key);
    3838              : 
    3839            0 :          size = sizeof(bar);
    3840            0 :          status = db_get_record1(hDB, hkey, &bar, &size, 0, strcomb1(cgif_bar_str).c_str());
    3841            0 :          if (status != DB_SUCCESS) {
    3842            0 :             cm_msg(MERROR, "show_custom_gif", "Cannot open data record for bar \"%s\"",
    3843              :                    key.name);
    3844            0 :             continue;
    3845              :          }
    3846              : 
    3847            0 :          if (bar.src[0] == 0) {
    3848            0 :             cm_msg(MERROR, "show_custom_gif", "Empty Src key for bar \"%s\"", key.name);
    3849            0 :             continue;
    3850              :          }
    3851              : 
    3852            0 :          db_find_key(hDB, 0, bar.src, &hkeyval);
    3853            0 :          if (!hkeyval) {
    3854            0 :             cm_msg(MERROR, "show_custom_gif", "Invalid Src key \"%s\" for bar \"%s\"",
    3855              :                    bar.src, key.name);
    3856            0 :             continue;
    3857              :          }
    3858              : 
    3859            0 :          db_get_key(hDB, hkeyval, &vkey);
    3860            0 :          size = sizeof(data);
    3861            0 :          status = db_get_value(hDB, 0, bar.src, data, &size, vkey.type, FALSE);
    3862            0 :          std::string value = db_sprintf(data, size, 0, vkey.type);
    3863            0 :          if (equal_ustring(value.c_str(), "NAN"))
    3864            0 :             continue;
    3865              : 
    3866            0 :          fvalue = atof(value.c_str());
    3867              : 
    3868            0 :          sscanf(bar.fgcolor, "%02x%02x%02x", &r, &g, &b);
    3869            0 :          fgcol = gdImageColorAllocate(im, r, g, b);
    3870            0 :          if (fgcol == -1)
    3871            0 :             fgcol = gdImageColorClosest(im, r, g, b);
    3872              : 
    3873            0 :          sscanf(bar.bgcolor, "%02x%02x%02x", &r, &g, &b);
    3874            0 :          bgcol = gdImageColorAllocate(im, r, g, b);
    3875            0 :          if (bgcol == -1)
    3876            0 :             bgcol = gdImageColorClosest(im, r, g, b);
    3877              : 
    3878            0 :          sscanf(bar.bdcolor, "%02x%02x%02x", &r, &g, &b);
    3879            0 :          bdcol = gdImageColorAllocate(im, r, g, b);
    3880            0 :          if (bdcol == -1)
    3881            0 :             bdcol = gdImageColorClosest(im, r, g, b);
    3882              : 
    3883            0 :          if (bar.min == bar.max)
    3884            0 :             bar.max += 1;
    3885              : 
    3886            0 :          if (bar.logscale) {
    3887            0 :             if (fvalue < 1E-20)
    3888            0 :                fvalue = 1E-20;
    3889            0 :             ratio = (log(fvalue) - log(bar.min)) / (log(bar.max) - log(bar.min));
    3890              :          } else
    3891            0 :             ratio = (fvalue - bar.min) / (bar.max - bar.min);
    3892            0 :          if (ratio < 0)
    3893            0 :             ratio = 0;
    3894            0 :          if (ratio > 1)
    3895            0 :             ratio = 1;
    3896              : 
    3897            0 :          if (bar.direction == 0) {
    3898              :             /* vertical */
    3899            0 :             ratio = (bar.height - 2) - ratio * (bar.height - 2);
    3900            0 :             r = (int) (ratio + 0.5);
    3901              : 
    3902            0 :             gdImageFilledRectangle(im, bar.x, bar.y, bar.x + bar.width,
    3903            0 :                                    bar.y + bar.height, bgcol);
    3904            0 :             gdImageRectangle(im, bar.x, bar.y, bar.x + bar.width, bar.y + bar.height,
    3905              :                              bdcol);
    3906            0 :             gdImageFilledRectangle(im, bar.x + 1, bar.y + r + 1, bar.x + bar.width - 1,
    3907            0 :                                    bar.y + bar.height - 1, fgcol);
    3908              : 
    3909            0 :             if (bar.axis == 1)
    3910            0 :                vaxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y + bar.height, bar.height, -3,
    3911              :                      -5, -7, -8, 0, bar.min, bar.max, bar.logscale);
    3912            0 :             else if (bar.axis == 2)
    3913            0 :                vaxis(im, gdFontSmall, bdcol, 0, bar.x + bar.width, bar.y + bar.height,
    3914              :                      bar.height, 3, 5, 7, 10, 0, bar.min, bar.max, bar.logscale);
    3915              : 
    3916              :          } else {
    3917              :             /* horizontal */
    3918            0 :             ratio = ratio * (bar.height - 2);
    3919            0 :             r = (int) (ratio + 0.5);
    3920              : 
    3921            0 :             gdImageFilledRectangle(im, bar.x, bar.y, bar.x + bar.height,
    3922            0 :                                    bar.y + bar.width, bgcol);
    3923            0 :             gdImageRectangle(im, bar.x, bar.y, bar.x + bar.height, bar.y + bar.width,
    3924              :                              bdcol);
    3925            0 :             gdImageFilledRectangle(im, bar.x + 1, bar.y + 1, bar.x + r,
    3926            0 :                                    bar.y + bar.width - 1, fgcol);
    3927              : 
    3928            0 :             if (bar.axis == 1)
    3929            0 :                haxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y, bar.height, -3, -5, -7, -18,
    3930              :                      0, bar.min, bar.max);
    3931            0 :             else if (bar.axis == 2)
    3932            0 :                haxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y + bar.width, bar.height, 3,
    3933              :                      5, 7, 8, 0, bar.min, bar.max);
    3934              :          }
    3935            0 :       }
    3936              :    }
    3937              : 
    3938              :    /*---- draw fills -----------------------------------------------*/
    3939              : 
    3940            0 :    db_find_key(hDB, hkeygif, "Fills", &hkeyroot);
    3941            0 :    if (hkeyroot) {
    3942            0 :       for (index = 0;; index++) {
    3943            0 :          db_enum_key(hDB, hkeyroot, index, &hkey);
    3944            0 :          if (!hkey)
    3945            0 :             break;
    3946            0 :          db_get_key(hDB, hkey, &key);
    3947              : 
    3948            0 :          size = sizeof(src);
    3949            0 :          src[0] = 0;
    3950            0 :          db_get_value(hDB, hkey, "Src", src, &size, TID_STRING, TRUE);
    3951              : 
    3952            0 :          if (src[0] == 0) {
    3953            0 :             cm_msg(MERROR, "show_custom_gif", "Empty Src key for Fill \"%s\"", key.name);
    3954            0 :             continue;
    3955              :          }
    3956              : 
    3957            0 :          if (!evaluate_src(key.name, src, &fvalue))
    3958            0 :             continue;
    3959              : 
    3960            0 :          x = y = 0;
    3961            0 :          size = sizeof(x);
    3962            0 :          db_get_value(hDB, hkey, "X", &x, &size, TID_INT, TRUE);
    3963            0 :          db_get_value(hDB, hkey, "Y", &y, &size, TID_INT, TRUE);
    3964              : 
    3965            0 :          size = sizeof(data);
    3966            0 :          status = db_get_value(hDB, hkey, "Limits", data, &size, TID_DOUBLE, FALSE);
    3967            0 :          if (status != DB_SUCCESS) {
    3968            0 :             cm_msg(MERROR, "show_custom_gif", "No \"Limits\" entry for Fill \"%s\"",
    3969              :                    key.name);
    3970            0 :             continue;
    3971              :          }
    3972            0 :          for (i = 0; i < size / (int) sizeof(double); i++)
    3973            0 :             if (*((double *) data + i) > fvalue)
    3974            0 :                break;
    3975            0 :          if (i > 0)
    3976            0 :             i--;
    3977              : 
    3978            0 :          db_find_key(hDB, hkey, "Fillcolors", &hkeyval);
    3979            0 :          if (!hkeyval) {
    3980            0 :             cm_msg(MERROR, "show_custom_gif", "No \"Fillcolors\" entry for Fill \"%s\"",
    3981              :                    key.name);
    3982            0 :             continue;
    3983              :          }
    3984              : 
    3985            0 :          size = sizeof(data);
    3986            0 :          strcpy(data, "FFFFFF");
    3987            0 :          status = db_get_data_index(hDB, hkeyval, data, &size, i, TID_STRING);
    3988            0 :          if (status == DB_SUCCESS) {
    3989            0 :             sscanf(data, "%02x%02x%02x", &r, &g, &b);
    3990            0 :             fgcol = gdImageColorAllocate(im, r, g, b);
    3991            0 :             if (fgcol == -1)
    3992            0 :                fgcol = gdImageColorClosest(im, r, g, b);
    3993            0 :             gdImageFill(im, x, y, fgcol);
    3994              :          }
    3995              :       }
    3996              :    }
    3997              : 
    3998              :    /* generate GIF */
    3999            0 :    gdImageInterlace(im, 1);
    4000            0 :    gdImageGif(im, &gb);
    4001            0 :    gdImageDestroy(im);
    4002            0 :    length = gb.size;
    4003              : 
    4004            0 :    rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
    4005            0 :    rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    4006              : 
    4007            0 :    rr->rsprintf("Content-Type: image/gif\r\n");
    4008            0 :    rr->rsprintf("Content-Length: %d\r\n", length);
    4009            0 :    rr->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
    4010            0 :    rr->rsprintf("Expires: Fri, 01-Jan-1983 00:00:00 GMT\r\n\r\n");
    4011              : 
    4012            0 :    rr->rmemcpy(gb.data, length);
    4013            0 : }
    4014              : 
    4015              : 
    4016              : 
    4017              : /*------------------------------------------------------------------*/
    4018              : 
    4019            0 : void do_jrpc_rev0(Param* p, Return* r)
    4020              : {
    4021              :    static RPC_LIST rpc_list[] = {
    4022              :       { 9999, "mhttpd_jrpc_rev0", {
    4023              :             {TID_STRING, RPC_IN}, // arg0
    4024              :             {TID_STRING, RPC_IN}, // arg1
    4025              :             {TID_STRING, RPC_IN}, // arg2
    4026              :             {TID_STRING, RPC_IN}, // arg3
    4027              :             {TID_STRING, RPC_IN}, // arg4
    4028              :             {TID_STRING, RPC_IN}, // arg5
    4029              :             {TID_STRING, RPC_IN}, // arg6
    4030              :             {TID_STRING, RPC_IN}, // arg7
    4031              :             {TID_STRING, RPC_IN}, // arg8
    4032              :             {TID_STRING, RPC_IN}, // arg9
    4033              :             {0}} },
    4034              :       { 0 }
    4035              :    };
    4036              : 
    4037            0 :    int count = 0, substring = 0, rpc;
    4038              : 
    4039            0 :    const char *xname   = p->getparam("name");
    4040            0 :    const char *srpc    = p->getparam("rpc");
    4041              : 
    4042            0 :    if (!srpc || !xname) {
    4043            0 :       show_text_header(r);
    4044            0 :       r->rsprintf("<INVALID_ARGUMENTS>");
    4045            0 :       return;
    4046              :    }
    4047              : 
    4048              :    char sname[256];
    4049            0 :    mstrlcpy(sname, xname, sizeof(sname));
    4050              : 
    4051            0 :    if (sname[strlen(sname)-1]=='*') {
    4052            0 :       sname[strlen(sname)-1] = 0;
    4053            0 :       substring = 1;
    4054              :    }
    4055              : 
    4056            0 :    rpc = atoi(srpc);
    4057              : 
    4058            0 :    if (rpc<RPC_MIN_ID || rpc>RPC_MAX_ID) {
    4059            0 :       show_text_header(r);
    4060            0 :       r->rsprintf("<INVALID_RPC_ID>");
    4061            0 :       return;
    4062              :    }
    4063              : 
    4064            0 :    rpc_list[0].id = rpc;
    4065            0 :    rpc_register_functions(rpc_list, NULL);
    4066              : 
    4067            0 :    show_text_header(r);
    4068            0 :    r->rsprintf("calling rpc %d | ", rpc);
    4069              : 
    4070              :    if (1) {
    4071              :       int status, i;
    4072              :       char str[256];
    4073              :       HNDLE hDB, hrootkey, hsubkey, hkey;
    4074              : 
    4075            0 :       cm_get_experiment_database(&hDB, NULL);
    4076              : 
    4077              :       /* find client which exports FCNA function */
    4078            0 :       status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
    4079            0 :       if (status == DB_SUCCESS) {
    4080            0 :          for (i=0; ; i++) {
    4081            0 :             status = db_enum_key(hDB, hrootkey, i, &hsubkey);
    4082            0 :             if (status == DB_NO_MORE_SUBKEYS)
    4083            0 :                break;
    4084              : 
    4085            0 :             sprintf(str, "RPC/%d", rpc);
    4086            0 :             status = db_find_key(hDB, hsubkey, str, &hkey);
    4087            0 :             if (status == DB_SUCCESS) {
    4088              :                char client_name[NAME_LENGTH];
    4089              :                HNDLE hconn;
    4090              :                int size;
    4091              : 
    4092            0 :                size = sizeof(client_name);
    4093            0 :                status = db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, FALSE);
    4094            0 :                if (status != DB_SUCCESS)
    4095            0 :                   continue;
    4096              : 
    4097            0 :                if (strlen(sname) > 0) {
    4098            0 :                   if (substring) {
    4099            0 :                      if (strstr(client_name, sname) != client_name)
    4100            0 :                         continue;
    4101              :                   } else {
    4102            0 :                      if (strcmp(sname, client_name) != 0)
    4103            0 :                         continue;
    4104              :                   }
    4105              :                }
    4106              : 
    4107            0 :                count++;
    4108              : 
    4109            0 :                r->rsprintf("client %s", client_name);
    4110              : 
    4111            0 :                status = cm_connect_client(client_name, &hconn);
    4112            0 :                r->rsprintf(" %d", status);
    4113              : 
    4114            0 :                if (status == RPC_SUCCESS) {
    4115            0 :                   status = rpc_client_call(hconn, rpc,
    4116              :                                            p->getparam("arg0"),
    4117              :                                            p->getparam("arg1"),
    4118              :                                            p->getparam("arg2"),
    4119              :                                            p->getparam("arg3"),
    4120              :                                            p->getparam("arg4"),
    4121              :                                            p->getparam("arg5"),
    4122              :                                            p->getparam("arg6"),
    4123              :                                            p->getparam("arg7"),
    4124              :                                            p->getparam("arg8"),
    4125              :                                            p->getparam("arg9")
    4126              :                                            );
    4127            0 :                   r->rsprintf(" %d", status);
    4128              : 
    4129              :                   //status = cm_disconnect_client(hconn, FALSE);
    4130            0 :                   r->rsprintf(" %d", status);
    4131              :                }
    4132              : 
    4133            0 :                r->rsprintf(" | ");
    4134              :             }
    4135            0 :          }
    4136              :       }
    4137              :    }
    4138              : 
    4139            0 :    r->rsprintf("rpc %d, called %d clients\n", rpc, count);
    4140              : }
    4141              : 
    4142              : /*------------------------------------------------------------------*/
    4143              : 
    4144            0 : void do_jrpc_rev1(Param* p, Return* r)
    4145              : {
    4146              :    static RPC_LIST rpc_list[] = {
    4147              :       { 9998, "mhttpd_jrpc_rev1", {
    4148              :             {TID_STRING, RPC_OUT}, // return string
    4149              :             {TID_INT,    RPC_IN},  // return string max length
    4150              :             {TID_STRING, RPC_IN}, // arg0
    4151              :             {TID_STRING, RPC_IN}, // arg1
    4152              :             {TID_STRING, RPC_IN}, // arg2
    4153              :             {TID_STRING, RPC_IN}, // arg3
    4154              :             {TID_STRING, RPC_IN}, // arg4
    4155              :             {TID_STRING, RPC_IN}, // arg5
    4156              :             {TID_STRING, RPC_IN}, // arg6
    4157              :             {TID_STRING, RPC_IN}, // arg7
    4158              :             {TID_STRING, RPC_IN}, // arg8
    4159              :             {TID_STRING, RPC_IN}, // arg9
    4160              :             {0}} },
    4161              :       { 0 }
    4162              :    };
    4163              : 
    4164            0 :    int status, substring = 0, rpc;
    4165              : 
    4166            0 :    const char *xname   = p->getparam("name");
    4167            0 :    const char *srpc    = p->getparam("rpc");
    4168              : 
    4169            0 :    if (!srpc || !xname) {
    4170            0 :       show_text_header(r);
    4171            0 :       r->rsprintf("<INVALID_ARGUMENTS>");
    4172            0 :       return;
    4173              :    }
    4174              : 
    4175              :    char sname[256];
    4176            0 :    mstrlcpy(sname, xname, sizeof(sname));
    4177              : 
    4178            0 :    if (sname[strlen(sname)-1]=='*') {
    4179            0 :       sname[strlen(sname)-1] = 0;
    4180            0 :       substring = 1;
    4181              :    }
    4182              : 
    4183            0 :    rpc = atoi(srpc);
    4184              : 
    4185            0 :    if (rpc<RPC_MIN_ID || rpc>RPC_MAX_ID) {
    4186            0 :       show_text_header(r);
    4187            0 :       r->rsprintf("<INVALID_RPC_ID>");
    4188            0 :       return;
    4189              :    }
    4190              : 
    4191            0 :    rpc_list[0].id = rpc;
    4192            0 :    status = rpc_register_functions(rpc_list, NULL);
    4193              : 
    4194              :    //printf("cm_register_functions() for format \'%s\' status %d\n", sformat, status);
    4195              : 
    4196            0 :    show_text_header(r);
    4197              : 
    4198            0 :    std::string reply_header;
    4199            0 :    std::string reply_body;
    4200              : 
    4201              :    //r->rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", HTTP_ENCODING);
    4202              :    //r->rsprintf("<!-- created by MHTTPD on (timestamp) -->\n");
    4203              :    //r->rsprintf("<jrpc_rev1>\n");
    4204              :    //r->rsprintf("  <rpc>%d</rpc>\n", rpc);
    4205              : 
    4206              :    if (1) {
    4207              :       HNDLE hDB, hrootkey, hsubkey, hkey;
    4208              : 
    4209            0 :       cm_get_experiment_database(&hDB, NULL);
    4210              : 
    4211            0 :       int buf_length = 1024;
    4212              : 
    4213            0 :       int max_reply_length = atoi(p->getparam("max_reply_length"));
    4214            0 :       if (max_reply_length > buf_length)
    4215            0 :          buf_length = max_reply_length;
    4216              : 
    4217            0 :       char* buf = (char*)malloc(buf_length);
    4218              : 
    4219            0 :       assert(buf != NULL);
    4220              : 
    4221              :       /* find client which exports our RPC function */
    4222            0 :       status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
    4223            0 :       if (status == DB_SUCCESS) {
    4224            0 :          for (int i=0; ; i++) {
    4225            0 :             status = db_enum_key(hDB, hrootkey, i, &hsubkey);
    4226            0 :             if (status == DB_NO_MORE_SUBKEYS)
    4227            0 :                break;
    4228              : 
    4229              :             char str[256];
    4230            0 :             sprintf(str, "RPC/%d", rpc);
    4231            0 :             status = db_find_key(hDB, hsubkey, str, &hkey);
    4232            0 :             if (status == DB_SUCCESS) {
    4233              :                char client_name[NAME_LENGTH];
    4234              :                HNDLE hconn;
    4235              :                int size;
    4236              : 
    4237            0 :                size = sizeof(client_name);
    4238            0 :                status = db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, FALSE);
    4239            0 :                if (status != DB_SUCCESS)
    4240            0 :                   continue;
    4241              : 
    4242            0 :                if (strlen(sname) > 0) {
    4243            0 :                   if (substring) {
    4244            0 :                      if (strstr(client_name, sname) != client_name)
    4245            0 :                         continue;
    4246              :                   } else {
    4247            0 :                      if (strcmp(sname, client_name) != 0)
    4248            0 :                         continue;
    4249              :                   }
    4250              :                }
    4251              : 
    4252              :                //r->rsprintf("  <client>\n");
    4253              :                //r->rsprintf("    <name>%s</name>\n", client_name);
    4254              : 
    4255            0 :                int connect_status = -1;
    4256            0 :                int call_status = -1;
    4257            0 :                int call_length = 0;
    4258            0 :                int disconnect_status = -1;
    4259              : 
    4260            0 :                connect_status = cm_connect_client(client_name, &hconn);
    4261              : 
    4262              :                //r->rsprintf("    <connect_status>%d</connect_status>\n", status);
    4263              : 
    4264            0 :                if (connect_status == RPC_SUCCESS) {
    4265            0 :                   buf[0] = 0;
    4266              : 
    4267            0 :                   call_status = rpc_client_call(hconn, rpc,
    4268              :                                                 buf,
    4269              :                                                 buf_length,
    4270              :                                                 p->getparam("arg0"),
    4271              :                                                 p->getparam("arg1"),
    4272              :                                                 p->getparam("arg2"),
    4273              :                                                 p->getparam("arg3"),
    4274              :                                                 p->getparam("arg4"),
    4275              :                                                 p->getparam("arg5"),
    4276              :                                                 p->getparam("arg6"),
    4277              :                                                 p->getparam("arg7"),
    4278              :                                                 p->getparam("arg8"),
    4279              :                                                 p->getparam("arg9")
    4280              :                                                 );
    4281              : 
    4282              :                   //r->rsprintf("    <rpc_status>%d</rpc_status>\n", status);
    4283              :                   ////r->rsprintf("    <data>%s</data>\n", buf);
    4284              :                   //r->rsputs("<data>");
    4285              :                   //r->rsputs(buf);
    4286              :                   //r->rsputs("</data>\n");
    4287              : 
    4288            0 :                   if (call_status == RPC_SUCCESS) {
    4289            0 :                      call_length = strlen(buf);
    4290            0 :                      reply_body += buf;
    4291              :                   }
    4292              : 
    4293              :                   //disconnect_status = cm_disconnect_client(hconn, FALSE);
    4294              :                   //r->rsprintf("    <disconnect_status>%d</disconnect_status>\n", status);
    4295              :                }
    4296              : 
    4297              :                //r->rsprintf("  </client>\n");
    4298              : 
    4299            0 :                if (reply_header.length() > 0)
    4300            0 :                   reply_header += " | ";
    4301              : 
    4302              :                char tmp[256];
    4303            0 :                sprintf(tmp, "%s %d %d %d %d", client_name, connect_status, call_status, disconnect_status, call_length);
    4304            0 :                reply_header += tmp;
    4305              :             }
    4306            0 :          }
    4307              :       }
    4308              : 
    4309            0 :       free(buf);
    4310              :    }
    4311              : 
    4312              :    //r->rsprintf("  <called_clients>%d</called_clients>\n", count);
    4313              :    //r->rsprintf("</jrpc_rev1>\n");
    4314              : 
    4315            0 :    if (reply_header.length() > 0) {
    4316            0 :       r->rsputs(reply_header.c_str());
    4317            0 :       r->rsputs(" || ");
    4318            0 :       r->rsputs(reply_body.c_str());
    4319            0 :       r->rsputs("\n");
    4320              :    }
    4321            0 : }
    4322              : 
    4323              : /*------------------------------------------------------------------*/
    4324              : 
    4325            0 : void do_jrpc(Param* p, Return* r)
    4326              : {
    4327              :    int status;
    4328              : 
    4329            0 :    const char *name   = p->getparam("name");
    4330            0 :    const char *cmd    = p->getparam("rcmd");
    4331            0 :    const char *args   = p->getparam("rarg");
    4332              : 
    4333            0 :    if (!name || !cmd || !args) {
    4334            0 :       show_text_header(r);
    4335            0 :       r->rsprintf("<INVALID_ARGUMENTS>");
    4336            0 :       return;
    4337              :    }
    4338              : 
    4339            0 :    show_text_header(r);
    4340              : 
    4341            0 :    int buf_length = 1024;
    4342              : 
    4343            0 :    int max_reply_length = atoi(p->getparam("max_reply_length"));
    4344            0 :    if (max_reply_length > buf_length)
    4345            0 :       buf_length = max_reply_length;
    4346              : 
    4347            0 :    char* buf = (char*)malloc(buf_length);
    4348            0 :    assert(buf != NULL);
    4349              : 
    4350            0 :    buf[0] = 0;
    4351              : 
    4352              :    HNDLE hconn;
    4353              : 
    4354            0 :    status = cm_connect_client(name, &hconn);
    4355              : 
    4356            0 :    if (status != RPC_SUCCESS) {
    4357            0 :       r->rsprintf("<RPC_CONNECT_ERROR>%d</RPC_CONNECT_ERROR>", status);
    4358            0 :       free(buf);
    4359            0 :       return;
    4360              :    }
    4361              : 
    4362            0 :    status = rpc_client_call(hconn, RPC_JRPC, cmd, args, buf, buf_length);
    4363              : 
    4364            0 :    if (status != RPC_SUCCESS) {
    4365            0 :       r->rsprintf("<RPC_CALL_ERROR>%d</RPC_CALL_ERROR>", status);
    4366            0 :       free(buf);
    4367            0 :       return;
    4368              :    }
    4369              : 
    4370            0 :    r->rsprintf("%s", buf);
    4371              : 
    4372              :    //status = cm_disconnect_client(hconn, FALSE);
    4373              : 
    4374            0 :    free(buf);
    4375              : }
    4376              : 
    4377              : /*------------------------------------------------------------------*/
    4378              : 
    4379            0 : void output_key(Param* p, Return* r, HNDLE hkey, int index, const char *format)
    4380              : {
    4381              :    int size, i;
    4382              :    HNDLE hDB, hsubkey;
    4383              :    KEY key;
    4384              :    char data[TEXT_SIZE];
    4385              : 
    4386            0 :    cm_get_experiment_database(&hDB, NULL);
    4387              : 
    4388            0 :    db_get_key(hDB, hkey, &key);
    4389            0 :    if (key.type == TID_KEY) {
    4390            0 :       for (i=0 ; ; i++) {
    4391            0 :          db_enum_key(hDB, hkey, i, &hsubkey);
    4392            0 :          if (!hsubkey)
    4393            0 :             break;
    4394            0 :          output_key(p, r, hsubkey, -1, format);
    4395              :       }
    4396              :    } else {
    4397            0 :       if (key.item_size <= (int)sizeof(data)) {
    4398            0 :          size = sizeof(data);
    4399            0 :          db_get_data(hDB, hkey, data, &size, key.type);
    4400            0 :          if (index == -1) {
    4401            0 :             for (i=0 ; i<key.num_values ; i++) {
    4402            0 :                if (p->isparam("name") && atoi(p->getparam("name")) == 1) {
    4403            0 :                   if (key.num_values == 1)
    4404            0 :                      r->rsprintf("%s:", key.name);
    4405              :                   else
    4406            0 :                      r->rsprintf("%s[%d]:", key.name, i);
    4407              :                }
    4408            0 :                std::string data_str;
    4409            0 :                if (format && format[0])
    4410            0 :                   data_str = db_sprintff(format, data, key.item_size, i, key.type);
    4411              :                else
    4412            0 :                   data_str = db_sprintf(data, key.item_size, i, key.type);
    4413            0 :                r->rsputs(data_str.c_str());
    4414            0 :                if (i<key.num_values-1)
    4415            0 :                   r->rsputs("\n");
    4416            0 :             }
    4417              :          } else {
    4418            0 :             if (p->isparam("name") && atoi(p->getparam("name")) == 1)
    4419            0 :                r->rsprintf("%s[%d]:", key.name, index);
    4420            0 :             if (index >= key.num_values)
    4421            0 :                r->rsputs("<DB_OUT_OF_RANGE>");
    4422              :             else {
    4423            0 :                std::string data_str;
    4424            0 :                if (p->isparam("format"))
    4425            0 :                   data_str = db_sprintff(p->getparam("format"), data, key.item_size, index, key.type);
    4426              :                else
    4427            0 :                   data_str = db_sprintf(data, key.item_size, index, key.type);
    4428            0 :                r->rsputs(data_str.c_str());
    4429            0 :             }
    4430              :          }
    4431            0 :          r->rsputs("\n");
    4432              :       }
    4433              :    }
    4434            0 : }
    4435              : 
    4436              : /*------------------------------------------------------------------*/
    4437              : 
    4438            0 : bool starts_with(const std::string& s1, const char* s2)
    4439              : {
    4440            0 :    if (s1.length() < strlen(s2))
    4441            0 :       return false;
    4442            0 :    return (strncasecmp(s1.c_str(), s2, strlen(s2)) == 0);
    4443              : }
    4444              : 
    4445              : //static bool ends_with_char(const std::string& s, char c)
    4446              : //{
    4447              : //   if (s.length() < 1)
    4448              : //      return false;
    4449              : //   return s[s.length()-1] == c;
    4450              : //}
    4451              : 
    4452              : /*------------------------------------------------------------------*/
    4453              : 
    4454            0 : void javascript_commands(Param* p, Return* r, const char *cookie_cpwd)
    4455              : {
    4456              :    int status;
    4457              :    int size, i, n, index, type;
    4458              :    unsigned int t;
    4459              :    char str[TEXT_SIZE], format[256], facility[256], user[256];
    4460              :    HNDLE hDB, hkey;
    4461              :    KEY key;
    4462              :    char data[TEXT_SIZE];
    4463              : 
    4464            0 :    cm_get_experiment_database(&hDB, NULL);
    4465              : 
    4466              :    // process common parameters
    4467              : 
    4468            0 :    const int ENCODING_NONE = 0;
    4469            0 :    const int ENCODING_ODB = 1;
    4470            0 :    const int ENCODING_XML = 2;
    4471            0 :    const int ENCODING_JSON = 3;
    4472              : 
    4473            0 :    std::string cmd_parameter;
    4474            0 :    std::string encoding_parameter;
    4475            0 :    int encoding = ENCODING_NONE; // default encoding
    4476            0 :    bool jsonp = false; // default is no JSONP wrapper
    4477            0 :    std::string jsonp_callback; // default is no JSONP
    4478            0 :    bool single = false; // single encoding
    4479            0 :    bool multiple = false; // multiple encoding
    4480            0 :    std::vector<std::string> odb; // multiple odb parameters
    4481              :    //HNDLE hodb; // ODB handle for single odb parameter
    4482              :    //std::vector<HNDLE> hodbm; // ODB handle for multiple odb parameter
    4483              : 
    4484            0 :    if (p->isparam("cmd")) {
    4485            0 :       cmd_parameter = p->getparam("cmd");
    4486              :    }
    4487              : 
    4488            0 :    if (p->isparam("encoding")) {
    4489            0 :       encoding_parameter = p->getparam("encoding");
    4490              :    }
    4491              : 
    4492            0 :    if (encoding_parameter.length() > 0) {
    4493            0 :       if (starts_with(encoding_parameter, "odb"))
    4494            0 :          encoding = ENCODING_ODB;
    4495            0 :       else if (starts_with(encoding_parameter, "xml"))
    4496            0 :          encoding = ENCODING_XML;
    4497            0 :       else if (starts_with(encoding_parameter, "json"))
    4498            0 :          encoding = ENCODING_JSON;
    4499              :    }
    4500              : 
    4501            0 :    if (encoding == ENCODING_JSON) {
    4502            0 :       if (p->isparam("callback")) {
    4503            0 :          jsonp = true;
    4504            0 :          jsonp_callback = p->getparam("callback");
    4505              :       }
    4506              :    }
    4507              : 
    4508            0 :    if (p->isparam("odb")) {
    4509            0 :       single = true;
    4510            0 :       odb.push_back(p->getparam("odb"));
    4511              :    }
    4512              : 
    4513            0 :    if (p->isparam("odb0")) {
    4514            0 :       multiple = true;
    4515            0 :       for (int i=0 ; ; i++) {
    4516              :          char ppath[256];
    4517            0 :          sprintf(ppath, "odb%d", i);
    4518            0 :          if (!p->isparam(ppath))
    4519            0 :             break;
    4520            0 :          odb.push_back(p->getparam(ppath));
    4521            0 :       }
    4522              :    }
    4523              : 
    4524              :    if (/* DISABLES CODE */ (0)) {
    4525              :       printf("command [%s], encoding %d [%s], jsonp %d, single %d, multiple %d, odb array size %d\n", cmd_parameter.c_str(), encoding, encoding_parameter.c_str(), jsonp, single, multiple, (int)odb.size());
    4526              :    }
    4527              : 
    4528              :    /* process "jset" command */
    4529            0 :    if (equal_ustring(p->getparam("cmd"), "jset")) {
    4530              : 
    4531            0 :       if (*p->getparam("pnam")) {
    4532            0 :          std::string ppath;
    4533            0 :          ppath += "/Custom/Pwd/";
    4534            0 :          ppath += p->getparam("pnam");
    4535            0 :          str[0] = 0;
    4536            0 :          db_get_value(hDB, 0, ppath.c_str(), str, &size, TID_STRING, TRUE);
    4537            0 :          if (!equal_ustring(cookie_cpwd, str)) {
    4538            0 :             show_text_header(r);
    4539            0 :             r->rsprintf("Invalid password!");
    4540            0 :             return;
    4541              :          }
    4542            0 :       }
    4543            0 :       mstrlcpy(str, p->getparam("odb"), sizeof(str));
    4544            0 :       if (strchr(str, '[')) {
    4545            0 :          if (*(strchr(str, '[')+1) == '*')
    4546            0 :             index = -1;
    4547              :          else
    4548            0 :             index = atoi(strchr(str, '[')+1);
    4549            0 :          *strchr(str, '[') = 0;
    4550              :       } else
    4551            0 :          index = 0;
    4552              : 
    4553            0 :       if (db_find_key(hDB, 0, str, &hkey) == DB_SUCCESS && p->isparam("value")) {
    4554            0 :          db_get_key(hDB, hkey, &key);
    4555            0 :          memset(data, 0, sizeof(data));
    4556            0 :          if (key.item_size <= (int)sizeof(data)) {
    4557            0 :             if (index == -1) {
    4558            0 :                const char* ptr = p->getparam("value");
    4559            0 :                for (i=0 ; ptr != NULL ; i++) {
    4560            0 :                   size = sizeof(data);
    4561            0 :                   db_sscanf(ptr, data, &size, 0, key.type);
    4562            0 :                   if (strchr(data, ','))
    4563            0 :                      *strchr(data, ',') = 0;
    4564            0 :                   db_set_data_index(hDB, hkey, data, key.item_size, i, key.type);
    4565            0 :                   ptr = strchr(ptr, ',');
    4566            0 :                   if (ptr != NULL)
    4567            0 :                      ptr++;
    4568              :                }
    4569              :             } else {
    4570            0 :                size = sizeof(data);
    4571            0 :                db_sscanf(p->getparam("value"), data, &size, 0, key.type);
    4572              : 
    4573              :                /* extend data size for single string if necessary */
    4574            0 :                if ((key.type == TID_STRING || key.type == TID_LINK)
    4575            0 :                    && (int) strlen(data) + 1 > key.item_size && key.num_values == 1) {
    4576            0 :                   key.item_size = strlen(data) + 1;
    4577            0 :                   db_set_data(hDB, hkey, data, key.item_size, 1, key.type);
    4578              :                } else
    4579            0 :                   db_set_data_index(hDB, hkey, data, key.item_size, index, key.type);
    4580              :             }
    4581              :          }
    4582              :       } else {
    4583            0 :          if (p->isparam("value") && p->isparam("type") && p->isparam("len")) {
    4584            0 :             int type = atoi(p->getparam("type"));
    4585            0 :             if (type == 0) {
    4586            0 :                show_text_header(r);
    4587            0 :                r->rsprintf("Invalid type %d!", type);
    4588            0 :                return;
    4589              :             }
    4590            0 :             db_create_key(hDB, 0, str, type);
    4591            0 :             db_find_key(hDB, 0, str, &hkey);
    4592            0 :             if (!hkey) {
    4593            0 :                show_text_header(r);
    4594            0 :                r->rsprintf("Cannot create \'%s\' type %d", str, type);
    4595            0 :                return;
    4596              :             }
    4597            0 :             db_get_key(hDB, hkey, &key);
    4598            0 :             memset(data, 0, sizeof(data));
    4599            0 :             size = sizeof(data);
    4600            0 :             db_sscanf(p->getparam("value"), data, &size, 0, key.type);
    4601            0 :             if (key.type == TID_STRING)
    4602            0 :                db_set_data(hDB, hkey, data, atoi(p->getparam("len")), 1, TID_STRING);
    4603              :             else {
    4604            0 :                for (i=0 ; i<atoi(p->getparam("len")) ; i++)
    4605            0 :                   db_set_data_index(hDB, hkey, data, rpc_tid_size(key.type), i, key.type);
    4606              :             }
    4607              :          }
    4608              :       }
    4609              : 
    4610            0 :       show_text_header(r);
    4611            0 :       r->rsprintf("OK");
    4612            0 :       return;
    4613              :    }
    4614              : 
    4615              :    /* process "jget" command */
    4616            0 :    if (equal_ustring(p->getparam("cmd"), "jget")) {
    4617              : 
    4618            0 :       if (p->isparam("odb")) {
    4619            0 :          mstrlcpy(str, p->getparam("odb"), sizeof(str));
    4620            0 :          if (strchr(str, '[')) {
    4621            0 :             if (*(strchr(str, '[')+1) == '*')
    4622            0 :                index = -1;
    4623              :             else
    4624            0 :                index = atoi(strchr(str, '[')+1);
    4625            0 :             *strchr(str, '[') = 0;
    4626              :          } else
    4627            0 :             index = 0;
    4628              : 
    4629            0 :          show_text_header(r);
    4630              : 
    4631            0 :          status = db_find_key(hDB, 0, str, &hkey);
    4632              : 
    4633            0 :          if (status == DB_SUCCESS)
    4634            0 :             output_key(p, r, hkey, index, p->getparam("format"));
    4635              :          else
    4636            0 :             r->rsputs("<DB_NO_KEY>");
    4637              :       }
    4638              : 
    4639            0 :       if (p->isparam("odb0")) {
    4640            0 :          show_text_header(r);
    4641            0 :          for (i=0 ; ; i++) {
    4642              :             char ppath[256];
    4643            0 :             sprintf(ppath, "odb%d", i);
    4644            0 :             sprintf(format, "format%d", i);
    4645            0 :             if (p->isparam(ppath)) {
    4646            0 :                mstrlcpy(str, p->getparam(ppath), sizeof(str));
    4647            0 :                if (strchr(str, '[')) {
    4648            0 :                   if (*(strchr(str, '[')+1) == '*')
    4649            0 :                      index = -1;
    4650              :                   else
    4651            0 :                      index = atoi(strchr(str, '[')+1);
    4652            0 :                   *strchr(str, '[') = 0;
    4653              :                } else
    4654            0 :                   index = 0;
    4655            0 :                if (i > 0)
    4656            0 :                   r->rsputs("$#----#$\n");
    4657            0 :                if (db_find_key(hDB, 0, str, &hkey) == DB_SUCCESS)
    4658            0 :                   output_key(p, r, hkey, index, p->getparam(format));
    4659              :                else
    4660            0 :                   r->rsputs("<DB_NO_KEY>");
    4661              : 
    4662              :             } else
    4663            0 :                break;
    4664            0 :          }
    4665              :       }
    4666              : 
    4667            0 :       return;
    4668              :    }
    4669              : 
    4670              :    /* process "jcopy" command */
    4671            0 :    if (equal_ustring(p->getparam("cmd"), "jcopy")) {
    4672              : 
    4673            0 :       bool fmt_odb  = false;
    4674            0 :       bool fmt_xml  = false;
    4675            0 :       bool fmt_json = true;
    4676            0 :       bool fmt_jsonp = false;
    4677            0 :       int follow_links = 1;
    4678            0 :       int save_keys = 1;
    4679            0 :       int recurse = 1;
    4680            0 :       const char* fmt = NULL;
    4681            0 :       const char* jsonp_callback = "callback";
    4682              : 
    4683            0 :       if (p->isparam("encoding")) {
    4684            0 :          fmt = p->getparam("encoding");
    4685            0 :       } else if (p->isparam("format")) {
    4686            0 :          fmt = p->getparam("format");
    4687              :       }
    4688              : 
    4689            0 :       if (fmt) {
    4690            0 :          fmt_odb  = (equal_ustring(fmt, "odb") > 0);
    4691            0 :          fmt_xml  = (equal_ustring(fmt, "xml") > 0);
    4692            0 :          fmt_json = (strstr(fmt, "json") != NULL);
    4693              : 
    4694            0 :          if (fmt_odb)
    4695            0 :             fmt_xml = fmt_json = false;
    4696            0 :          if (fmt_xml)
    4697            0 :             fmt_odb = fmt_json = false;
    4698            0 :          if (fmt_json)
    4699            0 :             fmt_odb = fmt_xml = false;
    4700              : 
    4701            0 :          if (fmt_json)
    4702            0 :             fmt_jsonp = (strstr(fmt, "-p") != NULL);
    4703            0 :          if (fmt_jsonp && p->isparam("callback"))
    4704            0 :             jsonp_callback = p->getparam("callback");
    4705            0 :          if (fmt_json && strstr(fmt, "-nofollowlinks"))
    4706            0 :             follow_links = 0;
    4707            0 :          if (fmt_json && strstr(fmt, "-nokeys"))
    4708            0 :             save_keys = 2;
    4709            0 :          if (fmt_json && strstr(fmt, "-nolastwritten"))
    4710            0 :             save_keys = 0;
    4711            0 :          if (fmt_json && strstr(fmt, "-norecurse"))
    4712            0 :             recurse = 0;
    4713              :       }
    4714              : 
    4715            0 :       if (p->isparam("odb")) {
    4716            0 :          mstrlcpy(str, p->getparam("odb"), sizeof(str));
    4717              : 
    4718            0 :          show_text_header(r);
    4719              : 
    4720            0 :          if (fmt_json)
    4721            0 :             status = db_find_link(hDB, 0, str, &hkey);
    4722              :          else
    4723            0 :             status = db_find_key(hDB, 0, str, &hkey);
    4724            0 :          if (status == DB_SUCCESS) {
    4725              : 
    4726            0 :             if (fmt_jsonp) {
    4727            0 :                r->rsputs(jsonp_callback);
    4728            0 :                r->rsputs("(");
    4729              :             }
    4730              : 
    4731            0 :             int end = 0;
    4732            0 :             int bufsize = WEB_BUFFER_SIZE;
    4733            0 :             char* buf = (char *)malloc(bufsize);
    4734              : 
    4735            0 :             if (fmt_xml)
    4736            0 :                db_copy_xml(hDB, hkey, buf, &bufsize, true);
    4737            0 :             else if (fmt_json)
    4738            0 :                db_copy_json_obsolete(hDB, hkey, &buf, &bufsize, &end, save_keys, follow_links, recurse);
    4739              :             else
    4740            0 :                db_copy(hDB, hkey, buf, &bufsize, (char *)"");
    4741              : 
    4742            0 :             r->rsputs(buf);
    4743            0 :             free(buf);
    4744              : 
    4745            0 :             if (fmt_jsonp) {
    4746            0 :                r->rsputs(");\n");
    4747              :             }
    4748              :          } else
    4749            0 :             r->rsputs("<DB_NO_KEY>");
    4750              :       }
    4751              : 
    4752            0 :       if (p->isparam("odb0")) {
    4753            0 :          show_text_header(r);
    4754            0 :          if (fmt_jsonp) {
    4755            0 :             r->rsputs(jsonp_callback);
    4756            0 :             r->rsputs("(");
    4757              :          }
    4758            0 :          if (fmt_xml) {
    4759            0 :             r->rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", HTTP_ENCODING);
    4760            0 :             r->rsputs("<jcopy>\n");
    4761            0 :             r->rsputs("<data>\n");
    4762            0 :          } else if (fmt_json)
    4763            0 :             r->rsputs("[\n");
    4764              :          else
    4765            0 :             r->rsputs("");
    4766            0 :          for (int i=0 ; ; i++) {
    4767              :             char ppath[256];
    4768            0 :             sprintf(ppath, "odb%d", i);
    4769            0 :             if (!p->isparam(ppath))
    4770            0 :                break;
    4771            0 :             mstrlcpy(str, p->getparam(ppath), sizeof(str));
    4772              : 
    4773            0 :             if (i > 0) {
    4774            0 :                if (fmt_xml)
    4775            0 :                   r->rsputs("</data>\n<data>\n");
    4776            0 :                else if (fmt_json)
    4777            0 :                   r->rsputs(",\n");
    4778              :                else
    4779            0 :                   r->rsputs("$#----#$\n");
    4780              :             }
    4781              : 
    4782            0 :             if (fmt_json)
    4783            0 :                status = db_find_link(hDB, 0, str, &hkey);
    4784              :             else
    4785            0 :                status = db_find_key(hDB, 0, str, &hkey);
    4786            0 :             if (status != DB_SUCCESS) {
    4787            0 :                if (fmt_xml)
    4788            0 :                   r->rsputs("<DB_NO_KEY/>\n");
    4789            0 :                else if (fmt_json) {
    4790              :                   char tmp[256];
    4791            0 :                   sprintf(tmp, "{ \"/error\" : %d }\n", status);
    4792            0 :                   r->rsputs(tmp);
    4793              :                } else
    4794            0 :                   r->rsputs("<DB_NO_KEY>\n");
    4795            0 :                continue;
    4796            0 :             }
    4797              : 
    4798            0 :             int end = 0;
    4799            0 :             int bufsize = WEB_BUFFER_SIZE;
    4800            0 :             char* buf = (char *)malloc(bufsize);
    4801              : 
    4802            0 :             if (fmt_xml) {
    4803            0 :                db_copy_xml(hDB, hkey, buf, &bufsize, true);
    4804            0 :                const char* s = strstr(buf, "-->");
    4805            0 :                if (s)
    4806            0 :                   s+=4;
    4807              :                else
    4808            0 :                   s = buf;
    4809            0 :                r->rsputs(s);
    4810            0 :             } else if (fmt_json) {
    4811            0 :                db_copy_json_obsolete(hDB, hkey, &buf, &bufsize, &end, save_keys, follow_links, recurse);
    4812            0 :                r->rsputs(buf);
    4813              :             } else {
    4814            0 :                db_copy(hDB, hkey, buf, &bufsize, (char *)"");
    4815            0 :                r->rsputs(buf);
    4816              :             }
    4817              : 
    4818            0 :             free(buf);
    4819            0 :          }
    4820              : 
    4821            0 :          if (fmt_xml)
    4822            0 :             r->rsputs("</data>\n</jcopy>\n");
    4823            0 :          else if (fmt_json)
    4824            0 :             r->rsputs("]\n");
    4825              :          else
    4826            0 :             r->rsputs("");
    4827              : 
    4828            0 :          if (fmt_jsonp) {
    4829            0 :             r->rsputs(");\n");
    4830              :          }
    4831              :       }
    4832            0 :       return;
    4833              :    }
    4834              : 
    4835              :    /* process "jkey" command */
    4836            0 :    if (equal_ustring(p->getparam("cmd"), "jkey")) {
    4837              : 
    4838              :       // test:
    4839              :       // curl "http://localhost:8080?cmd=jkey&odb0=/runinfo/run+number&odb1=/nonexistant&odb2=/&encoding=json&callback=aaa"
    4840              : 
    4841            0 :       show_text_header(r);
    4842              : 
    4843            0 :       if (jsonp) {
    4844            0 :          r->rsputs(jsonp_callback.c_str());
    4845            0 :          r->rsputs("(");
    4846              :       }
    4847              : 
    4848            0 :       if (multiple) {
    4849            0 :          switch (encoding) {
    4850            0 :          default:
    4851            0 :             break;
    4852            0 :          case ENCODING_JSON:
    4853            0 :             r->rsprintf("[ ");
    4854            0 :             break;
    4855              :          }
    4856              :       }
    4857              : 
    4858            0 :       for (unsigned i=0; i<odb.size(); i++) {
    4859            0 :          status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
    4860            0 :          if (status == DB_SUCCESS)
    4861            0 :             status = db_get_key(hDB, hkey, &key);
    4862            0 :          switch (encoding) {
    4863            0 :          default:
    4864            0 :             if (multiple && i>0)
    4865            0 :                r->rsputs("$#----#$\n");
    4866            0 :             if (status == DB_SUCCESS) {
    4867            0 :                r->rsprintf("%s\n", key.name);
    4868            0 :                r->rsprintf("TID_%s\n", rpc_tid_name(key.type));
    4869            0 :                r->rsprintf("%d\n", key.num_values);
    4870            0 :                r->rsprintf("%d\n", key.item_size);
    4871            0 :                r->rsprintf("%d\n", key.last_written);
    4872              :             } else {
    4873            0 :                r->rsputs("<DB_NO_KEY>\n");
    4874              :             }
    4875            0 :             break;
    4876            0 :          case ENCODING_JSON:
    4877            0 :             if (multiple && i>0)
    4878            0 :                r->rsprintf(", ");
    4879            0 :             if (status == DB_SUCCESS) {
    4880            0 :                r->rsprintf("{ ");
    4881            0 :                r->rsprintf("\"name\":\"%s\",", key.name);
    4882            0 :                r->rsprintf("\"type\":%d,", key.type);
    4883            0 :                r->rsprintf("\"type_name\":\"TID_%s\",", rpc_tid_name(key.type));
    4884            0 :                r->rsprintf("\"num_values\":%d,", key.num_values);
    4885            0 :                r->rsprintf("\"item_size\":%d,", key.item_size);
    4886            0 :                r->rsprintf("\"last_written\":%d", key.last_written);
    4887            0 :                r->rsprintf(" }");
    4888              :             } else {
    4889            0 :                r->rsprintf("{ \"/error\":%d }", status);
    4890              :             }
    4891            0 :             break;
    4892              :          }
    4893              :       }
    4894              : 
    4895            0 :       if (multiple) {
    4896            0 :          switch (encoding) {
    4897            0 :          default:
    4898            0 :             break;
    4899            0 :          case ENCODING_JSON:
    4900            0 :             r->rsprintf(" ]");
    4901            0 :             break;
    4902              :          }
    4903              :       }
    4904              : 
    4905            0 :       if (jsonp) {
    4906            0 :          r->rsputs(");\n");
    4907              :       }
    4908              : 
    4909            0 :       return;
    4910              :    }
    4911              : 
    4912              :    /* process "jcreate" command */
    4913            0 :    if (equal_ustring(p->getparam("cmd"), "jcreate")) {
    4914              : 
    4915              :       // test:
    4916              :       // curl "http://localhost:8080?cmd=jcreate&odb0=/test/foo&type0=7&odb1=/nonexistant&type1=100&odb2=/test/bar&type2=12&encoding=json&callback=aaa"
    4917              :       // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo&type=7"
    4918              :       // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo70&type=7&arraylen=10"
    4919              :       // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo12s&type=12&strlen=32"
    4920              :       // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo12s5&type=12&strlen=32&arraylen=5"
    4921              :       // curl "http://localhost:8080?cmd=jcreate&odb0=/test/foo12s5x&type0=12&strlen0=32&arraylen0=5"
    4922              : 
    4923              : 
    4924            0 :       show_text_header(r);
    4925              : 
    4926            0 :       if (jsonp) {
    4927            0 :          r->rsputs(jsonp_callback.c_str());
    4928            0 :          r->rsputs("(");
    4929              :       }
    4930              : 
    4931            0 :       if (multiple) {
    4932              :          switch (encoding) {
    4933            0 :          default:
    4934              :          case ENCODING_JSON:
    4935            0 :             r->rsprintf("[ ");
    4936            0 :             break;
    4937              :          }
    4938              :       }
    4939              : 
    4940            0 :       for (unsigned i=0; i<odb.size(); i++) {
    4941            0 :          HNDLE hkey = 0;
    4942            0 :          int type = 0;
    4943            0 :          int arraylength = 0;
    4944            0 :          int strlength = 0;
    4945              : 
    4946            0 :          if (single) {
    4947            0 :             type = atoi(p->getparam("type"));
    4948            0 :             arraylength = atoi(p->getparam("arraylen"));
    4949            0 :             strlength = atoi(p->getparam("strlen"));
    4950              :          }
    4951            0 :          else if (multiple) {
    4952              :             char buf[256];
    4953            0 :             sprintf(buf, "type%d", i);
    4954            0 :             type = atoi(p->getparam(buf));
    4955            0 :             sprintf(buf, "arraylen%d", i);
    4956            0 :             arraylength = atoi(p->getparam(buf));
    4957            0 :             sprintf(buf, "strlen%d", i);
    4958            0 :             strlength = atoi(p->getparam(buf));
    4959              :          }
    4960              : 
    4961            0 :          status = db_create_key(hDB, 0, odb[i].c_str(), type);
    4962              : 
    4963            0 :          if (status == DB_SUCCESS) {
    4964            0 :             status = db_find_link(hDB, 0, odb[i].c_str(), &hkey);
    4965              :          }
    4966              : 
    4967            0 :          if (status == DB_SUCCESS && hkey && type == TID_STRING && strlength > 0) {
    4968            0 :             char* s = (char*)calloc(strlength, 1); // initialized to zero
    4969            0 :             status = db_set_data(hDB, hkey, s, strlength, 1, TID_STRING);
    4970            0 :             free(s);
    4971              :          }
    4972              : 
    4973            0 :          if (status == DB_SUCCESS && hkey && arraylength > 1) {
    4974            0 :             status = db_set_num_values(hDB, hkey, arraylength);
    4975              :          }
    4976              : 
    4977              :          switch (encoding) {
    4978            0 :          default:
    4979              :          case ENCODING_JSON:
    4980            0 :             if (multiple && i>0)
    4981            0 :                r->rsprintf(", ");
    4982            0 :             r->rsprintf("%d", status);
    4983            0 :             break;
    4984              :          }
    4985              :       }
    4986              : 
    4987            0 :       if (multiple) {
    4988              :          switch (encoding) {
    4989            0 :          default:
    4990              :          case ENCODING_JSON:
    4991            0 :             r->rsprintf(" ]");
    4992            0 :             break;
    4993              :          }
    4994              :       }
    4995              : 
    4996            0 :       if (jsonp) {
    4997            0 :          r->rsputs(");\n");
    4998              :       }
    4999              : 
    5000            0 :       return;
    5001              :    }
    5002              : 
    5003              :    /* process "jresize" command */
    5004            0 :    if (equal_ustring(p->getparam("cmd"), "jresize")) {
    5005              : 
    5006              :       // test:
    5007              : 
    5008              :       // curl "http://localhost:8080?cmd=jresize&odb=/test/foo70&arraylen=5"
    5009              :       // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&arraylen=5"
    5010              :       // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&strlen=16"
    5011              :       // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&strlen=30&arraylen=10"
    5012              : 
    5013            0 :       show_text_header(r);
    5014              : 
    5015            0 :       if (jsonp) {
    5016            0 :          r->rsputs(jsonp_callback.c_str());
    5017            0 :          r->rsputs("(");
    5018              :       }
    5019              : 
    5020            0 :       if (multiple) {
    5021              :          switch (encoding) {
    5022            0 :          default:
    5023              :          case ENCODING_JSON:
    5024            0 :             r->rsprintf("[ ");
    5025            0 :             break;
    5026              :          }
    5027              :       }
    5028              : 
    5029            0 :       for (unsigned i=0; i<odb.size(); i++) {
    5030              :          HNDLE hkey;
    5031              :          KEY key;
    5032            0 :          int arraylength = 0;
    5033            0 :          int strlength = 0;
    5034              : 
    5035            0 :          if (single) {
    5036            0 :             arraylength = atoi(p->getparam("arraylen"));
    5037            0 :             strlength = atoi(p->getparam("strlen"));
    5038              :          }
    5039            0 :          else if (multiple) {
    5040              :             char buf[256];
    5041            0 :             sprintf(buf, "arraylen%d", i);
    5042            0 :             arraylength = atoi(p->getparam(buf));
    5043            0 :             sprintf(buf, "strlen%d", i);
    5044            0 :             strlength = atoi(p->getparam(buf));
    5045              :          }
    5046              : 
    5047            0 :          status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
    5048              : 
    5049            0 :          if (status == DB_SUCCESS && hkey) {
    5050            0 :             status = db_get_key(hDB, hkey, &key);
    5051              :          }
    5052              : 
    5053            0 :          if (status == DB_SUCCESS && hkey && key.type == TID_STRING && strlength > 0) {
    5054            0 :             int oldsize = key.item_size * key.num_values;
    5055            0 :             char* olddata = (char*)malloc(oldsize);
    5056            0 :             int size = oldsize;
    5057            0 :             status = db_get_data(hDB, hkey, olddata, &size, TID_STRING);
    5058              : 
    5059            0 :             if (status == DB_SUCCESS) {
    5060            0 :                int newsize = strlength * key.num_values;
    5061            0 :                char* s = (char*)calloc(newsize, 1); // initialized to zero
    5062            0 :                for (int k=0; k<key.num_values; k++) {
    5063            0 :                   mstrlcpy(s + strlength*k, olddata + key.item_size*k, strlength);
    5064              :                }
    5065              : 
    5066            0 :                status = db_set_data(hDB, hkey, s, newsize, key.num_values, TID_STRING);
    5067            0 :                free(s);
    5068              :             }
    5069              : 
    5070            0 :             free(olddata);
    5071              :          }
    5072              : 
    5073            0 :          if (status == DB_SUCCESS && hkey && arraylength > 0) {
    5074            0 :             status = db_set_num_values(hDB, hkey, arraylength);
    5075              :          }
    5076              : 
    5077              :          switch (encoding) {
    5078            0 :          default:
    5079              :          case ENCODING_JSON:
    5080            0 :             if (multiple && i>0)
    5081            0 :                r->rsprintf(", ");
    5082            0 :             r->rsprintf("%d", status);
    5083            0 :             break;
    5084              :          }
    5085              :       }
    5086              : 
    5087            0 :       if (multiple) {
    5088              :          switch (encoding) {
    5089            0 :          default:
    5090              :          case ENCODING_JSON:
    5091            0 :             r->rsprintf(" ]");
    5092            0 :             break;
    5093              :          }
    5094              :       }
    5095              : 
    5096            0 :       if (jsonp) {
    5097            0 :          r->rsputs(");\n");
    5098              :       }
    5099              : 
    5100            0 :       return;
    5101              :    }
    5102              : 
    5103              :    /* process "jrename" command */
    5104            0 :    if (equal_ustring(p->getparam("cmd"), "jrename")) {
    5105              : 
    5106              :       // test:
    5107              :       // curl "http://localhost:8080?cmd=jrename&odb0=/test/foo&type0=7&odb1=/nonexistant&type1=100&odb2=/test/bar&type2=12&encoding=json&callback=aaa"
    5108              :       // curl "http://localhost:8080?cmd=jrename&odb=/test/foo&name=foofoo"
    5109              : 
    5110            0 :       show_text_header(r);
    5111              : 
    5112            0 :       if (jsonp) {
    5113            0 :          r->rsputs(jsonp_callback.c_str());
    5114            0 :          r->rsputs("(");
    5115              :       }
    5116              : 
    5117            0 :       if (multiple) {
    5118              :          switch (encoding) {
    5119            0 :          default:
    5120              :          case ENCODING_JSON:
    5121            0 :             r->rsprintf("[ ");
    5122            0 :             break;
    5123              :          }
    5124              :       }
    5125              : 
    5126            0 :       for (unsigned i=0; i<odb.size(); i++) {
    5127            0 :          const char* name = NULL;
    5128            0 :          if (single)
    5129            0 :             name = p->getparam("name");
    5130            0 :          else if (multiple) {
    5131              :             char buf[256];
    5132            0 :             sprintf(buf, "name%d", i);
    5133            0 :             name = p->getparam(buf);
    5134              :          }
    5135            0 :          status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
    5136            0 :          if (status == DB_SUCCESS) {
    5137            0 :             status = db_rename_key(hDB, hkey, name);
    5138              :          }
    5139              :          switch (encoding) {
    5140            0 :          default:
    5141              :          case ENCODING_JSON:
    5142            0 :             if (multiple && i>0)
    5143            0 :                r->rsprintf(", ");
    5144            0 :             r->rsprintf("%d", status);
    5145            0 :             break;
    5146              :          }
    5147              :       }
    5148              : 
    5149            0 :       if (multiple) {
    5150              :          switch (encoding) {
    5151            0 :          default:
    5152              :          case ENCODING_JSON:
    5153            0 :             r->rsprintf(" ]");
    5154            0 :             break;
    5155              :          }
    5156              :       }
    5157              : 
    5158            0 :       if (jsonp) {
    5159            0 :          r->rsputs(");\n");
    5160              :       }
    5161              : 
    5162            0 :       return;
    5163              :    }
    5164              : 
    5165              :    /* process "jlink" command */
    5166            0 :    if (equal_ustring(p->getparam("cmd"), "jlink")) {
    5167              : 
    5168              :       // test:
    5169              :       // curl "http://localhost:8080?cmd=jlink&odb=/test/link&dest=/test/foo"
    5170              :       // curl "http://localhost:8080?cmd=jlink&odb0=/test/link0&dest0=/test/foo&odb1=/test/link1&dest1=/test/foo"
    5171              : 
    5172            0 :       show_text_header(r);
    5173              : 
    5174            0 :       if (jsonp) {
    5175            0 :          r->rsputs(jsonp_callback.c_str());
    5176            0 :          r->rsputs("(");
    5177              :       }
    5178              : 
    5179            0 :       if (multiple) {
    5180              :          switch (encoding) {
    5181            0 :          default:
    5182              :          case ENCODING_JSON:
    5183            0 :             r->rsprintf("[ ");
    5184            0 :             break;
    5185              :          }
    5186              :       }
    5187              : 
    5188            0 :       for (unsigned i=0; i<odb.size(); i++) {
    5189            0 :          const char* dest = NULL;
    5190            0 :          if (single)
    5191            0 :             dest = p->getparam("dest");
    5192            0 :          else if (multiple) {
    5193              :             char buf[256];
    5194            0 :             sprintf(buf, "dest%d", i);
    5195            0 :             dest = p->getparam(buf);
    5196              :          }
    5197              : 
    5198            0 :          status = db_create_link(hDB, 0, odb[i].c_str(), dest);
    5199              : 
    5200              :          switch (encoding) {
    5201              :          default:
    5202              :          case ENCODING_JSON:
    5203            0 :             if (multiple && i>0)
    5204            0 :                r->rsprintf(", ");
    5205            0 :             r->rsprintf("%d", status);
    5206            0 :             break;
    5207              :          }
    5208              :       }
    5209              : 
    5210            0 :       if (multiple) {
    5211              :          switch (encoding) {
    5212            0 :          default:
    5213              :          case ENCODING_JSON:
    5214            0 :             r->rsprintf(" ]");
    5215            0 :             break;
    5216              :          }
    5217              :       }
    5218              : 
    5219            0 :       if (jsonp) {
    5220            0 :          r->rsputs(");\n");
    5221              :       }
    5222              : 
    5223            0 :       return;
    5224              :    }
    5225              : 
    5226              :    /* process "jreorder" command */
    5227            0 :    if (equal_ustring(p->getparam("cmd"), "jreorder")) {
    5228              : 
    5229              :       // test:
    5230              :       // curl "http://localhost:8080?cmd=jreorder&odb0=/test/foo&index0=0&odb1=/test/bar&index1=1"
    5231              :       // curl "http://localhost:8080?cmd=jreorder&odb=/test/bar&index=0"
    5232              : 
    5233            0 :       show_text_header(r);
    5234              : 
    5235            0 :       if (jsonp) {
    5236            0 :          r->rsputs(jsonp_callback.c_str());
    5237            0 :          r->rsputs("(");
    5238              :       }
    5239              : 
    5240            0 :       if (multiple) {
    5241              :          switch (encoding) {
    5242            0 :          default:
    5243              :          case ENCODING_JSON:
    5244            0 :             r->rsprintf("[ ");
    5245            0 :             break;
    5246              :          }
    5247              :       }
    5248              : 
    5249            0 :       for (unsigned i=0; i<odb.size(); i++) {
    5250            0 :          int index = 0;
    5251            0 :          if (single)
    5252            0 :             index = atoi(p->getparam("index"));
    5253            0 :          else if (multiple) {
    5254              :             char buf[256];
    5255            0 :             sprintf(buf, "index%d", i);
    5256            0 :             index = atoi(p->getparam(buf));
    5257              :          }
    5258              : 
    5259            0 :          status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
    5260            0 :          if (status == DB_SUCCESS) {
    5261            0 :             status = db_reorder_key(hDB, hkey, index);
    5262              :          }
    5263              : 
    5264              :          switch (encoding) {
    5265            0 :          default:
    5266              :          case ENCODING_JSON:
    5267            0 :             if (multiple && i>0)
    5268            0 :                r->rsprintf(", ");
    5269            0 :             r->rsprintf("%d", status);
    5270            0 :             break;
    5271              :          }
    5272              :       }
    5273              : 
    5274            0 :       if (multiple) {
    5275              :          switch (encoding) {
    5276            0 :          default:
    5277              :          case ENCODING_JSON:
    5278            0 :             r->rsprintf(" ]");
    5279            0 :             break;
    5280              :          }
    5281              :       }
    5282              : 
    5283            0 :       if (jsonp) {
    5284            0 :          r->rsputs(");\n");
    5285              :       }
    5286              : 
    5287            0 :       return;
    5288              :    }
    5289              : 
    5290              :    /* process "jdelete" command */
    5291            0 :    if (equal_ustring(p->getparam("cmd"), "jdelete")) {
    5292              : 
    5293              :       // test:
    5294              :       // curl "http://localhost:8080?cmd=jdelete&odb0=/test/foo&odb1=/nonexistant&odb2=/test/bar&encoding=json&callback=aaa"
    5295              :       // curl "http://localhost:8080?cmd=jdelete&odb=/test/foo"
    5296              : 
    5297            0 :       show_text_header(r);
    5298              : 
    5299            0 :       if (jsonp) {
    5300            0 :          r->rsputs(jsonp_callback.c_str());
    5301            0 :          r->rsputs("(");
    5302              :       }
    5303              : 
    5304            0 :       if (multiple) {
    5305              :          switch (encoding) {
    5306            0 :          default:
    5307              :          case ENCODING_JSON:
    5308            0 :             r->rsprintf("[ ");
    5309            0 :             break;
    5310              :          }
    5311              :       }
    5312              : 
    5313            0 :       for (unsigned i=0; i<odb.size(); i++) {
    5314            0 :          BOOL follow_links = 0;
    5315            0 :          status = db_find_link(hDB, 0, odb[i].c_str(), &hkey);
    5316            0 :          if (status == DB_SUCCESS) {
    5317            0 :             status = db_delete_key(hDB, hkey, follow_links);
    5318              :          }
    5319              :          switch (encoding) {
    5320            0 :          default:
    5321              :          case ENCODING_JSON:
    5322            0 :             if (multiple && i>0)
    5323            0 :                r->rsprintf(", ");
    5324            0 :             r->rsprintf("%d", status);
    5325            0 :             break;
    5326              :          }
    5327              :       }
    5328              : 
    5329            0 :       if (multiple) {
    5330              :          switch (encoding) {
    5331            0 :          default:
    5332              :          case ENCODING_JSON:
    5333            0 :             r->rsprintf(" ]");
    5334            0 :             break;
    5335              :          }
    5336              :       }
    5337              : 
    5338            0 :       if (jsonp) {
    5339            0 :          r->rsputs(");\n");
    5340              :       }
    5341              : 
    5342            0 :       return;
    5343              :    }
    5344              : 
    5345              :    /* process "jmsg" command */
    5346            0 :    if (equal_ustring(p->getparam("cmd"), "jmsg")) {
    5347              : 
    5348            0 :       if (p->getparam("f") && *p->getparam("f"))
    5349            0 :          mstrlcpy(facility, p->getparam("f"), sizeof(facility));
    5350              :       else
    5351            0 :          mstrlcpy(facility, "midas", sizeof(facility));
    5352              : 
    5353            0 :       n = 1;
    5354            0 :       if (p->getparam("n") && *p->getparam("n"))
    5355            0 :          n = atoi(p->getparam("n"));
    5356              : 
    5357            0 :       t = 0;
    5358            0 :       if (p->getparam("t") && p->getparam("t"))
    5359            0 :          t = atoi(p->getparam("t"));
    5360              : 
    5361            0 :       show_text_header(r);
    5362            0 :       char* messages = NULL;
    5363            0 :       int num_messages = 0;
    5364            0 :       cm_msg_retrieve2(facility, t, n, &messages, &num_messages);
    5365            0 :       if (messages) {
    5366            0 :          r->rsputs(messages);
    5367            0 :          free(messages);
    5368              :       }
    5369            0 :       return;
    5370              :    }
    5371              : 
    5372              :    /* process "jgenmsg" command */
    5373            0 :    if (equal_ustring(p->getparam("cmd"), "jgenmsg")) {
    5374              : 
    5375            0 :       if (p->getparam("facility") && *p->getparam("facility"))
    5376            0 :          mstrlcpy(facility, p->getparam("facility"), sizeof(facility));
    5377              :       else
    5378            0 :          mstrlcpy(facility, "midas", sizeof(facility));
    5379              : 
    5380            0 :       if (p->getparam("user") && *p->getparam("user"))
    5381            0 :          mstrlcpy(user, p->getparam("user"), sizeof(user));
    5382              :       else
    5383            0 :          mstrlcpy(user, "javascript_commands", sizeof(user));
    5384              : 
    5385            0 :       if (p->getparam("type") && *p->getparam("type"))
    5386            0 :          type = atoi(p->getparam("type"));
    5387              :       else
    5388            0 :          type = MT_INFO;
    5389              : 
    5390            0 :       if (p->getparam("msg") && *p->getparam("msg")) {
    5391            0 :          cm_msg1(type, __FILE__, __LINE__, facility, user, "%s", p->getparam("msg"));
    5392              :       }
    5393              : 
    5394            0 :       show_text_header(r);
    5395            0 :       r->rsputs("Message successfully created\n");
    5396            0 :       return;
    5397              :    }
    5398              : 
    5399              :    /* process "jalm" command */
    5400            0 :    if (equal_ustring(p->getparam("cmd"), "jalm")) {
    5401              : 
    5402            0 :       show_text_header(r);
    5403            0 :       std::string alarms;
    5404            0 :       al_get_alarms(&alarms);
    5405            0 :       r->rsputs(alarms.c_str());
    5406            0 :       return;
    5407            0 :    }
    5408              : 
    5409              :    /* process "jrpc" command */
    5410            0 :    if (equal_ustring(p->getparam("cmd"), "jrpc_rev0")) {
    5411            0 :       do_jrpc_rev0(p, r);
    5412            0 :       return;
    5413              :    }
    5414              : 
    5415              :    /* process "jrpc" command */
    5416            0 :    if (equal_ustring(p->getparam("cmd"), "jrpc_rev1")) {
    5417            0 :       do_jrpc_rev1(p, r);
    5418            0 :       return;
    5419              :    }
    5420              : 
    5421              :    /* process "jrpc" command */
    5422            0 :    if (equal_ustring(p->getparam("cmd"), "jrpc")) {
    5423            0 :       do_jrpc(p, r);
    5424            0 :       return;
    5425              :    }
    5426            0 : }
    5427              : 
    5428              : /*------------------------------------------------------------------*/
    5429              : 
    5430            0 : void show_custom_page(Param* pp, Return* r, const char *cookie_cpwd)
    5431              : {
    5432              :    int size, n_var, index, edit;
    5433              :    char keypath[256], type[32], *p, *ps;
    5434              :    char pwd[256], tail[256];
    5435              :    HNDLE hDB, hkey;
    5436              :    KEY key;
    5437              :    char data[TEXT_SIZE];
    5438              : 
    5439            0 :    std::string path = pp->getparam("page");
    5440              : 
    5441            0 :    if (path[0] == 0) {
    5442            0 :       show_error_404(r, "show_custom_page: Invalid custom page: \"page\" parameter is empty");
    5443            0 :       return;
    5444              :    }
    5445              : 
    5446            0 :    if (strstr(path.c_str(), "..")) {
    5447            0 :       std::string str;
    5448            0 :       str += "Invalid custom page name \'";
    5449            0 :       str += path;
    5450            0 :       str += "\' contains \'..\'";
    5451            0 :       show_error_404(r, str.c_str());
    5452            0 :       return;
    5453            0 :    }
    5454              : 
    5455            0 :    if (strstr(path.c_str(), ".gif")) {
    5456            0 :       show_custom_gif(r, path.c_str());
    5457            0 :       return;
    5458              :    }
    5459              : 
    5460            0 :    if (strchr(path.c_str(), '.')) {
    5461            0 :       show_custom_file(r, path.c_str());
    5462            0 :       return;
    5463              :    }
    5464              : 
    5465            0 :    cm_get_experiment_database(&hDB, NULL);
    5466              : 
    5467            0 :    std::string xpath = std::string("/Custom/") + path;
    5468            0 :    db_find_key(hDB, 0, xpath.c_str(), &hkey);
    5469            0 :    if (!hkey) {
    5470            0 :       xpath = std::string("/Custom/") + path + "&";
    5471            0 :       db_find_key(hDB, 0, xpath.c_str(), &hkey);
    5472            0 :       if (!hkey) {
    5473            0 :          xpath = std::string("/Custom/") + path + "!";
    5474            0 :          db_find_key(hDB, 0, xpath.c_str(), &hkey);
    5475              :       }
    5476              :    }
    5477              : 
    5478            0 :    if (hkey) {
    5479              :       char* ctext;
    5480              :       int status;
    5481              : 
    5482            0 :       status = db_get_key(hDB, hkey, &key);
    5483            0 :       assert(status == DB_SUCCESS);
    5484            0 :       size = key.total_size;
    5485            0 :       ctext = (char*)malloc(size);
    5486            0 :       status = db_get_data(hDB, hkey, ctext, &size, TID_STRING);
    5487            0 :       if (status != DB_SUCCESS) {
    5488            0 :          std::string errtext = msprintf("show_custom_page: Error: db_get_data() for \"%s\" status %d", xpath.c_str(), status);
    5489            0 :          show_error_404(r, errtext.c_str());
    5490            0 :          free(ctext);
    5491            0 :          return;
    5492            0 :       }
    5493              : 
    5494            0 :       std::string content_type = "text/html";
    5495              : 
    5496              :       /* check for link */
    5497            0 :       if (std::string(ctext).substr(0, 5) == "?cmd=") {
    5498            0 :          redirect(r, ctext);
    5499            0 :          free(ctext);
    5500            0 :          return;
    5501              :       }
    5502              : 
    5503              :       /* check if filename */
    5504            0 :       if (strchr(ctext, '\n') == 0) {
    5505            0 :          std::string full_filename = add_custom_path(ctext);
    5506            0 :          int fh = open(full_filename.c_str(), O_RDONLY | O_BINARY);
    5507            0 :          if (fh < 0) {
    5508            0 :             std::string str = msprintf("show_custom_page: Cannot open file \"%s\", open() errno %d (%s)", full_filename.c_str(), errno, strerror(errno));
    5509            0 :             show_error_404(r, str.c_str());
    5510            0 :             free(ctext);
    5511            0 :             return;
    5512            0 :          }
    5513            0 :          free(ctext);
    5514            0 :          ctext = NULL;
    5515            0 :          off_t off = lseek(fh, 0, SEEK_END);
    5516            0 :          if (off < 0) {
    5517            0 :             std::string str = msprintf("show_custom_page: Cannot open file \"%s\", lseek(SEEK_END) errno %d (%s)", full_filename.c_str(), errno, strerror(errno));
    5518            0 :             show_error_404(r, str.c_str());
    5519            0 :             free(ctext);
    5520            0 :             close(fh);
    5521            0 :             return;
    5522            0 :          }
    5523            0 :          size_t size = off;
    5524            0 :          lseek(fh, 0, SEEK_SET);
    5525            0 :          ctext = (char*)malloc(size+1);
    5526            0 :          ssize_t rd = read(fh, ctext, size);
    5527            0 :          if (rd > 0) {
    5528            0 :             ctext[rd] = 0; // make sure string is zero-terminated
    5529            0 :             size = rd;
    5530              :          } else {
    5531            0 :             ctext[0] = 0;
    5532            0 :             size = 0;
    5533              :          }
    5534            0 :          close(fh);
    5535              : 
    5536            0 :          content_type = get_content_type(full_filename.c_str());
    5537            0 :       }
    5538              : 
    5539              :       /* check for valid password */
    5540            0 :       if (equal_ustring(pp->getparam("cmd"), "Edit")) {
    5541            0 :          p = ps = ctext;
    5542            0 :          n_var = 0;
    5543              :          do {
    5544              :             char format[256];
    5545              : 
    5546            0 :             p = find_odb_tag(ps, keypath, format, &edit, type, pwd, tail);
    5547            0 :             if (p == NULL)
    5548            0 :                break;
    5549            0 :             ps = strchr(p, '>') + 1;
    5550              : 
    5551            0 :             if (pwd[0] && n_var == atoi(pp->getparam("index"))) {
    5552              :                char str[256];
    5553            0 :                size = NAME_LENGTH;
    5554            0 :                mstrlcpy(str, path.c_str(), sizeof(str)); // FIXME: overflows "str"
    5555            0 :                if (strlen(str)>0 && str[strlen(str)-1] == '&')
    5556            0 :                   str[strlen(str)-1] = 0;
    5557            0 :                std::string ppath;
    5558            0 :                ppath += "/Custom/Pwd/";
    5559            0 :                if (pp->getparam("pnam") && *pp->getparam("pnam")) {
    5560            0 :                   ppath += pp->getparam("pnam");
    5561              :                } else {
    5562            0 :                   ppath += str;
    5563              :                }
    5564            0 :                str[0] = 0;
    5565            0 :                db_get_value(hDB, 0, ppath.c_str(), str, &size, TID_STRING, TRUE);
    5566            0 :                if (!equal_ustring(cookie_cpwd, str)) {
    5567            0 :                   show_error_404(r, "show_custom_page: Invalid password!");
    5568            0 :                   free(ctext);
    5569            0 :                   return;
    5570              :                } else
    5571            0 :                   break;
    5572            0 :             }
    5573              : 
    5574            0 :             n_var++;
    5575            0 :          } while (p != NULL);
    5576              :       }
    5577              : 
    5578              :       /* process toggle command */
    5579            0 :       if (equal_ustring(pp->getparam("cmd"), "Toggle")) {
    5580              : 
    5581            0 :          if (pp->getparam("pnam") && *pp->getparam("pnam")) {
    5582            0 :             std::string ppath;
    5583            0 :             ppath += "/Custom/Pwd/";
    5584            0 :             ppath += pp->getparam("pnam");
    5585            0 :             std::string str;
    5586            0 :             db_get_value_string(hDB, 0, ppath.c_str(), 0, &str, TRUE, 256);
    5587            0 :             if (!equal_ustring(cookie_cpwd, str.c_str())) {
    5588            0 :                show_error_404(r, "show_custom_page: Invalid password!");
    5589            0 :                free(ctext);
    5590            0 :                return;
    5591              :             }
    5592            0 :          }
    5593            0 :          std::string podb = pp->getparam("odb");
    5594            0 :          std::string::size_type pos = podb.find('[');
    5595            0 :          if (pos != std::string::npos) {
    5596            0 :             index = atoi(podb.substr(pos+1).c_str());
    5597            0 :             podb.resize(pos);
    5598              :             //printf("found index %d in [%s] [%s]\n", index, pp->getparam("odb"), podb.c_str());
    5599              :          } else
    5600            0 :             index = 0;
    5601              : 
    5602            0 :          if (db_find_key(hDB, 0, podb.c_str(), &hkey)) {
    5603            0 :             db_get_key(hDB, hkey, &key);
    5604            0 :             memset(data, 0, sizeof(data));
    5605            0 :             if (key.item_size <= (int)sizeof(data)) {
    5606            0 :                size = sizeof(data);
    5607            0 :                db_get_data_index(hDB, hkey, data, &size, index, key.type);
    5608            0 :                std::string data_str = db_sprintf(data, size, 0, key.type);
    5609            0 :                if (atoi(data_str.c_str()) == 0)
    5610            0 :                   db_sscanf("1", data, &size, 0, key.type);
    5611              :                else
    5612            0 :                   db_sscanf("0", data, &size, 0, key.type);
    5613            0 :                db_set_data_index(hDB, hkey, data, key.item_size, index, key.type);
    5614            0 :             }
    5615              :          }
    5616              : 
    5617              :          /* redirect (so that 'reload' does not toggle again) */
    5618            0 :          redirect(r, path.c_str());
    5619            0 :          free(ctext);
    5620            0 :          return;
    5621            0 :       }
    5622              : 
    5623              :       /* HTTP header */
    5624            0 :       r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    5625            0 :       r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    5626            0 :       r->rsprintf("Content-Type: %s; charset=%s\r\n\r\n", content_type.c_str(), HTTP_ENCODING);
    5627              : 
    5628              :       /* interprete text, replace <odb> tags with ODB values */
    5629            0 :       p = ps = ctext;
    5630            0 :       n_var = 0;
    5631              :       do {
    5632              :          char format[256];
    5633            0 :          p = find_odb_tag(ps, keypath, format, &edit, type, pwd, tail);
    5634            0 :          if (p != NULL)
    5635            0 :             *p = 0;
    5636            0 :          r->rsputs(ps);
    5637              : 
    5638            0 :          if (p == NULL)
    5639            0 :             break;
    5640            0 :          ps = strchr(p + 1, '>') + 1;
    5641              : 
    5642            0 :          show_odb_tag(pp, r, path.c_str(), keypath, format, n_var, edit, type, pwd, tail);
    5643            0 :          n_var++;
    5644              : 
    5645            0 :       } while (p != NULL);
    5646              : 
    5647            0 :       if (equal_ustring(pp->getparam("cmd"), "Set") || pp->isparam("cbi")) {
    5648              :          /* redirect (so that 'reload' does not change value) */
    5649            0 :          r->reset();
    5650            0 :          redirect(r, path.c_str());
    5651              :       }
    5652              : 
    5653            0 :       free(ctext);
    5654            0 :       ctext = NULL;
    5655            0 :    } else {
    5656            0 :       std::string str = msprintf("Invalid custom page: Page \"%s\" not found in ODB", path.c_str());
    5657            0 :       show_error_404(r, str.c_str());
    5658            0 :       return;
    5659            0 :    }
    5660            0 : }
    5661              : 
    5662              : /*------------------------------------------------------------------*/
    5663              : 
    5664            0 : static void show_cnaf_page(Param* p, Return* rr)
    5665              : {
    5666              :    char str[256];
    5667              :    int c, n, a, f, d, q, x, r, ia, id, w;
    5668              :    int i, size, status;
    5669              :    HNDLE hDB, hrootkey, hsubkey, hkey;
    5670              : 
    5671              :    static char client_name[NAME_LENGTH];
    5672              :    static HNDLE hconn = 0;
    5673              : 
    5674            0 :    cm_get_experiment_database(&hDB, NULL);
    5675              : 
    5676              :    /* find FCNA server if not specified */
    5677            0 :    if (hconn == 0) {
    5678              :       /* find client which exports FCNA function */
    5679            0 :       status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
    5680            0 :       if (status == DB_SUCCESS) {
    5681            0 :          for (i = 0;; i++) {
    5682            0 :             status = db_enum_key(hDB, hrootkey, i, &hsubkey);
    5683            0 :             if (status == DB_NO_MORE_SUBKEYS)
    5684            0 :                break;
    5685              : 
    5686            0 :             sprintf(str, "RPC/%d", RPC_CNAF16);
    5687            0 :             status = db_find_key(hDB, hsubkey, str, &hkey);
    5688            0 :             if (status == DB_SUCCESS) {
    5689            0 :                size = sizeof(client_name);
    5690            0 :                db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, TRUE);
    5691            0 :                break;
    5692              :             }
    5693              :          }
    5694              :       }
    5695              : 
    5696            0 :       if (client_name[0]) {
    5697            0 :          status = cm_connect_client(client_name, &hconn);
    5698            0 :          if (status != RPC_SUCCESS)
    5699            0 :             hconn = 0;
    5700              :       }
    5701              :    }
    5702              : 
    5703              :    /* header */
    5704            0 :    rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
    5705            0 :    rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    5706            0 :    rr->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
    5707              : 
    5708            0 :    rr->rsprintf("<html><head>\n");
    5709            0 :    rr->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
    5710            0 :    rr->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
    5711            0 :    rr->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
    5712            0 :    rr->rsprintf("<title>MIDAS CAMAC interface</title></head>\n");
    5713            0 :    rr->rsprintf("<body><form method=\"GET\" action=\"CNAF\">\n\n");
    5714              : 
    5715              :    /* title row */
    5716              : 
    5717            0 :    size = sizeof(str);
    5718            0 :    str[0] = 0;
    5719            0 :    db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
    5720              : 
    5721            0 :    rr->rsprintf("<table border=3 cellpadding=1>\n");
    5722            0 :    rr->rsprintf("<tr><th colspan=3>MIDAS experiment \"%s\"", str);
    5723              : 
    5724            0 :    if (client_name[0] == 0)
    5725            0 :       rr->rsprintf("<th colspan=3 class=\"redLight\">No CAMAC server running</tr>\n");
    5726            0 :    else if (hconn == 0)
    5727            0 :       rr->rsprintf("<th colspan=3 class=\"redLight\">Cannot connect to %s</tr>\n", client_name);
    5728              :    else
    5729            0 :       rr->rsprintf("<th colspan=3>CAMAC server: %s</tr>\n", client_name);
    5730              : 
    5731              :    /* default values */
    5732            0 :    c = n = 1;
    5733            0 :    a = f = d = q = x = 0;
    5734            0 :    r = 1;
    5735            0 :    ia = id = w = 0;
    5736              : 
    5737              :    /*---- menu buttons ----*/
    5738              : 
    5739            0 :    rr->rsprintf("<tr><td colspan=3>\n");
    5740            0 :    rr->rsprintf("<input type=submit name=cmd value=Execute>\n");
    5741              : 
    5742            0 :    rr->rsprintf("<td colspan=3>\n");
    5743            0 :    rr->rsprintf("<input type=submit name=cmd value=ODB>\n");
    5744            0 :    rr->rsprintf("<input type=submit name=cmd value=Status>\n");
    5745            0 :    rr->rsprintf("<input type=submit name=cmd value=Help>\n");
    5746            0 :    rr->rsprintf("</tr>\n\n");
    5747              : 
    5748              :    /* header */
    5749            0 :    rr->rsprintf("<tr><th>N");
    5750            0 :    rr->rsprintf("<th>A");
    5751            0 :    rr->rsprintf("<th>F");
    5752            0 :    rr->rsprintf("<th colspan=3>Data");
    5753              : 
    5754              :    /* execute commands */
    5755            0 :    size = sizeof(d);
    5756              : 
    5757            0 :    const char* cmd = p->getparam("cmd");
    5758            0 :    if (equal_ustring(cmd, "C cycle")) {
    5759            0 :       rpc_client_call(hconn, RPC_CNAF16, CNAF_CRATE_CLEAR, 0, 0, 0, 0, 0, &d, &size, &x,
    5760              :                       &q);
    5761              : 
    5762            0 :       rr->rsprintf("<tr><td colspan=6 class=\"greenLight\">C cycle executed sucessfully</tr>\n");
    5763            0 :    } else if (equal_ustring(cmd, "Z cycle")) {
    5764            0 :       rpc_client_call(hconn, RPC_CNAF16, CNAF_CRATE_ZINIT, 0, 0, 0, 0, 0, &d, &size, &x,
    5765              :                       &q);
    5766              : 
    5767            0 :       rr->rsprintf("<tr><td colspan=6 class=\"greenLight\">Z cycle executed sucessfully</tr>\n");
    5768            0 :    } else if (equal_ustring(cmd, "Clear inhibit")) {
    5769            0 :       rpc_client_call(hconn, RPC_CNAF16, CNAF_INHIBIT_CLEAR, 0, 0, 0, 0, 0, &d, &size, &x,
    5770              :                       &q);
    5771              : 
    5772              :       rr->rsprintf
    5773            0 :           ("<tr><td colspan=6 class=\"greenLight\">Clear inhibit executed sucessfully</tr>\n");
    5774            0 :    } else if (equal_ustring(cmd, "Set inhibit")) {
    5775            0 :       rpc_client_call(hconn, RPC_CNAF16, CNAF_INHIBIT_SET, 0, 0, 0, 0, 0, &d, &size, &x,
    5776              :                       &q);
    5777              : 
    5778              :       rr->rsprintf
    5779            0 :           ("<tr><td colspan=6 class=\"greenLight\">Set inhibit executed sucessfully</tr>\n");
    5780            0 :    } else if (equal_ustring(cmd, "Execute")) {
    5781            0 :       c = atoi(p->getparam("C"));
    5782            0 :       n = atoi(p->getparam("N"));
    5783            0 :       a = atoi(p->getparam("A"));
    5784            0 :       f = atoi(p->getparam("F"));
    5785            0 :       r = atoi(p->getparam("R"));
    5786            0 :       w = atoi(p->getparam("W"));
    5787            0 :       id = atoi(p->getparam("ID"));
    5788            0 :       ia = atoi(p->getparam("IA"));
    5789              : 
    5790            0 :       const char* pd = p->getparam("D");
    5791            0 :       if (strncmp(pd, "0x", 2) == 0)
    5792            0 :          sscanf(pd + 2, "%x", &d);
    5793              :       else
    5794            0 :          d = atoi(p->getparam("D"));
    5795              : 
    5796              :       /* limit repeat range */
    5797            0 :       if (r == 0)
    5798            0 :          r = 1;
    5799            0 :       if (r > 100)
    5800            0 :          r = 100;
    5801            0 :       if (w > 1000)
    5802            0 :          w = 1000;
    5803              : 
    5804            0 :       for (i = 0; i < r; i++) {
    5805            0 :          status = SUCCESS;
    5806              : 
    5807            0 :          if (hconn) {
    5808            0 :             size = sizeof(d);
    5809              :             status =
    5810            0 :                 rpc_client_call(hconn, RPC_CNAF24, CNAF, 0, c, n, a, f, &d, &size, &x,
    5811              :                                 &q);
    5812              : 
    5813            0 :             if (status == RPC_NET_ERROR) {
    5814              :                /* try to reconnect */
    5815              :                //cm_disconnect_client(hconn, FALSE);
    5816            0 :                status = cm_connect_client(client_name, &hconn);
    5817            0 :                if (status != RPC_SUCCESS) {
    5818            0 :                   hconn = 0;
    5819            0 :                   client_name[0] = 0;
    5820              :                }
    5821              : 
    5822            0 :                if (hconn) {
    5823            0 :                   status = rpc_client_call(hconn, RPC_CNAF24, CNAF, 0, c, n, a, f, &d, &size, &x, &q);
    5824              :                }
    5825              :             }
    5826              :          }
    5827              : 
    5828            0 :          if (status != SUCCESS) {
    5829              :             rr->rsprintf
    5830            0 :                 ("<tr><td colspan=6 class=\"redLight\">Error executing function, code = %d</tr>",
    5831              :                  status);
    5832              :          } else {
    5833            0 :             rr->rsprintf("<tr align=center><td>%d", n);
    5834            0 :             rr->rsprintf("<td>%d", a);
    5835            0 :             rr->rsprintf("<td>%d", f);
    5836            0 :             rr->rsprintf("<td colspan=3>%d / 0x%04X  Q%d X%d", d, d, q, x);
    5837              :          }
    5838              : 
    5839            0 :          d += id;
    5840            0 :          a += ia;
    5841              : 
    5842            0 :          if (w > 0)
    5843            0 :             ss_sleep(w);
    5844              :       }
    5845              :    }
    5846              : 
    5847              :    /* input fields */
    5848              :    rr->rsprintf
    5849            0 :        ("<tr align=center><td><input type=text size=3 name=N value=%d>\n",
    5850              :         n);
    5851            0 :    rr->rsprintf("<td><input type=text size=3 name=A value=%d>\n", a);
    5852            0 :    rr->rsprintf("<td><input type=text size=3 name=F value=%d>\n", f);
    5853              :    rr->rsprintf
    5854            0 :        ("<td colspan=3><input type=text size=8 name=D value=%d></tr>\n",
    5855              :         d);
    5856              : 
    5857              :    /* control fields */
    5858            0 :    rr->rsprintf("<tr><td colspan=2>Repeat");
    5859            0 :    rr->rsprintf("<td><input type=text size=3 name=R value=%d>\n", r);
    5860              : 
    5861              :    rr->rsprintf
    5862            0 :        ("<td align=center colspan=3><input type=submit name=cmd value=\"C cycle\">\n");
    5863            0 :    rr->rsprintf("<input type=submit name=cmd value=\"Z cycle\">\n");
    5864              : 
    5865            0 :    rr->rsprintf("<tr><td colspan=2>Repeat delay [ms]");
    5866            0 :    rr->rsprintf("<td><input type=text size=3 name=W value=%d>\n", w);
    5867              : 
    5868              :    rr->rsprintf
    5869            0 :        ("<td align=center colspan=3><input type=submit name=cmd value=\"Set inhibit\">\n");
    5870            0 :    rr->rsprintf("<input type=submit name=cmd value=\"Clear inhibit\">\n");
    5871              : 
    5872            0 :    rr->rsprintf("<tr><td colspan=2>Data increment");
    5873            0 :    rr->rsprintf("<td><input type=text size=3 name=ID value=%d>\n", id);
    5874              : 
    5875              :    rr->rsprintf
    5876            0 :        ("<td colspan=3 align=center>Branch <input type=text size=3 name=B value=0>\n");
    5877              : 
    5878            0 :    rr->rsprintf("<tr><td colspan=2>A increment");
    5879            0 :    rr->rsprintf("<td><input type=text size=3 name=IA value=%d>\n", ia);
    5880              : 
    5881              :    rr->rsprintf
    5882            0 :        ("<td colspan=3 align=center>Crate <input type=text size=3 name=C value=%d>\n",
    5883              :         c);
    5884              : 
    5885            0 :    rr->rsprintf("</table></body>\r\n");
    5886            0 : }
    5887              : 
    5888              : /*------------------------------------------------------------------*/
    5889              : 
    5890              : #ifdef HAVE_MSCB
    5891              : 
    5892              : typedef struct {
    5893              :    signed char id;
    5894              :    char name[32];
    5895              : } NAME_TABLE;
    5896              : 
    5897              : static const NAME_TABLE prefix_table[] = {
    5898              :    {PRFX_PICO, "pico",},
    5899              :    {PRFX_NANO, "nano",},
    5900              :    {PRFX_MICRO, "micro",},
    5901              :    {PRFX_MILLI, "milli",},
    5902              :    {PRFX_NONE, "",},
    5903              :    {PRFX_KILO, "kilo",},
    5904              :    {PRFX_MEGA, "mega",},
    5905              :    {PRFX_GIGA, "giga",},
    5906              :    {PRFX_TERA, "tera",},
    5907              :    {99}
    5908              : };
    5909              : 
    5910              : static const NAME_TABLE unit_table[] = {
    5911              : 
    5912              :    {UNIT_METER, "meter",},
    5913              :    {UNIT_GRAM, "gram",},
    5914              :    {UNIT_SECOND, "second",},
    5915              :    {UNIT_MINUTE, "minute",},
    5916              :    {UNIT_HOUR, "hour",},
    5917              :    {UNIT_AMPERE, "ampere",},
    5918              :    {UNIT_KELVIN, "kelvin",},
    5919              :    {UNIT_CELSIUS, "deg. celsius",},
    5920              :    {UNIT_FARENHEIT, "deg. farenheit",},
    5921              : 
    5922              :    {UNIT_HERTZ, "hertz",},
    5923              :    {UNIT_PASCAL, "pascal",},
    5924              :    {UNIT_BAR, "bar",},
    5925              :    {UNIT_WATT, "watt",},
    5926              :    {UNIT_VOLT, "volt",},
    5927              :    {UNIT_OHM, "ohm",},
    5928              :    {UNIT_TESLA, "tesls",},
    5929              :    {UNIT_LITERPERSEC, "liter/sec",},
    5930              :    {UNIT_RPM, "RPM",},
    5931              :    {UNIT_FARAD, "farad",},
    5932              : 
    5933              :    {UNIT_BOOLEAN, "boolean",},
    5934              :    {UNIT_BYTE, "byte",},
    5935              :    {UNIT_WORD, "word",},
    5936              :    {UNIT_DWORD, "dword",},
    5937              :    {UNIT_ASCII, "ascii",},
    5938              :    {UNIT_STRING, "string",},
    5939              :    {UNIT_BAUD, "baud",},
    5940              : 
    5941              :    {UNIT_PERCENT, "percent",},
    5942              :    {UNIT_PPM, "RPM",},
    5943              :    {UNIT_COUNT, "counts",},
    5944              :    {UNIT_FACTOR, "factor",},
    5945              :    {0}
    5946              : };
    5947              : 
    5948              : /*------------------------------------------------------------------*/
    5949              : 
    5950            0 : void print_mscb_var(char *value, char *evalue, char *unit, MSCB_INFO_VAR *info_chn, void *pdata)
    5951              : {
    5952              :    char str[80];
    5953              :    signed short sdata;
    5954              :    unsigned short usdata;
    5955              :    signed int idata;
    5956              :    unsigned int uidata;
    5957              :    float fdata;
    5958              :    int i;
    5959              : 
    5960            0 :    value[0] = 0;
    5961            0 :    evalue[0] = 0;
    5962              : 
    5963            0 :    if (info_chn->unit == UNIT_STRING) {
    5964            0 :       memset(str, 0, sizeof(str));
    5965            0 :       strncpy(str, (char *)pdata, info_chn->width);
    5966            0 :       for (i = 0; i < (int) strlen(str); i++)
    5967            0 :          switch (str[i]) {
    5968            0 :          case 1:
    5969            0 :             strcat(value, "\\001");
    5970            0 :             break;
    5971            0 :          case 2:
    5972            0 :             strcat(value, "\\002");
    5973            0 :             break;
    5974            0 :          case 9:
    5975            0 :             strcat(value, "\\t");
    5976            0 :             break;
    5977            0 :          case 10:
    5978            0 :             strcat(value, "\\n");
    5979            0 :             break;
    5980            0 :          case 13:
    5981            0 :             strcat(value, "\\r");
    5982            0 :             break;
    5983            0 :          default:
    5984            0 :             value[strlen(value) + 1] = 0;
    5985            0 :             value[strlen(value)] = str[i];
    5986            0 :             break;
    5987              :          }
    5988            0 :       mstrlcpy(evalue, value, 256);
    5989              :    } else {
    5990            0 :       switch (info_chn->width) {
    5991            0 :       case 0:
    5992            0 :          strcpy(value, "0");
    5993            0 :          strcpy(evalue, "0");
    5994            0 :          break;
    5995              : 
    5996            0 :       case 1:
    5997            0 :          if (info_chn->flags & MSCBF_SIGNED) {
    5998            0 :             sprintf(value, "%d (0x%02X/", *((signed char *)pdata), *((signed char *)pdata));
    5999            0 :             sprintf(evalue, "%d", *((signed char *)pdata));
    6000              :          } else {
    6001            0 :             sprintf(value, "%u (0x%02X/", *((unsigned char *)pdata), *((unsigned char *)pdata));
    6002            0 :             sprintf(evalue, "%u", *((unsigned char *)pdata));
    6003              :          }
    6004              : 
    6005            0 :          for (i = 0; i < 8; i++)
    6006            0 :             if (*((unsigned char *)pdata) & (0x80 >> i))
    6007            0 :                sprintf(value + strlen(value), "1");
    6008              :             else
    6009            0 :                sprintf(value + strlen(value), "0");
    6010            0 :          sprintf(value + strlen(value), ")");
    6011            0 :          break;
    6012              : 
    6013            0 :       case 2:
    6014            0 :          if (info_chn->flags & MSCBF_SIGNED) {
    6015            0 :             sdata = *((signed short *)pdata);
    6016            0 :             WORD_SWAP(&sdata);
    6017            0 :             sprintf(value, "%d (0x%04X)", sdata, sdata);
    6018            0 :             sprintf(evalue, "%d", sdata);
    6019              :          } else {
    6020            0 :             usdata = *((unsigned short *)pdata);
    6021            0 :             WORD_SWAP(&usdata);
    6022            0 :             sprintf(value, "%u (0x%04X)", usdata, usdata);
    6023            0 :             sprintf(evalue, "%u", usdata);
    6024              :          }
    6025            0 :          break;
    6026              : 
    6027            0 :       case 4:
    6028            0 :          if (info_chn->flags & MSCBF_FLOAT) {
    6029            0 :             fdata = *((float *)pdata);
    6030            0 :             DWORD_SWAP(&fdata);
    6031            0 :             sprintf(value, "%1.6lg", fdata);
    6032            0 :             sprintf(evalue, "%1.6lg", fdata);
    6033              :          } else {
    6034            0 :             if (info_chn->flags & MSCBF_SIGNED) {
    6035            0 :                idata = *((signed int *)pdata);
    6036            0 :                DWORD_SWAP(&idata);
    6037            0 :                sprintf(value, "%d (0x%08X)", idata, idata);
    6038            0 :                sprintf(evalue, "%d", idata);
    6039              :             } else {
    6040            0 :                uidata = *((unsigned int *)pdata);
    6041            0 :                DWORD_SWAP(&uidata);
    6042            0 :                sprintf(value, "%u (0x%08X)", uidata, uidata);
    6043            0 :                sprintf(evalue, "%u", uidata);
    6044              :             }
    6045              :          }
    6046            0 :          break;
    6047              :       }
    6048              :    }
    6049              : 
    6050              :    /* evaluate prefix */
    6051            0 :    unit[0] = 0;
    6052            0 :    if (info_chn->prefix) {
    6053            0 :       for (i = 0; prefix_table[i].id != 99; i++)
    6054            0 :         if ((unsigned char)prefix_table[i].id == info_chn->prefix)
    6055            0 :             break;
    6056            0 :       if (prefix_table[i].id)
    6057            0 :          strcpy(unit, prefix_table[i].name);
    6058              :    }
    6059              : 
    6060              :    /* evaluate unit */
    6061            0 :    if (info_chn->unit && info_chn->unit != UNIT_STRING) {
    6062            0 :       for (i = 0; unit_table[i].id; i++)
    6063            0 :         if ((unsigned char)unit_table[i].id == info_chn->unit)
    6064            0 :             break;
    6065            0 :       if (unit_table[i].id)
    6066            0 :          strcat(unit, unit_table[i].name);
    6067              :    }
    6068            0 : }
    6069              : 
    6070            0 : static int cmp_int(const void *a, const void *b)
    6071              : {
    6072            0 :   return *((int *)a) > *((int *)b);
    6073              : }
    6074              : 
    6075              : /*------------------------------------------------------------------*/
    6076              : 
    6077            0 : void create_mscb_tree()
    6078              : {
    6079              :    HNDLE hDB, hKeySubm, hKeyEq, hKeyAdr, hKey, hKeyDev;
    6080              :    KEY key;
    6081              :    int i, j, k, l, size, address[1000], dev_badr[1000], dev_adr[1000], dev_chn[1000],
    6082              :       n_address, n_dev_adr;
    6083              :    char mscb_dev[256], mscb_pwd[32], eq_name[32];
    6084              : 
    6085            0 :    cm_get_experiment_database(&hDB, NULL);
    6086              : 
    6087            0 :    db_create_key(hDB, 0, "MSCB/Submaster", TID_KEY);
    6088            0 :    db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
    6089            0 :    assert(hKeySubm);
    6090              : 
    6091              :    /*---- go through equipment list ----*/
    6092            0 :    db_find_key(hDB, 0, "Equipment", &hKeyEq);
    6093            0 :    if (hKeyEq) {
    6094            0 :       for (i=0 ; ; i++) {
    6095            0 :          db_enum_key(hDB, hKeyEq, i, &hKey);
    6096            0 :          if (!hKey)
    6097            0 :             break;
    6098            0 :          db_get_key(hDB, hKey, &key);
    6099            0 :          strcpy(eq_name, key.name);
    6100            0 :          db_find_key(hDB, hKey, "Settings/Devices", &hKeyDev);
    6101            0 :          if (hKeyDev) {
    6102            0 :             for (j=0 ;; j++) {
    6103            0 :                db_enum_key(hDB, hKeyDev, j, &hKey);
    6104            0 :                if (!hKey)
    6105            0 :                   break;
    6106              : 
    6107            0 :                if (db_find_key(hDB, hKey, "MSCB Address", &hKeyAdr) == DB_SUCCESS) {
    6108              :                   /* mscbdev type of device */
    6109            0 :                   size = sizeof(mscb_dev);
    6110            0 :                   if (db_get_value(hDB, hKey, "Device", mscb_dev, &size, TID_STRING, FALSE) != DB_SUCCESS)
    6111            0 :                      continue;
    6112            0 :                   size = sizeof(mscb_pwd);
    6113            0 :                   if (db_get_value(hDB, hKey, "Pwd", mscb_pwd, &size, TID_STRING, FALSE) != DB_SUCCESS)
    6114            0 :                      continue;
    6115              : 
    6116            0 :                   size = sizeof(dev_adr);
    6117            0 :                   db_get_data(hDB, hKeyAdr, dev_adr, &size, TID_INT);
    6118            0 :                   n_dev_adr = size / sizeof(int);
    6119            0 :                } else if (db_find_key(hDB, hKey, "Block Address", &hKeyAdr) == DB_SUCCESS) {
    6120              :                   /* mscbhvr type of device */
    6121            0 :                   size = sizeof(mscb_dev);
    6122            0 :                   if (db_get_value(hDB, hKey, "MSCB Device", mscb_dev, &size, TID_STRING, FALSE) != DB_SUCCESS)
    6123            0 :                      continue;
    6124            0 :                   size = sizeof(mscb_pwd);
    6125            0 :                   if (db_get_value(hDB, hKey, "MSCB Pwd", mscb_pwd, &size, TID_STRING, FALSE) != DB_SUCCESS)
    6126            0 :                      continue;
    6127              : 
    6128            0 :                   n_dev_adr = 0;
    6129            0 :                   size = sizeof(dev_badr);
    6130            0 :                   db_get_data(hDB, hKeyAdr, dev_badr, &size, TID_INT);
    6131            0 :                   size = sizeof(dev_chn);
    6132            0 :                   if (db_get_value(hDB, hKey, "Block Channels", dev_chn, &size, TID_INT, FALSE) == DB_SUCCESS) {
    6133            0 :                      for (k=0 ; k<size/(int)sizeof(int) && n_dev_adr < (int)(sizeof(dev_adr)/sizeof(int)) ; k++) {
    6134            0 :                         for (l=0 ; l<dev_chn[k] ; l++)
    6135            0 :                            dev_adr[n_dev_adr++] = dev_badr[k]+l;
    6136              :                      }
    6137              :                   }
    6138              :                } else
    6139            0 :                   continue;
    6140              : 
    6141              :                /* create or open submaster entry */
    6142            0 :                db_find_key(hDB, hKeySubm, mscb_dev, &hKey);
    6143            0 :                if (!hKey) {
    6144            0 :                   db_create_key(hDB, hKeySubm, mscb_dev, TID_KEY);
    6145            0 :                   db_find_key(hDB, hKeySubm, mscb_dev, &hKey);
    6146            0 :                   assert(hKey);
    6147              :                }
    6148              : 
    6149              :                /* get old address list */
    6150            0 :                size = sizeof(address);
    6151            0 :                if (db_get_value(hDB, hKey, "Address", address, &size, TID_INT, FALSE) == DB_SUCCESS)
    6152            0 :                   n_address = size / sizeof(int);
    6153              :                else
    6154            0 :                   n_address = 0;
    6155              : 
    6156              :                /* merge with new address list */
    6157            0 :                for (k=0 ; k<n_dev_adr ; k++) {
    6158            0 :                   for (l=0 ; l<n_address ; l++)
    6159            0 :                      if (address[l] == dev_adr[k])
    6160            0 :                         break;
    6161              : 
    6162            0 :                   if (l == n_address)
    6163            0 :                      address[n_address++] = dev_adr[k];
    6164              :                }
    6165              : 
    6166              :                /* sort address list */
    6167            0 :                qsort(address, n_address, sizeof(int), cmp_int);
    6168              : 
    6169              :                /* store new address list */
    6170            0 :                db_set_value(hDB, hKey, "Pwd", mscb_pwd, 32, 1, TID_STRING);
    6171            0 :                db_set_value(hDB, hKey, "Comment", eq_name, 32, 1, TID_STRING);
    6172            0 :                db_set_value(hDB, hKey, "Address", address, n_address*sizeof(int), n_address, TID_INT);
    6173              :             }
    6174              :          }
    6175              :       }
    6176              :    }
    6177            0 : }
    6178              : 
    6179              : /*------------------------------------------------------------------*/
    6180              : 
    6181            0 : void show_mscb_page(Param* p, Return* r, int refresh)
    6182              : {
    6183              :    int i, j, n, ind, fi, fd, status, size, n_addr, cur_node, adr, show_hidden;
    6184              :    unsigned int uptime;
    6185              :    BOOL comment_created;
    6186              :    float fvalue;
    6187              :    char *pd;
    6188              :    char dbuf[256], evalue[256], unit[256], cur_subm_name[256];
    6189              :    HNDLE hDB, hKeySubm, hKeyCurSubm, hKey, hKeyAddr, hKeyComm;
    6190              :    KEY key;
    6191              :    MSCB_INFO info;
    6192              :    MSCB_INFO_VAR info_var;
    6193              :    int ping_addr[0x10000];
    6194              : 
    6195            0 :    cm_get_experiment_database(&hDB, NULL);
    6196              : 
    6197            0 :    status = db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
    6198            0 :    if (!hKeySubm)
    6199            0 :       create_mscb_tree();
    6200              : 
    6201            0 :    mstrlcpy(cur_subm_name, p->getparam("subm"), sizeof(cur_subm_name));
    6202            0 :    if (cur_subm_name[0] == 0) {
    6203            0 :       db_enum_key(hDB, hKeySubm, 0, &hKeyCurSubm);
    6204            0 :       if (!hKeyCurSubm) {
    6205              :          char errorstr[256];
    6206            0 :          sprintf(errorstr, "No submaster defined under /MSCB/Submaster");
    6207            0 :          show_error(r, errorstr);
    6208            0 :          return;
    6209              :       }
    6210            0 :       db_get_key(hDB, hKeyCurSubm, &key);
    6211            0 :       strcpy(cur_subm_name, key.name);
    6212              :    } else
    6213            0 :       db_find_key(hDB, hKeySubm, cur_subm_name, &hKeyCurSubm);
    6214              : 
    6215            0 :    if (p->isparam("node"))
    6216            0 :       cur_node = atoi(p->getparam("node"));
    6217              :    else
    6218            0 :       cur_node = -1;
    6219              : 
    6220              :    /* perform MSCB rescan */
    6221            0 :    if (p->isparam("mcmd") && equal_ustring(p->getparam("mcmd"), "Rescan") && p->isparam("subm")) {
    6222              :       /* create Pwd and Comment if not there */
    6223              :       char tmp[32];
    6224            0 :       size = 32;
    6225            0 :       tmp[0] = 0;
    6226            0 :       db_get_value(hDB, hKeyCurSubm, "Pwd", (void *)tmp, &size, TID_STRING, true);
    6227            0 :       tmp[0] = 0;
    6228            0 :       db_get_value(hDB, hKeyCurSubm, "Comment", (void *)tmp, &size, TID_STRING, true);
    6229              : 
    6230            0 :       db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
    6231            0 :       std::vector<int> addr;
    6232            0 :       std::vector<char> node_comment;
    6233            0 :       if (hKeyAddr) {
    6234              :          /* get current address array */
    6235            0 :          db_get_key(hDB, hKeyAddr, &key);
    6236            0 :          n_addr = key.num_values;
    6237            0 :          addr.resize(n_addr);
    6238            0 :          size = sizeof(int)*n_addr;
    6239            0 :          db_get_data(hDB, hKeyAddr, addr.data(), &size, TID_INT);
    6240              :       } else {
    6241              :          /* create new address array */
    6242            0 :          db_create_key(hDB, hKeyCurSubm, "Address", TID_INT);
    6243            0 :          db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
    6244            0 :          n_addr = 0;
    6245            0 :          addr.resize(1);
    6246              :       }
    6247              : 
    6248            0 :       comment_created = FALSE;
    6249            0 :       db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
    6250            0 :       if (hKeyComm) {
    6251              :          /* get current node comments */
    6252            0 :          db_get_key(hDB, hKeyComm, &key);
    6253            0 :          node_comment.resize(32*key.num_values);
    6254            0 :          size = 32*key.num_values;
    6255            0 :          db_get_data(hDB, hKeyComm, node_comment.data(), &size, TID_STRING);
    6256              :       } else {
    6257              :          /* create new comment array */
    6258            0 :          db_create_key(hDB, hKeyCurSubm, "Node comment", TID_STRING);
    6259            0 :          db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
    6260            0 :          node_comment.resize(32);
    6261            0 :          comment_created = TRUE;
    6262              :       }
    6263              : 
    6264            0 :       fd = mscb_init(cur_subm_name, 0, "", FALSE);
    6265            0 :       if (fd >= 0) {
    6266              :          /* fill table of possible addresses */
    6267            0 :          for (i=0 ; i<0x10000 ; i++)
    6268            0 :             ping_addr[i] = 0;
    6269            0 :          for (i=0 ; i<1000 ; i++)        // 0..999
    6270            0 :             ping_addr[i] = 1;
    6271            0 :          for (i=0 ; i<0x10000 ; i+=100)  // 100, 200, ...
    6272            0 :             ping_addr[i] = 1;
    6273            0 :          for (i=0 ; i<0x10000 ; i+= 0x100)
    6274            0 :             ping_addr[i] = 1;            // 256, 512, ...
    6275            0 :          for (i=0xFF00 ; i<0x10000 ; i++)
    6276            0 :             ping_addr[i] = 1;            // 0xFF00-0xFFFF
    6277              : 
    6278            0 :          for (ind = n = 0; ind < 0x10000; ind++) {
    6279            0 :             if (!ping_addr[ind])
    6280            0 :                continue;
    6281              : 
    6282            0 :             status = mscb_ping(fd, (unsigned short) ind, 1, 0);
    6283            0 :             if (status == MSCB_SUCCESS) {
    6284              : 
    6285              :                /* node found, search next 100 as well */
    6286            0 :                for (j=ind; j<ind+100 && j<0x10000 ; j++)
    6287            0 :                   if (j >= 0)
    6288            0 :                      ping_addr[j] = 1;
    6289              : 
    6290            0 :                status = mscb_info(fd, (unsigned short) ind, &info);
    6291              : 
    6292            0 :                if (status == MSCB_SUCCESS) {
    6293              :                   /* check if node already in list */
    6294            0 :                   for (j=0 ; j<n_addr ; j++)
    6295            0 :                      if (addr[j] == ind)
    6296            0 :                         break;
    6297            0 :                   if (j == n_addr) {
    6298            0 :                      addr.resize(n_addr+1);
    6299            0 :                      addr[n_addr] = ind;
    6300            0 :                      node_comment.resize(32*(n_addr+1));
    6301              :                      /* use node name as default comment */
    6302            0 :                      strncpy(node_comment.data()+n_addr*32, info.node_name, 32);
    6303            0 :                      n_addr ++;
    6304            0 :                   } else if (comment_created) {
    6305            0 :                      node_comment.resize(32*n_addr);
    6306              :                      /* use node name as default comment */
    6307            0 :                      strncpy(node_comment.data()+j*32, info.node_name, 32);
    6308              :                   }
    6309              :                }
    6310              :             }
    6311              :          }
    6312              : 
    6313            0 :          db_set_data(hDB, hKeyAddr, addr.data(), n_addr*sizeof(int), n_addr, TID_INT);
    6314            0 :          db_set_data(hDB, hKeyComm, node_comment.data(), n_addr*32, n_addr, TID_STRING);
    6315              : 
    6316              :          char redirstr[512];
    6317            0 :          sprintf(redirstr, "?cmd=mscb&subm=%s", cur_subm_name);
    6318            0 :          redirect(r, redirstr);
    6319            0 :          return;
    6320              : 
    6321              :       } else {
    6322              :          char errorstr[512];
    6323            0 :          sprintf(errorstr, "Cannot talk to submaster \"%s\"", cur_subm_name);
    6324            0 :          show_error(r, errorstr);
    6325            0 :          return;
    6326              :       }
    6327            0 :    }
    6328              : 
    6329              :    /* write data to node */
    6330            0 :    if (p->isparam("subm") && p->isparam("node") &&
    6331            0 :        p->isparam("idx") && p->isparam("value")) {
    6332            0 :       i = atoi(p->getparam("idx"));
    6333              :       char value[256];
    6334            0 :       mstrlcpy(value, p->getparam("value"), sizeof(value));
    6335              : 
    6336            0 :       fd = mscb_init(cur_subm_name, 0, "", FALSE);
    6337            0 :       if (fd >= 0) {
    6338            0 :          status = mscb_info_variable(fd,
    6339              :                                      (unsigned short) cur_node, (unsigned char) i, &info_var);
    6340            0 :          if (status == MSCB_SUCCESS) {
    6341            0 :             if (info_var.unit == UNIT_STRING) {
    6342              :                char valstr[256];
    6343            0 :                mstrlcpy(valstr, value, sizeof(valstr));
    6344            0 :                if (strlen(valstr) > 0 && valstr[strlen(valstr) - 1] == '\n')
    6345            0 :                   valstr[strlen(valstr) - 1] = 0;
    6346              : 
    6347            0 :                status = mscb_write(fd, (unsigned short) cur_node,
    6348            0 :                                    (unsigned char) i, valstr, strlen(valstr) + 1);
    6349              :             } else {
    6350            0 :                if (info_var.flags & MSCBF_FLOAT) {
    6351            0 :                   fvalue = (float) atof(value);
    6352            0 :                   memcpy(&dbuf, &fvalue, sizeof(float));
    6353              :                } else {
    6354            0 :                   if (value[1] == 'x')
    6355            0 :                      sscanf(value + 2, "%x", (int *)&dbuf);
    6356              :                   else
    6357            0 :                      *((int *)dbuf) = atoi(value);
    6358              :                }
    6359              : 
    6360            0 :                status = mscb_write(fd, (unsigned short) cur_node,
    6361            0 :                                    (unsigned char) i, dbuf, info_var.width);
    6362              :             }
    6363              :          }
    6364              :       }
    6365              :       char redirstr[512];
    6366            0 :       sprintf(redirstr, "?cmd=mscb&subm=%s&node=%d", cur_subm_name, cur_node);
    6367            0 :       redirect(r, redirstr);
    6368            0 :       return;
    6369              :    }
    6370              : 
    6371            0 :    if (p->isparam("hidden"))
    6372            0 :       show_hidden = atoi(p->getparam("hidden"));
    6373              :    else
    6374            0 :       show_hidden = FALSE;
    6375              : 
    6376            0 :    show_header(r, "MSCB", "GET", "./", refresh);
    6377            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
    6378            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
    6379            0 :    show_navigation_bar(r, "MSCB");
    6380              : 
    6381              :    /* style sheet */
    6382            0 :    r->rsprintf("<style type=\"text/css\">\r\n");
    6383            0 :    r->rsprintf("select { width:150px; background-color:#FFFFE0; font-size:12px; }\r\n");
    6384            0 :    r->rsprintf(".subm {\r\n");
    6385            0 :    r->rsprintf("  background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
    6386            0 :    r->rsprintf("  padding:5px;\r\n");
    6387            0 :    r->rsprintf("  vertical-align:top;\r\n");
    6388            0 :    r->rsprintf("  font-size:16px;\r\n");
    6389            0 :    r->rsprintf("  border-right:1px solid #808080;\r\n");
    6390            0 :    r->rsprintf("}\r\n");
    6391            0 :    r->rsprintf(".node {\r\n");
    6392            0 :    r->rsprintf("  background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
    6393            0 :    r->rsprintf("  padding:5px;\r\n");
    6394            0 :    r->rsprintf("  vertical-align:top;\r\n");
    6395            0 :    r->rsprintf("  font-size:16px;\r\n");
    6396            0 :    r->rsprintf("  border-right:1px solid #808080;\r\n");
    6397            0 :    r->rsprintf("}\r\n");
    6398            0 :    r->rsprintf(".vars {\r\n");
    6399            0 :    r->rsprintf("  background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
    6400            0 :    r->rsprintf("  padding:5px;\r\n");
    6401            0 :    r->rsprintf("  vertical-align:top;\r\n");
    6402            0 :    r->rsprintf("  font-size:10px;\r\n");
    6403            0 :    r->rsprintf("}\r\n");
    6404            0 :    r->rsprintf(".v1 {\r\n");
    6405            0 :    r->rsprintf("  padding:3px;\r\n");
    6406            0 :    r->rsprintf("  font-weight:bold;\r\n");
    6407            0 :    r->rsprintf("  font-size:12px;\r\n");
    6408            0 :    r->rsprintf("}\r\n");
    6409            0 :    r->rsprintf(".v2 {\r\n");
    6410            0 :    r->rsprintf("  background-color:#F0F0F0;\r\n");
    6411            0 :    r->rsprintf("  padding:3px;\r\n");
    6412            0 :    r->rsprintf("  font-size:12px;\r\n");
    6413            0 :    r->rsprintf("  border:1px solid #808080;\r\n");
    6414            0 :    r->rsprintf("  border-right:1px solid #FFFFFF;\r\n");
    6415            0 :    r->rsprintf("  border-bottom:1px solid #FFFFFF;\r\n");
    6416            0 :    r->rsprintf("}\r\n");
    6417            0 :    r->rsprintf(".v3 {\r\n");
    6418            0 :    r->rsprintf("  padding:3px;\r\n");
    6419            0 :    r->rsprintf("  font-size:12px;\r\n");
    6420            0 :    r->rsprintf("}\r\n");
    6421            0 :    r->rsprintf("</style>\r\n\r\n");
    6422              : 
    6423              :    /* javascript */
    6424            0 :    r->rsprintf("<script type=\"text/javascript\">\r\n");
    6425            0 :    r->rsprintf("function mscb_edit(index, value)\r\n");
    6426            0 :    r->rsprintf("{\r\n");
    6427            0 :    r->rsprintf("   var new_value = prompt('Please enter new value', value);\r\n");
    6428            0 :    r->rsprintf("   if (new_value != undefined) {\r\n");
    6429            0 :    r->rsprintf("     window.location.search = '?cmd=mscb&subm=%s&node=%d&idx='+index+'&value='+new_value;\n", cur_subm_name, cur_node);
    6430            0 :    r->rsprintf("   }\n");
    6431            0 :    r->rsprintf("}\r\n");
    6432            0 :    r->rsprintf("</script>\r\n\r\n");
    6433              : 
    6434              :    /*---- main content ----*/
    6435              : 
    6436            0 :    r->rsprintf("<table class=\"mtable\">");  //main table
    6437            0 :    r->rsprintf("<tr><th class=\"mtableheader\" colspan=2>MSCB</th><tr>");
    6438              : 
    6439              :    /*---- menu buttons ----*/
    6440              : 
    6441            0 :    r->rsprintf("<tr><td colspan=2>\n");
    6442            0 :    r->rsprintf("<table width=100%%><tr>\n");
    6443            0 :    r->rsprintf("<td><input type=button value=Reload onclick=\"window.location.search='?cmd=mscb&subm=%s&node=%d&rnd=%d'\"></td>\n", cur_subm_name, cur_node, rand());
    6444              : 
    6445            0 :    r->rsprintf("<tr><td colspan=\"2\" cellpadding=\"0\" cellspacing=\"0\">\r\n");
    6446              : 
    6447            0 :    status = db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
    6448            0 :    if (status != DB_SUCCESS) {
    6449            0 :       r->rsprintf("<h1>No MSCB Submasters defined in ODB</h1>\r\n");
    6450            0 :       r->rsprintf("</td></tr>\r\n");
    6451            0 :       r->rsprintf("</table>\r\n"); //submaster table
    6452            0 :       r->rsprintf("</td></tr>\r\n");
    6453            0 :       r->rsprintf("</table>\r\n");  //main table
    6454            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
    6455            0 :       r->rsprintf("</form>\n");
    6456            0 :       r->rsprintf("</body></html>\r\n");
    6457            0 :       return;
    6458              :    }
    6459              : 
    6460            0 :    r->rsprintf("<table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\">");
    6461              : 
    6462              :    /*---- submaster list ----*/
    6463            0 :    r->rsprintf("<tr><td class=\"subm\">\r\n");
    6464            0 :    r->rsprintf("Submaster<hr>\r\n");
    6465              : 
    6466              :    /* count submasters */
    6467            0 :    for (i = 0;;i++) {
    6468            0 :       db_enum_key(hDB, hKeySubm, i, &hKey);
    6469            0 :       if (!hKey)
    6470            0 :          break;
    6471              :    }
    6472            0 :    if (i<2)
    6473            0 :       i = 2;
    6474              : 
    6475            0 :    r->rsprintf("<select name=\"subm\" id=\"subm\" size=%d ", i);
    6476            0 :    r->rsprintf("onChange=\"window.location.search='?cmd=mscb&subm='+document.getElementById('subm').value;\">\r\n");
    6477            0 :    hKeyCurSubm = 0;
    6478            0 :    for (i = 0;;i++) {
    6479            0 :       db_enum_key(hDB, hKeySubm, i, &hKey);
    6480            0 :       if (!hKey)
    6481            0 :          break;
    6482            0 :       db_get_key(hDB, hKey, &key);
    6483              :       char str[NAME_LENGTH+10+256];
    6484            0 :       mstrlcpy(str, key.name, sizeof(str));
    6485              :       char comment[256];
    6486            0 :       size = sizeof(comment);
    6487            0 :       if (db_get_value(hDB, hKey, "Comment", comment, &size, TID_STRING, FALSE) == DB_SUCCESS) {
    6488            0 :          mstrlcat(str, ": ", sizeof(str));
    6489            0 :          mstrlcat(str, comment, sizeof(str));
    6490              :       }
    6491              : 
    6492            0 :       if ((cur_subm_name[0] && equal_ustring(cur_subm_name, key.name)) ||
    6493            0 :           (cur_subm_name[0] == 0 && i == 0)) {
    6494            0 :          r->rsprintf("<option value=\"%s\" selected>%s</option>\r\n", key.name, str);
    6495            0 :          hKeyCurSubm = hKey;
    6496              :       } else
    6497            0 :          r->rsprintf("<option value=\"%s\">%s</option>\r\n", key.name, str);
    6498            0 :    }
    6499            0 :    r->rsprintf("</select>\r\n");
    6500              : 
    6501              :    /*---- node list ----*/
    6502            0 :    r->rsprintf("<td class=\"node\">\r\n");
    6503            0 :    r->rsprintf("Node ");
    6504              : 
    6505            0 :    r->rsprintf("<script type=\"text/javascript\">\n");
    6506            0 :    r->rsprintf("<!--\n");
    6507            0 :    r->rsprintf("function rescan()\n");
    6508            0 :    r->rsprintf("{\n");
    6509            0 :    r->rsprintf("   flag = confirm('Rescan can take up to one minute.');\n");
    6510            0 :    r->rsprintf("   if (flag == true)\n");
    6511            0 :    r->rsprintf("      window.location.href = '?cmd=mscb&mcmd=Rescan&subm=%s';\n", cur_subm_name);
    6512            0 :    r->rsprintf("}\n");
    6513            0 :    r->rsprintf("//-->\n");
    6514            0 :    r->rsprintf("</script>\n");
    6515              : 
    6516            0 :    r->rsprintf("<input type=button name=cmd value=\"Rescan\" onClick=\"rescan();\">");
    6517            0 :    r->rsprintf("<hr>\r\n");
    6518              : 
    6519            0 :    if (!hKeyCurSubm) {
    6520            0 :       r->rsprintf("No submaster found in ODB\r\n");
    6521            0 :       r->rsprintf("</td></tr>\r\n");
    6522            0 :       r->rsprintf("</table>\r\n");  //inner submaster table
    6523            0 :       r->rsprintf("</td></tr>\r\n");
    6524            0 :       r->rsprintf("</table>\r\n");  //submaster table
    6525            0 :       r->rsprintf("</td></tr>\r\n");
    6526            0 :       r->rsprintf("</table>\r\n"); //main table
    6527            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
    6528            0 :       r->rsprintf("</form>\n");
    6529            0 :       r->rsprintf("</body></html>\r\n");
    6530            0 :       return;
    6531              :    }
    6532              : 
    6533            0 :    db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
    6534            0 :    db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
    6535              : 
    6536            0 :    i = 10;
    6537            0 :    if (hKeyAddr) {
    6538            0 :       db_get_key(hDB, hKeyAddr, &key);
    6539            0 :       i = key.num_values;
    6540              : 
    6541            0 :       if (hKeyComm == 0) {
    6542            0 :          db_create_key(hDB, hKeyCurSubm, "Node comment", TID_STRING);
    6543            0 :          db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
    6544              :       }
    6545            0 :       db_get_key(hDB, hKeyComm, &key);
    6546            0 :       if (key.num_values < i) {
    6547            0 :          char str[32] = "";
    6548            0 :          for (int j=key.num_values ; j<i ; j++)
    6549            0 :             db_set_data_index(hDB, hKeyComm, str, 32, j, TID_STRING);
    6550              :       }
    6551              :    }
    6552            0 :    if (i < 2)
    6553            0 :       i = 2;
    6554              : 
    6555            0 :    r->rsprintf("<select name=\"node\" id=\"node\" size=%d ", i);
    6556            0 :    r->rsprintf("onChange=\"window.location.search='?cmd=mscb&subm=%s&node='+document.getElementById('node').value;\">\r\n", cur_subm_name);
    6557              : 
    6558            0 :    if (hKeyAddr) {
    6559            0 :       db_get_key(hDB, hKeyAddr, &key);
    6560            0 :       size = sizeof(adr);
    6561              : 
    6562              :       /* check if current node is in list */
    6563            0 :       for (i = 0; i<key.num_values ;i++) {
    6564            0 :          size = sizeof(adr);
    6565            0 :          db_get_data_index(hDB, hKeyAddr, &adr, &size, i, TID_INT);
    6566            0 :          if (adr == cur_node)
    6567            0 :             break;
    6568              :       }
    6569            0 :       if (i == key.num_values) // if not found, use first one in list
    6570            0 :          db_get_data_index(hDB, hKeyAddr, &cur_node, &size, 0, TID_INT);
    6571              : 
    6572            0 :       for (i = 0; i<key.num_values ;i++) {
    6573              :          char str[100+256];
    6574            0 :          size = sizeof(adr);
    6575            0 :          db_get_data_index(hDB, hKeyAddr, &adr, &size, i, TID_INT);
    6576            0 :          if (hKeyComm) {
    6577              :             char comment[256];
    6578            0 :             size = sizeof(comment);
    6579            0 :             db_get_data_index(hDB, hKeyComm, comment, &size, i, TID_STRING);
    6580            0 :             sprintf(str, "%d: %s", adr, comment);
    6581              :          } else {
    6582            0 :             sprintf(str, "%d", adr);
    6583              :          }
    6584            0 :          if (cur_node == 0 && i == 0)
    6585            0 :             cur_node = adr;
    6586            0 :          if (adr == cur_node)
    6587            0 :             r->rsprintf("<option selected>%s</option>\r\n", str);
    6588              :          else
    6589            0 :             r->rsprintf("<option>%s</option>\r\n", str);
    6590              :       }
    6591              :    }
    6592            0 :    r->rsprintf("</select>\r\n");
    6593              : 
    6594              :    /*---- node contents ----*/
    6595            0 :    r->rsprintf("<td class=\"vars\">\r\n");
    6596            0 :    r->rsprintf("<table>\r\n");
    6597            0 :    db_get_key(hDB, hKeyCurSubm, &key);
    6598            0 :    if (cur_node != -1)
    6599            0 :       r->rsprintf("<tr><td colspan=3 align=center><b>%s:%d</b>", key.name, cur_node);
    6600              :    else
    6601            0 :       r->rsprintf("<tr><td colspan=3 align=center><b>%s</b>", key.name);
    6602            0 :    r->rsprintf("<hr></td></tr>\r\n");
    6603              : 
    6604              :    char passwd[32];
    6605            0 :    passwd[0] = 0;
    6606            0 :    size = 32;
    6607            0 :    db_get_value(hDB, hKeyCurSubm, "Pwd", passwd, &size, TID_STRING, TRUE);
    6608              : 
    6609            0 :    fd = mscb_init(key.name, 0, passwd, FALSE);
    6610            0 :    if (fd < 0) {
    6611            0 :       if (fd == EMSCB_WRONG_PASSWORD)
    6612            0 :          r->rsprintf("<tr><td colspan=3><b>Invalid password</b></td>");
    6613              :       else
    6614            0 :          r->rsprintf("<tr><td colspan=3><b>Submaster does not respond</b></td>");
    6615            0 :       goto mscb_error;
    6616              :    }
    6617            0 :    mscb_set_eth_max_retry(fd, 3);
    6618            0 :    mscb_set_max_retry(1);
    6619              : 
    6620            0 :    status = mscb_ping(fd, cur_node, 0, 1);
    6621            0 :    if (status != MSCB_SUCCESS) {
    6622            0 :       r->rsprintf("<tr><td colspan=3><b>No response from node</b></td>");
    6623            0 :       goto mscb_error;
    6624              :    }
    6625            0 :    status = mscb_info(fd, (unsigned short) cur_node, &info);
    6626            0 :    if (status != MSCB_SUCCESS) {
    6627            0 :       r->rsprintf("<tr><td colspan=3><b>No response from node</b></td>");
    6628            0 :       goto mscb_error;
    6629              :    }
    6630              :    char tr16[17];
    6631            0 :    mstrlcpy(tr16, info.node_name, sizeof(tr16));
    6632            0 :    r->rsprintf("<tr><td class=\"v1\">Node name<td colspan=2 class=\"v2\">%s</tr>\n", tr16);
    6633            0 :    r->rsprintf("<tr><td class=\"v1\">GIT revision<td colspan=2 class=\"v2\">%d</tr>\n", info.revision);
    6634              : 
    6635            0 :    if (info.rtc[0] && info.rtc[0] != 0xFF) {
    6636            0 :       for (i=0 ; i<6 ; i++)
    6637            0 :          info.rtc[i] = (info.rtc[i] / 0x10) * 10 + info.rtc[i] % 0x10;
    6638            0 :       r->rsprintf("<tr><td class=\"v1\">Real Time Clock<td colspan=2 class=\"v2\">%02d-%02d-%02d %02d:%02d:%02d</td>\n",
    6639            0 :          info.rtc[0], info.rtc[1], info.rtc[2],
    6640            0 :          info.rtc[3], info.rtc[4], info.rtc[5]);
    6641              :    }
    6642              : 
    6643            0 :    status = mscb_uptime(fd, (unsigned short) cur_node, &uptime);
    6644            0 :     if (status == MSCB_SUCCESS)
    6645            0 :       r->rsprintf("<tr><td class=\"v1\">Uptime<td colspan=2 class=\"v2\">%dd %02dh %02dm %02ds</tr>\n",
    6646              :              uptime / (3600 * 24),
    6647            0 :              (uptime % (3600 * 24)) / 3600, (uptime % 3600) / 60,
    6648              :              (uptime % 60));
    6649              : 
    6650            0 :    r->rsprintf("<tr><td colspan=3><hr></td></tr>\r\n");
    6651              : 
    6652              :    /* check for hidden variables */
    6653            0 :    for (i=0 ; i < info.n_variables ; i++) {
    6654            0 :       mscb_info_variable(fd, cur_node, i, &info_var);
    6655            0 :       if (info_var.flags & MSCBF_HIDDEN)
    6656            0 :          break;
    6657              :    }
    6658            0 :    if (i < info.n_variables) {
    6659              :       char str[32];
    6660            0 :       strcpy(str, show_hidden ? " checked" : "");
    6661            0 :       r->rsprintf("<tr><td colspan=3><input type=checkbox%s name=\"hidden\" value=\"1\"", str);
    6662            0 :       r->rsprintf("onChange=\"window.location.search=?cmd=mscb&subm=%s&node=%d&hidden=1\">Display hidden variables<hr></td></tr>\r\n", cur_subm_name, cur_node);
    6663              :    }
    6664              : 
    6665              :    /* read variables in blocks of 100 bytes */
    6666            0 :    for (fi=0 ; fi < info.n_variables ; ) {
    6667            0 :       for (i=fi,size=0 ; i < info.n_variables && size < 100; i++) {
    6668            0 :          mscb_info_variable(fd, cur_node, i, &info_var);
    6669            0 :          size += info_var.width;
    6670              :       }
    6671              : 
    6672            0 :       size = sizeof(dbuf);
    6673            0 :       status = mscb_read_range(fd, cur_node, fi, i-1, dbuf, &size);
    6674            0 :       if (status != MSCB_SUCCESS) {
    6675            0 :          r->rsprintf("<tr><td colspan=3><b>Error reading data from node</b></td>");
    6676            0 :          goto mscb_error;
    6677              :       }
    6678            0 :       pd = dbuf;
    6679              : 
    6680            0 :       for (j=fi ; j<i ; j++) {
    6681            0 :          status = mscb_info_variable(fd, cur_node, j, &info_var);
    6682            0 :          if ((info_var.flags & MSCBF_HIDDEN) == 0 || show_hidden) {
    6683              :             char tr8[9];
    6684            0 :             mstrlcpy(tr8, info_var.name, sizeof(tr8));
    6685            0 :             r->rsprintf("<tr><td class=\"v1\">%s</td>\r\n", tr8);
    6686            0 :             r->rsprintf("<td class=\"v2\">\r\n");
    6687              :             char value[256];
    6688            0 :             print_mscb_var(value, evalue, unit, &info_var, pd);
    6689            0 :             r->rsprintf("<a href=\"#\" onClick=\"mscb_edit(%d,'%s')\">%s</a>",
    6690              :                j, evalue, value);
    6691            0 :             r->rsprintf("</td><td class=\"v3\">%s</td>", unit);
    6692            0 :             r->rsprintf("</tr>\r\n");
    6693              :          }
    6694            0 :          pd += info_var.width;
    6695              :       }
    6696              : 
    6697            0 :       fi = i;
    6698              :    }
    6699              : 
    6700            0 : mscb_error:
    6701            0 :    r->rsprintf("</tr></table>\r\n");
    6702            0 :    r->rsprintf("</td></tr></table>\r\n");
    6703            0 :    r->rsprintf("</td></tr></table>\r\n");
    6704            0 :    r->rsprintf("</td></tr></table>\r\n");
    6705            0 :    r->rsprintf("</div></body></html>\r\n");
    6706              : }
    6707              : 
    6708              : #endif // HAVE_MSCB
    6709              : 
    6710              : /*------------------------------------------------------------------*/
    6711              : 
    6712            0 : void show_password_page(Return* r, const char* dec_path, const char *password)
    6713              : {
    6714            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    6715            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    6716            0 :    r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
    6717              : 
    6718            0 :    r->rsprintf("<html><head>\n");
    6719            0 :    r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
    6720            0 :    r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
    6721            0 :    r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
    6722            0 :    r->rsprintf("<title>Enter password</title></head><body>\n\n");
    6723              : 
    6724            0 :    r->rsprintf("<form method=\"GET\" action=\".\">\n\n");
    6725              : 
    6726              :    /*---- page header ----*/
    6727            0 :    r->rsprintf("<table class=\"headerTable\"><tr><td></td><tr></table>\n");
    6728              : 
    6729            0 :    r->rsprintf("<table class=\"dialogTable\">\n");  //main table
    6730            0 :    if (password[0])
    6731            0 :       r->rsprintf("<tr><th class=\"redLight\">Wrong password!</tr>\n");
    6732              : 
    6733            0 :    r->rsprintf("<tr><th>Please enter password</tr>\n");
    6734            0 :    r->rsprintf("<tr><td align=center><input type=password name=pwd></tr>\n");
    6735            0 :    r->rsprintf("<tr><td align=center><input type=submit value=Submit></tr>");
    6736              : 
    6737            0 :    r->rsprintf("</table>\n");
    6738              : 
    6739            0 :    r->rsprintf("</div>\n"); // closing for <div id="mmain">
    6740            0 :    r->rsprintf("</form>\n");
    6741            0 :    r->rsprintf("</body></html>\r\n");
    6742            0 : }
    6743              : 
    6744              : /*------------------------------------------------------------------*/
    6745              : 
    6746            0 : BOOL check_web_password(Return* r, HNDLE hDB, const char* dec_path, const char *password, const char *redir)
    6747              : {
    6748              :    HNDLE hkey;
    6749              :    INT size;
    6750              :    char str[256];
    6751              : 
    6752              :    /* check for password */
    6753            0 :    db_find_key(hDB, 0, "/Experiment/Security/Web Password", &hkey);
    6754            0 :    if (hkey) {
    6755            0 :       size = sizeof(str);
    6756            0 :       db_get_data(hDB, hkey, str, &size, TID_STRING);
    6757            0 :       if (strcmp(password, str) == 0)
    6758            0 :          return TRUE;
    6759              : 
    6760              :       /* show web password page */
    6761            0 :       r->rsprintf("HTTP/1.1 200 Document follows\r\n");
    6762            0 :       r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    6763            0 :       r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
    6764              : 
    6765            0 :       r->rsprintf("<html><head>\n");
    6766            0 :       r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
    6767            0 :       r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
    6768            0 :       r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
    6769            0 :       r->rsprintf("<title>Enter password</title></head><body>\n\n");
    6770              : 
    6771            0 :       r->rsprintf("<form method=\"GET\" action=\".\">\n\n");
    6772              : 
    6773              :       /* define hidden fields for current experiment and destination */
    6774            0 :       if (redir[0])
    6775            0 :          r->rsprintf("<input type=hidden name=redir value=\"%s\">\n", redir);
    6776              : 
    6777              :       /*---- page header ----*/
    6778            0 :       r->rsprintf("<table class=\"headerTable\"><tr><td></td><tr></table>\n");
    6779              : 
    6780            0 :       r->rsprintf("<table class=\"dialogTable\">\n");  //main table
    6781              : 
    6782            0 :       if (password[0])
    6783            0 :          r->rsprintf("<tr><th class=\"redLight\">Wrong password!</tr>\n");
    6784              : 
    6785              :       r->rsprintf
    6786            0 :           ("<tr><th>Please enter password to obtain write access</tr>\n");
    6787            0 :       r->rsprintf("<tr><td align=center><input type=password name=wpwd></tr>\n");
    6788            0 :       r->rsprintf("<tr><td align=center><input type=submit value=Submit></tr>");
    6789              : 
    6790            0 :       r->rsprintf("</table>\n");
    6791              : 
    6792            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
    6793            0 :       r->rsprintf("</form>\n");
    6794            0 :       r->rsprintf("</body></html>\r\n");
    6795              : 
    6796            0 :       return FALSE;
    6797              :    } else
    6798            0 :       return TRUE;
    6799              : }
    6800              : 
    6801              : /*------------------------------------------------------------------*/
    6802              : 
    6803            0 : void show_odb_page(Param* pp, Return* r, const char* dec_path, int write_access)
    6804              : {
    6805              :    int keyPresent, size, status, line, link_index;
    6806              :    char colspan;
    6807              :    char style[32];
    6808              :    HNDLE hDB, hkey, hkeyroot;
    6809              :    KEY key;
    6810              :    DWORD delta;
    6811              : 
    6812            0 :    cm_get_experiment_database(&hDB, NULL);
    6813              : 
    6814              :    //printf("path [%s]\n", dec_path);
    6815              : 
    6816            0 :    if (strcmp(dec_path, "root") == 0) {
    6817            0 :       dec_path = "";
    6818              :    }
    6819              : 
    6820              :    char xdecpath[256];
    6821            0 :    mstrlcpy(xdecpath, dec_path, sizeof(xdecpath));
    6822            0 :    if (strrchr(xdecpath, '/'))
    6823            0 :       mstrlcpy(xdecpath, strrchr(xdecpath, '/')+1, sizeof(xdecpath));
    6824            0 :    if (xdecpath[0] == 0)
    6825            0 :       mstrlcpy(xdecpath, "root", sizeof(xdecpath));
    6826            0 :    show_header(r, "MIDAS online database", "", xdecpath, 0);
    6827              : 
    6828              :    /* use javascript file */
    6829            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
    6830            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
    6831            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"obsolete.js\"></script>\n");
    6832            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"controls.js\"></script>\n");
    6833              : 
    6834              :    /* find key via path */
    6835            0 :    status = db_find_key(hDB, 0, dec_path, &hkeyroot);
    6836            0 :    if (status != DB_SUCCESS) {
    6837            0 :       r->rsprintf("Error: cannot find key %s<P>\n", dec_path);
    6838            0 :       r->rsprintf("</body></html>\r\n");
    6839            0 :       return;
    6840              :    }
    6841              : 
    6842              :    char xdec_path[MAX_ODB_PATH];
    6843              : 
    6844              :    /* if key is not of type TID_KEY, cut off key name */
    6845            0 :    db_get_key(hDB, hkeyroot, &key);
    6846            0 :    if (key.type != TID_KEY) {
    6847            0 :       mstrlcpy(xdec_path, dec_path, sizeof(xdec_path));
    6848              :       
    6849              :       /* strip variable name from path */
    6850            0 :       char* p = xdec_path + strlen(xdec_path) - 1;
    6851            0 :       while (*p && *p != '/')
    6852            0 :          *p-- = 0;
    6853            0 :       if (*p == '/')
    6854            0 :          *p = 0;
    6855              : 
    6856            0 :       status = db_find_key(hDB, 0, xdec_path, &hkeyroot);
    6857            0 :       if (status != DB_SUCCESS) {
    6858            0 :          r->rsprintf("Error: cannot find key %s<P>\n", xdec_path);
    6859            0 :          r->rsprintf("</body></html>\r\n");
    6860            0 :          return;
    6861              :       }
    6862              : 
    6863            0 :       dec_path = xdec_path;
    6864              :    }
    6865              : 
    6866              :    //mstrlcpy(enc_path, dec_path, enc_path_size);
    6867              :    //urlEncode(enc_path, enc_path_size);
    6868              : 
    6869            0 :    std::string odbpath = db_get_path(hDB, hkeyroot);
    6870              : 
    6871              :    /*---- navigation bar ----*/
    6872              : 
    6873            0 :    colspan = 7;
    6874              : 
    6875            0 :    if (elog_mode) {
    6876            0 :       r->rsprintf("<table class=\"mtableheader\">\n");
    6877            0 :       r->rsprintf("<tr><td colspan=%d>\n", colspan);
    6878            0 :       r->rsprintf("<input type=button value=ELog onclick=\"self.location=\'?cmd=Alarms\';\">\n");
    6879            0 :       r->rsprintf("</td></tr></table>\n\n");
    6880              :    } else
    6881            0 :       show_navigation_bar(r, "ODB");
    6882              : 
    6883              :    /*---- begin ODB directory table ----*/
    6884              : 
    6885            0 :    r->rsprintf("<table class=\"mtable\" style=\"border-spacing:0px;\">\n");
    6886            0 :    r->rsprintf("<tr><th colspan=%d class=\"mtableheader\">Online Database Browser</tr>\n", colspan);
    6887              :    //buttons:
    6888            0 :    if(!elog_mode){
    6889            0 :       r->rsprintf("<tr><td colspan=%d>\n", colspan);
    6890            0 :       r->rsprintf("<input type=button value=Find onclick=\"self.location=\'?cmd=Find\';\">\n");
    6891            0 :       r->rsprintf("<input type=button value=Create onclick=\"dlgShow('dlgCreate')\">\n");
    6892            0 :       r->rsprintf("<input type=button value=Link   onclick=\"dlgShow('dlgLink')\">\n");
    6893            0 :       r->rsprintf("<input type=button value=Delete onclick=\"dlgShow('dlgDelete')\">\n");
    6894            0 :       r->rsprintf("<input type=button value=\"Create Elog from this page\" onclick=\"self.location=\'?cmd=Create Elog from this page&odb_path=%s\';\">\n", urlEncode(odbpath.c_str()).c_str());
    6895            0 :       r->rsprintf("<input type=button value=\"Show open records\" onclick=\"self.location=\'?cmd=odb_sor&odb_path=%s\';\">\n", urlEncode(odbpath.c_str()).c_str());
    6896            0 :       r->rsprintf("<input type=button value=\"Show ODB clients\" onclick=\"self.location=\'?cmd=odb_scl\';\">\n");
    6897            0 :       r->rsprintf("</td></tr>\n");
    6898              :    }
    6899              : 
    6900              :    /*---- Build the Delete dialog------------------------------------*/
    6901              : 
    6902            0 :    std::string dd = "";
    6903              : 
    6904            0 :    dd += "<!-- Demo dialog -->\n";
    6905            0 :    dd += "<div id=\"dlgDelete\" class=\"dlgFrame\">\n";
    6906            0 :    dd += "<div class=\"dlgTitlebar\">Delete ODB entry</div>\n";
    6907            0 :    dd += "<div class=\"dlgPanel\">\n";
    6908            0 :    dd += "<div id=odbpath>";
    6909            0 :    dd += "\"";
    6910            0 :    dd += MJsonNode::Encode(odbpath.c_str());
    6911            0 :    dd += "\"";
    6912            0 :    dd += "</div>\n";
    6913            0 :    dd += "<div><br></div>\n";
    6914              : 
    6915            0 :    dd += "<table class=\"dialogTable\">\n";
    6916            0 :    dd += "<th colspan=2>Delete ODB entries:</th>\n";
    6917              : 
    6918            0 :    std::vector<std::string> delete_list;
    6919              : 
    6920            0 :    int count_delete = 0;
    6921              : 
    6922              :    /*---- ODB display -----------------------------------------------*/
    6923              : 
    6924              :    /* display root key */
    6925            0 :    r->rsprintf("<tr><td colspan=%d class='ODBpath'><b>", colspan);
    6926            0 :    r->rsprintf("<a href=\"?cmd=oldodb\">/</a> \n");
    6927              : 
    6928            0 :    std::string enc_root_path;
    6929              : 
    6930              :    /*---- display path ----*/
    6931              :    {
    6932            0 :       const char* p = dec_path;
    6933            0 :       while (*p) {
    6934            0 :          std::string pd;
    6935            0 :          while (*p && *p != '/')
    6936            0 :             pd += *p++;
    6937              :          
    6938            0 :          enc_root_path += urlEncode(pd.c_str());
    6939              :          
    6940            0 :          if (pd.length() > 0)
    6941            0 :             r->rsprintf("<a href=\"?cmd=oldodb&odb_path=%s\">%s</a>\n / ", enc_root_path.c_str(), pd.c_str());
    6942              :          
    6943            0 :          enc_root_path += "/";
    6944            0 :          if (*p == '/')
    6945            0 :             p++;
    6946            0 :       }
    6947              :    }
    6948              : 
    6949            0 :    r->rsprintf("</b></tr>\n");
    6950              : 
    6951              :    /* enumerate subkeys */
    6952            0 :    keyPresent = 0;
    6953            0 :    for(int scan=0; scan<2; scan++){
    6954            0 :       if(scan==1 && keyPresent==1) {
    6955            0 :          r->rsprintf("<tr class=\"titleRow\">\n");
    6956            0 :          r->rsprintf("<th class=\"ODBkey\">Key</th>\n");
    6957            0 :          r->rsprintf("<th class=\"ODBvalue\">Value&nbsp;");
    6958            0 :          r->rsprintf("<script type=\"text/javascript\">\n");
    6959            0 :          r->rsprintf("function expand()\n");
    6960            0 :          r->rsprintf("{\n");
    6961            0 :          r->rsprintf("  var n = document.getElementsByName('ext');\n");
    6962            0 :          r->rsprintf("  for (i=0 ; i<n.length ; i++) {\n");
    6963            0 :          r->rsprintf("    if (n[i].style.display == 'none')\n");
    6964            0 :          r->rsprintf("       n[i].style.display = 'table-cell';\n");
    6965            0 :          r->rsprintf("    else\n");
    6966            0 :          r->rsprintf("       n[i].style.display = 'none';\n");
    6967            0 :          r->rsprintf("  }\n");
    6968            0 :          r->rsprintf("  if (document.getElementById('expp').expflag === true) {\n");
    6969            0 :          r->rsprintf("    document.getElementById('expp').expflag = false;\n");
    6970            0 :          r->rsprintf("    document.getElementById('expp').innerHTML = '&#x21E5;';\n");
    6971            0 :          r->rsprintf("  } else {\n");
    6972            0 :          r->rsprintf("    document.getElementById('expp').expflag = true;\n");
    6973            0 :          r->rsprintf("    document.getElementById('expp').innerHTML = '&#x21E4;';\n");
    6974            0 :          r->rsprintf("  }\n");
    6975            0 :          r->rsprintf("}\n");
    6976            0 :          r->rsprintf("</script>");
    6977            0 :          r->rsprintf("<div style=\"display:inline;float:right\"><a id=\"expp\"href=\"#\" onClick=\"expand();return false;\">&#x21E5;</div>");
    6978            0 :          r->rsprintf("</th>\n");
    6979            0 :          r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Type</th>\n");
    6980            0 :          r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">#Val</th>\n");
    6981            0 :          r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Size</th>\n");
    6982            0 :          r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Written</th>\n");
    6983            0 :          r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Mode</th>\n");
    6984            0 :          r->rsprintf("</tr>\n");
    6985              :       }
    6986            0 :       line = 0;
    6987            0 :       for (int i = 0;; i++) {
    6988            0 :          db_enum_link(hDB, hkeyroot, i, &hkey);
    6989            0 :          if (!hkey)
    6990            0 :             break;
    6991            0 :          db_get_link(hDB, hkey, &key);
    6992              : 
    6993            0 :          if (scan == 0) {
    6994            0 :             delete_list.push_back(key.name);
    6995              :          }
    6996              : 
    6997            0 :          if (line % 2 == 0)
    6998            0 :             mstrlcpy(style, "ODBtableEven", sizeof(style));
    6999              :          else
    7000            0 :             mstrlcpy(style, "ODBtableOdd", sizeof(style));
    7001              : 
    7002            0 :          std::string keyname = key.name;
    7003            0 :          std::string enc_keyname = urlEncode(key.name);
    7004              : 
    7005            0 :          std::string enc_full_path = enc_root_path + enc_keyname;
    7006              : 
    7007            0 :          std::string odb_path = dec_path;
    7008            0 :          if (odb_path.length() > 0 && odb_path[odb_path.length() - 1] != '/')
    7009            0 :             odb_path += "/";
    7010            0 :          odb_path += key.name;
    7011              : 
    7012              :          /* resolve links */
    7013            0 :          std::string enc_link_ref;
    7014              :          char link_name[MAX_ODB_PATH];
    7015            0 :          link_name[0] = 0;
    7016            0 :          status = DB_SUCCESS;
    7017            0 :          if (key.type == TID_LINK) {
    7018            0 :             size = sizeof(link_name);
    7019            0 :             db_get_link_data(hDB, hkey, link_name, &size, TID_LINK);
    7020              : 
    7021            0 :             status = db_find_key(hDB, 0, link_name, &hkey);
    7022              : 
    7023            0 :             if (status == DB_SUCCESS)
    7024            0 :                db_get_key(hDB, hkey, &key);
    7025              : 
    7026              :             //sprintf(link_ref, "?cmd=Set&odb_path=%s", full_path);
    7027            0 :             enc_link_ref = "?cmd=Set&odb_path=";
    7028            0 :             enc_link_ref += enc_full_path;
    7029              : 
    7030            0 :             if (status == DB_SUCCESS && link_name[0] == 0) {
    7031              :                // fake the case when an empty link somehow resolves
    7032            0 :                sprintf(link_name, "%s", "(empty)");
    7033              :             }
    7034              :          }
    7035              : 
    7036            0 :          std::string enc_ref;
    7037              : 
    7038            0 :          if (link_name[0]) {
    7039            0 :             if (enc_root_path.back() == '/' && link_name[0] == '/') {
    7040              :                //sprintf(ref, "?cmd=Set&odb_path=%s%s", root_path, link_name+1);
    7041            0 :                enc_ref = "";
    7042            0 :                enc_ref += "?cmd=Set&odb_path=";
    7043            0 :                enc_ref += enc_root_path;
    7044            0 :                enc_ref += urlEncode(link_name + 1);
    7045              :             } else {
    7046              :                //sprintf(ref, "?cmd=Set&odb_path=%s%s", root_path, link_name);
    7047            0 :                enc_ref = "";
    7048            0 :                enc_ref += "?cmd=Set&odb_path=";
    7049            0 :                enc_ref += enc_root_path;
    7050            0 :                enc_ref += urlEncode(link_name);
    7051              :             }
    7052              :          } else {
    7053              :             //sprintf(ref, "?cmd=Set&odb_path=%s", full_path);
    7054            0 :             enc_ref = "";
    7055            0 :             enc_ref += "?cmd=Set&odb_path=";
    7056            0 :             enc_ref += enc_full_path;
    7057              :          }
    7058              : 
    7059            0 :          if (status != DB_SUCCESS) {
    7060            0 :             if (scan == 1) {
    7061            0 :                r->rsprintf("<tr><td class=\"yellowLight\">");
    7062            0 :                r->rsprintf("%s <i>&rarr; <a href=\"%s\">%s</a></i><td><b><div style=\"color:red\">&lt;cannot resolve link&gt;</div></b></tr>\n", keyname.c_str(), enc_link_ref.c_str(), link_name[0]?link_name:"(empty)");
    7063              :             }
    7064              :          } else {
    7065              : 
    7066            0 :             if (key.type == TID_KEY && scan == 0) {
    7067              :                /* for keys, don't display data value */
    7068            0 :                r->rsprintf("<tr><td colspan=%d class=\"ODBdirectory\"><a href=\"?cmd=oldodb&odb_path=%s\">&#x25B6 %s</a>\n", colspan, enc_full_path.c_str(), keyname.c_str());
    7069            0 :                if (link_name[0])
    7070            0 :                   r->rsprintf("<i>&rarr; <a href=\"%s\">%s</a></i>", enc_link_ref.c_str(), link_name);
    7071            0 :                r->rsprintf("</tr>\n");
    7072            0 :             } else if(key.type != TID_KEY && scan == 1) {
    7073              : 
    7074            0 :                if (strchr(link_name, '['))
    7075            0 :                   link_index = atoi(strchr(link_name, '[')+1);
    7076              :                else
    7077            0 :                   link_index = -1;
    7078              : 
    7079              :                /* display single value */
    7080            0 :                if (key.num_values == 1 || link_index != -1) {
    7081              :                   char data[TEXT_SIZE];
    7082            0 :                   size = sizeof(data);
    7083            0 :                   db_get_data(hDB, hkey, data, &size, key.type);
    7084              : 
    7085            0 :                   std::string data_str;
    7086              : 
    7087            0 :                   if (link_index != -1)
    7088            0 :                      data_str = db_sprintf(data, key.item_size, link_index, key.type);
    7089              :                   else
    7090            0 :                      data_str = db_sprintf(data, key.item_size, 0, key.type);
    7091              : 
    7092            0 :                   if (key.type == TID_STRING) {
    7093            0 :                      if (size == sizeof(data)) {
    7094            0 :                         data_str += "...(truncated)";
    7095              :                      }
    7096              :                   }
    7097              : 
    7098            0 :                   std::string hex_str;
    7099              : 
    7100            0 :                   if (key.type != TID_STRING) {
    7101            0 :                      if (link_index != -1)
    7102            0 :                         hex_str = db_sprintfh(data, key.item_size, link_index, key.type);
    7103              :                      else
    7104            0 :                         hex_str = db_sprintfh(data, key.item_size, 0, key.type);
    7105              :                   }
    7106              : 
    7107            0 :                   if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
    7108            0 :                      data_str = "(empty)";
    7109            0 :                      hex_str  = "";
    7110              :                   }
    7111              : 
    7112            0 :                   r->rsprintf("<tr>\n");
    7113            0 :                   if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
    7114            0 :                      if (link_name[0]) {
    7115            0 :                         r->rsprintf("<td class=\"ODBkey\">\n");
    7116            0 :                         r->rsprintf("%s <i>&rarr; ", keyname.c_str());
    7117            0 :                         r->rsprintf("<a href=\"%s\">%s</a></i>\n", enc_link_ref.c_str(), link_name);
    7118            0 :                         r->rsprintf("<td class=\"%s\">\n", style);
    7119            0 :                         if (!write_access)
    7120            0 :                            r->rsprintf("%s (%s)", data_str.c_str(), hex_str.c_str());
    7121              :                         else {
    7122            0 :                            r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
    7123            0 :                            r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">%s (%s)</a>\n", odb_path.c_str(), data_str.c_str(), hex_str.c_str());
    7124              :                         }
    7125              :                      } else {
    7126            0 :                         r->rsprintf("<td class=\"ODBkey\">\n");
    7127            0 :                         r->rsprintf("%s<td class=\"%s\">", keyname.c_str(), style);
    7128            0 :                         if (!write_access)
    7129            0 :                            r->rsprintf("%s (%s)", data_str.c_str(), hex_str.c_str());
    7130              :                         else {
    7131            0 :                            r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
    7132            0 :                            r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">%s (%s)</a>\n", odb_path.c_str(), data_str.c_str(), hex_str.c_str());
    7133              :                         }
    7134              :                      }
    7135              :                   } else {
    7136            0 :                      if (strchr(data_str.c_str(), '\n')) {
    7137            0 :                         if (link_name[0]) {
    7138            0 :                            r->rsprintf("<td class=\"ODBkey\">");
    7139            0 :                            r->rsprintf("%s <i>&rarr; <a href=\"%s\">%s</a></i><td class=\"ODBvalue\">", keyname.c_str(), enc_link_ref.c_str(), link_name);
    7140              :                         } else
    7141            0 :                            r->rsprintf("<td class=\"ODBkey\">%s<td class=\"%s\">", keyname.c_str(), style);
    7142            0 :                         r->rsprintf("\n<pre>");
    7143            0 :                         strencode3(r, data_str.c_str());
    7144            0 :                         r->rsprintf("</pre>");
    7145            0 :                         if (strlen(data) > data_str.length())
    7146            0 :                            r->rsprintf("<i>... (%d bytes total)<p>\n", (int)strlen(data));
    7147              : 
    7148            0 :                         r->rsprintf("<a href=\"%s\">Edit</a>\n", enc_ref.c_str());
    7149              :                      } else {
    7150            0 :                         if (link_name[0]) {
    7151            0 :                            r->rsprintf("<td class=\"ODBkey\">\n");
    7152            0 :                            r->rsprintf("%s <i>&rarr; <a href=\"%s\">%s</a></i><td class=\"%s\">", keyname.c_str(), enc_link_ref.c_str(), link_name, style);
    7153            0 :                            if (!write_access)
    7154            0 :                               strencode(r, data_str.c_str());
    7155              :                            else {
    7156            0 :                               r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
    7157            0 :                               r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", odb_path.c_str());
    7158            0 :                               strencode(r, data_str.c_str());
    7159            0 :                               r->rsprintf("</a>\n");
    7160              :                            }
    7161              :                         } else {
    7162            0 :                            r->rsprintf("<td class=\"ODBkey\">%s<td class=\"%s\">", keyname.c_str(), style);
    7163            0 :                            if (!write_access) {
    7164            0 :                               strencode(r, data_str.c_str());
    7165              :                            } else {
    7166            0 :                               r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
    7167            0 :                               r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", odb_path.c_str());
    7168            0 :                               strencode(r, data_str.c_str());
    7169            0 :                               r->rsprintf("</a>\n");
    7170              :                            }
    7171              :                         }
    7172              :                      }
    7173              :                   }
    7174              : 
    7175              :                   /* extended key information */
    7176            0 :                   r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
    7177            0 :                   r->rsprintf("%s", rpc_tid_name(key.type));
    7178            0 :                   r->rsprintf("</td>\n");
    7179              : 
    7180            0 :                   r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
    7181            0 :                   r->rsprintf("%d", key.num_values);
    7182            0 :                   r->rsprintf("</td>\n");
    7183              : 
    7184            0 :                   r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
    7185            0 :                   r->rsprintf("%d", key.item_size);
    7186            0 :                   r->rsprintf("</td>\n");
    7187              : 
    7188            0 :                   r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
    7189            0 :                   db_get_key_time(hDB, hkey, &delta);
    7190            0 :                   if (delta < 60)
    7191            0 :                      r->rsprintf("%ds", delta);
    7192            0 :                   else if (delta < 3600)
    7193            0 :                      r->rsprintf("%1.0lfm", delta / 60.0);
    7194            0 :                   else if (delta < 86400)
    7195            0 :                      r->rsprintf("%1.0lfh", delta / 3600.0);
    7196            0 :                   else if (delta < 86400 * 99)
    7197            0 :                      r->rsprintf("%1.0lfd", delta / 86400.0);
    7198              :                   else
    7199            0 :                      r->rsprintf(">99d");
    7200            0 :                   r->rsprintf("</td>\n");
    7201              : 
    7202            0 :                   r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
    7203            0 :                   if (key.access_mode & MODE_READ)
    7204            0 :                      r->rsprintf("R");
    7205            0 :                   if (key.access_mode & MODE_WRITE)
    7206            0 :                      r->rsprintf("W");
    7207            0 :                   if (key.access_mode & MODE_DELETE)
    7208            0 :                      r->rsprintf("D");
    7209            0 :                   if (key.access_mode & MODE_EXCLUSIVE)
    7210            0 :                      r->rsprintf("E");
    7211            0 :                   r->rsprintf("</td>\n");
    7212              : 
    7213            0 :                   line++;
    7214            0 :                   r->rsprintf("</tr>\n");
    7215            0 :                } else { /* display array value */
    7216              :                   /* check for exceeding length */
    7217            0 :                   if (key.num_values > 1000 && !pp->isparam("all"))
    7218            0 :                      r->rsprintf("<tr><td class=\"ODBkey\">%s<td class=\"%s\"><span style=\"font-style: italic\"><a href=\"?cmd=oldodb&odb_path=%s&all=1\">... %d values ...</a></span>\n", keyname.c_str(), style, enc_full_path.c_str(), key.num_values);
    7219              :                   else {
    7220              :                      /* display first value */
    7221            0 :                      if (link_name[0])
    7222            0 :                         r->rsprintf("<tr><td class=\"ODBkey\" rowspan=%d>%s<br><i>&rarr; <a href=\"%s\">%s</a></i>\n", key.num_values, keyname.c_str(), enc_link_ref.c_str(), link_name);
    7223              :                      else
    7224            0 :                         r->rsprintf("<tr><td class=\"ODBkey\" rowspan=%d>%s\n", key.num_values, keyname.c_str());
    7225              : 
    7226            0 :                      for (int j = 0; j < key.num_values; j++) {
    7227            0 :                         if (line % 2 == 0)
    7228            0 :                            mstrlcpy(style, "ODBtableEven", sizeof(style));
    7229              :                         else
    7230            0 :                            mstrlcpy(style, "ODBtableOdd", sizeof(style));
    7231              : 
    7232              :                         char data[TEXT_SIZE];
    7233            0 :                         size = sizeof(data);
    7234            0 :                         db_get_data_index(hDB, hkey, data, &size, j, key.type);
    7235            0 :                         std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
    7236              : 
    7237            0 :                         std::string hex_str;
    7238            0 :                         if (key.type == TID_STRING || key.type == TID_LINK) {
    7239            0 :                            hex_str = "";
    7240              :                         } else {
    7241            0 :                            hex_str = db_sprintfh(data, key.item_size, 0, key.type);
    7242              :                         }
    7243              : 
    7244            0 :                         if (key.type == TID_STRING) {
    7245            0 :                            if (size == sizeof(data)) {
    7246            0 :                               data_str += "...(truncated)";
    7247              :                            }
    7248              :                         }
    7249              : 
    7250            0 :                         if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
    7251            0 :                            data_str = "(empty)";
    7252            0 :                            hex_str  = "";
    7253              :                         }
    7254              : 
    7255              :                         //sprintf(ref, "?cmd=Set&odb_path=%s&index=%d", full_path, j);
    7256            0 :                         enc_ref = "";
    7257            0 :                         enc_ref += "?cmd=Set&odb_path=";
    7258            0 :                         enc_ref += enc_full_path;
    7259            0 :                         enc_ref += "&index=";
    7260            0 :                         enc_ref += toString(j);
    7261              : 
    7262            0 :                         std::string tmpstr;
    7263              :                         //sprintf(str, "%s[%d]", odb_path, j);
    7264            0 :                         tmpstr += odb_path;
    7265            0 :                         tmpstr += "[";
    7266            0 :                         tmpstr += toString(j);
    7267            0 :                         tmpstr += "]";
    7268              : 
    7269            0 :                         if (j > 0)
    7270            0 :                            r->rsprintf("<tr>");
    7271              : 
    7272            0 :                         r->rsprintf("<td class=\"%s\">[%d]&nbsp;", style, j);
    7273            0 :                         if (!write_access)
    7274            0 :                            r->rsprintf("<a href=\"%s\">", enc_ref.c_str());
    7275              :                         else {
    7276            0 :                            r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), tmpstr.c_str());
    7277            0 :                            r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", tmpstr.c_str());
    7278              :                         }
    7279            0 :                         if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
    7280            0 :                            r->rsprintf("%s (%s)</a>\n", data_str.c_str(), hex_str.c_str());
    7281              :                         else
    7282            0 :                            r->rsprintf("%s</a>\n", data_str.c_str());
    7283              : 
    7284            0 :                         if (j == 0) {
    7285              :                            /* extended key information */
    7286            0 :                            r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
    7287            0 :                            r->rsprintf("%s", rpc_tid_name(key.type));
    7288            0 :                            r->rsprintf("</td>\n");
    7289              : 
    7290            0 :                            r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
    7291            0 :                            r->rsprintf("%d", key.num_values);
    7292            0 :                            r->rsprintf("</td>\n");
    7293              : 
    7294            0 :                            r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
    7295            0 :                            r->rsprintf("%d", key.item_size);
    7296            0 :                            r->rsprintf("</td>\n");
    7297              : 
    7298            0 :                            r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
    7299            0 :                            db_get_key_time(hDB, hkey, &delta);
    7300            0 :                            if (delta < 60)
    7301            0 :                               r->rsprintf("%ds", delta);
    7302            0 :                            else if (delta < 3600)
    7303            0 :                               r->rsprintf("%1.0lfm", delta / 60.0);
    7304            0 :                            else if (delta < 86400)
    7305            0 :                               r->rsprintf("%1.0lfh", delta / 3600.0);
    7306            0 :                            else if (delta < 86400 * 99)
    7307            0 :                               r->rsprintf("%1.0lfh", delta / 86400.0);
    7308              :                            else
    7309            0 :                               r->rsprintf(">99d");
    7310            0 :                            r->rsprintf("</td>\n");
    7311              : 
    7312            0 :                            r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
    7313            0 :                            if (key.access_mode & MODE_READ)
    7314            0 :                               r->rsprintf("R");
    7315            0 :                            if (key.access_mode & MODE_WRITE)
    7316            0 :                               r->rsprintf("W");
    7317            0 :                            if (key.access_mode & MODE_DELETE)
    7318            0 :                               r->rsprintf("D");
    7319            0 :                            if (key.access_mode & MODE_EXCLUSIVE)
    7320            0 :                               r->rsprintf("E");
    7321            0 :                            r->rsprintf("</td>\n");
    7322              :                         }
    7323            0 :                         line++;
    7324            0 :                      }
    7325              : 
    7326            0 :                      r->rsprintf("</tr>\n");
    7327              :                   }
    7328              :                }
    7329            0 :             } else if(key.type != TID_KEY){
    7330            0 :                keyPresent = 1;  //flag that we've seen a key on the first pass, and should therefore write the Key / Value headline
    7331              :             }
    7332              :          }
    7333            0 :       }
    7334              :    }
    7335            0 :    r->rsprintf("</table>\n");
    7336            0 :    r->rsprintf("</div>\n"); // <div id="mmain">
    7337              : 
    7338              :    /*---- Build the Delete dialog------------------------------------*/
    7339              : 
    7340            0 :    std::sort(delete_list.begin(), delete_list.end());
    7341              : 
    7342            0 :    for (unsigned i=0; i<delete_list.size(); i++) {
    7343            0 :       std::string name = delete_list[i];
    7344              : 
    7345            0 :       dd += "<tr><td style=\"text-align:left;\" align=left><input align=left type=checkbox id=delete";
    7346            0 :       dd += toString(count_delete++);
    7347            0 :       dd += " value=\'";
    7348            0 :       dd += "\"";
    7349            0 :       dd += MJsonNode::Encode(name.c_str());
    7350            0 :       dd += "\"";
    7351            0 :       dd += "\'>";
    7352            0 :       dd += name;
    7353            0 :       dd += "</input></td></tr>\n";
    7354            0 :    }
    7355              : 
    7356            0 :    dd += "</table>\n";
    7357            0 :    dd += "<input type=button value=Delete onClick='mhttpd_delete_page_handle_delete(event);'>\n";
    7358            0 :    dd += "<input type=button value=Cancel onClick='mhttpd_delete_page_handle_cancel(event);'>\n";
    7359            0 :    dd += "</div>\n";
    7360            0 :    dd += "</div>\n";
    7361              : 
    7362            0 :    r->rsputs(dd.c_str());
    7363              : 
    7364              :    /*---- Build the Create dialog------------------------------------*/
    7365              : 
    7366            0 :    std::string cd = "";
    7367              : 
    7368            0 :    cd += "<!-- Demo dialog -->\n";
    7369            0 :    cd += "<div id=\"dlgCreate\" class=\"dlgFrame\">\n";
    7370            0 :    cd += "<div class=\"dlgTitlebar\">Create ODB entry</div>\n";
    7371            0 :    cd += "<div class=\"dlgPanel\">\n";
    7372            0 :    cd += "<br />\n";
    7373            0 :    cd += "<div id=odbpath>";
    7374            0 :    cd += "\"";
    7375            0 :    cd += MJsonNode::Encode(odbpath.c_str());
    7376            0 :    cd += "\"";
    7377            0 :    cd += "</div>\n";
    7378            0 :    cd += "<div><br></div>\n";
    7379              : 
    7380            0 :    cd += "<table class=\"dialogTable\">\n";
    7381            0 :    cd += "<th colspan=2>Create ODB entry:</th>\n";
    7382            0 :    cd += "<tr>";
    7383            0 :    cd += "<td>Type";
    7384            0 :    cd += "<td>";
    7385            0 :    cd += "<select type=text size=1 id=create_tid name=type>";
    7386            0 :    cd += "<option value=7>Integer (32-bit)";
    7387            0 :    cd += "<option value=9>Float (4 Bytes)";
    7388            0 :    cd += "<option value=12>String";
    7389            0 :    cd += "<option selected value=15>Subdirectory";
    7390            0 :    cd += "<option value=1>Byte";
    7391            0 :    cd += "<option value=2>Signed byte";
    7392            0 :    cd += "<option value=3>Character (8-bit)";
    7393            0 :    cd += "<option value=4>Word (16-bit)";
    7394            0 :    cd += "<option value=5>Short integer (16-bit)";
    7395            0 :    cd += "<option value=6>Double Word (32-bit)";
    7396            0 :    cd += "<option value=8>Boolean";
    7397            0 :    cd += "<option value=10>Double float (8 Bytes)";
    7398              :    //cd += "<option value=16>Symbolic link";
    7399            0 :    cd += "</select>";
    7400            0 :    cd += "</tr>\n";
    7401            0 :    cd += "<tr><td>Name<td><input type=text size=31 maxlength=31 id=create_name name=value></tr>\n";
    7402            0 :    cd += "<tr><td>Array size<td><input type=text size=31 maxlength=31 id=create_array_length name=index value=1></tr>\n";
    7403            0 :    cd += "<tr><td>String length<td><input type=text size=31 maxlength=31 id=create_strlen name=strlen value=32></tr>\n";
    7404            0 :    cd += "</table>\n";
    7405            0 :    cd += "<input type=button value=Create onClick='mhttpd_create_page_handle_create(event);'>\n";
    7406            0 :    cd += "<input type=button value=Cancel onClick='mhttpd_create_page_handle_cancel(event);'>\n";
    7407            0 :    cd += "</div>\n";
    7408            0 :    cd += "</div>\n";
    7409              : 
    7410            0 :    r->rsputs(cd.c_str());
    7411              : 
    7412              :    /*---- Build the Link dialog------------------------------------*/
    7413              : 
    7414            0 :    std::string ld = "";
    7415              : 
    7416            0 :    ld += "<!-- Demo dialog -->\n";
    7417            0 :    ld += "<div id=\"dlgLink\" class=\"dlgFrame\">\n";
    7418            0 :    ld += "<div class=\"dlgTitlebar\">Create a link to an ODB entry</div>\n";
    7419            0 :    ld += "<div class=\"dlgPanel\">\n";
    7420            0 :    ld += "<br />\n";
    7421            0 :    ld += "<div id=link_odbpath>";
    7422            0 :    ld += "\"";
    7423            0 :    ld += MJsonNode::Encode(odbpath.c_str());
    7424            0 :    ld += "\"";
    7425            0 :    ld += "</div>\n";
    7426            0 :    ld += "<div><br></div>\n";
    7427              : 
    7428            0 :    ld += "<table class=\"dialogTable\">\n";
    7429            0 :    ld += "<th colspan=2>Create a link to an ODB entry:</th>\n";
    7430            0 :    ld += "<tr><td>Name<td><input type=text size=31 maxlength=31 id=link_name name=value></tr>\n";
    7431            0 :    ld += "<tr><td>Link target<td><input type=text size=31 maxlength=256 id=link_target name=target></tr>\n";
    7432            0 :    ld += "</table>\n";
    7433            0 :    ld += "<input type=button value=Link onClick='mhttpd_link_page_handle_link(event);'>\n";
    7434            0 :    ld += "<input type=button value=Cancel onClick='mhttpd_link_page_handle_cancel(event);'>\n";
    7435            0 :    ld += "</div>\n";
    7436            0 :    ld += "</div>\n";
    7437              : 
    7438            0 :    r->rsputs(ld.c_str());
    7439            0 : }
    7440              : 
    7441              : /*------------------------------------------------------------------*/
    7442              : 
    7443            0 : void show_set_page(Param* pp, Return* r,
    7444              :                    const char *group,
    7445              :                    int index, const char *value)
    7446              : {
    7447              :    int status, size;
    7448              :    HNDLE hDB, hkey;
    7449              :    KEY key;
    7450              :    char data[TEXT_SIZE];
    7451              : 
    7452            0 :    std::string odb_path = pp->getparam("odb_path");
    7453              : 
    7454              :    //printf("show_set_page: odb_path [%s] group [%s] index %d value [%s]\n", odb_path.c_str(), group, index, value);
    7455              : 
    7456            0 :    cm_get_experiment_database(&hDB, NULL);
    7457              : 
    7458              :    /* show set page if no value is given */
    7459            0 :    if (!pp->isparam("value") && !*pp->getparam("text")) {
    7460            0 :       status = db_find_link(hDB, 0, odb_path.c_str(), &hkey);
    7461            0 :       if (status != DB_SUCCESS) {
    7462            0 :          r->rsprintf("Error: cannot find key %s<P>\n", odb_path.c_str());
    7463            0 :          return;
    7464              :       }
    7465            0 :       db_get_link(hDB, hkey, &key);
    7466              : 
    7467            0 :       show_header(r, "Set value", "POST", "", 0);
    7468              :       //close header:
    7469            0 :       r->rsprintf("</table>");
    7470              : 
    7471              :       //main table:
    7472            0 :       r->rsprintf("<table class=\"dialogTable\">");
    7473              : 
    7474            0 :       if (index > 0)
    7475            0 :          r->rsprintf("<input type=hidden name=index value=\"%d\">\n", index);
    7476              :       else
    7477            0 :          index = 0;
    7478              : 
    7479            0 :       if (group[0])
    7480            0 :          r->rsprintf("<input type=hidden name=group value=\"%s\">\n", group);
    7481              : 
    7482            0 :       r->rsprintf("<input type=hidden name=odb_path value=\"%s\">\n", odb_path.c_str());
    7483              : 
    7484            0 :       std::string data_str1 = rpc_tid_name(key.type);
    7485            0 :       std::string str1;
    7486            0 :       if (key.num_values > 1) {
    7487            0 :          data_str1 += msprintf("[%d]", key.num_values);
    7488            0 :          str1 = msprintf("%s[%d]", odb_path.c_str(), index);
    7489              :       } else
    7490            0 :          str1 = odb_path.c_str();
    7491              : 
    7492            0 :       r->rsprintf("<tr><th colspan=2>Set new value - type = %s</tr>\n", data_str1.c_str());
    7493            0 :       r->rsprintf("<tr><td>%s<td>\n", str1.c_str());
    7494              : 
    7495              :       /* set current value as default */
    7496            0 :       size = sizeof(data);
    7497            0 :       db_get_link_data(hDB, hkey, data, &size, key.type);
    7498            0 :       std::string data_str = db_sprintf(data, key.item_size, index, key.type);
    7499              : 
    7500            0 :       if (equal_ustring(data_str.c_str(), "<NULL>"))
    7501            0 :          data_str = "";
    7502              : 
    7503            0 :       if (strchr(data_str.c_str(), '\n') != NULL) {
    7504            0 :          r->rsprintf("<textarea rows=20 cols=80 name=\"text\">\n");
    7505            0 :          strencode3(r, data);
    7506            0 :          r->rsprintf("</textarea>\n");
    7507              :       } else {
    7508            0 :          size = 20;
    7509            0 :          if ((int) data_str.length() > size)
    7510            0 :             size = data_str.length() + 3;
    7511            0 :          if (size > 80)
    7512            0 :             size = 80;
    7513              : 
    7514            0 :          r->rsprintf("<input type=\"text\" size=%d maxlength=256 name=\"value\" value=\"", size);
    7515            0 :          strencode(r, data_str.c_str());
    7516            0 :          r->rsprintf("\">\n");
    7517              :       }
    7518              : 
    7519            0 :       r->rsprintf("</tr>\n");
    7520              : 
    7521            0 :       r->rsprintf("<tr><td align=center colspan=2>");
    7522            0 :       r->rsprintf("<input type=submit name=cmd value=Set>");
    7523            0 :       r->rsprintf("<input type=submit name=cmd value=Cancel>");
    7524            0 :       r->rsprintf("</tr>");
    7525            0 :       r->rsprintf("</table>");
    7526              : 
    7527            0 :       r->rsprintf("<input type=hidden name=cmd value=Set>\n");
    7528              : 
    7529            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
    7530            0 :       r->rsprintf("</form>\n");
    7531            0 :       r->rsprintf("</body></html>\r\n");
    7532            0 :       return;
    7533            0 :    } else {
    7534              :       /* set value */
    7535              : 
    7536            0 :       status = db_find_link(hDB, 0, odb_path.c_str(), &hkey);
    7537            0 :       if (status != DB_SUCCESS) {
    7538            0 :          r->rsprintf("Error: cannot find key %s<P>\n", odb_path.c_str());
    7539            0 :          return;
    7540              :       }
    7541            0 :       db_get_link(hDB, hkey, &key);
    7542              : 
    7543            0 :       memset(data, 0, sizeof(data));
    7544              : 
    7545            0 :       if (pp->getparam("text") && *pp->getparam("text"))
    7546            0 :          mstrlcpy(data, pp->getparam("text"), sizeof(data));
    7547              :       else
    7548            0 :          db_sscanf(value, data, &size, 0, key.type);
    7549              : 
    7550            0 :       if (index < 0)
    7551            0 :          index = 0;
    7552              : 
    7553              :       /* extend data size for single string if necessary */
    7554            0 :       if ((key.type == TID_STRING || key.type == TID_LINK)
    7555            0 :           && (int) strlen(data) + 1 > key.item_size && key.num_values == 1)
    7556            0 :          key.item_size = strlen(data) + 1;
    7557              : 
    7558            0 :       if (key.item_size == 0)
    7559            0 :          key.item_size = rpc_tid_size(key.type);
    7560              : 
    7561            0 :       if (key.num_values > 1)
    7562            0 :          status = db_set_link_data_index(hDB, hkey, data, key.item_size, index, key.type);
    7563              :       else
    7564            0 :          status = db_set_link_data(hDB, hkey, data, key.item_size, 1, key.type);
    7565              : 
    7566            0 :       if (status == DB_NO_ACCESS)
    7567            0 :          r->rsprintf("<h2>Write access not allowed</h2>\n");
    7568              : 
    7569            0 :       redirect(r, "");
    7570              : 
    7571            0 :       return;
    7572              :    }
    7573            0 : }
    7574              : 
    7575              : /*------------------------------------------------------------------*/
    7576              : 
    7577            0 : void show_find_page(Return* r, const char *value)
    7578              : {
    7579              :    HNDLE hDB, hkey;
    7580              : 
    7581            0 :    cm_get_experiment_database(&hDB, NULL);
    7582              : 
    7583            0 :    if (value[0] == 0) {
    7584              :       /* without value, show find dialog */
    7585            0 :       show_header(r, "Find value", "GET", "", 0);
    7586              : 
    7587              :       //end header:
    7588            0 :       r->rsprintf("</table>");
    7589              : 
    7590              :       //find dialog:
    7591            0 :       r->rsprintf("<table class=\"dialogTable\">");
    7592              : 
    7593            0 :       r->rsprintf("<tr><th colspan=2>Find string in Online Database</tr>\n");
    7594            0 :       r->rsprintf("<tr><td>Enter substring (case insensitive)\n");
    7595              : 
    7596            0 :       r->rsprintf("<td><input type=\"text\" size=\"20\" maxlength=\"80\" name=\"value\">\n");
    7597            0 :       r->rsprintf("</tr>");
    7598              : 
    7599            0 :       r->rsprintf("<tr><td align=center colspan=2>");
    7600            0 :       r->rsprintf("<input type=submit name=cmd value=Find>");
    7601            0 :       r->rsprintf("<input type=submit name=cmd value=Cancel>");
    7602            0 :       r->rsprintf("</tr>");
    7603            0 :       r->rsprintf("</table>");
    7604              : 
    7605            0 :       r->rsprintf("<input type=hidden name=cmd value=Find>");
    7606              : 
    7607            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
    7608            0 :       r->rsprintf("</form>\n");
    7609            0 :       r->rsprintf("</body></html>\r\n");
    7610              :    } else {
    7611            0 :       show_header(r, "Search results", "GET", "", 0);
    7612              : 
    7613            0 :       r->rsprintf("<table class=\"mtable\">\n");
    7614            0 :       r->rsprintf("<tr><th colspan=2 class=\"mtableheader\">");
    7615            0 :       r->rsprintf("Results of search for substring \"%s\"</tr>\n", value);
    7616            0 :       r->rsprintf("<tr><th class=\"titlerow\">Key<th>Value</tr>\n");
    7617              : 
    7618              :       /* start from root */
    7619            0 :       db_find_key(hDB, 0, "", &hkey);
    7620            0 :       assert(hkey);
    7621              : 
    7622              :       /* scan tree, call "search_callback" for each key */
    7623              :       search_data data;
    7624            0 :       data.r = r;
    7625            0 :       data.search_name = value;
    7626              : 
    7627            0 :       db_scan_tree(hDB, hkey, 0, search_callback, (void *)&data);
    7628              : 
    7629            0 :       r->rsprintf("</table>");
    7630            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
    7631            0 :       r->rsprintf("</form>\n");
    7632            0 :       r->rsprintf("</body></html>\r\n");
    7633              :    }
    7634            0 : }
    7635              : 
    7636              : /*------------------------------------------------------------------*/
    7637              : 
    7638              : #define LN10 2.302585094
    7639              : #define LOG2 0.301029996
    7640              : #define LOG5 0.698970005
    7641              : 
    7642            0 : void haxis(gdImagePtr im, gdFont * font, int col, int gcol,
    7643              :            int x1, int y1, int width,
    7644              :            int minor, int major, int text, int label, int grid, double xmin, double xmax)
    7645              : {
    7646              :    double dx, int_dx, frac_dx, x_act, label_dx, major_dx, x_screen, maxwidth;
    7647              :    int tick_base, major_base, label_base, n_sig1, n_sig2, xs;
    7648              :    char str[80];
    7649            0 :    double base[] = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
    7650              : 
    7651            0 :    if (xmax <= xmin || width <= 0)
    7652            0 :       return;
    7653              : 
    7654              :    /* use 5 as min tick distance */
    7655            0 :    dx = (xmax - xmin) / (double) (width / 5);
    7656              : 
    7657            0 :    frac_dx = modf(log(dx) / LN10, &int_dx);
    7658            0 :    if (frac_dx < 0) {
    7659            0 :       frac_dx += 1;
    7660            0 :       int_dx -= 1;
    7661              :    }
    7662              : 
    7663            0 :    tick_base = frac_dx < LOG2 ? 1 : frac_dx < LOG5 ? 2 : 3;
    7664            0 :    major_base = label_base = tick_base + 1;
    7665              : 
    7666              :    /* rounding up of dx, label_dx */
    7667            0 :    dx = pow(10, int_dx) * base[tick_base];
    7668            0 :    major_dx = pow(10, int_dx) * base[major_base];
    7669            0 :    label_dx = major_dx;
    7670              : 
    7671              :    /* number of significant digits */
    7672            0 :    if (xmin == 0)
    7673            0 :       n_sig1 = 0;
    7674              :    else
    7675            0 :       n_sig1 = (int) floor(log(fabs(xmin)) / LN10) - (int) floor(log(fabs(label_dx)) / LN10) + 1;
    7676              : 
    7677            0 :    if (xmax == 0)
    7678            0 :       n_sig2 = 0;
    7679              :    else
    7680            0 :       n_sig2 =
    7681            0 :          (int) floor(log(fabs(xmax)) / LN10) - (int) floor(log(fabs(label_dx)) / LN10) + 1;
    7682              : 
    7683            0 :    n_sig1 = MAX(n_sig1, n_sig2);
    7684            0 :    n_sig1 = MAX(n_sig1, 4);
    7685              : 
    7686              :    /* determination of maximal width of labels */
    7687            0 :    sprintf(str, "%1.*lG", n_sig1, floor(xmin / dx) * dx);
    7688            0 :    maxwidth = font->h / 2 * strlen(str);
    7689            0 :    sprintf(str, "%1.*lG", n_sig1, floor(xmax / dx) * dx);
    7690            0 :    maxwidth = MAX(maxwidth, font->h / 2 * strlen(str));
    7691            0 :    sprintf(str, "%1.*lG", n_sig1, floor(xmax / dx) * dx + label_dx);
    7692            0 :    maxwidth = MAX(maxwidth, font->h / 2 * strlen(str));
    7693              : 
    7694              :    /* increasing label_dx, if labels would overlap */
    7695            0 :    while (maxwidth > 0.7 * label_dx / (xmax - xmin) * width) {
    7696            0 :       label_base++;
    7697            0 :       label_dx = pow(10, int_dx) * base[label_base];
    7698            0 :       if (label_base % 3 == 2 && major_base % 3 == 1) {
    7699            0 :          major_base++;
    7700            0 :          major_dx = pow(10, int_dx) * base[major_base];
    7701              :       }
    7702              :    }
    7703              : 
    7704            0 :    x_act = floor(xmin / dx) * dx;
    7705              : 
    7706            0 :    gdImageLine(im, x1, y1, x1 + width, y1, col);
    7707              : 
    7708              :    do {
    7709            0 :       x_screen = (x_act - xmin) / (xmax - xmin) * width + x1;
    7710            0 :       xs = (int) (x_screen + 0.5);
    7711              : 
    7712            0 :       if (x_screen > x1 + width + 0.001)
    7713            0 :          break;
    7714              : 
    7715            0 :       if (x_screen >= x1) {
    7716            0 :          if (fabs(floor(x_act / major_dx + 0.5) - x_act / major_dx) <
    7717            0 :              dx / major_dx / 10.0) {
    7718              : 
    7719            0 :             if (fabs(floor(x_act / label_dx + 0.5) - x_act / label_dx) <
    7720            0 :                 dx / label_dx / 10.0) {
    7721              :                /* label tick mark */
    7722            0 :                gdImageLine(im, xs, y1, xs, y1 + text, col);
    7723              : 
    7724              :                /* grid line */
    7725            0 :                if (grid != 0 && xs > x1 && xs < x1 + width)
    7726            0 :                   gdImageLine(im, xs, y1, xs, y1 + grid, col);
    7727              : 
    7728              :                /* label */
    7729            0 :                if (label != 0) {
    7730            0 :                   sprintf(str, "%1.*lG", n_sig1, x_act);
    7731            0 :                   gdImageString(im, font, (int) xs - font->w * strlen(str) / 2,
    7732              :                                 y1 + label, str, col);
    7733              :                }
    7734              :             } else {
    7735              :                /* major tick mark */
    7736            0 :                gdImageLine(im, xs, y1, xs, y1 + major, col);
    7737              : 
    7738              :                /* grid line */
    7739            0 :                if (grid != 0 && xs > x1 && xs < x1 + width)
    7740            0 :                   gdImageLine(im, xs, y1 - 1, xs, y1 + grid, gcol);
    7741              :             }
    7742              : 
    7743              :          } else
    7744              :             /* minor tick mark */
    7745            0 :             gdImageLine(im, xs, y1, xs, y1 + minor, col);
    7746              : 
    7747              :       }
    7748              : 
    7749            0 :       x_act += dx;
    7750              : 
    7751              :       /* supress 1.23E-17 ... */
    7752            0 :       if (fabs(x_act) < dx / 100)
    7753            0 :          x_act = 0;
    7754              : 
    7755              :    } while (1);
    7756              : }
    7757              : 
    7758              : /*------------------------------------------------------------------*/
    7759              : 
    7760            0 : void sec_to_label(char *result, int sec, int base, int force_date)
    7761              : {
    7762              :    char mon[80];
    7763              :    time_t t_sec;
    7764              : 
    7765            0 :    t_sec = (time_t) sec;
    7766              : 
    7767              :    struct tm tms;
    7768            0 :    localtime_r(&t_sec, &tms);
    7769            0 :    strcpy(mon, mname[tms.tm_mon]);
    7770            0 :    mon[3] = 0;
    7771              : 
    7772            0 :    if (force_date) {
    7773            0 :       if (base < 600)
    7774            0 :          sprintf(result, "%02d %s %02d %02d:%02d:%02d",
    7775            0 :                  tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min,
    7776              :                  tms.tm_sec);
    7777            0 :       else if (base < 3600 * 24)
    7778            0 :          sprintf(result, "%02d %s %02d %02d:%02d",
    7779            0 :                  tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min);
    7780              :       else
    7781            0 :          sprintf(result, "%02d %s %02d", tms.tm_mday, mon, tms.tm_year % 100);
    7782              :    } else {
    7783            0 :       if (base < 600)
    7784            0 :          sprintf(result, "%02d:%02d:%02d", tms.tm_hour, tms.tm_min, tms.tm_sec);
    7785            0 :       else if (base < 3600 * 3)
    7786            0 :          sprintf(result, "%02d:%02d", tms.tm_hour, tms.tm_min);
    7787            0 :       else if (base < 3600 * 24)
    7788            0 :          sprintf(result, "%02d %s %02d %02d:%02d",
    7789            0 :                  tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min);
    7790              :       else
    7791            0 :          sprintf(result, "%02d %s %02d", tms.tm_mday, mon, tms.tm_year % 100);
    7792              :    }
    7793            0 : }
    7794              : 
    7795            0 : void taxis(gdImagePtr im, gdFont * font, int col, int gcol,
    7796              :            int x1, int y1, int width, int xr,
    7797              :            int minor, int major, int text, int label, int grid, double xmin, double xmax)
    7798              : {
    7799              :    int dx, x_act, label_dx, major_dx, x_screen, maxwidth;
    7800              :    int tick_base, major_base, label_base, xs, xl;
    7801              :    char str[80];
    7802            0 :    const int base[] = { 1, 5, 10, 60, 300, 600, 1800, 3600, 3600 * 6, 3600 * 12, 3600 * 24, 0 };
    7803              :    time_t ltime;
    7804              :    int force_date, d1, d2;
    7805              :    struct tm tms;
    7806              : 
    7807            0 :    if (xmax <= xmin || width <= 0)
    7808            0 :       return;
    7809              : 
    7810              :    /* force date display if xmax not today */
    7811            0 :    ltime = ss_time();
    7812            0 :    localtime_r(&ltime, &tms);
    7813            0 :    d1 = tms.tm_mday;
    7814            0 :    ltime = (time_t) xmax;
    7815            0 :    localtime_r(&ltime, &tms);
    7816            0 :    d2 = tms.tm_mday;
    7817            0 :    force_date = (d1 != d2);
    7818              : 
    7819              :    /* use 5 pixel as min tick distance */
    7820            0 :    dx = (int) ((xmax - xmin) / (double) (width / 5) + 0.5);
    7821              : 
    7822            0 :    for (tick_base = 0; base[tick_base]; tick_base++) {
    7823            0 :       if (base[tick_base] > dx)
    7824            0 :          break;
    7825              :    }
    7826            0 :    if (!base[tick_base])
    7827            0 :       tick_base--;
    7828            0 :    dx = base[tick_base];
    7829              : 
    7830            0 :    if (base[tick_base + 1])
    7831            0 :       major_base = tick_base + 1;
    7832              :    else
    7833            0 :       major_base = tick_base;
    7834            0 :    major_dx = base[major_base];
    7835              : 
    7836            0 :    if (base[major_base + 1])
    7837            0 :       label_base = major_base + 1;
    7838              :    else
    7839            0 :       label_base = major_base;
    7840            0 :    label_dx = base[label_base];
    7841              : 
    7842              :    do {
    7843            0 :       sec_to_label(str, (int) (xmin + 0.5), label_dx, force_date);
    7844            0 :       maxwidth = font->h / 2 * strlen(str);
    7845              : 
    7846              :       /* increasing label_dx, if labels would overlap */
    7847            0 :       if (maxwidth > 0.7 * label_dx / (xmax - xmin) * width) {
    7848            0 :          if (base[label_base + 1])
    7849            0 :             label_dx = base[++label_base];
    7850              :          else
    7851            0 :             label_dx += 3600 * 24;
    7852              :       } else
    7853            0 :          break;
    7854              :    } while (1);
    7855              : 
    7856            0 :    x_act =
    7857            0 :        (int) floor((double) (xmin - ss_timezone()) / label_dx) * label_dx + ss_timezone();
    7858              : 
    7859            0 :    gdImageLine(im, x1, y1, x1 + width, y1, col);
    7860              : 
    7861              :    do {
    7862            0 :       x_screen = (int) ((x_act - xmin) / (xmax - xmin) * width + x1 + 0.5);
    7863            0 :       xs = (int) (x_screen + 0.5);
    7864              : 
    7865            0 :       if (x_screen > x1 + width + 0.001)
    7866            0 :          break;
    7867              : 
    7868            0 :       if (x_screen >= x1) {
    7869            0 :          if ((x_act - ss_timezone()) % major_dx == 0) {
    7870            0 :             if ((x_act - ss_timezone()) % label_dx == 0) {
    7871              :                /* label tick mark */
    7872            0 :                gdImageLine(im, xs, y1, xs, y1 + text, col);
    7873              : 
    7874              :                /* grid line */
    7875            0 :                if (grid != 0 && xs > x1 && xs < x1 + width)
    7876            0 :                   gdImageLine(im, xs, y1, xs, y1 + grid, col);
    7877              : 
    7878              :                /* label */
    7879            0 :                if (label != 0) {
    7880            0 :                   sec_to_label(str, x_act, label_dx, force_date);
    7881              : 
    7882              :                   /* if labels at edge, shift them in */
    7883            0 :                   xl = (int) xs - font->w * strlen(str) / 2;
    7884            0 :                   if (xl < 0)
    7885            0 :                      xl = 0;
    7886            0 :                   if (xl + font->w * (int) strlen(str) > xr)
    7887            0 :                      xl = xr - font->w * strlen(str);
    7888            0 :                   gdImageString(im, font, xl, y1 + label, str, col);
    7889              :                }
    7890              :             } else {
    7891              :                /* major tick mark */
    7892            0 :                gdImageLine(im, xs, y1, xs, y1 + major, col);
    7893              : 
    7894              :                /* grid line */
    7895            0 :                if (grid != 0 && xs > x1 && xs < x1 + width)
    7896            0 :                   gdImageLine(im, xs, y1 - 1, xs, y1 + grid, gcol);
    7897              :             }
    7898              : 
    7899              :          } else
    7900              :             /* minor tick mark */
    7901            0 :             gdImageLine(im, xs, y1, xs, y1 + minor, col);
    7902              : 
    7903              :       }
    7904              : 
    7905            0 :       x_act += dx;
    7906              : 
    7907              :       /* supress 1.23E-17 ... */
    7908            0 :       if (fabs((double)x_act) < dx / 100)
    7909            0 :          x_act = 0;
    7910              : 
    7911              :    } while (1);
    7912              : }
    7913              : 
    7914              : /*------------------------------------------------------------------*/
    7915              : 
    7916            0 : int vaxis(gdImagePtr im, gdFont * font, int col, int gcol,
    7917              :           int x1, int y1, int width,
    7918              :           int minor, int major, int text, int label, int grid, double ymin, double ymax,
    7919              :           BOOL logaxis)
    7920              : {
    7921              :    double dy, int_dy, frac_dy, y_act, label_dy, major_dy, y_screen, y_next;
    7922              :    int tick_base, major_base, label_base, n_sig1, n_sig2, ys, max_width;
    7923              :    int last_label_y;
    7924              :    char str[80];
    7925            0 :    const double base[] = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
    7926              : 
    7927            0 :    if (ymax <= ymin || width <= 0)
    7928            0 :       return 0;
    7929              : 
    7930              :    // data type "double" only has about 15 significant digits, if difference between ymax and ymin is less than 10 significant digits, bailout! Otherwise code below goes into infinite loop
    7931            0 :    if (fabs(ymax - ymin) <= 1e-10)
    7932            0 :       return 0;
    7933              : 
    7934            0 :    if (logaxis) {
    7935            0 :       dy = pow(10, floor(log(ymin) / LN10));
    7936            0 :       label_dy = dy;
    7937            0 :       major_dy = dy * 10;
    7938            0 :       n_sig1 = 4;
    7939              :    } else {
    7940            0 :       dy = (ymax - ymin) / (double) (width / 5);
    7941              : 
    7942            0 :       frac_dy = modf(log(dy) / LN10, &int_dy);
    7943            0 :       if (frac_dy < 0) {
    7944            0 :          frac_dy += 1;
    7945            0 :          int_dy -= 1;
    7946              :       }
    7947              : 
    7948            0 :       tick_base = frac_dy < LOG2 ? 1 : frac_dy < LOG5 ? 2 : 3;
    7949            0 :       major_base = label_base = tick_base + 1;
    7950              : 
    7951              :       /* rounding up of dy, label_dy */
    7952            0 :       dy = pow(10, int_dy) * base[tick_base];
    7953            0 :       major_dy = pow(10, int_dy) * base[major_base];
    7954            0 :       label_dy = major_dy;
    7955              : 
    7956              :       /* number of significant digits */
    7957            0 :       if (ymin == 0)
    7958            0 :          n_sig1 = 0;
    7959              :       else
    7960            0 :          n_sig1 =
    7961            0 :              (int) floor(log(fabs(ymin)) / LN10) -
    7962            0 :              (int) floor(log(fabs(label_dy)) / LN10) + 1;
    7963              : 
    7964            0 :       if (ymax == 0)
    7965            0 :          n_sig2 = 0;
    7966              :       else
    7967            0 :          n_sig2 =
    7968            0 :              (int) floor(log(fabs(ymax)) / LN10) -
    7969            0 :              (int) floor(log(fabs(label_dy)) / LN10) + 1;
    7970              : 
    7971            0 :       n_sig1 = MAX(n_sig1, n_sig2);
    7972            0 :       n_sig1 = MAX(n_sig1, 4);
    7973              : 
    7974              :       /* increasing label_dy, if labels would overlap */
    7975            0 :       while (label_dy / (ymax - ymin) * width < 1.5 * font->h) {
    7976            0 :          label_base++;
    7977            0 :          label_dy = pow(10, int_dy) * base[label_base];
    7978            0 :          if (label_base % 3 == 2 && major_base % 3 == 1) {
    7979            0 :             major_base++;
    7980            0 :             major_dy = pow(10, int_dy) * base[major_base];
    7981              :          }
    7982              :       }
    7983              :    }
    7984              : 
    7985            0 :    max_width = 0;
    7986            0 :    y_act = floor(ymin / dy) * dy;
    7987              : 
    7988            0 :    if (x1 != 0 || y1 != 0)
    7989            0 :       gdImageLine(im, x1, y1, x1, y1 - width, col);
    7990              : 
    7991            0 :    last_label_y = y1 + 2 * font->h;
    7992              : 
    7993              :    do {
    7994            0 :       if (logaxis)
    7995            0 :          y_screen = y1 - (log(y_act) - log(ymin)) / (log(ymax) - log(ymin)) * width;
    7996              :       else
    7997            0 :          y_screen = y1 - (y_act - ymin) / (ymax - ymin) * width;
    7998            0 :       ys = (int) (y_screen + 0.5);
    7999              : 
    8000            0 :       if (y_screen < y1 - width - 0.001)
    8001            0 :          break;
    8002              : 
    8003            0 :       if (y_screen <= y1 + 0.001) {
    8004            0 :          if (fabs(floor(y_act / major_dy + 0.5) - y_act / major_dy) <
    8005            0 :              dy / major_dy / 10.0) {
    8006            0 :             if (fabs(floor(y_act / label_dy + 0.5) - y_act / label_dy) <
    8007            0 :                 dy / label_dy / 10.0) {
    8008            0 :                if (x1 != 0 || y1 != 0) {
    8009              :                   /* label tick mark */
    8010            0 :                   gdImageLine(im, x1, ys, x1 + text, ys, col);
    8011              : 
    8012              :                   /* grid line */
    8013            0 :                   if (grid != 0 && y_screen < y1 && y_screen > y1 - width) {
    8014            0 :                      if (grid > 0)
    8015            0 :                         gdImageLine(im, x1 + 1, ys, x1 + grid, ys, gcol);
    8016              :                      else
    8017            0 :                         gdImageLine(im, x1 - 1, ys, x1 + grid, ys, gcol);
    8018              :                   }
    8019              : 
    8020              :                   /* label */
    8021            0 :                   if (label != 0) {
    8022            0 :                      sprintf(str, "%1.*lG", n_sig1, y_act);
    8023            0 :                      if (label < 0)
    8024            0 :                         gdImageString(im, font, x1 + label - font->w * strlen(str),
    8025            0 :                                       ys - font->h / 2, str, col);
    8026              :                      else
    8027            0 :                         gdImageString(im, font, x1 + label, ys - font->h / 2, str, col);
    8028              : 
    8029            0 :                      last_label_y = ys - font->h / 2;
    8030              :                   }
    8031              :                } else {
    8032            0 :                   sprintf(str, "%1.*lG", n_sig1, y_act);
    8033            0 :                   max_width = MAX(max_width, (int) (font->w * strlen(str)));
    8034              :                }
    8035              :             } else {
    8036            0 :                if (x1 != 0 || y1 != 0) {
    8037              :                   /* major tick mark */
    8038            0 :                   gdImageLine(im, x1, ys, x1 + major, ys, col);
    8039              : 
    8040              :                   /* grid line */
    8041            0 :                   if (grid != 0 && y_screen < y1 && y_screen > y1 - width)
    8042            0 :                      gdImageLine(im, x1, ys, x1 + grid, ys, col);
    8043              :                }
    8044              :             }
    8045            0 :             if (logaxis) {
    8046            0 :                dy *= 10;
    8047            0 :                major_dy *= 10;
    8048            0 :                label_dy *= 10;
    8049              :             }
    8050              : 
    8051              :          } else {
    8052            0 :             if (x1 != 0 || y1 != 0) {
    8053              :                /* minor tick mark */
    8054            0 :                gdImageLine(im, x1, ys, x1 + minor, ys, col);
    8055              :             }
    8056              : 
    8057              :             /* for logaxis, also put labes on minor tick marks */
    8058            0 :             if (logaxis) {
    8059            0 :                if (label != 0) {
    8060            0 :                   if (x1 != 0 || y1 != 0) {
    8061              :                      /* calculate position of next major label */
    8062            0 :                      y_next = pow(10, floor(log(y_act) / LN10) + 1);
    8063            0 :                      y_screen =
    8064            0 :                          (int) (y1 -
    8065            0 :                                 (log(y_next) - log(ymin)) / (log(ymax) -
    8066            0 :                                                              log(ymin)) * width + 0.5);
    8067              : 
    8068            0 :                      if (ys + font->h / 2 < last_label_y
    8069            0 :                          && ys - font->h / 2 > y_screen + font->h / 2) {
    8070            0 :                         sprintf(str, "%1.*lG", n_sig1, y_act);
    8071            0 :                         if (label < 0)
    8072            0 :                            gdImageString(im, font, x1 + label - font->w * strlen(str),
    8073            0 :                                          ys - font->h / 2, str, col);
    8074              :                         else
    8075            0 :                            gdImageString(im, font, x1 + label, ys - font->h / 2, str,
    8076              :                                          col);
    8077              :                      }
    8078              : 
    8079            0 :                      last_label_y = ys - font->h / 2;
    8080              :                   } else {
    8081            0 :                      sprintf(str, "%1.*lG", n_sig1, y_act);
    8082            0 :                      max_width = MAX(max_width, (int) (font->w * strlen(str)));
    8083              :                   }
    8084              :                }
    8085              :             }
    8086              :          }
    8087              :       }
    8088              : 
    8089            0 :       y_act += dy;
    8090              : 
    8091              :       /* supress 1.23E-17 ... */
    8092            0 :       if (fabs(y_act) < dy / 100)
    8093            0 :          y_act = 0;
    8094              : 
    8095              :    } while (1);
    8096              : 
    8097            0 :    return max_width + abs(label);
    8098              : }
    8099              : 
    8100              : /*------------------------------------------------------------------*/
    8101              : 
    8102            0 : int time_to_sec(const char *str)
    8103              : {
    8104              :    double s;
    8105              : 
    8106            0 :    s = atof(str);
    8107            0 :    switch (str[strlen(str) - 1]) {
    8108            0 :    case 'm':
    8109              :    case 'M':
    8110            0 :       s *= 60;
    8111            0 :       break;
    8112            0 :    case 'h':
    8113              :    case 'H':
    8114            0 :       s *= 3600;
    8115            0 :       break;
    8116            0 :    case 'd':
    8117              :    case 'D':
    8118            0 :       s *= 3600 * 24;
    8119            0 :       break;
    8120              :    }
    8121              : 
    8122            0 :    return (int) s;
    8123              : }
    8124              : 
    8125              : /*------------------------------------------------------------------*/
    8126              : 
    8127            0 : time_t string_to_time(const char *str)
    8128              : {
    8129            0 :    time_t t = 0;
    8130            0 :    for (; *str != 0; str++) {
    8131            0 :       if (*str < '0')
    8132            0 :          break;
    8133            0 :       if (*str > '9')
    8134            0 :          break;
    8135            0 :       t *= 10;
    8136            0 :       t += *str - '0';
    8137              :    }
    8138            0 :    return t;
    8139              : }
    8140              : 
    8141              : /*------------------------------------------------------------------*/
    8142              : 
    8143            0 : std::string time_to_string(time_t t)
    8144              : {
    8145              :    char buf[256];
    8146            0 :    sprintf(buf, "%.0f", (double)t);
    8147            0 :    return buf;
    8148              : }
    8149              : 
    8150              : /*------------------------------------------------------------------*/
    8151              : 
    8152              : static bool gDoSetupHistoryWatch = true;
    8153              : static bool gDoReloadHistory = false;
    8154              : 
    8155            0 : static void history_watch_callback(HNDLE hDB, HNDLE hKey, int index, void* info)
    8156              : {
    8157              :    //printf("history_watch_callback %d %d %d\n", hDB, hKey, index);
    8158            0 :    gDoReloadHistory = true;
    8159            0 :    cm_msg(MINFO, "history_watch_callback", "History configuration may have changed, will reconnect");
    8160            0 : }
    8161              : 
    8162              : static MidasHistoryInterface* gMh = NULL;
    8163              : static HNDLE gMhkey = 0;
    8164              : 
    8165              : /*------------------------------------------------------------------*/
    8166              : 
    8167            0 : static MidasHistoryInterface* get_history(bool reset = false)
    8168              : {
    8169              :    int status;
    8170              :    HNDLE hDB;
    8171              : 
    8172              :    // history reconnect requested by watch callback?
    8173              : 
    8174            0 :    if (gDoReloadHistory) {
    8175            0 :       gDoReloadHistory = false;
    8176            0 :       reset = true;
    8177              :    }
    8178              : 
    8179              :    // disconnect from previous history
    8180              : 
    8181            0 :    if (reset && gMh) {
    8182            0 :       gMh->hs_disconnect();
    8183            0 :       delete gMh;
    8184            0 :       gMh = NULL;
    8185            0 :       gMhkey = 0;
    8186              :    }
    8187              : 
    8188            0 :    status = cm_get_experiment_database(&hDB, NULL);
    8189            0 :    assert(status == CM_SUCCESS);
    8190              : 
    8191              :    // setup a watch on history configuration
    8192              : 
    8193            0 :    if (gDoSetupHistoryWatch) {
    8194              :       HNDLE hKey;
    8195            0 :       gDoSetupHistoryWatch = false;
    8196              : 
    8197            0 :       status = db_find_key(hDB, 0, "/Logger/History", &hKey);
    8198            0 :       if (status == DB_SUCCESS)
    8199            0 :          status = db_watch(hDB, hKey, history_watch_callback, NULL);
    8200              : 
    8201            0 :       status = db_find_key(hDB, 0, "/History/LoggerHistoryChannel", &hKey);
    8202            0 :       if (status == DB_SUCCESS)
    8203            0 :          status = db_watch(hDB, hKey, history_watch_callback, NULL);
    8204              :    }
    8205              : 
    8206              :    // find out if ODB settings have changed and we need to connect to a different history channel
    8207              : 
    8208            0 :    HNDLE hKey = 0;
    8209            0 :    status = hs_find_reader_channel(hDB, &hKey, verbose);
    8210            0 :    if (status != HS_SUCCESS)
    8211            0 :       return gMh;
    8212              : 
    8213              :    //printf("mh %p, hKey %d, mhkey %d\n", mh, hKey, mhkey);
    8214              : 
    8215            0 :    if (gMh && hKey == gMhkey) // same channel as before
    8216            0 :       return gMh;
    8217              : 
    8218            0 :    if (gMh) {
    8219            0 :       delete gMh;
    8220            0 :       gMh = NULL;
    8221            0 :       gMhkey = 0;
    8222              :    }
    8223              : 
    8224            0 :    status = hs_get_history(hDB, hKey, HS_GET_READER|HS_GET_INACTIVE, verbose, &gMh);
    8225            0 :    if (status != HS_SUCCESS || gMh==NULL) {
    8226            0 :       cm_msg(MERROR, "get_history", "Cannot configure history, hs_get_history() status %d", status);
    8227            0 :       gMh = NULL;
    8228            0 :       return NULL;
    8229              :    }
    8230              : 
    8231            0 :    gMhkey = hKey;
    8232              : 
    8233              :    // cm_msg(MINFO, "get_history", "Reading history from channel \'%s\' type \'%s\'", mh->name, mh->type);
    8234              : 
    8235            0 :    return gMh;
    8236              : }
    8237              : 
    8238              : /*------------------------------------------------------------------*/
    8239              : 
    8240              : #ifdef OS_WINNT
    8241              : #undef DELETE
    8242              : #endif
    8243              : 
    8244              : #define ALLOC(t,n) (t*)calloc(sizeof(t),(n))
    8245              : #define DELETE(x) if (x) { free(x); (x)=NULL; }
    8246              : #define DELETEA(x, n) if (x) { for (int i=0; i<(n); i++) { free((x)[i]); (x)[i]=NULL; }; DELETE(x); }
    8247              : #define STRDUP(x) strdup(x)
    8248              : 
    8249              : struct HistoryData
    8250              : {
    8251              :    int nvars;
    8252              :    int alloc_nvars;
    8253              :    char** event_names;
    8254              :    char** var_names;
    8255              :    int* var_index;
    8256              :    int* odb_index;
    8257              :    int* status;
    8258              :    int* num_entries;
    8259              :    time_t** t;
    8260              :    double** v;
    8261              : 
    8262              :    bool have_last_written;
    8263              :    time_t* last_written;
    8264              : 
    8265              :    time_t tstart;
    8266              :    time_t tend;
    8267              :    time_t scale;
    8268              : 
    8269            0 :    void Allocate(int xnvars) {
    8270            0 :       if (alloc_nvars > 0)
    8271            0 :          Free();
    8272            0 :       nvars = 0;
    8273            0 :       alloc_nvars = xnvars;
    8274            0 :       event_names = ALLOC(char*, alloc_nvars);
    8275            0 :       var_names = ALLOC(char*, alloc_nvars);
    8276            0 :       var_index = ALLOC(int, alloc_nvars);
    8277            0 :       odb_index = ALLOC(int, alloc_nvars);
    8278            0 :       status = ALLOC(int, alloc_nvars);
    8279            0 :       num_entries = ALLOC(int, alloc_nvars);
    8280            0 :       t = ALLOC(time_t*, alloc_nvars);
    8281            0 :       v = ALLOC(double*, alloc_nvars);
    8282              : 
    8283            0 :       have_last_written = false;
    8284            0 :       last_written = ALLOC(time_t, alloc_nvars);
    8285            0 :    }
    8286              : 
    8287            0 :    void Free() {
    8288            0 :       DELETEA(event_names, alloc_nvars);
    8289            0 :       DELETEA(var_names, alloc_nvars);
    8290            0 :       DELETE(var_index);
    8291            0 :       DELETE(odb_index);
    8292            0 :       DELETE(status);
    8293            0 :       DELETE(num_entries);
    8294            0 :       DELETEA(t, alloc_nvars);
    8295            0 :       DELETEA(v, alloc_nvars);
    8296            0 :       DELETE(last_written);
    8297            0 :       nvars = 0;
    8298            0 :       alloc_nvars = 0;
    8299            0 :       have_last_written = false;
    8300            0 :    }
    8301              : 
    8302            0 :    void Print() const {
    8303            0 :       printf("this %p, nvars %d. tstart %d, tend %d, scale %d\n", this, nvars, (int)tstart, (int)tend, (int)scale);
    8304            0 :       for (int i=0; i<nvars; i++) {
    8305            0 :          printf("var[%d]: [%s/%s][%d] %d entries, status %d", i, event_names[i], var_names[i], var_index[i], num_entries[i], status[i]);
    8306            0 :          if (status[i]==HS_SUCCESS && num_entries[i]>0 && t[i] && v[i])
    8307            0 :             printf(", t %d:%d, v %g:%g", (int)t[i][0], (int)t[i][num_entries[i]-1], v[i][0], v[i][num_entries[i]-1]);
    8308            0 :          printf(" last_written %d", (int)last_written[i]);
    8309            0 :          printf("\n");
    8310              :       }
    8311            0 :    }
    8312              : 
    8313            0 :    HistoryData() // ctor
    8314            0 :    {
    8315            0 :       nvars = 0;
    8316            0 :       alloc_nvars = 0;
    8317            0 :       have_last_written = false;
    8318            0 :       tstart = 0;
    8319            0 :       tend = 0;
    8320            0 :       scale = 0;
    8321            0 :    }
    8322              : 
    8323            0 :    ~HistoryData() // dtor
    8324              :    {
    8325            0 :       if (alloc_nvars > 0)
    8326            0 :          Free();
    8327            0 :    }
    8328              : };
    8329              : 
    8330              : #define READ_HISTORY_DATA         0x1
    8331              : #define READ_HISTORY_RUNMARKER    0x2
    8332              : #define READ_HISTORY_LAST_WRITTEN 0x4
    8333              : 
    8334              : struct HistVar
    8335              : {
    8336              :    std::string event_name;
    8337              :    std::string tag_name;
    8338              :    std::string formula;
    8339              :    std::string colour;
    8340              :    std::string label;
    8341              :    bool        show_raw_value = false;
    8342              :    int         order = -1;
    8343              :    double factor = 1.0;
    8344              :    double offset = 0;
    8345              :    double voffset = 0;
    8346              : };
    8347              : 
    8348              : struct HistPlot
    8349              : {
    8350              :    std::string timescale = "1h";
    8351              :    double minimum = 0;
    8352              :    double maximum = 0;
    8353              :    bool zero_ylow = false;
    8354              :    bool log_axis  = false;
    8355              :    bool show_run_markers = true;
    8356              :    bool show_values = true;
    8357              :    bool show_fill   = true;
    8358              :    bool show_factor = false;
    8359              :    bool enable_factor = true;
    8360              : 
    8361              :    std::vector<HistVar> vars;
    8362              : };
    8363              : 
    8364              : static void LoadHistPlotFromOdb(MVOdb* odb, HistPlot* hp, const char* group, const char* panel);
    8365              : 
    8366            0 : int read_history(const HistPlot& hp, /*HNDLE hDB, const char *group, const char *panel,*/ int index, int flags, time_t tstart, time_t tend, time_t scale, HistoryData *data)
    8367              : {
    8368              :    //HNDLE hkeypanel, hkeydvar, hkey;
    8369              :    //KEY key;
    8370              :    //char path[256];
    8371              :    //int n_vars;
    8372              :    int status;
    8373            0 :    int debug = 1;
    8374              : 
    8375              :    //mstrlcpy(path, group, sizeof(path));
    8376              :    //mstrlcat(path, "/", sizeof(path));
    8377              :    //mstrlcat(path, panel, sizeof(path));
    8378              : 
    8379              :    //printf("read_history, path %s, index %d, flags 0x%x, start %d, end %d, scale %d, data %p\n", path, index, flags, (int)tstart, (int)tend, (int)scale, data);
    8380              : 
    8381              :    /* connect to history */
    8382            0 :    MidasHistoryInterface* mh = get_history();
    8383            0 :    if (mh == NULL) {
    8384              :       //r->rsprintf(str, "History is not configured\n");
    8385            0 :       return HS_FILE_ERROR;
    8386              :    }
    8387              : 
    8388              : #if 0
    8389              :    /* check panel name in ODB */
    8390              :    status = db_find_key(hDB, 0, "/History/Display", &hkey);
    8391              :    if (!hkey) {
    8392              :       cm_msg(MERROR, "read_history", "Cannot find \'/History/Display\' in ODB, status %d", status);
    8393              :       return HS_FILE_ERROR;
    8394              :    }
    8395              : 
    8396              :    /* check panel name in ODB */
    8397              :    status = db_find_key(hDB, hkey, path, &hkeypanel);
    8398              :    if (!hkeypanel) {
    8399              :       cm_msg(MERROR, "read_history", "Cannot find \'%s\' in ODB, status %d", path, status);
    8400              :       return HS_FILE_ERROR;
    8401              :    }
    8402              : 
    8403              :    status = db_find_key(hDB, hkeypanel, "Variables", &hkeydvar);
    8404              :    if (!hkeydvar) {
    8405              :       cm_msg(MERROR, "read_history", "Cannot find \'%s/Variables\' in ODB, status %d", path, status);
    8406              :       return HS_FILE_ERROR;
    8407              :    }
    8408              : 
    8409              :    db_get_key(hDB, hkeydvar, &key);
    8410              :    n_vars = key.num_values;
    8411              : #endif
    8412              : 
    8413            0 :    data->Allocate(hp.vars.size()+2);
    8414              : 
    8415            0 :    data->tstart = tstart;
    8416            0 :    data->tend = tend;
    8417            0 :    data->scale = scale;
    8418              : 
    8419            0 :    for (size_t i=0; i<hp.vars.size(); i++) {
    8420            0 :       if (index != -1 && (size_t)index != i)
    8421            0 :          continue;
    8422              : 
    8423              :       //char str[256];
    8424              :       //int size = sizeof(str);
    8425              :       //status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
    8426              :       //if (status != DB_SUCCESS) {
    8427              :       //   cm_msg(MERROR, "read_history", "Cannot read tag %d in panel %s, status %d", i, path, status);
    8428              :       //   continue;
    8429              :       //}
    8430              : 
    8431              :       /* split varname in event, variable and index: "event/tag[index]" */
    8432              : 
    8433              :       //char *p = strchr(str, ':');
    8434              :       //if (!p)
    8435              :          //   p = strchr(str, '/');
    8436              :       //
    8437              :       //if (!p) {
    8438              :       //   cm_msg(MERROR, "read_history", "Tag \"%s\" has wrong format in panel \"%s\"", str, path);
    8439              :       //   continue;
    8440              :       //}
    8441              : 
    8442              :       //*p = 0;
    8443              : 
    8444            0 :       data->odb_index[data->nvars] = i;
    8445            0 :       data->event_names[data->nvars] = STRDUP(hp.vars[i].event_name.c_str());
    8446            0 :       data->var_names[data->nvars] = STRDUP(hp.vars[i].tag_name.c_str());
    8447            0 :       data->var_index[data->nvars] = 0;
    8448              : 
    8449            0 :       char *q = strchr(data->var_names[data->nvars], '[');
    8450            0 :       if (q) {
    8451            0 :          data->var_index[data->nvars] = atoi(q+1);
    8452            0 :          *q = 0;
    8453              :       }
    8454              : 
    8455            0 :       data->nvars++;
    8456              :    } // loop over variables
    8457              : 
    8458              :    /* write run markes if selected */
    8459            0 :    if (flags & READ_HISTORY_RUNMARKER) {
    8460              : 
    8461            0 :       data->event_names[data->nvars+0] = STRDUP("Run transitions");
    8462            0 :       data->event_names[data->nvars+1] = STRDUP("Run transitions");
    8463              : 
    8464            0 :       data->var_names[data->nvars+0] = STRDUP("State");
    8465            0 :       data->var_names[data->nvars+1] = STRDUP("Run number");
    8466              : 
    8467            0 :       data->var_index[data->nvars+0] = 0;
    8468            0 :       data->var_index[data->nvars+1] = 0;
    8469              : 
    8470            0 :       data->odb_index[data->nvars+0] = -1;
    8471            0 :       data->odb_index[data->nvars+1] = -2;
    8472              : 
    8473            0 :       data->nvars += 2;
    8474              :    }
    8475              : 
    8476            0 :    bool get_last_written = false;
    8477              : 
    8478            0 :    if (flags & READ_HISTORY_DATA) {
    8479            0 :       status = mh->hs_read(tstart, tend, scale,
    8480              :                            data->nvars,
    8481            0 :                            data->event_names,
    8482            0 :                            data->var_names,
    8483            0 :                            data->var_index,
    8484              :                            data->num_entries,
    8485              :                            data->t,
    8486              :                            data->v,
    8487              :                            data->status);
    8488              : 
    8489            0 :       if (debug) {
    8490            0 :          printf("read_history: nvars %d, hs_read() status %d\n", data->nvars, status);
    8491            0 :          for (int i=0; i<data->nvars; i++) {
    8492            0 :             printf("read_history: %d: event [%s], var [%s], index %d, odb index %d, status %d, num_entries %d\n", i, data->event_names[i], data->var_names[i], data->var_index[i], data->odb_index[i], data->status[i], data->num_entries[i]);
    8493              :          }
    8494              :       }
    8495              : 
    8496            0 :       if (status != HS_SUCCESS) {
    8497            0 :          cm_msg(MERROR, "read_history", "Complete history failure, hs_read() status %d, see messages", status);
    8498            0 :          return HS_FILE_ERROR;
    8499              :       }
    8500              : 
    8501            0 :       for (int i=0; i<data->nvars; i++) {
    8502            0 :          if (data->status[i] != HS_SUCCESS || data->num_entries[i] < 1) {
    8503            0 :             get_last_written = true;
    8504            0 :             break;
    8505              :          }
    8506              :       }
    8507              :    }
    8508              : 
    8509            0 :    if (flags & READ_HISTORY_LAST_WRITTEN)
    8510            0 :       get_last_written = true;
    8511              : 
    8512            0 :    if (get_last_written) {
    8513            0 :       data->have_last_written = true;
    8514              : 
    8515            0 :       status = mh->hs_get_last_written(
    8516              :                            tstart,
    8517              :                            data->nvars,
    8518            0 :                            data->event_names,
    8519            0 :                            data->var_names,
    8520            0 :                            data->var_index,
    8521              :                            data->last_written);
    8522              : 
    8523            0 :       if (status != HS_SUCCESS) {
    8524            0 :          data->have_last_written = false;
    8525              :       }
    8526              :    }
    8527              : 
    8528            0 :    return SUCCESS;
    8529              : }
    8530              : 
    8531            0 : int get_hist_last_written(MVOdb* odb, const char *group, const char *panel, time_t endtime, int index, int want_all, time_t *plastwritten)
    8532              : {
    8533              :    //HNDLE hDB;
    8534              :    int status;
    8535              : 
    8536            0 :    time_t now = ss_time();
    8537              : 
    8538            0 :    if (endtime == 0)
    8539            0 :       endtime = now;
    8540              : 
    8541            0 :    HistoryData  hsxxx;
    8542            0 :    HistoryData* hsdata = &hsxxx;
    8543              : 
    8544              :    //cm_get_experiment_database(&hDB, NULL);
    8545              : 
    8546            0 :    HistPlot hp;
    8547            0 :    LoadHistPlotFromOdb(odb, &hp, group, panel);
    8548              : 
    8549            0 :    double tstart = ss_millitime();
    8550              : 
    8551            0 :    int flags = READ_HISTORY_LAST_WRITTEN;
    8552              : 
    8553            0 :    status = read_history(hp, /*hDB, group, panel,*/ index, flags, endtime, endtime, 0, hsdata);
    8554              : 
    8555            0 :    if (status != HS_SUCCESS) {
    8556              :       //sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
    8557            0 :       return status;
    8558              :    }
    8559              : 
    8560            0 :    if (!hsdata->have_last_written) {
    8561              :       //sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
    8562            0 :       return HS_FILE_ERROR;
    8563              :    }
    8564              : 
    8565            0 :    int count = 0;
    8566            0 :    time_t tmin = endtime;
    8567            0 :    time_t tmax = 0;
    8568              : 
    8569            0 :    for (int k=0; k<hsdata->nvars; k++) {
    8570            0 :       int i = hsdata->odb_index[k];
    8571              : 
    8572            0 :       if (i<0)
    8573            0 :          continue;
    8574            0 :       if (index != -1 && index != i)
    8575            0 :          continue;
    8576              : 
    8577            0 :       time_t lw = hsdata->last_written[k];
    8578              : 
    8579            0 :       if (lw==0) // no last_written for this variable, skip it.
    8580            0 :          continue;
    8581              : 
    8582            0 :       if (lw > endtime)
    8583            0 :          lw = endtime; // just in case hs_get_last_written() returns dates in the "future" for this plot
    8584              : 
    8585            0 :       if (lw > tmax)
    8586            0 :          tmax = lw;
    8587              : 
    8588            0 :       if (lw < tmin)
    8589            0 :          tmin = lw;
    8590              : 
    8591            0 :       count++;
    8592              : 
    8593              :       //printf("odb index %d, last_written[%d] = %.0f, tmin %.0f, tmax %.0f, endtime %.0f\n", i, k, (double)lw, (double)tmin, (double)tmax, (double)endtime);
    8594              :    }
    8595              : 
    8596            0 :    if (count == 0) // all variables have no last_written
    8597            0 :       return HS_FILE_ERROR;
    8598              : 
    8599            0 :    if (want_all)
    8600            0 :       *plastwritten = tmin; // all variables have data
    8601              :    else
    8602            0 :       *plastwritten = tmax; // at least one variable has data
    8603              : 
    8604              :    //printf("tmin %.0f, tmax %.0f, endtime %.0f, last written %.0f\n", (double)tmin, (double)tmax, (double)endtime, (double)*plastwritten);
    8605              : 
    8606            0 :    double tend = ss_millitime();
    8607              : 
    8608              :    if (/* DISABLES CODE */ (0))
    8609              :       printf("get_hist_last_written: elapsed time %f ms\n", tend-tstart);
    8610              : 
    8611            0 :    return HS_SUCCESS;
    8612            0 : }
    8613              : 
    8614            0 : void generate_hist_graph(MVOdb* odb, Return* rr, const char *hgroup, const char *hpanel, char *buffer, int *buffer_size,
    8615              :                          int width, int height,
    8616              :                          time_t xendtime,
    8617              :                          int scale,
    8618              :                          int index,
    8619              :                          int labels, const char *bgcolor, const char *fgcolor, const char *gridcolor)
    8620              : {
    8621              :    HNDLE hDB;
    8622              :    //KEY key;
    8623              :    gdImagePtr im;
    8624              :    gdGifBuffer gb;
    8625              :    int i, j, k, l;
    8626              :    //int n_vars;
    8627              :    int size, status, r, g, b;
    8628              :    //int x_marker;
    8629              :    int length;
    8630              :    int white, grey, red;
    8631              :    //int black, ltgrey, green, blue;
    8632              :    int fgcol, bgcol, gridcol;
    8633              :    int curve_col[MAX_VARS];
    8634              :    int state_col[3];
    8635              :    char str[256], *p;
    8636              :    //INT var_index[MAX_VARS];
    8637              :    //char event_name[MAX_VARS][NAME_LENGTH];
    8638              :    //char tag_name[MAX_VARS][64];
    8639              :    //char var_name[MAX_VARS][NAME_LENGTH];
    8640              :    //char varname[64];
    8641              :    //char key_name[256];
    8642              :    //float factor[MAX_VARS], offset[MAX_VARS];
    8643              :    //BOOL logaxis, runmarker;
    8644              :    //double xmin, xrange;
    8645              :    double ymin, ymax;
    8646              :    double upper_limit[MAX_VARS], lower_limit[MAX_VARS];
    8647              :    //float minvalue = (float) -HUGE_VAL;
    8648              :    //float maxvalue = (float) +HUGE_VAL;
    8649              :    //int show_values = 0;
    8650              :    //int sort_vars = 0;
    8651              :    //int old_vars = 0;
    8652              :    time_t starttime, endtime;
    8653              :    int flags;
    8654              : 
    8655            0 :    time_t now = ss_time();
    8656              : 
    8657            0 :    if (xendtime == 0)
    8658            0 :       xendtime = now;
    8659              : 
    8660            0 :    HistPlot hp;
    8661            0 :    LoadHistPlotFromOdb(odb, &hp, hgroup, hpanel);
    8662              : 
    8663            0 :    std::vector<int> var_index; var_index.resize(hp.vars.size());
    8664              : 
    8665            0 :    for (size_t i=0; i<hp.vars.size(); i++) {
    8666            0 :       var_index[i] = 0;
    8667            0 :       const char *vp = strchr(hp.vars[i].tag_name.c_str(), '[');
    8668            0 :       if (vp) {
    8669            0 :          var_index[i] = atoi(vp + 1);
    8670              :       }
    8671              :    }
    8672              : 
    8673            0 :    int logaxis = hp.log_axis;
    8674            0 :    double minvalue = hp.minimum;
    8675            0 :    double maxvalue = hp.maximum;
    8676              :    
    8677            0 :    if ((minvalue == 0) && (maxvalue == 0)) {
    8678            0 :       minvalue = -HUGE_VAL;
    8679            0 :       maxvalue = +HUGE_VAL;
    8680              :    }
    8681              : 
    8682            0 :    std::vector<int> x[MAX_VARS];
    8683            0 :    std::vector<double> y[MAX_VARS];
    8684              : 
    8685            0 :    HistoryData  hsxxx;
    8686            0 :    HistoryData* hsdata = &hsxxx;
    8687              : 
    8688            0 :    cm_get_experiment_database(&hDB, NULL);
    8689              : 
    8690              :    /* generate image */
    8691            0 :    im = gdImageCreate(width, height);
    8692              : 
    8693              :    /* allocate standard colors */
    8694            0 :    sscanf(bgcolor, "%02x%02x%02x", &r, &g, &b);
    8695            0 :    bgcol = gdImageColorAllocate(im, r, g, b);
    8696            0 :    sscanf(fgcolor, "%02x%02x%02x", &r, &g, &b);
    8697            0 :    fgcol = gdImageColorAllocate(im, r, g, b);
    8698            0 :    sscanf(gridcolor, "%02x%02x%02x", &r, &g, &b);
    8699            0 :    gridcol = gdImageColorAllocate(im, r, g, b);
    8700              : 
    8701            0 :    grey = gdImageColorAllocate(im, 192, 192, 192);
    8702              :    //ltgrey = gdImageColorAllocate(im, 208, 208, 208);
    8703            0 :    white = gdImageColorAllocate(im, 255, 255, 255);
    8704              :    //black = gdImageColorAllocate(im, 0, 0, 0);
    8705            0 :    red = gdImageColorAllocate(im, 255, 0, 0);
    8706              :    //green = gdImageColorAllocate(im, 0, 255, 0);
    8707              :    //blue = gdImageColorAllocate(im, 0, 0, 255);
    8708              : 
    8709            0 :    curve_col[0] = gdImageColorAllocate(im, 0, 0, 255);
    8710            0 :    curve_col[1] = gdImageColorAllocate(im, 0, 192, 0);
    8711            0 :    curve_col[2] = gdImageColorAllocate(im, 255, 0, 0);
    8712            0 :    curve_col[3] = gdImageColorAllocate(im, 0, 192, 192);
    8713            0 :    curve_col[4] = gdImageColorAllocate(im, 255, 0, 255);
    8714            0 :    curve_col[5] = gdImageColorAllocate(im, 192, 192, 0);
    8715            0 :    curve_col[6] = gdImageColorAllocate(im, 128, 128, 128);
    8716            0 :    curve_col[7] = gdImageColorAllocate(im, 128, 255, 128);
    8717            0 :    curve_col[8] = gdImageColorAllocate(im, 255, 128, 128);
    8718            0 :    curve_col[9] = gdImageColorAllocate(im, 128, 128, 255);
    8719            0 :    for (i=10; i<MAX_VARS; i++)
    8720            0 :       curve_col[i] = gdImageColorAllocate(im, 128, 128, 128);
    8721              : 
    8722            0 :    state_col[0] = gdImageColorAllocate(im, 255, 0, 0);
    8723            0 :    state_col[1] = gdImageColorAllocate(im, 255, 255, 0);
    8724            0 :    state_col[2] = gdImageColorAllocate(im, 0, 255, 0);
    8725              : 
    8726              :    /* Set transparent color. */
    8727            0 :    gdImageColorTransparent(im, grey);
    8728              : 
    8729              :    /* Title */
    8730            0 :    gdImageString(im, gdFontGiant, width / 2 - (strlen(hpanel) * gdFontGiant->w) / 2, 2, (char*)hpanel, fgcol);
    8731              : 
    8732              :    /* connect to history */
    8733            0 :    MidasHistoryInterface *mh = get_history();
    8734            0 :    if (mh == NULL) {
    8735            0 :       sprintf(str, "History is not configured, see messages");
    8736            0 :       gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8737            0 :       goto error;
    8738              :    }
    8739              : 
    8740              :    ///* check panel name in ODB */
    8741              :    //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
    8742              :    //db_find_key(hDB, 0, str, &hkeypanel);
    8743              :    //if (!hkeypanel) {
    8744              :    //   sprintf(str, "Cannot find /History/Display/%s/%s in ODB", hgroup, hpanel);
    8745              :    //   gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8746              :    //   goto error;
    8747              :    //}
    8748              : 
    8749              :    //db_find_key(hDB, hkeypanel, "Variables", &hkeydvar);
    8750              :    //if (!hkeydvar) {
    8751              :    //   sprintf(str, "Cannot find /History/Display/%s/%s/Variables in ODB", hgroup, hpanel);
    8752              :    //   gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8753              :    //   goto error;
    8754              :    //}
    8755              : 
    8756              :    //db_get_key(hDB, hkeydvar, &key);
    8757              :    //n_vars = key.num_values;
    8758              : 
    8759            0 :    if (hp.vars.empty()) {
    8760            0 :       sprintf(str, "No variables in panel %s/%s", hgroup, hpanel);
    8761            0 :       gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8762            0 :       goto error;
    8763              :    }
    8764              : 
    8765            0 :    if (hp.vars.size() > MAX_VARS) {
    8766            0 :       sprintf(str, "Too many variables in panel %s/%s", hgroup, hpanel);
    8767            0 :       gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8768            0 :       goto error;
    8769              :    }
    8770              : 
    8771            0 :    ymin = ymax = 0;
    8772              :    //logaxis = runmarker = 0;
    8773              : 
    8774            0 :    for (i = 0; i < (int)hp.vars.size(); i++) {
    8775            0 :       if (index != -1 && index != i)
    8776            0 :          continue;
    8777              : 
    8778              :       //size = sizeof(str);
    8779              :       //status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
    8780              :       //if (status != DB_SUCCESS) {
    8781              :       //   sprintf(str, "Cannot read tag %d in panel %s/%s, status %d", i, hgroup, hpanel, status);
    8782              :       //   gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8783              :       //   goto error;
    8784              :       //}
    8785              :       //
    8786              :       //mstrlcpy(tag_name[i], str, sizeof(tag_name[0]));
    8787              : 
    8788              :       ///* split varname in event, variable and index */
    8789              :       //char *tp = strchr(tag_name[i], ':');
    8790              :       //if (tp) {
    8791              :       //   mstrlcpy(event_name[i], tag_name[i], sizeof(event_name[0]));
    8792              :       //   char *ep = strchr(event_name[i], ':');
    8793              :       //   if (ep)
    8794              :       //      *ep = 0;
    8795              :       //   mstrlcpy(var_name[i], tp+1, sizeof(var_name[0]));
    8796              :       //   var_index[i] = 0;
    8797              :       //   char *vp = strchr(var_name[i], '[');
    8798              :       //   if (vp) {
    8799              :       //      var_index[i] = atoi(vp + 1);
    8800              :       //      *vp = 0;
    8801              :       //  }
    8802              :       //} else {
    8803              :       //   sprintf(str, "Tag \"%s\" has wrong format in panel \"%s/%s\"", tag_name[i], hgroup, hpanel);
    8804              :       //   gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    8805              :       //   goto error;
    8806              :       //}
    8807              :       //
    8808              :       //db_find_key(hDB, hkeypanel, "Colour", &hkey);
    8809              :       //if (hkey) {
    8810              :       //   size = sizeof(str);
    8811              :       //   status = db_get_data_index(hDB, hkey, str, &size, i, TID_STRING);
    8812              :       //   if (status == DB_SUCCESS) {
    8813              :       //      if (str[0] == '#') {
    8814              :       //         char sss[3];
    8815              :       //         int r, g, b;
    8816              :       //
    8817              :       //         sss[0] = str[1];
    8818              :       //         sss[1] = str[2];
    8819              :       //         sss[2] = 0;
    8820              :       //         r = strtoul(sss, NULL, 16);
    8821              :       //         sss[0] = str[3];
    8822              :       //         sss[1] = str[4];
    8823              :       //         sss[2] = 0;
    8824              :       //         g = strtoul(sss, NULL, 16);
    8825              :       //         sss[0] = str[5];
    8826              :       //         sss[1] = str[6];
    8827              :       //         sss[2] = 0;
    8828              :       //         b = strtoul(sss, NULL, 16);
    8829              :       //
    8830              :       //         curve_col[i] = gdImageColorAllocate(im, r, g, b);
    8831              :       //            }
    8832              :       //         }
    8833              :       //}
    8834              : 
    8835            0 :       if (hp.vars[i].colour[0] == '#') {
    8836            0 :          const char* str = hp.vars[i].colour.c_str();
    8837              :          char sss[3];
    8838              :          int r, g, b;
    8839              :          
    8840            0 :          sss[0] = str[1];
    8841            0 :          sss[1] = str[2];
    8842            0 :          sss[2] = 0;
    8843            0 :          r = strtoul(sss, NULL, 16);
    8844            0 :          sss[0] = str[3];
    8845            0 :          sss[1] = str[4];
    8846            0 :          sss[2] = 0;
    8847            0 :          g = strtoul(sss, NULL, 16);
    8848            0 :          sss[0] = str[5];
    8849            0 :          sss[1] = str[6];
    8850            0 :          sss[2] = 0;
    8851            0 :          b = strtoul(sss, NULL, 16);
    8852              :          
    8853            0 :          curve_col[i] = gdImageColorAllocate(im, r, g, b);
    8854              :       }
    8855              : 
    8856              :       /* get timescale */
    8857            0 :       if (scale == 0) {
    8858              :          //std::string ts = "1h";
    8859              :          //status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
    8860              :          //if (status != DB_SUCCESS) {
    8861              :          //   /* delete old integer key */
    8862              :          //   db_find_key(hDB, hkeypanel, "Timescale", &hkey);
    8863              :          //   if (hkey)
    8864              :          //      db_delete_key(hDB, hkey, FALSE);
    8865              :          //
    8866              :          //   ts = "1h";
    8867              :          //   status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
    8868              :          //}
    8869              : 
    8870            0 :          scale = time_to_sec(hp.timescale.c_str());
    8871              :       }
    8872              : 
    8873              :       //for (j = 0; j < MAX_VARS; j++) {
    8874              :       //   factor[j] = 1;
    8875              :       //   offset[j] = 0;
    8876              :       //}
    8877              : 
    8878              :       ///* get factors */
    8879              :       //size = sizeof(float) * n_vars;
    8880              :       //db_get_value(hDB, hkeypanel, "Factor", factor, &size, TID_FLOAT, TRUE);
    8881              : 
    8882              :       ///* get offsets */
    8883              :       //size = sizeof(float) * n_vars;
    8884              :       //db_get_value(hDB, hkeypanel, "Offset", offset, &size, TID_FLOAT, TRUE);
    8885              : 
    8886              :       ///* get axis type */
    8887              :       //size = sizeof(logaxis);
    8888              :       //logaxis = 0;
    8889              :       //db_get_value(hDB, hkeypanel, "Log axis", &logaxis, &size, TID_BOOL, TRUE);
    8890              : 
    8891              :       ///* get show_values type */
    8892              :       //size = sizeof(show_values);
    8893              :       //show_values = 0;
    8894              :       //db_get_value(hDB, hkeypanel, "Show values", &show_values, &size, TID_BOOL, TRUE);
    8895              : 
    8896              :       ///* get sort_vars type */
    8897              :       //size = sizeof(sort_vars);
    8898              :       //sort_vars = 0;
    8899              :       //db_get_value(hDB, hkeypanel, "Sort vars", &sort_vars, &size, TID_BOOL, TRUE);
    8900              : 
    8901              :       ///* get old_vars type */
    8902              :       //size = sizeof(old_vars);
    8903              :       //old_vars = 0;
    8904              :       //db_get_value(hDB, hkeypanel, "Show old vars", &old_vars, &size, TID_BOOL, TRUE);
    8905              : 
    8906              :       ///* get min value */
    8907              :       //size = sizeof(minvalue);
    8908              :       //minvalue = (float) -HUGE_VAL;
    8909              :       //db_get_value(hDB, hkeypanel, "Minimum", &minvalue, &size, TID_FLOAT, TRUE);
    8910              : 
    8911              :       ///* get max value */
    8912              :       //size = sizeof(maxvalue);
    8913              :       //maxvalue = (float) +HUGE_VAL;
    8914              :       //db_get_value(hDB, hkeypanel, "Maximum", &maxvalue, &size, TID_FLOAT, TRUE);
    8915              : 
    8916              :       //if ((minvalue == 0) && (maxvalue == 0)) {
    8917              :       //   minvalue = (float) -HUGE_VAL;
    8918              :       //   maxvalue = (float) +HUGE_VAL;
    8919              :       //}
    8920              : 
    8921              :       ///* get runmarker flag */
    8922              :       //size = sizeof(runmarker);
    8923              :       //runmarker = 1;
    8924              :       //db_get_value(hDB, hkeypanel, "Show run markers", &runmarker, &size, TID_BOOL, TRUE);
    8925              : 
    8926              :       /* make ODB path from tag name */
    8927            0 :       std::string odbpath;
    8928            0 :       HNDLE hkeyeq = 0;
    8929              :       HNDLE hkeyroot;
    8930            0 :       db_find_key(hDB, 0, "/Equipment", &hkeyroot);
    8931            0 :       if (hkeyroot) {
    8932            0 :          for (j = 0;; j++) {
    8933              :             HNDLE hkeyeq;
    8934            0 :             db_enum_key(hDB, hkeyroot, j, &hkeyeq);
    8935              : 
    8936            0 :             if (!hkeyeq)
    8937            0 :                break;
    8938              : 
    8939              :             KEY key;
    8940            0 :             db_get_key(hDB, hkeyeq, &key);
    8941            0 :             if (equal_ustring(key.name, hp.vars[i].event_name.c_str())) {
    8942              :                /* check if variable is individual key under variables/ */
    8943            0 :                sprintf(str, "Variables/%s", hp.vars[i].tag_name.c_str());
    8944              :                HNDLE hkey;
    8945            0 :                db_find_key(hDB, hkeyeq, str, &hkey);
    8946            0 :                if (hkey) {
    8947              :                   //sprintf(odbpath, "/Equipment/%s/Variables/%s", event_name[i], var_name[i]);
    8948            0 :                   odbpath = "";
    8949            0 :                   odbpath += "/Equipment/";
    8950            0 :                   odbpath += hp.vars[i].event_name;
    8951            0 :                   odbpath += "/Variables/";
    8952            0 :                   odbpath += hp.vars[i].tag_name;
    8953            0 :                   break;
    8954              :                }
    8955              : 
    8956              :                /* check if variable is in setttins/names array */
    8957              :                HNDLE hkeynames;
    8958            0 :                db_find_key(hDB, hkeyeq, "Settings/Names", &hkeynames);
    8959            0 :                if (hkeynames) {
    8960              :                   /* extract variable name and Variables/<key> */
    8961            0 :                   mstrlcpy(str, hp.vars[i].tag_name.c_str(), sizeof(str));
    8962            0 :                   p = str + strlen(str) - 1;
    8963            0 :                   while (p > str && *p != ' ')
    8964            0 :                      p--;
    8965            0 :                   std::string key_name = p + 1;
    8966            0 :                   *p = 0;
    8967              : 
    8968            0 :                   std::string varname = str;
    8969              : 
    8970              :                   /* find key in single name array */
    8971            0 :                   db_get_key(hDB, hkeynames, &key);
    8972            0 :                   for (k = 0; k < key.num_values; k++) {
    8973            0 :                      size = sizeof(str);
    8974            0 :                      db_get_data_index(hDB, hkeynames, str, &size, k, TID_STRING);
    8975            0 :                      if (equal_ustring(str, varname.c_str())) {
    8976              :                         //sprintf(odbpath, "/Equipment/%s/Variables/%s[%d]", event_name[i], key_name, k);
    8977            0 :                         odbpath = "";
    8978            0 :                         odbpath += "/Equipment/";
    8979            0 :                         odbpath += hp.vars[i].event_name;
    8980            0 :                         odbpath += "/Variables/";
    8981            0 :                         odbpath += key_name;
    8982            0 :                         odbpath += "[";
    8983            0 :                         odbpath += toString(k);
    8984            0 :                         odbpath += "]";
    8985            0 :                         break;
    8986              :                      }
    8987              :                   }
    8988            0 :                } else {
    8989              :                   /* go through /variables/<name> entries */
    8990              :                   HNDLE hkeyvars;
    8991            0 :                   db_find_key(hDB, hkeyeq, "Variables", &hkeyvars);
    8992            0 :                   if (hkeyvars) {
    8993            0 :                      for (k = 0;; k++) {
    8994            0 :                         db_enum_key(hDB, hkeyvars, k, &hkey);
    8995              : 
    8996            0 :                         if (!hkey)
    8997            0 :                            break;
    8998              : 
    8999              :                         /* find "settins/names <key>" for this key */
    9000            0 :                         db_get_key(hDB, hkey, &key);
    9001              : 
    9002              :                         /* find key in key_name array */
    9003            0 :                         std::string key_name = key.name;
    9004              : 
    9005            0 :                         std::string path;
    9006              :                         //sprintf(str, "Settings/Names %s", key_name);
    9007            0 :                         path += "Settings/Names ";
    9008            0 :                         path += key_name;
    9009              : 
    9010              :                         HNDLE hkeynames;
    9011            0 :                         db_find_key(hDB, hkeyeq, path.c_str(), &hkeynames);
    9012            0 :                         if (hkeynames) {
    9013            0 :                            db_get_key(hDB, hkeynames, &key);
    9014            0 :                            for (l = 0; l < key.num_values; l++) {
    9015            0 :                               size = sizeof(str);
    9016            0 :                               db_get_data_index(hDB, hkeynames, str, &size, l, TID_STRING);
    9017            0 :                               if (equal_ustring(str, hp.vars[i].tag_name.c_str())) {
    9018              :                                  //sprintf(odbpath, "/Equipment/%s/Variables/%s[%d]", event_name[i], key_name, l);
    9019            0 :                                  odbpath = "";
    9020            0 :                                  odbpath += "/Equipment/";
    9021            0 :                                  odbpath += hp.vars[i].event_name;
    9022            0 :                                  odbpath += "/Variables/";
    9023            0 :                                  odbpath += key_name;
    9024            0 :                                  odbpath += "[";
    9025            0 :                                  odbpath += toString(l);
    9026            0 :                                  odbpath += "]";
    9027            0 :                                  break;
    9028              :                               }
    9029              :                            }
    9030              :                         }
    9031            0 :                      }
    9032              :                   }
    9033              :                }
    9034              : 
    9035            0 :                break;
    9036              :             }
    9037            0 :          }
    9038              : 
    9039            0 :          if (!hkeyeq) {
    9040            0 :             db_find_key(hDB, 0, "/History/Links", &hkeyroot);
    9041            0 :             if (hkeyroot) {
    9042            0 :                for (j = 0;; j++) {
    9043              :                   HNDLE hkey;
    9044            0 :                   db_enum_link(hDB, hkeyroot, j, &hkey);
    9045              : 
    9046            0 :                   if (!hkey)
    9047            0 :                      break;
    9048              : 
    9049              :                   KEY key;
    9050            0 :                   db_get_key(hDB, hkey, &key);
    9051            0 :                   if (equal_ustring(key.name, hp.vars[i].event_name.c_str())) {
    9052            0 :                      db_enum_key(hDB, hkeyroot, j, &hkey);
    9053            0 :                      db_find_key(hDB, hkey, hp.vars[i].tag_name.c_str(), &hkey);
    9054            0 :                      if (hkey) {
    9055            0 :                         db_get_key(hDB, hkey, &key);
    9056            0 :                         odbpath = db_get_path(hDB, hkey);
    9057            0 :                         if (key.num_values > 1) {
    9058            0 :                            odbpath += "[";
    9059            0 :                            odbpath += toString(var_index[i]);
    9060            0 :                            odbpath += "]";
    9061              :                         }
    9062            0 :                         break;
    9063              :                      }
    9064              :                   }
    9065            0 :                }
    9066              :             }
    9067              :          }
    9068              :       }
    9069              : 
    9070              :       /* search alarm limits */
    9071            0 :       upper_limit[i] = lower_limit[i] = -12345;
    9072            0 :       db_find_key(hDB, 0, "Alarms/Alarms", &hkeyroot);
    9073            0 :       if (odbpath.length() > 0 && hkeyroot) {
    9074            0 :          for (j = 0;; j++) {
    9075              :             HNDLE hkey;
    9076            0 :             db_enum_key(hDB, hkeyroot, j, &hkey);
    9077              : 
    9078            0 :             if (!hkey)
    9079            0 :                break;
    9080              : 
    9081            0 :             size = sizeof(str);
    9082            0 :             db_get_value(hDB, hkey, "Condition", str, &size, TID_STRING, TRUE);
    9083              : 
    9084            0 :             if (strstr(str, odbpath.c_str())) {
    9085            0 :                if (strchr(str, '<')) {
    9086            0 :                   p = strchr(str, '<') + 1;
    9087            0 :                   if (*p == '=')
    9088            0 :                      p++;
    9089            0 :                   if (hp.enable_factor) {
    9090            0 :                      lower_limit[i] = (hp.vars[i].factor * (atof(p) - hp.vars[i].voffset) + hp.vars[i].offset);
    9091              :                   } else {
    9092            0 :                      lower_limit[i] = atof(p);
    9093              :                   }
    9094              :                }
    9095            0 :                if (strchr(str, '>')) {
    9096            0 :                   p = strchr(str, '>') + 1;
    9097            0 :                   if (*p == '=')
    9098            0 :                      p++;
    9099            0 :                   if (hp.enable_factor) {
    9100            0 :                      upper_limit[i] = (hp.vars[i].factor * (atof(p) - hp.vars[i].voffset) + hp.vars[i].offset);
    9101              :                   } else {
    9102            0 :                      upper_limit[i] = atof(p);
    9103              :                   }
    9104              :                }
    9105              :             }
    9106            0 :          }
    9107              :       }
    9108            0 :    } // loop over variables
    9109              : 
    9110              :    //starttime = now - scale + toffset;
    9111              :    //endtime = now + toffset;
    9112              : 
    9113            0 :    starttime = xendtime - scale;
    9114            0 :    endtime = xendtime;
    9115              : 
    9116              :    //printf("now %d, scale %d, xendtime %d, starttime %d, endtime %d\n", now, scale, xendtime, starttime, endtime);
    9117              : 
    9118            0 :    flags = READ_HISTORY_DATA;
    9119            0 :    if (hp.show_run_markers)
    9120            0 :       flags |= READ_HISTORY_RUNMARKER;
    9121              : 
    9122            0 :    status = read_history(hp, /*hDB, hgroup, hpanel,*/ index, flags, starttime, endtime, scale/1000+1, hsdata);
    9123              : 
    9124            0 :    if (status != HS_SUCCESS) {
    9125            0 :       sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
    9126            0 :       gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
    9127            0 :       goto error;
    9128              :    }
    9129              : 
    9130              :    DWORD n_point[MAX_VARS];
    9131              :    char var_status[MAX_VARS][256];
    9132              : 
    9133            0 :    for (int k=0; k<hsdata->nvars; k++) {
    9134            0 :       int i = hsdata->odb_index[k];
    9135              : 
    9136            0 :       if (i<0)
    9137            0 :          continue;
    9138              : 
    9139            0 :       if (index != -1 && index != i)
    9140            0 :          continue;
    9141              : 
    9142            0 :       n_point[i] = 0;
    9143              : 
    9144            0 :       var_status[i][0] = 0;
    9145            0 :       if (hsdata->status[k] == HS_UNDEFINED_VAR) {
    9146            0 :          sprintf(var_status[i], "not found in history");
    9147            0 :          continue;
    9148            0 :       } else if (hsdata->status[k] != HS_SUCCESS) {
    9149            0 :          sprintf(var_status[i], "hs_read() error %d, see messages", hsdata->status[k]);
    9150            0 :          continue;
    9151              :       }
    9152              : 
    9153            0 :       int n_vp = 0;
    9154            0 :       for (int j=0; j<hsdata->num_entries[k]; j++) {
    9155            0 :          int xx = (int)(hsdata->t[k][j]);
    9156            0 :          double yy = hsdata->v[k][j];
    9157              : 
    9158              :          /* skip NaNs */
    9159            0 :          if (ss_isnan(yy))
    9160            0 :             continue;
    9161              : 
    9162              :          /* skip INFs */
    9163            0 :          if (!ss_isfin(yy))
    9164            0 :             continue;
    9165              : 
    9166              :          /* avoid overflow */
    9167            0 :          if (yy > 1E30)
    9168            0 :             yy = 1E30f;
    9169              : 
    9170              :          /* apply factor and offset */
    9171            0 :          if (hp.enable_factor) {
    9172            0 :             yy = hp.vars[i].factor * (yy - hp.vars[i].voffset) + hp.vars[i].offset;
    9173              :          }
    9174              : 
    9175              :          /* calculate ymin and ymax */
    9176            0 :          if ((i == 0 || index != -1) && n_vp == 0)
    9177            0 :             ymin = ymax = yy;
    9178              :          else {
    9179            0 :             if (yy > ymax)
    9180            0 :                ymax = yy;
    9181            0 :             if (yy < ymin)
    9182            0 :                ymin = yy;
    9183              :          }
    9184              : 
    9185              :          /* increment number of valid points */
    9186              : 
    9187            0 :          x[i].push_back(xx);
    9188            0 :          y[i].push_back(yy);
    9189              : 
    9190            0 :          n_vp++;
    9191              : 
    9192              :       } // loop over data
    9193              : 
    9194            0 :       n_point[i] = n_vp;
    9195              : 
    9196            0 :       assert(x[i].size() == y[i].size());
    9197              :    }
    9198              : 
    9199              :    //int flag;
    9200              :    int xmaxm;
    9201              :    int row;
    9202              :    int xold;
    9203              :    int yold;
    9204              :    int aoffset;
    9205              :    double yb1, yb2, yf1, yf2;
    9206              :    int xs, ys;
    9207              :    int x1, x2;
    9208              :    int y1, y2;
    9209              :    int xs_old;
    9210              :    double ybase;
    9211              : 
    9212              :    gdPoint poly[3];
    9213              : 
    9214            0 :    if (ymin < minvalue)
    9215            0 :       ymin = minvalue;
    9216              : 
    9217            0 :    if (ymax > maxvalue)
    9218            0 :       ymax = maxvalue;
    9219              : 
    9220              :    /* check if ylow = 0 */
    9221            0 :    if (index == -1) {
    9222              :       //flag = 0;
    9223              :       //size = sizeof(flag);
    9224              :       //db_get_value(hDB, hkeypanel, "Zero ylow", &flag, &size, TID_BOOL, TRUE);
    9225            0 :       if (hp.zero_ylow && ymin > 0)
    9226            0 :          ymin = 0;
    9227              :    }
    9228              : 
    9229              :    /* if min and max too close together, switch to linear axis */
    9230            0 :    if (logaxis && ymin > 0 && ymax > 0) {
    9231            0 :       yb1 = pow(10, floor(log(ymin) / LN10));
    9232            0 :       yf1 = floor(ymin / yb1);
    9233            0 :       yb2 = pow(10, floor(log(ymax) / LN10));
    9234            0 :       yf2 = floor(ymax / yb2);
    9235              : 
    9236            0 :       if (yb1 == yb2 && yf1 == yf2)
    9237            0 :          logaxis = 0;
    9238              :       else {
    9239              :          /* round down and up ymin and ymax */
    9240            0 :          ybase = pow(10, floor(log(ymin) / LN10));
    9241            0 :          ymin = (floor(ymin / ybase) * ybase);
    9242            0 :          ybase = pow(10, floor(log(ymax) / LN10));
    9243            0 :          ymax = ((floor(ymax / ybase) + 1) * ybase);
    9244              :       }
    9245              :    }
    9246              : 
    9247              :    /* avoid negative limits for log axis */
    9248            0 :    if (logaxis) {
    9249            0 :       if (ymax <= 0)
    9250            0 :          ymax = 1;
    9251            0 :       if (ymin <= 0)
    9252            0 :          ymin = 1E-12f;
    9253              :    }
    9254              : 
    9255              :    /* increase limits by 5% */
    9256            0 :    if (ymin == 0 && ymax == 0) {
    9257            0 :       ymin = -1;
    9258            0 :       ymax = 1;
    9259              :    } else {
    9260            0 :       if (!logaxis) {
    9261            0 :          ymax += (ymax - ymin) / 20.f;
    9262              : 
    9263            0 :          if (ymin != 0)
    9264            0 :             ymin -= (ymax - ymin) / 20.f;
    9265              :       }
    9266              :    }
    9267              : 
    9268              :    /* avoid ymin == ymax */
    9269            0 :    if (ymax == ymin) {
    9270            0 :       if (logaxis) {
    9271            0 :          ymax *= 2;
    9272            0 :          ymin /= 2;
    9273              :       } else {
    9274            0 :          ymax += 10;
    9275            0 :          ymin -= 10;
    9276              :       }
    9277              :    }
    9278              : 
    9279              :    /* calculate X limits */
    9280              :    //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
    9281              :    //xmax = (double) (toffset / 3600.0);
    9282              :    //xrange = xmax - xmin;
    9283              :    //xrange = scale/3600.0;
    9284              : 
    9285              :    /* caluclate required space for Y-axis */
    9286            0 :    aoffset = vaxis(im, gdFontSmall, fgcol, gridcol, 0, 0, height, -3, -5, -7, -8, 0, ymin, ymax, logaxis);
    9287            0 :    aoffset += 2;
    9288              : 
    9289            0 :    x1 = aoffset;
    9290            0 :    y1 = height - 20;
    9291            0 :    x2 = width - 20;
    9292            0 :    y2 = 20;
    9293              : 
    9294            0 :    gdImageFilledRectangle(im, x1, y2, x2, y1, bgcol);
    9295              : 
    9296              :    /* draw axis frame */
    9297            0 :    taxis(im, gdFontSmall, fgcol, gridcol, x1, y1, x2 - x1, width, 3, 5, 9, 10, 0, (double)starttime, (double)endtime);
    9298              : 
    9299            0 :    vaxis(im, gdFontSmall, fgcol, gridcol, x1, y1, y1 - y2, -3, -5, -7, -8, x2 - x1, ymin, ymax, logaxis);
    9300            0 :    gdImageLine(im, x1, y2, x2, y2, fgcol);
    9301            0 :    gdImageLine(im, x2, y2, x2, y1, fgcol);
    9302              : 
    9303            0 :    xs = ys = xold = yold = 0;
    9304              : 
    9305              :    /* old code for run markers, new code is below */
    9306              : 
    9307              :    /* write run markes if selected */
    9308              :    if (/* DISABLES CODE */ (0) && hp.show_run_markers) {
    9309              : 
    9310              :       const char* event_names[] = {
    9311              :          "Run transitions",
    9312              :          "Run transitions",
    9313              :          0 };
    9314              : 
    9315              :       const char* tag_names[] = {
    9316              :          "State",
    9317              :          "Run number",
    9318              :          0 };
    9319              : 
    9320              :       const int tag_indexes[] = {
    9321              :          0,
    9322              :          0,
    9323              :          0 };
    9324              : 
    9325              :       int num_entries[3];
    9326              :       time_t *tbuf[3];
    9327              :       double *dbuf[3];
    9328              :       int     st[3];
    9329              : 
    9330              :       num_entries[0] = 0;
    9331              :       num_entries[1] = 0;
    9332              : 
    9333              :       status = mh->hs_read(starttime - scale, endtime, 0,
    9334              :                            2, event_names, tag_names, tag_indexes,
    9335              :                            num_entries, tbuf, dbuf, st);
    9336              : 
    9337              :       //printf("read run info: status %d, entries %d %d\n", status, num_entries[0], num_entries[1]);
    9338              : 
    9339              :       int n_marker = num_entries[0];
    9340              : 
    9341              :       if (status == HS_SUCCESS && n_marker > 0 && n_marker < 100) {
    9342              :          xs_old = -1;
    9343              :          xmaxm = x1;
    9344              :          for (j = 0; j < (int) n_marker; j++) {
    9345              :             int col;
    9346              : 
    9347              :             // explicit algebra manipulation to clarify computations:
    9348              : 
    9349              :             //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
    9350              :             //xrange = scale/3600.0;
    9351              :             //time_t starttime = now - scale + toffset;
    9352              : 
    9353              :             //x_marker = (int)(tbuf[1][j] - now);
    9354              :             //xs = (int) ((x_marker / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
    9355              :             //xs = (int) (((tbuf[1][j] - now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
    9356              :             //xs = (int) (((tbuf[1][j] - now) / 3600.0 - (-scale / 3600.0 + toffset / 3600.0)) / (scale/3600.0) * (x2 - x1) + x1 + 0.5);
    9357              :             //xs = (int) (((tbuf[1][j] - now) - (-scale + toffset)) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
    9358              :             xs = (int) ((tbuf[1][j] - starttime) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
    9359              : 
    9360              :             if (xs < x1)
    9361              :                continue;
    9362              :             if (xs >= x2)
    9363              :                continue;
    9364              : 
    9365              :             double run_number = dbuf[1][j];
    9366              : 
    9367              :             if (xs <= xs_old)
    9368              :                xs = xs_old + 1;
    9369              :             xs_old = xs;
    9370              : 
    9371              :             if (dbuf[0][j] == 1)
    9372              :                col = state_col[0];
    9373              :             else if (dbuf[0][j] == 2)
    9374              :                col = state_col[1];
    9375              :             else if (dbuf[0][j] == 3)
    9376              :                col = state_col[2];
    9377              :             else
    9378              :                col = state_col[0];
    9379              : 
    9380              :             gdImageDashedLine(im, xs, y1, xs, y2, col);
    9381              : 
    9382              :             sprintf(str, "%.0f", run_number);
    9383              : 
    9384              :             if (dbuf[0][j] == STATE_RUNNING) {
    9385              :                if (xs > xmaxm) {
    9386              :                   gdImageStringUp(im, gdFontSmall, xs + 0, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
    9387              :                   xmaxm = xs - 2 + gdFontSmall->h;
    9388              :                }
    9389              :             } else if (dbuf[0][j] == STATE_STOPPED) {
    9390              :                if (xs + 2 - gdFontSmall->h > xmaxm) {
    9391              :                   gdImageStringUp(im, gdFontSmall, xs + 2 - gdFontSmall->h, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
    9392              :                   xmaxm = xs - 1;
    9393              :                }
    9394              :             }
    9395              :          }
    9396              :       }
    9397              : 
    9398              :       if (num_entries[0]) {
    9399              :          free(tbuf[0]);
    9400              :          free(dbuf[0]);
    9401              :          tbuf[0] = NULL;
    9402              :          dbuf[0] = NULL;
    9403              :       }
    9404              : 
    9405              :       if (num_entries[1]) {
    9406              :          free(tbuf[1]);
    9407              :          free(dbuf[1]);
    9408              :          tbuf[1] = NULL;
    9409              :          dbuf[1] = NULL;
    9410              :       }
    9411              :    }
    9412              : 
    9413              :    /* write run markes if selected */
    9414            0 :    if (hp.show_run_markers) {
    9415              : 
    9416            0 :       int index_state = -1;
    9417            0 :       int index_run_number = -1;
    9418              : 
    9419            0 :       for (int k=0; k<hsdata->nvars; k++) {
    9420            0 :          if (hsdata->odb_index[k] == -1)
    9421            0 :             index_state = k;
    9422              : 
    9423            0 :          if (hsdata->odb_index[k] == -2)
    9424            0 :             index_run_number = k;
    9425              :       }
    9426              : 
    9427            0 :       bool ok = true;
    9428              : 
    9429            0 :       if (ok)
    9430            0 :          ok = (index_state >= 0) && (index_run_number >= 0);
    9431              : 
    9432            0 :       if (ok)
    9433            0 :          ok = (hsdata->status[index_state] == HS_SUCCESS);
    9434              : 
    9435            0 :       if (ok)
    9436            0 :          ok = (hsdata->status[index_run_number] == HS_SUCCESS);
    9437              : 
    9438              :       if (/* DISABLES CODE */ (0) && ok)
    9439              :          printf("read run info: indexes: %d, %d, status: %d, %d, entries: %d, %d\n", index_state, index_run_number, hsdata->status[index_state], hsdata->status[index_run_number], hsdata->num_entries[index_state], hsdata->num_entries[index_run_number]);
    9440              : 
    9441            0 :       if (ok)
    9442            0 :          ok = (hsdata->num_entries[index_state] == hsdata->num_entries[index_run_number]);
    9443              : 
    9444            0 :       int n_marker = hsdata->num_entries[index_state];
    9445              : 
    9446            0 :       if (ok && n_marker > 0 && n_marker < 100) {
    9447            0 :          xs_old = -1;
    9448            0 :          xmaxm = x1;
    9449            0 :          for (j = 0; j < (int) n_marker; j++) {
    9450              :             int col;
    9451              : 
    9452              :             // explicit algebra manipulation to clarify computations:
    9453              : 
    9454              :             //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
    9455              :             //xrange = scale/3600.0;
    9456              :             //time_t starttime = now - scale + toffset;
    9457              : 
    9458              :             //x_marker = (int)(tbuf[1][j] - now);
    9459              :             //xs = (int) ((x_marker / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
    9460              :             //xs = (int) (((tbuf[1][j] - now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
    9461              :             //xs = (int) (((tbuf[1][j] - now) / 3600.0 - (-scale / 3600.0 + toffset / 3600.0)) / (scale/3600.0) * (x2 - x1) + x1 + 0.5);
    9462              :             //xs = (int) (((tbuf[1][j] - now) - (-scale + toffset)) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
    9463            0 :             xs = (int) ((hsdata->t[index_state][j] - starttime) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
    9464              : 
    9465            0 :             if (xs < x1)
    9466            0 :                continue;
    9467            0 :             if (xs >= x2)
    9468            0 :                continue;
    9469              : 
    9470            0 :             double run_number = hsdata->v[index_run_number][j];
    9471              : 
    9472            0 :             if (xs <= xs_old)
    9473            0 :                xs = xs_old + 1;
    9474            0 :             xs_old = xs;
    9475              : 
    9476            0 :             int state = (int)hsdata->v[index_state][j];
    9477              : 
    9478            0 :             if (state == 1)
    9479            0 :                col = state_col[0];
    9480            0 :             else if (state == 2)
    9481            0 :                col = state_col[1];
    9482            0 :             else if (state == 3)
    9483            0 :                col = state_col[2];
    9484              :             else
    9485            0 :                col = state_col[0];
    9486              : 
    9487            0 :             gdImageDashedLine(im, xs, y1, xs, y2, col);
    9488              : 
    9489            0 :             sprintf(str, "%.0f", run_number);
    9490              : 
    9491            0 :             if (state == STATE_RUNNING) {
    9492            0 :                if (xs > xmaxm) {
    9493            0 :                   gdImageStringUp(im, gdFontSmall, xs + 0, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
    9494            0 :                   xmaxm = xs - 2 + gdFontSmall->h;
    9495              :                }
    9496            0 :             } else if (state == STATE_STOPPED) {
    9497            0 :                if (xs + 2 - gdFontSmall->h > xmaxm) {
    9498            0 :                   gdImageStringUp(im, gdFontSmall, xs + 2 - gdFontSmall->h, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
    9499            0 :                   xmaxm = xs - 1;
    9500              :                }
    9501              :             }
    9502              :          }
    9503              :       }
    9504              :    }
    9505              : 
    9506            0 :    for (i = 0; i < (int)hp.vars.size(); i++) {
    9507            0 :       if (index != -1 && index != i)
    9508            0 :          continue;
    9509              : 
    9510              :       /* draw alarm limits */
    9511            0 :       if (lower_limit[i] != -12345) {
    9512            0 :          if (logaxis) {
    9513            0 :             if (lower_limit[i] <= 0)
    9514            0 :                ys = y1;
    9515              :             else
    9516            0 :                ys = (int) (y1 - (log(lower_limit[i]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
    9517              :          } else {
    9518            0 :             ys = (int) (y1 - (lower_limit[i] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
    9519              :          }
    9520              : 
    9521            0 :          if (xs < 0)
    9522            0 :             xs = 0;
    9523            0 :          if (xs >= width)
    9524            0 :             xs = width-1;
    9525            0 :          if (ys < 0)
    9526            0 :             ys = 0;
    9527            0 :          if (ys >= height)
    9528            0 :             ys = height-1;
    9529              : 
    9530            0 :          if (ys > y2 && ys < y1) {
    9531            0 :             gdImageDashedLine(im, x1, ys, x2, ys, curve_col[i]);
    9532              : 
    9533            0 :             poly[0].x = x1;
    9534            0 :             poly[0].y = ys;
    9535            0 :             poly[1].x = x1 + 5;
    9536            0 :             poly[1].y = ys;
    9537            0 :             poly[2].x = x1;
    9538            0 :             poly[2].y = ys - 5;
    9539              : 
    9540            0 :             gdImageFilledPolygon(im, poly, 3, curve_col[i]);
    9541              :          }
    9542              :       }
    9543            0 :       if (upper_limit[i] != -12345) {
    9544            0 :          if (logaxis) {
    9545            0 :             if (upper_limit[i] <= 0)
    9546            0 :                ys = y1;
    9547              :             else
    9548            0 :                ys = (int) (y1 - (log(upper_limit[i]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
    9549              :          } else {
    9550            0 :             ys = (int) (y1 - (upper_limit[i] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
    9551              :          }
    9552              : 
    9553            0 :          if (xs < 0)
    9554            0 :             xs = 0;
    9555            0 :          if (xs >= width)
    9556            0 :             xs = width-1;
    9557            0 :          if (ys < 0)
    9558            0 :             ys = 0;
    9559            0 :          if (ys >= height)
    9560            0 :             ys = height-1;
    9561              : 
    9562            0 :          if (ys > y2 && ys < y1) {
    9563            0 :             gdImageDashedLine(im, x1, ys, x2, ys, curve_col[i]);
    9564              : 
    9565            0 :             poly[0].x = x1;
    9566            0 :             poly[0].y = ys;
    9567            0 :             poly[1].x = x1 + 5;
    9568            0 :             poly[1].y = ys;
    9569            0 :             poly[2].x = x1;
    9570            0 :             poly[2].y = ys + 5;
    9571              : 
    9572            0 :             gdImageFilledPolygon(im, poly, 3, curve_col[i]);
    9573              :          }
    9574              :       }
    9575              : 
    9576            0 :       for (j = 0; j < (int) n_point[i]; j++) {
    9577              :          //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
    9578              :          //xrange = scale/3600.0;
    9579              :          //xs = (int) (((x[i][j]-now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
    9580              :          //xs = (int) (((x[i][j] - now + scale - toffset) / 3600.0) / xrange * (x2 - x1) + x1 + 0.5);
    9581              :          //xs = (int) (((x[i][j] - starttime) / 3600.0) / xrange * (x2 - x1) + x1 + 0.5);
    9582            0 :          xs = (int) (((x[i][j] - starttime)/1.0) / (1.0*scale) * (x2 - x1) + x1 + 0.5);
    9583              : 
    9584            0 :          if (logaxis) {
    9585            0 :             if (y[i][j] <= 0)
    9586            0 :                ys = y1;
    9587              :             else
    9588            0 :                ys = (int) (y1 - (log(y[i][j]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
    9589              :          } else {
    9590            0 :             ys = (int) (y1 - (y[i][j] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
    9591              :          }
    9592              : 
    9593            0 :          if (xs < 0)
    9594            0 :             xs = 0;
    9595            0 :          if (xs >= width)
    9596            0 :             xs = width-1;
    9597            0 :          if (ys < 0)
    9598            0 :             ys = 0;
    9599            0 :          if (ys >= height)
    9600            0 :             ys = height-1;
    9601              : 
    9602            0 :          if (j > 0)
    9603            0 :             gdImageLine(im, xold, yold, xs, ys, curve_col[i]);
    9604            0 :          xold = xs;
    9605            0 :          yold = ys;
    9606              :       }
    9607              : 
    9608            0 :       if (n_point[i] > 0) {
    9609            0 :          poly[0].x = xs;
    9610            0 :          poly[0].y = ys;
    9611            0 :          poly[1].x = xs + 12;
    9612            0 :          poly[1].y = ys - 6;
    9613            0 :          poly[2].x = xs + 12;
    9614            0 :          poly[2].y = ys + 6;
    9615              : 
    9616            0 :          gdImageFilledPolygon(im, poly, 3, curve_col[i]);
    9617              :       }
    9618              :    }
    9619              : 
    9620            0 :    if (labels) {
    9621            0 :       for (i = 0; i < (int)hp.vars.size(); i++) {
    9622            0 :          if (index != -1 && index != i)
    9623            0 :             continue;
    9624              : 
    9625              :          //str[0] = 0;
    9626              :          //status = db_find_key(hDB, hkeypanel, "Label", &hkeydvar);
    9627              :          //if (status == DB_SUCCESS) {
    9628              :          //   size = sizeof(str);
    9629              :          //   status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
    9630              :          //}
    9631              : 
    9632            0 :          std::string str = hp.vars[i].label.c_str();
    9633              : 
    9634            0 :          if (str.empty()) {
    9635            0 :             if (hp.enable_factor) {
    9636            0 :                str = hp.vars[i].tag_name;
    9637              : 
    9638            0 :                if (hp.vars[i].voffset > 0)
    9639            0 :                   str += msprintf(" - %G", hp.vars[i].voffset);
    9640            0 :                else if (hp.vars[i].voffset < 0)
    9641            0 :                   str += msprintf(" + %G", -hp.vars[i].voffset);
    9642              : 
    9643            0 :                if (hp.vars[i].factor != 1) {
    9644            0 :                   if (hp.vars[i].voffset == 0)
    9645            0 :                      str += msprintf(" * %+G", hp.vars[i].factor);
    9646              :                   else {
    9647            0 :                      str = msprintf("(%s) * %+G", str.c_str(), hp.vars[i].factor);
    9648              :                   }
    9649              :                }
    9650              : 
    9651            0 :                if (hp.vars[i].offset > 0)
    9652            0 :                   str += msprintf(" + %G", hp.vars[i].offset);
    9653            0 :                else if (hp.vars[i].offset < 0)
    9654            0 :                   str += msprintf(" - %G", -hp.vars[i].offset);
    9655              : 
    9656              :             } else {
    9657            0 :                str = hp.vars[i].tag_name;
    9658              :             }
    9659              :          }
    9660              : 
    9661            0 :          int k=0;
    9662            0 :          for (int j=0; j<hsdata->nvars; j++)
    9663            0 :             if (hsdata->odb_index[j] == i) {
    9664            0 :                k = j;
    9665            0 :                break;
    9666              :             }
    9667              : 
    9668              :          if (/* DISABLES CODE */ (0)) {
    9669              :             printf("graph %d: odb index %d, n_point %d, num_entries %d, have_last_written %d %d, status %d, var_status [%s]\n", i, k, n_point[i], hsdata->num_entries[k], hsdata->have_last_written, (int)hsdata->last_written[k], hsdata->status[k], var_status[i]);
    9670              :          }
    9671              : 
    9672            0 :          if (hp.show_values) {
    9673              :             char xstr[256];
    9674            0 :             if (n_point[i] > 0) {
    9675            0 :                sprintf(xstr," = %g", y[i][n_point[i]-1]);
    9676            0 :             } else if (hsdata->num_entries[k] > 0) {
    9677            0 :                sprintf(xstr," = all data is NaN or INF");
    9678            0 :             } else if (hsdata->have_last_written) {
    9679            0 :                if (hsdata->last_written[k]) {
    9680              :                   char ctimebuf[32];
    9681            0 :                   ctime_r(&hsdata->last_written[k], ctimebuf);
    9682            0 :                   sprintf(xstr," = last data %s", ctimebuf);
    9683              :                   // kill trailing '\n'
    9684            0 :                   char*s = strchr(xstr, '\n');
    9685            0 :                   if (s) *s=0;
    9686              :                   // clear the unnecessary error status report
    9687            0 :                   if (hsdata->status[k] == HS_UNDEFINED_VAR)
    9688            0 :                      var_status[i][0] = 0;
    9689              :                } else {
    9690            0 :                   sprintf(xstr," = no data ever");
    9691              :                }
    9692              :             } else {
    9693            0 :                sprintf(xstr," = no data");
    9694              :             }
    9695            0 :             str += xstr;
    9696              :          }
    9697              : 
    9698            0 :          if (strlen(var_status[i]) > 1) {
    9699            0 :             str += msprintf(" (%s)", var_status[i]);
    9700              :          }
    9701              : 
    9702            0 :          row = index == -1 ? i : 0;
    9703              : 
    9704            0 :          gdImageFilledRectangle(im,
    9705              :                                 x1 + 10,
    9706            0 :                                 y2 + 10 + row * (gdFontMediumBold->h + 10),
    9707            0 :                                 x1 + 10 + str.length() * gdFontMediumBold->w + 10,
    9708            0 :                                 y2 + 10 + row * (gdFontMediumBold->h + 10) +
    9709            0 :                                 gdFontMediumBold->h + 2 + 2, white);
    9710            0 :          gdImageRectangle(im, x1 + 10, y2 + 10 + row * (gdFontMediumBold->h + 10),
    9711            0 :                           x1 + 10 + str.length() * gdFontMediumBold->w + 10,
    9712            0 :                           y2 + 10 + row * (gdFontMediumBold->h + 10) +
    9713            0 :                           gdFontMediumBold->h + 2 + 2, curve_col[i]);
    9714              : 
    9715            0 :          gdImageString(im, gdFontMediumBold,
    9716            0 :                        x1 + 10 + 5, y2 + 10 + 2 + row * (gdFontMediumBold->h + 10),
    9717              :                        (char*)str.c_str(),
    9718              :                        curve_col[i]);
    9719            0 :       }
    9720              :    }
    9721              : 
    9722            0 :    gdImageRectangle(im, x1, y2, x2, y1, fgcol);
    9723              : 
    9724            0 :  error:
    9725              : 
    9726              :    /* generate GIF */
    9727            0 :    gdImageInterlace(im, 1);
    9728            0 :    gdImageGif(im, &gb);
    9729            0 :    gdImageDestroy(im);
    9730            0 :    length = gb.size;
    9731              : 
    9732            0 :    if (buffer == NULL) {
    9733            0 :       rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
    9734            0 :       rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
    9735              : 
    9736            0 :       rr->rsprintf("Content-Type: image/gif\r\n");
    9737            0 :       rr->rsprintf("Content-Length: %d\r\n", length);
    9738            0 :       rr->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
    9739            0 :       rr->rsprintf("Expires: Fri, 01-Jan-1983 00:00:00 GMT\r\n\r\n");
    9740              : 
    9741            0 :       rr->rmemcpy(gb.data, length);
    9742              :    } else {
    9743            0 :       if (length > *buffer_size) {
    9744            0 :          printf("return buffer too small\n");
    9745            0 :          return;
    9746              :       }
    9747              : 
    9748            0 :       memcpy(buffer, gb.data, length);
    9749            0 :       *buffer_size = length;
    9750              :    }
    9751            0 : }
    9752              : 
    9753              : /*------------------------------------------------------------------*/
    9754              : 
    9755            0 : time_t mktime_with_dst(const struct tm* ptms)
    9756              : {
    9757              :    // this silly stuff is required to correctly handle daylight savings time (Summer time/Winter time)
    9758              :    // when we fill "struct tm" from user input, we cannot know if daylight savings time is in effect
    9759              :    // and we do not know how to initialize the value of tms.tm_isdst.
    9760              :    // This can cause the output of mktime() to be off by one hour.
    9761              :    // (Rules for daylight savings time are set by national and local govt and in some locations, changes yearly)
    9762              :    // (There are no locations with 2 hour or half-hour daylight savings that I know of)
    9763              :    // (Yes, "man mktime" talks about using "tms.tm_isdst = -1")
    9764              :    //
    9765              :    // We assume the user is using local time and we convert in two steps:
    9766              :    //
    9767              :    // first we convert "struct tm" to "time_t" using mktime() with unknown tm_isdst
    9768              :    // second we convert "time_t" back to "struct tm" using localtime_r()
    9769              :    // this fills "tm_isdst" with correct value from the system time zone database
    9770              :    // then we reset all the time fields (except for sub-minute fields not affected by daylight savings)
    9771              :    // and call mktime() again, now with the correct value of "tm_isdst".
    9772              :    // K.O. 2013-09-14
    9773              : 
    9774            0 :    struct tm tms = *ptms;
    9775              :    struct tm tms2;
    9776            0 :    time_t t1 = ss_mktime(&tms);
    9777            0 :    localtime_r(&t1, &tms2);
    9778            0 :    tms2.tm_year = ptms->tm_year;
    9779            0 :    tms2.tm_mon  = ptms->tm_mon;
    9780            0 :    tms2.tm_mday = ptms->tm_mday;
    9781            0 :    tms2.tm_hour = ptms->tm_hour;
    9782            0 :    tms2.tm_min  = ptms->tm_min;
    9783            0 :    time_t t2 = ss_mktime(&tms2);
    9784              :    //printf("t1 %.0f, t2 %.0f, diff %d\n", (double)t1, (double)t2, (int)(t1-t2));
    9785            0 :    return t2;
    9786              : }
    9787              : 
    9788              : /*------------------------------------------------------------------*/
    9789              : 
    9790            0 : static std::string add_param_to_url(const char* name, const char* value)
    9791              : {
    9792            0 :    std::string s;
    9793            0 :    s += name; // FIXME: should be URI-encoded
    9794            0 :    s += "=";
    9795            0 :    s += value; // FIXME: should be URI-encoded
    9796            0 :    return s;
    9797            0 : }
    9798              : 
    9799              : /*------------------------------------------------------------------*/
    9800              : 
    9801            0 : void show_query_page(Param* p, Return* r)
    9802              : {
    9803              :    int i;
    9804              :    HNDLE hDB;
    9805              : 
    9806            0 :    if (p->getparam("m1") && *p->getparam("m1")) {
    9807              :       struct tm tms;
    9808            0 :       memset(&tms, 0, sizeof(struct tm));
    9809              : 
    9810            0 :       tms.tm_year = atoi(p->getparam("y1")) % 100;
    9811              : 
    9812            0 :       std::string m1 = p->getparam("m1");
    9813            0 :       for (i = 0; i < 12; i++)
    9814            0 :          if (equal_ustring(m1.c_str(), mname[i]))
    9815            0 :             break;
    9816            0 :       if (i == 12)
    9817            0 :          i = 0;
    9818              : 
    9819            0 :       tms.tm_mon = i;
    9820            0 :       tms.tm_mday = atoi(p->getparam("d1"));
    9821            0 :       tms.tm_hour = atoi(p->getparam("h1"));
    9822              : 
    9823            0 :       if (tms.tm_year < 90)
    9824            0 :          tms.tm_year += 100;
    9825              : 
    9826            0 :       time_t ltime_start = mktime_with_dst(&tms);
    9827              : 
    9828            0 :       memset(&tms, 0, sizeof(struct tm));
    9829            0 :       tms.tm_year = atoi(p->getparam("y2")) % 100;
    9830              : 
    9831            0 :       std::string m2 = p->getparam("m2");
    9832            0 :       for (i = 0; i < 12; i++)
    9833            0 :          if (equal_ustring(m2.c_str(), mname[i]))
    9834            0 :             break;
    9835            0 :       if (i == 12)
    9836            0 :          i = 0;
    9837              : 
    9838            0 :       tms.tm_mon = i;
    9839            0 :       tms.tm_mday = atoi(p->getparam("d2"));
    9840            0 :       tms.tm_hour = atoi(p->getparam("h2"));
    9841              : 
    9842            0 :       if (tms.tm_year < 90)
    9843            0 :          tms.tm_year += 100;
    9844              : 
    9845            0 :       time_t ltime_end = mktime_with_dst(&tms);
    9846              : 
    9847            0 :       if (ltime_end == ltime_start)
    9848            0 :          ltime_end += 3600 * 24;
    9849              : 
    9850            0 :       std::string redir;
    9851            0 :       redir += "?cmd=oldhistory&";
    9852            0 :       redir += add_param_to_url("group", p->getparam("group"));
    9853            0 :       redir += "&";
    9854            0 :       redir += add_param_to_url("panel", p->getparam("panel"));
    9855            0 :       redir += "&";
    9856            0 :       redir += add_param_to_url("scale", toString((int)(ltime_end - ltime_start)).c_str());
    9857            0 :       redir += "&";
    9858            0 :       redir += add_param_to_url("time", time_to_string(ltime_end).c_str());
    9859            0 :       if (p->isparam("hindex")) {
    9860            0 :          redir += "&";
    9861            0 :          redir += add_param_to_url("index", p->getparam("hindex"));
    9862              :       }
    9863            0 :       redirect(r, redir.c_str());
    9864            0 :       return;
    9865            0 :    }
    9866              : 
    9867            0 :    cm_get_experiment_database(&hDB, NULL);
    9868            0 :    show_header(r, "History", "GET", "", 0);
    9869              : 
    9870              :    /* set the times */
    9871              : 
    9872            0 :    time_t now = time(NULL);
    9873              : 
    9874            0 :    time_t starttime = now - 3600 * 24;
    9875            0 :    time_t endtime = now;
    9876            0 :    bool full_day = true;
    9877              : 
    9878            0 :    if (p->isparam("htime")) {
    9879            0 :       endtime = string_to_time(p->getparam("htime"));
    9880              : 
    9881            0 :       if (p->isparam("hscale")) {
    9882            0 :          starttime = endtime - atoi(p->getparam("hscale"));
    9883            0 :          full_day = false;
    9884              :       } else {
    9885            0 :          starttime = endtime - 3600 * 24;
    9886            0 :          full_day = false;
    9887              :       }
    9888              :    }
    9889              : 
    9890              :    /* menu buttons */
    9891            0 :    r->rsprintf("<tr><td colspan=2>\n");
    9892            0 :    r->rsprintf("<input type=hidden name=cmd value=OldHistory>\n");
    9893            0 :    r->rsprintf("<input type=submit name=hcmd value=Query>\n");
    9894            0 :    r->rsprintf("<input type=submit name=hcmd value=Cancel>\n");
    9895            0 :    if (p->isparam("group"))
    9896            0 :       r->rsprintf("<input type=hidden name=group value=\"%s\">\n", p->getparam("group"));
    9897            0 :    if (p->isparam("panel"))
    9898            0 :       r->rsprintf("<input type=hidden name=panel value=\"%s\">\n", p->getparam("panel"));
    9899            0 :    if (p->isparam("htime"))
    9900            0 :       r->rsprintf("<input type=hidden name=htime value=\"%s\">\n", p->getparam("htime"));
    9901            0 :    if (p->isparam("hscale"))
    9902            0 :       r->rsprintf("<input type=hidden name=hscale value=\"%s\">\n", p->getparam("hscale"));
    9903            0 :    if (p->isparam("hindex"))
    9904            0 :       r->rsprintf("<input type=hidden name=hindex value=\"%s\">\n", p->getparam("hindex"));
    9905            0 :    r->rsprintf("</tr>\n\n");
    9906            0 :    r->rsprintf("</table>");  //end header
    9907              : 
    9908            0 :    r->rsprintf("<table class=\"dialogTable\">");  //main table
    9909              : 
    9910              :    struct tm tms;
    9911            0 :    localtime_r(&starttime, &tms);
    9912            0 :    tms.tm_year += 1900;
    9913              : 
    9914            0 :    r->rsprintf("<tr><td nowrap>Start date:</td>");
    9915              : 
    9916            0 :    r->rsprintf("<td>Month: <select name=\"m1\">\n");
    9917            0 :    r->rsprintf("<option value=\"\">\n");
    9918            0 :    for (i = 0; i < 12; i++)
    9919            0 :       if (i == tms.tm_mon)
    9920            0 :          r->rsprintf("<option selected value=\"%s\">%s\n", mname[i], mname[i]);
    9921              :       else
    9922            0 :          r->rsprintf("<option value=\"%s\">%s\n", mname[i], mname[i]);
    9923            0 :    r->rsprintf("</select>\n");
    9924              : 
    9925            0 :    r->rsprintf("&nbsp;Day: <select name=\"d1\">");
    9926            0 :    r->rsprintf("<option selected value=\"\">\n");
    9927            0 :    for (i = 0; i < 31; i++)
    9928            0 :       if (i + 1 == tms.tm_mday)
    9929            0 :          r->rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
    9930              :       else
    9931            0 :          r->rsprintf("<option value=%d>%d\n", i + 1, i + 1);
    9932            0 :    r->rsprintf("</select>\n");
    9933              : 
    9934            0 :    int start_hour = tms.tm_hour;
    9935            0 :    if (full_day)
    9936            0 :       start_hour = 0;
    9937              : 
    9938            0 :    r->rsprintf("&nbsp;Hour: <input type=\"text\" size=5 maxlength=5 name=\"h1\" value=\"%d\">", start_hour);
    9939              : 
    9940            0 :    r->rsprintf("&nbsp;Year: <input type=\"text\" size=5 maxlength=5 name=\"y1\" value=\"%d\">", tms.tm_year);
    9941            0 :    r->rsprintf("</td></tr>\n");
    9942              : 
    9943            0 :    r->rsprintf("<tr><td nowrap>End date:</td>");
    9944              : 
    9945            0 :    localtime_r(&endtime, &tms);
    9946            0 :    tms.tm_year += 1900;
    9947              : 
    9948            0 :    r->rsprintf("<td>Month: <select name=\"m2\">\n");
    9949            0 :    r->rsprintf("<option value=\"\">\n");
    9950            0 :    for (i = 0; i < 12; i++)
    9951            0 :       if (i == tms.tm_mon)
    9952            0 :          r->rsprintf("<option selected value=\"%s\">%s\n", mname[i], mname[i]);
    9953              :       else
    9954            0 :          r->rsprintf("<option value=\"%s\">%s\n", mname[i], mname[i]);
    9955            0 :    r->rsprintf("</select>\n");
    9956              : 
    9957            0 :    r->rsprintf("&nbsp;Day: <select name=\"d2\">");
    9958            0 :    r->rsprintf("<option selected value=\"\">\n");
    9959            0 :    for (i = 0; i < 31; i++)
    9960            0 :       if (i + 1 == tms.tm_mday)
    9961            0 :          r->rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
    9962              :       else
    9963            0 :          r->rsprintf("<option value=%d>%d\n", i + 1, i + 1);
    9964            0 :    r->rsprintf("</select>\n");
    9965              : 
    9966            0 :    int end_hour = tms.tm_hour;
    9967            0 :    if (full_day)
    9968            0 :       end_hour = 24;
    9969              : 
    9970            0 :    r->rsprintf("&nbsp;Hour: <input type=\"text\" size=5 maxlength=5 name=\"h2\" value=\"%d\">", end_hour);
    9971              : 
    9972            0 :    r->rsprintf("&nbsp;Year: <input type=\"text\" size=5 maxlength=5 name=\"y2\" value=\"%d\">", tms.tm_year);
    9973            0 :    r->rsprintf("</td></tr>\n");
    9974              : 
    9975            0 :    r->rsprintf("</table>\n");
    9976            0 :    r->rsprintf("</div>\n"); // closing for <div id="mmain">
    9977            0 :    r->rsprintf("</form>\n");
    9978            0 :    r->rsprintf("</body></html>\r\n");
    9979              : }
    9980              : 
    9981              : /*------------------------------------------------------------------*/
    9982              : /* history plot code starts here                                    */
    9983              : /*------------------------------------------------------------------*/
    9984              : 
    9985            0 : static int cmp_names(const void *a, const void *b)
    9986              : {
    9987              :   int i;
    9988            0 :   const char*sa = (const char*)a;
    9989            0 :   const char*sb = (const char*)b;
    9990              : 
    9991            0 :   int debug = 0;
    9992              : 
    9993              :   // Cannot use strcmp() because it does not know how to compare numerical values, e.g.
    9994              :   // it thinks "111" is smaller than "9"
    9995              :   //return strcmp(sa, sb);
    9996              : 
    9997            0 :   if (debug)
    9998            0 :     printf("compare [%s] and [%s]\n", sa, sb);
    9999              : 
   10000            0 :   for (i=0; ; i++) {
   10001            0 :     if (sa[i]==0 && sb[i]==0)
   10002            0 :       return 0; // both strings have the same length and the same characters
   10003              : 
   10004              :     //printf("index %d, char [%c] [%c], isdigit %d %d\n", i, sa[i], sb[i], isdigit(sa[i]), isdigit(sb[i]));
   10005              : 
   10006            0 :     if (isdigit(sa[i]) && isdigit(sb[i])) {
   10007            0 :       int va = atoi(sa+i);
   10008            0 :       int vb = atoi(sb+i);
   10009              : 
   10010            0 :       if (debug)
   10011            0 :         printf("index %d, values %d %d\n", i, va, vb);
   10012              : 
   10013            0 :       if (va < vb)
   10014            0 :         return -1;
   10015            0 :       else if (va > vb)
   10016            0 :         return 1;
   10017              : 
   10018              :       // values are equal, skip the the end of the digits, compare any trailing text
   10019            0 :       continue;
   10020            0 :     }
   10021              : 
   10022            0 :     if (sa[i]==sb[i]) {
   10023            0 :       continue;
   10024              :     }
   10025              : 
   10026            0 :     if (debug)
   10027            0 :       printf("index %d, char [%c] [%c]\n", i, sa[i], sb[i]);
   10028              : 
   10029            0 :     if (sa[i] == 0) // string sa is shorter
   10030            0 :       return -1;
   10031            0 :     else if (sb[i] == 0) // string sb is shorter
   10032            0 :       return 1;
   10033              : 
   10034            0 :     if (sa[i]<sb[i])
   10035            0 :       return -1;
   10036              :     else
   10037            0 :       return 1;
   10038            0 :   }
   10039              : 
   10040              :   // NOT REACHED
   10041              : }
   10042              : 
   10043            0 : const bool cmp_events(const std::string& a, const std::string& b)
   10044              : {
   10045            0 :   return cmp_names(a.c_str(), b.c_str()) < 0;
   10046              : }
   10047              : 
   10048            0 : const bool cmp_events1(const std::string& a, const std::string& b)
   10049              : {
   10050            0 :   return a < b;
   10051              : }
   10052              : 
   10053            0 : const bool cmp_tags(const TAG& a, const TAG& b)
   10054              : {
   10055            0 :   return cmp_names(a.name, b.name) < 0;
   10056              : }
   10057              : 
   10058              : #if 0
   10059              : static int cmp_tags(const void *a, const void *b)
   10060              : {
   10061              :   const TAG*sa = (const TAG*)a;
   10062              :   const TAG*sb = (const TAG*)b;
   10063              :   return cmp_names(sa->name, sb->name);
   10064              : }
   10065              : 
   10066              : static void sort_tags(int ntags, TAG* tags)
   10067              : {
   10068              :    qsort(tags, ntags, sizeof(TAG), cmp_tags);
   10069              : }
   10070              : #endif
   10071              : 
   10072            0 : int xdb_get_data_index(HNDLE hDB, const char* str, void *value, int size, int index, int tid)
   10073              : {
   10074              :    HNDLE hKey;
   10075            0 :    int status = db_find_key(hDB, 0, str, &hKey);
   10076            0 :    if (status != DB_SUCCESS)
   10077            0 :       return status;
   10078              : 
   10079              :    KEY key;
   10080            0 :    db_get_key(hDB, hKey, &key);
   10081            0 :    if (index >= key.num_values)
   10082            0 :       return DB_OUT_OF_RANGE;
   10083              : 
   10084            0 :    status = db_get_data_index(hDB, hKey, value, &size, index, tid);
   10085            0 :    return status;
   10086              : }
   10087              : 
   10088            0 : static int xdb_find_key(HNDLE hDB, HNDLE dir, const char* str, HNDLE* hKey, int tid, int size)
   10089              : {
   10090            0 :    int status = db_find_key(hDB, dir, str, hKey);
   10091            0 :    if (status == DB_SUCCESS)
   10092            0 :       return status;
   10093              : 
   10094            0 :    db_create_key(hDB, dir, str, tid);
   10095            0 :    status = db_find_key(hDB, dir, str, hKey);
   10096            0 :    if (status != DB_SUCCESS || !*hKey) {
   10097            0 :       cm_msg(MERROR, "xdb_find_key", "Invalid ODB path \"%s\"", str);
   10098            0 :       str = "bad_xdb_find_key";
   10099            0 :       db_create_key(hDB, dir, str, tid);
   10100            0 :       db_find_key(hDB, dir, str, hKey);
   10101              :    }
   10102            0 :    assert(*hKey);
   10103              : 
   10104            0 :    if (tid == TID_STRING) {
   10105            0 :       db_set_data_index(hDB, *hKey, "", size, 0, TID_STRING);
   10106              :    }
   10107              : 
   10108            0 :    return status;
   10109              : }
   10110              : 
   10111            0 : static bool cmp_vars(const HistVar &a, const HistVar &b)
   10112              : {
   10113            0 :    return a.order < b.order;
   10114              : }
   10115              : 
   10116            0 : static void PrintHistPlot(const HistPlot& hp)
   10117              : {
   10118            0 :    printf("hist plot: %d variables\n", (int)hp.vars.size());
   10119            0 :    printf("timescale: %s, minimum: %f, maximum: %f, zero_ylow: %d, log_axis: %d, show_run_markers: %d, show_values: %d, show_fill: %d, show_factor %d, enable_factor: %d\n", hp.timescale.c_str(), hp.minimum, hp.maximum, hp.zero_ylow, hp.log_axis, hp.show_run_markers, hp.show_values, hp.show_fill, hp.show_factor, hp.enable_factor);
   10120              :    
   10121            0 :    for (size_t i=0; i<hp.vars.size(); i++) {
   10122            0 :       printf("var[%d] event [%s][%s] formula [%s], colour [%s] label [%s] show_raw_value %d factor %f offset %f voffset %f order %d\n", (int)i, hp.vars[i].event_name.c_str(), hp.vars[i].tag_name.c_str(), hp.vars[i].formula.c_str(), hp.vars[i].colour.c_str(), hp.vars[i].label.c_str(), hp.vars[i].show_raw_value, hp.vars[i].factor, hp.vars[i].offset , hp.vars[i].voffset, hp.vars[i].order);
   10123              :    }
   10124            0 : }
   10125              : 
   10126            0 : static std::string NextHistPlotColour(const HistPlot& hp)
   10127              : {
   10128            0 :    const char* const colour[] =
   10129              :       {
   10130              :        "#00AAFF", "#FF9000", "#FF00A0", "#00C030",
   10131              :        "#A0C0D0", "#D0A060", "#C04010", "#807060",
   10132              :        "#F0C000", "#2090A0", "#D040D0", "#90B000",
   10133              :        "#B0B040", "#B0B0FF", "#FFA0A0", "#A0FFA0",
   10134              :        NULL };
   10135              : 
   10136            0 :    for (int i=0; colour[i]; i++) {
   10137            0 :       bool in_use = false;
   10138              :       
   10139            0 :       for (size_t j=0; j<hp.vars.size(); j++)
   10140            0 :          if (hp.vars[j].colour == colour[i]) {
   10141            0 :             in_use = true;
   10142            0 :             break;
   10143              :          }
   10144              :       
   10145            0 :       if (!in_use)
   10146            0 :          return colour[i];
   10147              :    }
   10148              :    
   10149            0 :    return "#808080";
   10150              : }
   10151              : 
   10152            0 : static int NextHistPlotOrder(const HistPlot& hp)
   10153              : {
   10154            0 :    int order = 0;
   10155            0 :    for (size_t i=0; i<hp.vars.size(); i++)
   10156            0 :       if (hp.vars[i].order > order)
   10157            0 :          order = hp.vars[i].order;
   10158            0 :    return order + 10;
   10159              : }
   10160              : 
   10161            0 : static void SplitEventAndTagNames(std::string var_name, std::string& event_name, std::string& tag_name) {
   10162            0 :    event_name = "";
   10163            0 :    tag_name = "";
   10164              : 
   10165            0 :    std::vector<size_t> colons;
   10166              : 
   10167            0 :    for (size_t i = 0; i < var_name.size(); i++) {
   10168            0 :       if (var_name[i] == ':') {
   10169            0 :          colons.push_back(i);
   10170              :       }
   10171              :    }
   10172              : 
   10173            0 :    if (colons.size() == 0) {
   10174              :       // No colons - leave the tag name empty
   10175            0 :       event_name = var_name;
   10176              :    } else { 
   10177              :       size_t split_pos;
   10178            0 :       size_t slash_pos = var_name.find("/");
   10179            0 :       bool uses_per_variable_naming = (slash_pos != std::string::npos);
   10180              : 
   10181            0 :       if (uses_per_variable_naming && colons.size() % 2 == 1) {
   10182            0 :          size_t middle_colon_pos = colons[colons.size() / 2];
   10183            0 :          std::string slash_to_mid = var_name.substr(slash_pos + 1, middle_colon_pos - slash_pos - 1);
   10184            0 :          std::string mid_to_end = var_name.substr(middle_colon_pos + 1);
   10185              : 
   10186            0 :          if (slash_to_mid == mid_to_end) {
   10187              :             // Special case - we have a string of the form Beamlime/GS2:FC1:GS2:FC1.
   10188              :             // Logger has already warned people that having colons in the equipment/event 
   10189              :             // names is a bad idea, so we only need to worry about them in the tag name.
   10190            0 :             split_pos = middle_colon_pos;
   10191              :          } else {
   10192              :             // We have a string of the form Beamlime/Demand:GS2:FC1. Split at the first colon.
   10193            0 :             split_pos = colons[0];
   10194              :          }
   10195            0 :       } else {
   10196              :          // Normal case - split at the fist colon.
   10197            0 :          split_pos = colons[0];
   10198              :       }
   10199              : 
   10200            0 :       event_name = var_name.substr(0, split_pos);
   10201            0 :       tag_name = var_name.substr(split_pos + 1);
   10202              :    }
   10203            0 : }
   10204              : 
   10205            0 : static void LoadHistPlotFromOdb(MVOdb* odb, HistPlot* hp, const char* group, const char* panel)
   10206              : {
   10207            0 :    std::string path = "History/Display/";
   10208            0 :    path += group;
   10209            0 :    path += "/";
   10210            0 :    path += panel;
   10211              :    
   10212            0 :    MVOdb* o = odb->Chdir(path.c_str());
   10213            0 :    if (!o) {
   10214            0 :       return;
   10215              :    }
   10216              : 
   10217            0 :    o->RS("Timescale",   &hp->timescale);
   10218            0 :    o->RD("Minimum",     &hp->minimum);
   10219            0 :    o->RD("Maximum",     &hp->maximum);
   10220            0 :    o->RB("Zero ylow",   &hp->zero_ylow);
   10221            0 :    o->RB("Log axis",    &hp->log_axis);
   10222            0 :    o->RB("Zero ylow",   &hp->zero_ylow);
   10223            0 :    o->RB("Show run markers", &hp->show_run_markers);
   10224            0 :    o->RB("Show values", &hp->show_values);
   10225            0 :    o->RB("Show fill",   &hp->show_fill);
   10226            0 :    o->RB("Show factor", &hp->show_factor);
   10227              :    //o->RB("Enable factor and offset", &hp->enable_factor);
   10228              : 
   10229            0 :    std::vector<std::string> hist_vars;
   10230            0 :    std::vector<std::string> hist_formula;
   10231            0 :    std::vector<std::string> hist_colour;
   10232            0 :    std::vector<std::string> hist_label;
   10233            0 :    std::vector<bool>   hist_show_raw_value;
   10234            0 :    std::vector<double> hist_factor;
   10235            0 :    std::vector<double> hist_offset;
   10236            0 :    std::vector<double> hist_voffset;
   10237              : 
   10238            0 :    o->RSA("Variables", &hist_vars);
   10239            0 :    o->RSA("Formula",   &hist_formula);
   10240            0 :    o->RSA("Colour",    &hist_colour);
   10241            0 :    o->RSA("Label",     &hist_label);
   10242            0 :    o->RBA("Show raw value", &hist_show_raw_value);
   10243            0 :    o->RDA("Factor",    &hist_factor);
   10244            0 :    o->RDA("Offset",    &hist_offset);
   10245            0 :    o->RDA("VOffset",   &hist_voffset);
   10246              : 
   10247              :    // fix broken plots with "factor" all zero. for reasons
   10248              :    // unknown the new history code has corrupted many
   10249              :    // history plot definitions like this. K.O.
   10250              :    {
   10251            0 :       bool all_zero = true;
   10252            0 :       for (size_t i=0; i<hist_factor.size(); i++) {
   10253            0 :          if (hist_factor[i] != 0)
   10254            0 :             all_zero = false;
   10255              :       }
   10256            0 :       if (all_zero) {
   10257            0 :          for (size_t i=0; i<hist_factor.size(); i++) {
   10258            0 :             hist_factor[i] = 1.0;
   10259              :          }
   10260              :       }
   10261              :    }
   10262              : 
   10263            0 :    size_t num = std::max(hist_vars.size(), hist_formula.size());
   10264            0 :    num = std::max(num, hist_colour.size());
   10265            0 :    num = std::max(num, hist_label.size());
   10266            0 :    num = std::max(num, hist_show_raw_value.size());
   10267            0 :    num = std::max(num, hist_factor.size());
   10268            0 :    num = std::max(num, hist_offset.size());
   10269            0 :    num = std::max(num, hist_voffset.size());
   10270              : 
   10271            0 :    hist_vars.resize(num);
   10272            0 :    hist_formula.resize(num);
   10273            0 :    hist_colour.resize(num);
   10274            0 :    hist_label.resize(num);
   10275            0 :    hist_show_raw_value.resize(num);
   10276            0 :    hist_factor.resize(num, 1.0);
   10277            0 :    hist_offset.resize(num, 0.0);
   10278            0 :    hist_voffset.resize(num, 0.0);
   10279              : 
   10280            0 :    for (size_t i=0; i<num; i++) {
   10281            0 :       HistVar v;
   10282              : 
   10283            0 :       SplitEventAndTagNames(hist_vars[i], v.event_name, v.tag_name);
   10284              : 
   10285            0 :       v.formula = hist_formula[i];
   10286            0 :       v.colour  = hist_colour[i];
   10287            0 :       v.label   = hist_label[i];
   10288            0 :       v.show_raw_value = hist_show_raw_value[i];
   10289            0 :       v.factor  = hist_factor[i];
   10290            0 :       v.offset  = hist_offset[i];
   10291            0 :       v.voffset = hist_voffset[i];
   10292            0 :       v.order   = NextHistPlotOrder(*hp);
   10293              : 
   10294              :       // one-time migration of factor and offset to formula
   10295            0 :       if (hp->enable_factor && v.formula.empty()) {
   10296            0 :          if (v.factor!=1 || v.offset!=0 || v.voffset!=0) {
   10297            0 :             v.formula = msprintf("%g%+g*(x%+g)", v.offset, v.factor, -v.voffset);
   10298              :          }
   10299              :       }
   10300              :       
   10301            0 :       hp->vars.push_back(v);
   10302            0 :    }
   10303              :    
   10304              : //   printf("Load from ODB %s: ", path.c_str());
   10305              : //   PrintHistPlot(*hp);
   10306              : 
   10307            0 :    delete o;
   10308            0 : }
   10309              : 
   10310            0 : static void LoadHistPlotFromParam(HistPlot* hp, Param* p)
   10311              : {
   10312            0 :    hp->timescale        = p->getparam("timescale");
   10313            0 :    hp->minimum          = strtod(p->getparam("minimum"), NULL);
   10314            0 :    hp->maximum          = strtod(p->getparam("maximum"), NULL);
   10315            0 :    hp->zero_ylow        = *p->getparam("zero_ylow");
   10316            0 :    hp->log_axis         = *p->getparam("log_axis");
   10317            0 :    hp->show_run_markers = *p->getparam("run_markers");
   10318            0 :    hp->show_values      = *p->getparam("show_values");
   10319            0 :    hp->show_fill        = *p->getparam("show_fill");
   10320            0 :    hp->show_factor      = *p->getparam("show_factor");
   10321              :    //hp->enable_factor    = *p->getparam("enable_factor");
   10322              :    
   10323            0 :    for (int index=0; ; index++) {
   10324              :       char str[256];
   10325            0 :       sprintf(str, "event%d", index);
   10326              :       
   10327              :       //printf("param event %d: [%s] [%s] [%d]\n", index, str, p->getparam(str), *p->getparam(str));
   10328              :       
   10329            0 :       if (!p->isparam(str))
   10330            0 :          break;
   10331              :       
   10332            0 :       if (*p->getparam(str) == '/') // "/empty"
   10333            0 :          continue;
   10334              :       
   10335            0 :       HistVar v;
   10336              : 
   10337            0 :       v.event_name = p->xgetparam(str);
   10338              :       
   10339            0 :       sprintf(str, "var%d", index);
   10340            0 :       v.tag_name = p->xgetparam(str);
   10341              :       
   10342            0 :       sprintf(str, "form%d", index);
   10343            0 :       v.formula = p->xgetparam(str);
   10344              :       
   10345            0 :       sprintf(str, "col%d", index);
   10346            0 :       v.colour = p->xgetparam(str);
   10347              :       
   10348            0 :       sprintf(str, "lab%d", index);
   10349            0 :       v.label = p->xgetparam(str);
   10350              :       
   10351            0 :       sprintf(str, "raw%d", index);
   10352            0 :       v.show_raw_value = atoi(p->xgetparam(str).c_str());
   10353              :       
   10354            0 :       sprintf(str, "factor%d", index);
   10355            0 :       if (p->isparam(str)) {
   10356            0 :          v.factor = atof(p->xgetparam(str).c_str());
   10357              :       } else {
   10358            0 :          v.factor = 1.0;
   10359              :       }
   10360              :       
   10361            0 :       sprintf(str, "offset%d", index);
   10362            0 :       v.offset = atof(p->xgetparam(str).c_str());
   10363              :       
   10364            0 :       sprintf(str, "voffset%d", index);
   10365            0 :       v.voffset = atof(p->xgetparam(str).c_str());
   10366              :       
   10367            0 :       sprintf(str, "ord%d", index);
   10368            0 :       if (p->isparam(str)) {
   10369            0 :          v.order = atoi(p->xgetparam(str).c_str());
   10370              :       } else {
   10371            0 :          v.order = NextHistPlotOrder(*hp);
   10372              :       }
   10373              : 
   10374            0 :       hp->vars.push_back(v);
   10375            0 :    }
   10376              :    
   10377              :    /* correctly number newly added variables */
   10378            0 :    for (size_t index=0; index<hp->vars.size(); index++) {
   10379            0 :       if (hp->vars[index].order < 0)
   10380            0 :          hp->vars[index].order = NextHistPlotOrder(*hp);
   10381              :    }
   10382              :    
   10383              : //   printf("Load from param:\n");
   10384              : //   PrintHistPlot(*hp);
   10385            0 : }
   10386              : 
   10387            0 : static void AddHistPlotSelectedParam(HistPlot& hp, Param* p)
   10388              : {
   10389            0 :    int seln = atoi(p->getparam("seln"));
   10390            0 :    for (int i=0; i<seln; i++) {
   10391              :       char str[256];
   10392            0 :       sprintf(str, "sel%d", i);
   10393              :       
   10394            0 :       std::string par = p->getparam(str);
   10395            0 :       if (par.length() < 1)
   10396            0 :          continue;
   10397              : 
   10398            0 :       std::string event_name, tag_name;
   10399            0 :       SplitEventAndTagNames(par, event_name, tag_name);
   10400              :       
   10401            0 :       if (tag_name == "")
   10402            0 :          continue;
   10403              :       
   10404            0 :       HistVar v;
   10405              :       
   10406            0 :       v.event_name = event_name;
   10407            0 :       v.tag_name   = tag_name;
   10408            0 :       v.colour     = NextHistPlotColour(hp);
   10409            0 :       v.order      = NextHistPlotOrder(hp);
   10410              :       
   10411            0 :       hp.vars.push_back(v);
   10412            0 :    }
   10413            0 : }
   10414              : 
   10415            0 : static void SaveHistPlotToOdb(MVOdb* odb, const HistPlot& hp, const char* group, const char* panel)
   10416              : {
   10417            0 :    if (strlen(group) < 1) {
   10418            0 :       cm_msg(MERROR, "SaveHistPlotToOdb", "Error: Cannot write history plot to ODB, group \"%s\", panel \"%s\", invalid group name", group, panel);
   10419            0 :       return;
   10420              :    }
   10421              : 
   10422            0 :    if (strlen(panel) < 1) {
   10423            0 :       cm_msg(MERROR, "SaveHistPlotToOdb", "Error: Cannot write history plot to ODB, group \"%s\", panel \"%s\", invalid panel name", group, panel);
   10424            0 :       return;
   10425              :    }
   10426              :    
   10427            0 :    std::string path = "History/Display/";
   10428            0 :    path += group;
   10429            0 :    path += "/";
   10430            0 :    path += panel;
   10431              : 
   10432              : //   printf("Save to ODB %s: ", path.c_str());
   10433              : //   PrintHistPlot(hp);
   10434              : 
   10435            0 :    MVOdb* o = odb->Chdir(path.c_str(), true);
   10436              :    
   10437            0 :    o->WS("Timescale", hp.timescale.c_str());
   10438            0 :    o->WD("Minimum", hp.minimum);
   10439            0 :    o->WD("Maximum", hp.maximum);
   10440            0 :    o->WB("Zero ylow", hp.zero_ylow);
   10441            0 :    o->WB("Log axis", hp.log_axis);
   10442            0 :    o->WB("Show run markers", hp.show_run_markers);
   10443            0 :    o->WB("Show values", hp.show_values);
   10444            0 :    o->WB("Show fill", hp.show_fill);
   10445            0 :    o->WB("Show factor and offset", hp.show_factor);
   10446              :    //o->WB("Enable factor and offset", hp.enable_factor);
   10447              : 
   10448            0 :    std::vector<std::string> hist_vars;
   10449            0 :    std::vector<std::string> hist_formula;
   10450            0 :    std::vector<std::string> hist_colour;
   10451            0 :    std::vector<std::string> hist_label;
   10452            0 :    std::vector<bool>        hist_show_raw_value;
   10453            0 :    std::vector<double> hist_factor;
   10454            0 :    std::vector<double> hist_offset;
   10455            0 :    std::vector<double> hist_voffset;
   10456              : 
   10457            0 :    for (size_t i=0; i<hp.vars.size(); i++) {
   10458            0 :       hist_vars.push_back(hp.vars[i].event_name + ":" + hp.vars[i].tag_name);
   10459            0 :       hist_formula.push_back(hp.vars[i].formula);
   10460            0 :       hist_colour.push_back(hp.vars[i].colour);
   10461            0 :       hist_label.push_back(hp.vars[i].label);
   10462            0 :       hist_show_raw_value.push_back(hp.vars[i].show_raw_value);
   10463            0 :       hist_factor.push_back(hp.vars[i].factor);
   10464            0 :       hist_offset.push_back(hp.vars[i].offset);
   10465            0 :       hist_voffset.push_back(hp.vars[i].voffset);
   10466              :    }
   10467              : 
   10468            0 :    if (hp.vars.size() > 0) {
   10469            0 :       o->WSA("Variables", hist_vars, 64);
   10470            0 :       o->WSA("Formula",   hist_formula, 64);
   10471            0 :       o->WSA("Colour",    hist_colour, NAME_LENGTH);
   10472            0 :       o->WSA("Label",     hist_label, NAME_LENGTH);
   10473            0 :       o->WBA("Show raw value", hist_show_raw_value);
   10474            0 :       o->WDA("Factor",    hist_factor);
   10475            0 :       o->WDA("Offset",    hist_offset);
   10476            0 :       o->WDA("VOffset",   hist_voffset);
   10477              :    } else {
   10478            0 :       o->Delete("Variables");
   10479            0 :       o->Delete("Formula");
   10480            0 :       o->Delete("Colour");
   10481            0 :       o->Delete("Label");
   10482            0 :       o->Delete("Show raw value");
   10483            0 :       o->Delete("Factor");
   10484            0 :       o->Delete("Offset");
   10485            0 :       o->Delete("VOffset");
   10486              :    }
   10487              : 
   10488            0 :    delete o;
   10489            0 : }
   10490              : 
   10491            0 : static void DeleteHistPlotDeleted(HistPlot& hp)
   10492              : {
   10493              :    /* delete variables according to "hist_order" */
   10494              :    
   10495              :    while (1) {
   10496            0 :       bool something_deleted = false;
   10497            0 :       for (unsigned i=0; i<hp.vars.size(); i++) {
   10498            0 :          if (hp.vars[i].order <= 0) {
   10499            0 :             hp.vars.erase(hp.vars.begin() + i);
   10500            0 :             something_deleted = true;
   10501              :          }
   10502              :       }
   10503            0 :       if (!something_deleted)
   10504            0 :          break;
   10505            0 :    }
   10506            0 : }
   10507              : 
   10508            0 : static void SortHistPlotVars(HistPlot& hp)
   10509              : {
   10510              :    /* sort variables according to "hist_order" */
   10511              :    
   10512            0 :    bool need_sort = false;
   10513            0 :    for (size_t i=1; i<hp.vars.size(); i++) {
   10514            0 :       if (hp.vars[i-1].order >= hp.vars[i].order) {
   10515            0 :          need_sort = true;
   10516              :       }
   10517              :    }
   10518              :    
   10519            0 :    if (need_sort) {
   10520              :       /* sort variables by order */
   10521            0 :       std::sort(hp.vars.begin(), hp.vars.end(), cmp_vars);
   10522              :       
   10523              :       /* renumber the variables according to the new sorted order */
   10524            0 :       for (size_t index=0; index<hp.vars.size(); index++)
   10525            0 :          hp.vars[index].order = (index+1)*10;
   10526              :    }
   10527            0 : }
   10528              : 
   10529            0 : void show_hist_config_page(MVOdb* odb, Param* p, Return* r, const char *hgroup, const char *hpanel)
   10530              : {
   10531              :    int status;
   10532            0 :    int max_display_events = 20;
   10533            0 :    int max_display_tags = 200;
   10534              :    char str[256], hcmd[256];
   10535              : 
   10536            0 :    odb->RI("History/MaxDisplayEvents", &max_display_events, true);
   10537            0 :    odb->RI("History/MaxDisplayTags", &max_display_tags, true);
   10538              : 
   10539            0 :    mstrlcpy(hcmd, p->getparam("hcmd"), sizeof(hcmd));
   10540              : 
   10541            0 :    if (equal_ustring(hcmd, "Clear history cache")) {
   10542              :       //printf("clear history cache!\n");
   10543            0 :       strcpy(hcmd, "Refresh");
   10544            0 :       MidasHistoryInterface *mh = get_history();
   10545            0 :       if (mh)
   10546            0 :          mh->hs_clear_cache();
   10547              :    }
   10548              : 
   10549              :    //printf("cmd [%s]\n", cmd);
   10550              :    //printf("cmdx [%s]\n", p->getparam("cmdx"));
   10551              : 
   10552            0 :    HistPlot hp;
   10553              : 
   10554            0 :    if (equal_ustring(hcmd, "refresh") || equal_ustring(hcmd, "save")) {
   10555            0 :       LoadHistPlotFromParam(&hp, p);
   10556            0 :       DeleteHistPlotDeleted(hp);
   10557              :    } else {
   10558            0 :       LoadHistPlotFromOdb(odb, &hp, hgroup, hpanel);
   10559              :    }
   10560              : 
   10561            0 :    SortHistPlotVars(hp);
   10562              : 
   10563            0 :    if (strlen(p->getparam("seln")) > 0)
   10564            0 :       AddHistPlotSelectedParam(hp, p);
   10565              : 
   10566              :    //hp->Print();
   10567              : 
   10568            0 :    if (hcmd[0] && equal_ustring(hcmd, "save")) {
   10569            0 :       SaveHistPlotToOdb(odb, hp, hgroup, hpanel);
   10570              : 
   10571            0 :       if (p->getparam("redir") && *p->getparam("redir"))
   10572            0 :          redirect(r, p->getparam("redir"));
   10573              :       else {
   10574            0 :          sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
   10575            0 :          redirect(r, str);
   10576              :       }
   10577            0 :       return;
   10578              :    }
   10579              : 
   10580            0 :    show_header(r, "History Config", "GET", "", 0);
   10581            0 :    r->rsprintf("</table>");  //close header table
   10582              : 
   10583            0 :    r->rsprintf("<table class=\"mtable\">"); //open main table
   10584              : 
   10585            0 :    r->rsprintf("<tr><th colspan=11 class=\"subStatusTitle\">History Panel \"%s\" / \"%s\"</th></tr>\n", hgroup, hpanel);
   10586              : 
   10587              :    /* menu buttons */
   10588            0 :    r->rsprintf("<tr><td colspan=11>\n");
   10589              : 
   10590            0 :    r->rsprintf("<input type=button value=Refresh ");
   10591            0 :    r->rsprintf("onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">\n");
   10592              : 
   10593            0 :    r->rsprintf("<input type=button value=Save ");
   10594            0 :    r->rsprintf("onclick=\"document.form1.hcmd.value='Save';document.form1.submit()\">\n");
   10595              : 
   10596              :    {
   10597            0 :    r->rsprintf("<input type=button value=Cancel ");
   10598            0 :    std::string url = "?cmd=oldhistory&group=";
   10599            0 :    url += hgroup;
   10600            0 :    url += "&panel=";
   10601            0 :    url += hpanel;
   10602            0 :    url += "&hcmd=Cancel";
   10603            0 :    if (p->getparam("redir")) {
   10604            0 :       url += "&redir=";
   10605            0 :       url += urlEncode(p->getparam("redir"));
   10606              :    }
   10607            0 :    r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
   10608            0 :    }
   10609              :    {
   10610            0 :    r->rsprintf("<input type=button value=\"Edit in ODB\"");
   10611            0 :    std::string url = "?cmd=odb&odb_path=";
   10612            0 :    url += "/History/Display/";
   10613            0 :    url += urlEncode(hgroup);
   10614            0 :    url += "/";
   10615            0 :    url += urlEncode(hpanel);
   10616            0 :    r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
   10617            0 :    }
   10618              :    {
   10619            0 :    r->rsprintf("<input type=button value=\"Edit in new editor\"");
   10620            0 :    std::string url = "?cmd=hs_edit";
   10621            0 :    url += "&group=";
   10622            0 :    url += urlEncode(hgroup);
   10623            0 :    url += "&panel=";
   10624            0 :    url += urlEncode(hpanel);
   10625            0 :    if (p->getparam("redir")) {
   10626            0 :       url += "&redir=";
   10627            0 :       url += urlEncode(p->getparam("redir"));
   10628              :    }
   10629            0 :    r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
   10630            0 :    }
   10631            0 :    r->rsprintf("<input type=button value=\"Clear history cache\"");
   10632            0 :    r->rsprintf("onclick=\"document.form1.hcmd.value='Clear history cache';document.form1.submit()\">\n");
   10633            0 :    r->rsprintf("<input type=button value=\"Delete panel\"");
   10634            0 :    r->rsprintf("onclick=\"window.location.search='?cmd=oldhistory&group=%s&panel=%s&hcmd=Delete%%20panel'\">\n", hgroup, hpanel);
   10635            0 :    r->rsprintf("</td></tr>\n");
   10636              : 
   10637            0 :    r->rsprintf("<tr><td colspan=11>\n");
   10638              : 
   10639              :    /* sort_vars */
   10640            0 :    int sort_vars = *p->getparam("sort_vars");
   10641            0 :    r->rsprintf("<input type=checkbox %s name=sort_vars value=1 onclick=\"this.form.submit();\">Sort variable names", sort_vars?"checked":"");
   10642              : 
   10643              :    /* old_vars */
   10644            0 :    int old_vars = *p->getparam("old_vars");
   10645            0 :    r->rsprintf("&nbsp;&nbsp;<input type=checkbox %s name=old_vars value=1 onclick=\"this.form.submit();\">Show deleted and renamed variables", old_vars?"checked":"");
   10646              : 
   10647            0 :    if (hp.show_factor)
   10648            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_factor value=1 onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">");
   10649              :    else
   10650            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_factor value=1 onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">");
   10651            0 :    r->rsprintf("Show&nbsp;factor&nbsp;and&nbsp;offset\n");
   10652              : 
   10653              :    /* hidden command for refresh */
   10654            0 :    r->rsprintf("<input type=hidden name=cmd value=Oldhistory>\n");
   10655            0 :    r->rsprintf("<input type=hidden name=hcmd value=Refresh>\n");
   10656            0 :    r->rsprintf("<input type=hidden name=panel value=\"%s\">\n", hpanel);
   10657            0 :    r->rsprintf("<input type=hidden name=group value=\"%s\">\n", hgroup);
   10658              : 
   10659            0 :    if (p->getparam("redir") && *p->getparam("redir"))
   10660            0 :       r->rsprintf("<input type=hidden name=redir value=\"%s\">\n", p->getparam("redir"));
   10661              : 
   10662            0 :    r->rsprintf("</td></tr>\n");
   10663              : 
   10664            0 :    r->rsprintf("<tr><td colspan=4 style='text-align:right'>Time scale (in units 'm', 'h', 'd'):</td>\n");
   10665            0 :    r->rsprintf("<td colspan=3><input type=text size=12 name=timescale value=%s></td><td colspan=4></td></tr>\n", hp.timescale.c_str());
   10666              : 
   10667            0 :    r->rsprintf("<tr><td colspan=4 style='text-align:right'>Minimum (set to '-inf' for autoscale):</td>\n");
   10668            0 :    r->rsprintf("<td colspan=3><input type=text size=12 name=minimum value=%f></td><td colspan=4></td></tr>\n", hp.minimum);
   10669              : 
   10670            0 :    r->rsprintf("<tr><td colspan=4 style='text-align:right'>Maximum (set to 'inf' for autoscale):</td>\n");
   10671            0 :    r->rsprintf("<td colspan=3><input type=text size=12 name=maximum value=%f></td><td colspan=4></td></tr>\n", hp.maximum);
   10672              : 
   10673            0 :    r->rsprintf("<tr><td colspan=11>");
   10674              :    
   10675            0 :    if (hp.zero_ylow)
   10676            0 :       r->rsprintf("<input type=checkbox checked name=zero_ylow value=1>");
   10677              :    else
   10678            0 :       r->rsprintf("<input type=checkbox name=zero_ylow value=1>");
   10679            0 :    r->rsprintf("Zero&nbsp;Y;&nbsp;axis\n");
   10680              : 
   10681            0 :    if (hp.log_axis)
   10682            0 :       r->rsprintf("<input type=checkbox checked name=log_axis value=1>");
   10683              :    else
   10684            0 :       r->rsprintf("<input type=checkbox name=log_axis value=1>");
   10685            0 :    r->rsprintf("Logarithmic&nbsp;Y&nbsp;axis\n");
   10686              : 
   10687            0 :    if (hp.show_run_markers)
   10688            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=run_markers value=1>");
   10689              :    else
   10690            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=run_markers value=1>");
   10691            0 :    r->rsprintf("Show&nbsp;run&nbsp;markers\n");
   10692              : 
   10693            0 :    if (hp.show_values)
   10694            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_values value=1>");
   10695              :    else
   10696            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_values value=1>");
   10697            0 :    r->rsprintf("Show&nbsp;values&nbsp;of&nbsp;variables\n");
   10698              : 
   10699            0 :    if (hp.show_fill)
   10700            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_fill value=1>");
   10701              :    else
   10702            0 :       r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_fill value=1>");
   10703            0 :    r->rsprintf("Show&nbsp;graph&nbsp;fill\n");
   10704              : 
   10705            0 :    r->rsprintf("</td></tr>\n");
   10706              : 
   10707              :    /*---- events and variables ----*/
   10708              : 
   10709              :    /* get display event name */
   10710              : 
   10711            0 :    MidasHistoryInterface* mh = get_history();
   10712            0 :    if (mh == NULL) {
   10713            0 :       r->rsprintf(str, "History is not configured\n");
   10714            0 :       return;
   10715              :    }
   10716              : 
   10717            0 :    time_t t = time(NULL);
   10718              : 
   10719            0 :    if (old_vars)
   10720            0 :       t = 0;
   10721              : 
   10722            0 :    std::vector<std::string> events;
   10723              : 
   10724            0 :    if (!old_vars)
   10725            0 :       hs_read_event_list(&events);
   10726              : 
   10727            0 :    if (events.size() == 0)
   10728            0 :       mh->hs_get_events(t, &events);
   10729              : 
   10730              : #if 0
   10731              :    for (unsigned i=0; i<events.size(); i++)
   10732              :       printf("event %d: \"%s\"\n", i, events[i].c_str());
   10733              : #endif
   10734              : 
   10735              :    // has to be sorted or equipment name code below would not work
   10736              :    //std::sort(events.begin(), events.end(), cmp_events);
   10737            0 :    std::sort(events.begin(), events.end(), cmp_events1);
   10738              : 
   10739            0 :    if (strlen(p->getparam("cmdx")) > 0) {
   10740            0 :       r->rsprintf("<tr><th colspan=8 class=\"subStatusTitle\">List of available history variables</th></tr>\n");
   10741            0 :       r->rsprintf("<tr><th colspan=1>Sel<th colspan=1>Equipment<th colspan=1>Event<th colspan=1>Variable</tr>\n");
   10742              : 
   10743            0 :       std::string cmdx = p->xgetparam("cmdx");
   10744            0 :       std::string xeqname;
   10745              : 
   10746            0 :       int i=0;
   10747            0 :       for (unsigned e=0; e<events.size(); e++) {
   10748            0 :          std::string eqname;
   10749            0 :          eqname = events[e].substr(0, events[e].find("/"));
   10750              : 
   10751            0 :          if (eqname.length() < 1)
   10752            0 :             eqname = events[e];
   10753              : 
   10754            0 :          bool once = false;
   10755            0 :          if (eqname != xeqname)
   10756            0 :             once = true;
   10757              : 
   10758            0 :          std::string qcmd = "Expand " + eqname;
   10759              : 
   10760              :          //printf("param [%s] is [%s]\n", qcmd.c_str(), p->getparam(qcmd.c_str()));
   10761              : 
   10762            0 :          bool collapsed = true;
   10763              : 
   10764            0 :          if (cmdx == qcmd)
   10765            0 :             collapsed = false;
   10766              : 
   10767            0 :          if (strlen(p->getparam(qcmd.c_str())) > 0)
   10768            0 :             collapsed = false;
   10769              : 
   10770            0 :          if (collapsed) {
   10771            0 :             if (eqname == xeqname)
   10772            0 :                continue;
   10773              : 
   10774            0 :             r->rsprintf("<tr align=left>\n");
   10775            0 :             r->rsprintf("<td></td>\n");
   10776            0 :             r->rsprintf("<td>%s</td>\n", eqname.c_str());
   10777            0 :             r->rsprintf("<td><input type=submit name=cmdx value=\"%s\"></td>\n", qcmd.c_str());
   10778            0 :             r->rsprintf("<td>%s</td>\n", "");
   10779            0 :             r->rsprintf("</tr>\n");
   10780            0 :             xeqname = eqname;
   10781            0 :             continue;
   10782              :          }
   10783              : 
   10784            0 :          if (once)
   10785            0 :             r->rsprintf("<tr><input type=hidden name=\"%s\" value=%d></tr>\n", qcmd.c_str(), 1);
   10786              : 
   10787            0 :          std::string rcmd = "Expand " + events[e];
   10788              : 
   10789              :          //printf("param [%s] is [%s]\n", rcmd.c_str(), p->getparam(rcmd.c_str()));
   10790              : 
   10791            0 :          collapsed = true;
   10792              : 
   10793            0 :          if (cmdx == rcmd)
   10794            0 :             collapsed = false;
   10795              : 
   10796            0 :          if (strlen(p->getparam(rcmd.c_str())) > 0)
   10797            0 :             collapsed = false;
   10798              : 
   10799            0 :          if (collapsed) {
   10800            0 :             r->rsprintf("<tr align=left>\n");
   10801            0 :             r->rsprintf("<td></td>\n");
   10802            0 :             r->rsprintf("<td>%s</td>\n", eqname.c_str());
   10803            0 :             r->rsprintf("<td>%s</td>\n", events[e].c_str());
   10804            0 :             r->rsprintf("<td><input type=submit name=cmdx value=\"%s\"></td>\n", rcmd.c_str());
   10805            0 :             r->rsprintf("</tr>\n");
   10806            0 :             continue;
   10807              :          }
   10808              : 
   10809            0 :          r->rsprintf("<tr><input type=hidden name=\"%s\" value=%d></tr>\n", rcmd.c_str(), 1);
   10810              : 
   10811            0 :          xeqname = eqname;
   10812              : 
   10813            0 :          std::vector<TAG> tags;
   10814              : 
   10815            0 :          status = mh->hs_get_tags(events[e].c_str(), t, &tags);
   10816              : 
   10817            0 :          if (status == HS_SUCCESS && tags.size() > 0) {
   10818              : 
   10819            0 :             if (sort_vars)
   10820            0 :                std::sort(tags.begin(), tags.end(), cmp_tags);
   10821              : 
   10822            0 :             for (unsigned v=0; v<tags.size(); v++) {
   10823              : 
   10824            0 :                for (unsigned j=0; j<tags[v].n_data; j++) {
   10825              :                   char tagname[256];
   10826              : 
   10827            0 :                   if (tags[v].n_data == 1)
   10828            0 :                      sprintf(tagname, "%s", tags[v].name);
   10829              :                   else
   10830            0 :                      sprintf(tagname, "%s[%d]", tags[v].name, j);
   10831              : 
   10832            0 :                   bool checked = false;
   10833              : #if 0
   10834              :                   for (int index=0; index<MAX_VARS; index++) {
   10835              :                     if (equal_ustring(vars[index].event_name, events[e].c_str()) && equal_ustring(vars[index].var_name, tagname)) {
   10836              :                       checked = true;
   10837              :                       break;
   10838              :                     }
   10839              :                   }
   10840              : #endif
   10841              : 
   10842            0 :                   r->rsprintf("<tr align=left>\n");
   10843            0 :                   r->rsprintf("<td><input type=checkbox %s name=\"sel%d\" value=\"%s:%s\"></td>\n", checked?"checked":"", i++, events[e].c_str(), tagname);
   10844            0 :                   r->rsprintf("<td>%s</td>\n", eqname.c_str());
   10845            0 :                   r->rsprintf("<td>%s</td>\n", events[e].c_str());
   10846            0 :                   r->rsprintf("<td>%s</td>\n", tagname);
   10847            0 :                   r->rsprintf("</tr>\n");
   10848              :                }
   10849              :             }
   10850              :          }
   10851            0 :       }
   10852              : 
   10853            0 :       r->rsprintf("<tr>\n");
   10854            0 :       r->rsprintf("<td></td>\n");
   10855            0 :       r->rsprintf("<td>\n");
   10856            0 :       r->rsprintf("<input type=hidden name=seln value=%d>\n", i);
   10857            0 :       r->rsprintf("<input type=submit value=\"Add Selected\">\n");
   10858            0 :       r->rsprintf("</td>\n");
   10859            0 :       r->rsprintf("</tr>\n");
   10860            0 :    }
   10861              : 
   10862            0 :    r->rsprintf("<tr><td colspan=11 style='text-align:left'>New history: displayed_value = formula(history_value)</td></tr>\n");
   10863            0 :    r->rsprintf("<tr><td colspan=11 style='text-align:left'>Old history: displayed_value = offset + factor*(history_value - voffset)</td></tr>\n");
   10864            0 :    r->rsprintf("<tr><td colspan=11 style='text-align:left'>Formula format: \"3*x+4\", \"10*Math.sin(x)\", etc. all javascript math functions can be used</td></tr>\n");
   10865            0 :    r->rsprintf("<tr><td colspan=11 style='text-align:left'>To display the raw history value instead of computed formula or offset value, check the \"raw\" checkbox</td></tr>\n");
   10866            0 :    r->rsprintf("<tr><td colspan=11 style='text-align:left'>To reorder entries: enter new ordering in the \"order\" column and press \"refresh\"</td></tr>\n");
   10867            0 :    r->rsprintf("<tr><td colspan=11 style='text-align:left'>To delete entries: enter \"-1\" or leave blank the \"order\" column and press \"refresh\"</td></tr>\n");
   10868              : 
   10869            0 :    r->rsprintf("<tr>\n");
   10870            0 :    r->rsprintf("<th>Col<th>Event<th>Variable<th>Formula<th>Colour<th>Label<th>Raw<th>Order");
   10871            0 :    if (hp.show_factor) {
   10872            0 :       r->rsprintf("<th>Factor<th>Offset<th>VOffset");
   10873              :    }
   10874            0 :    r->rsprintf("</tr>\n");
   10875              : 
   10876              :    //print_vars(vars);
   10877              : 
   10878            0 :    size_t nvars = hp.vars.size();
   10879            0 :    for (size_t index = 0; index <= nvars; index++) {
   10880              : 
   10881            0 :       r->rsprintf("<tr>");
   10882              : 
   10883            0 :       if (index < nvars) {
   10884            0 :          if (hp.vars[index].colour.empty())
   10885            0 :             hp.vars[index].colour = NextHistPlotColour(hp);
   10886            0 :          r->rsprintf("<td style=\"background-color:%s\">&nbsp;<td>\n", hp.vars[index].colour.c_str());
   10887              :       } else {
   10888            0 :          r->rsprintf("<td>&nbsp;<td>\n");
   10889              :       }
   10890              : 
   10891              :       /* event and variable selection */
   10892              : 
   10893            0 :       r->rsprintf("<select name=\"event%d\" size=1 onChange=\"document.form1.submit()\">\n", (int)index);
   10894              : 
   10895              :       /* enumerate events */
   10896              : 
   10897              :       /* empty option */
   10898            0 :       r->rsprintf("<option value=\"/empty\">&lt;empty&gt;\n");
   10899              : 
   10900            0 :       if (index==nvars) { // last "empty" entry
   10901            0 :          for (unsigned e=0; e<events.size(); e++) {
   10902            0 :             const char *p = events[e].c_str();
   10903            0 :             r->rsprintf("<option value=\"%s\">%s\n", p, p);
   10904              :          }
   10905            0 :       } else if ((int)events.size() > max_display_events) { // too many events
   10906            0 :          r->rsprintf("<option selected value=\"%s\">%s\n", hp.vars[index].event_name.c_str(), hp.vars[index].event_name.c_str());
   10907            0 :          r->rsprintf("<option>(%d events omitted)\n", (int)events.size());
   10908              :       } else { // show all events
   10909            0 :          bool found = false;
   10910            0 :          for (unsigned e=0; e<events.size(); e++) {
   10911            0 :             const char *s = "";
   10912            0 :             const char *p = events[e].c_str();
   10913            0 :             if (equal_ustring(hp.vars[index].event_name.c_str(), p)) {
   10914            0 :                s = "selected";
   10915            0 :                found = true;
   10916              :             }
   10917            0 :             r->rsprintf("<option %s value=\"%s\">%s\n", s, p, p);
   10918              :          }
   10919            0 :          if (!found) {
   10920            0 :             const char *p = hp.vars[index].event_name.c_str();
   10921            0 :             r->rsprintf("<option selected value=\"%s\">%s\n", p, p);
   10922              :          }
   10923              :       }
   10924              : 
   10925            0 :       r->rsprintf("</select></td>\n");
   10926              : 
   10927              :       //if (hp.vars[index].order <= 0)
   10928              :       //   hp.vars[index].order = (index+1)*10;
   10929              : 
   10930            0 :       if (index < nvars) {
   10931            0 :          bool found_tag = false;
   10932            0 :          std::string selected_tag = hp.vars[index].tag_name;
   10933              : 
   10934            0 :          r->rsprintf("<td><select name=\"var%d\">\n", (int)index);
   10935              : 
   10936            0 :          std::vector<TAG> tags;
   10937              : 
   10938            0 :          status = mh->hs_get_tags(hp.vars[index].event_name.c_str(), t, &tags);
   10939              : 
   10940            0 :          if (status == HS_SUCCESS && tags.size() > 0) {
   10941              : 
   10942              :             if (/* DISABLES CODE */ (0)) {
   10943              :                printf("Compare %d\n", cmp_names("AAA", "BBB"));
   10944              :                printf("Compare %d\n", cmp_names("BBB", "AAA"));
   10945              :                printf("Compare %d\n", cmp_names("AAA", "AAA"));
   10946              :                printf("Compare %d\n", cmp_names("A", "AAA"));
   10947              :                printf("Compare %d\n", cmp_names("A111", "A1"));
   10948              :                printf("Compare %d\n", cmp_names("A111", "A2"));
   10949              :                printf("Compare %d\n", cmp_names("A111", "A222"));
   10950              :                printf("Compare %d\n", cmp_names("A111a", "A111b"));
   10951              :             }
   10952              : 
   10953            0 :             if (sort_vars)
   10954            0 :                std::sort(tags.begin(), tags.end(), cmp_tags);
   10955              : 
   10956              :             if (/* DISABLES CODE */ (0)) {
   10957              :                printf("Event [%s] %d tags\n", hp.vars[index].event_name.c_str(), (int)tags.size());
   10958              : 
   10959              :                for (unsigned v=0; v<tags.size(); v++) {
   10960              :                  printf("tag[%d] [%s]\n", v, tags[v].name);
   10961              :                }
   10962              :             }
   10963              : 
   10964            0 :             unsigned count_tags = 0;
   10965            0 :             for (unsigned v=0; v<tags.size(); v++)
   10966            0 :                count_tags += tags[v].n_data;
   10967              : 
   10968              :             //printf("output %d option tags\n", count_tags);
   10969              : 
   10970            0 :             if ((int)count_tags < max_display_tags) {
   10971            0 :                for (unsigned v=0; v<tags.size(); v++) {
   10972              : 
   10973            0 :                  for (unsigned j=0; j<tags[v].n_data; j++) {
   10974            0 :                     std::string tagname;
   10975              : 
   10976            0 :                     if (tags[v].n_data == 1)
   10977            0 :                        tagname = tags[v].name;
   10978              :                     else {
   10979              :                        char buf[256];
   10980            0 :                        sprintf(buf, "[%d]", j);
   10981            0 :                        tagname = std::string(tags[v].name) + buf;
   10982              :                     }
   10983              : 
   10984            0 :                     if (equal_ustring(selected_tag.c_str(), tagname.c_str())) {
   10985            0 :                        r->rsprintf("<option selected value=\"%s\">%s\n", tagname.c_str(), tagname.c_str());
   10986            0 :                        found_tag = true;
   10987              :                     }
   10988              :                     else
   10989            0 :                        r->rsprintf("<option value=\"%s\">%s\n", tagname.c_str(), tagname.c_str());
   10990              : 
   10991              :                     //printf("%d [%s] [%s] [%s][%s] %d\n", (int)index, vars[index].event_name, tagname, vars[index].var_name, selected_var, found_var);
   10992            0 :                  }
   10993              :                }
   10994              :             }
   10995              :          }
   10996              : 
   10997            0 :          if (!found_tag)
   10998            0 :             if (hp.vars[index].tag_name.length() > 0)
   10999            0 :                r->rsprintf("<option selected value=\"%s\">%s\n", hp.vars[index].tag_name.c_str(), hp.vars[index].tag_name.c_str());
   11000              : 
   11001            0 :          r->rsprintf("</select></td>\n");
   11002            0 :          r->rsprintf("<td><input type=text size=15 maxlength=256 name=\"form%d\" value=%s></td>\n", (int)index, hp.vars[index].formula.c_str());
   11003            0 :          r->rsprintf("<td><input type=text size=8 maxlength=10 name=\"col%d\" value=%s></td>\n", (int)index, hp.vars[index].colour.c_str());
   11004            0 :          r->rsprintf("<td><input type=text size=8 maxlength=%d name=\"lab%d\" value=\"%s\"></td>\n", NAME_LENGTH, (int)index, hp.vars[index].label.c_str());
   11005            0 :          if (hp.vars[index].show_raw_value)
   11006            0 :             r->rsprintf("<td><input type=checkbox checked name=\"raw%d\" value=1></td>", (int)index);
   11007              :          else
   11008            0 :             r->rsprintf("<td><input type=checkbox name=\"raw%d\" value=1></td>", (int)index);
   11009            0 :          r->rsprintf("<td><input type=text size=3 maxlength=32 name=\"ord%d\" value=\"%d\"></td>\n", (int)index, hp.vars[index].order);
   11010            0 :          if (hp.show_factor) {
   11011            0 :             r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"factor%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].factor);
   11012            0 :             r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"offset%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].offset);
   11013            0 :             r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"voffset%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].voffset);
   11014              :          } else {
   11015            0 :             r->rsprintf("<input type=hidden name=\"factor%d\" value=\"%f\">\n", (int)index, hp.vars[index].factor);
   11016            0 :             r->rsprintf("<input type=hidden name=\"offset%d\" value=\"%f\">\n", (int)index, hp.vars[index].offset);
   11017            0 :             r->rsprintf("<input type=hidden name=\"voffset%d\" value=\"%f\">\n", (int)index, hp.vars[index].voffset);
   11018              :          }
   11019            0 :       } else {
   11020            0 :          r->rsprintf("<td colspan=2><input type=submit name=cmdx value=\"List all variables\"></td>\n");
   11021              :       }
   11022              : 
   11023            0 :       r->rsprintf("</tr>\n");
   11024              :    }
   11025              : 
   11026            0 :    r->rsprintf("</table>\n");
   11027              :    //r->rsprintf("</form>\n");
   11028            0 :    r->rsprintf("</div>\n"); // closing for <div id="mmain">
   11029            0 :    r->rsprintf("</form>\n");
   11030            0 :    r->rsprintf("</body></html>\r\n");
   11031            0 : }
   11032              : 
   11033              : /*------------------------------------------------------------------*/
   11034              : 
   11035            0 : void export_hist(MVOdb* odb, Return* r, const char *group, const char *panel, time_t endtime, int scale, int index, int labels)
   11036              : {
   11037              :    //HNDLE hDB, hkey, hkeypanel;
   11038              :    //int size;
   11039              :    int status;
   11040              :    //char str[256];
   11041              : 
   11042            0 :    int debug = 0;
   11043              : 
   11044            0 :    ss_tzset(); // required for localtime_r()
   11045              : 
   11046              : #if 0
   11047              :    cm_get_experiment_database(&hDB, NULL);
   11048              : 
   11049              :    /* check panel name in ODB */
   11050              :    sprintf(str, "/History/Display/%s/%s", group, panel);
   11051              :    db_find_key(hDB, 0, str, &hkeypanel);
   11052              :    if (!hkeypanel) {
   11053              :       sprintf(str, "Cannot find /History/Display/%s/%s in ODB\n", group, panel);
   11054              :       show_error(r, str);
   11055              :       return;
   11056              :    }
   11057              : 
   11058              :    /* get runmarker flag */
   11059              :    BOOL runmarker = 1;
   11060              :    size = sizeof(runmarker);
   11061              :    db_get_value(hDB, hkeypanel, "Show run markers", &runmarker, &size, TID_BOOL, TRUE);
   11062              : 
   11063              :    if (scale == 0) {
   11064              :       /* get timescale */
   11065              :       std::string ts = "1h";
   11066              :       status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
   11067              :       if (status != DB_SUCCESS) {
   11068              :          /* delete old integer key */
   11069              :          db_find_key(hDB, hkeypanel, "Timescale", &hkey);
   11070              :          if (hkey)
   11071              :             db_delete_key(hDB, hkey, FALSE);
   11072              : 
   11073              :          ts = "1h";
   11074              :          status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
   11075              :       }
   11076              : 
   11077              :       scale = time_to_sec(ts.c_str());
   11078              :    }
   11079              : #endif
   11080              : 
   11081            0 :    time_t now = ss_time();
   11082              : 
   11083            0 :    if (endtime == 0)
   11084            0 :       endtime = now;
   11085              : 
   11086            0 :    HistoryData hsxxx;
   11087            0 :    HistoryData* hsdata = &hsxxx;
   11088              : 
   11089            0 :    HistPlot hp;
   11090            0 :    LoadHistPlotFromOdb(odb, &hp, group, panel);
   11091              : 
   11092            0 :    time_t starttime = endtime - scale;
   11093              : 
   11094              :    //printf("start %.0f, end %.0f, scale %.0f\n", (double)starttime, (double)endtime, (double)scale);
   11095              : 
   11096            0 :    status = read_history(hp, /*hDB, group, panel,*/ index, hp.show_run_markers, starttime, endtime, 0, hsdata);
   11097            0 :    if (status != HS_SUCCESS) {
   11098              :       char str[256];
   11099            0 :       sprintf(str, "History error, status %d\n", status);
   11100            0 :       show_error(r, str);
   11101            0 :       return;
   11102              :    }
   11103              : 
   11104            0 :    if (debug)
   11105            0 :       hsdata->Print();
   11106              : 
   11107            0 :    int *i_var = (int *)malloc(sizeof(int)*hsdata->nvars);
   11108              : 
   11109            0 :    assert(i_var != NULL);
   11110              : 
   11111            0 :    for (int i = 0; i < hsdata->nvars; i++)
   11112            0 :       i_var[i] = -1;
   11113              : 
   11114            0 :    time_t t = 0;
   11115              : 
   11116              :    /* find first time where all variables are available */
   11117            0 :    for (int i = 0; i < hsdata->nvars; i++)
   11118            0 :       if (hsdata->odb_index[i] >= 0)
   11119            0 :          if (hsdata->num_entries[i] > 0)
   11120            0 :             if ((t == 0) || (hsdata->t[i][0] > t))
   11121            0 :                t = hsdata->t[i][0];
   11122              : 
   11123            0 :    if (t == 0 && hsdata->nvars > 1) {
   11124            0 :       show_error(r, "No history available for choosen period");
   11125            0 :       free(i_var);
   11126            0 :       return;
   11127              :    }
   11128              : 
   11129            0 :    int run_index = -1;
   11130            0 :    int state_index = -1;
   11131            0 :    int n_run_number = 0;
   11132            0 :    time_t* t_run_number = NULL;
   11133            0 :    if (hp.show_run_markers)
   11134            0 :       for (int i = 0; i < hsdata->nvars; i++) {
   11135            0 :          if (hsdata->odb_index[i] == -2) {
   11136            0 :             n_run_number = hsdata->num_entries[i];
   11137            0 :             t_run_number = hsdata->t[i];
   11138            0 :             run_index = i;
   11139            0 :          } else if (hsdata->odb_index[i] == -1) {
   11140            0 :             state_index = i;
   11141              :          }
   11142              :       }
   11143              : 
   11144              :    //printf("runmarker %d, state %d, run %d\n", runmarker, state_index, run_index);
   11145              : 
   11146              :    /* header */
   11147            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
   11148            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
   11149            0 :    r->rsprintf("Accept-Ranges: bytes\r\n");
   11150            0 :    r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
   11151            0 :    r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
   11152            0 :    r->rsprintf("Content-Type: text/plain\r\n");
   11153            0 :    r->rsprintf("Content-disposition: attachment; filename=\"export.csv\"\r\n");
   11154            0 :    r->rsprintf("\r\n");
   11155              : 
   11156              :    /* output header line with variable names */
   11157            0 :    if (hp.show_run_markers && t_run_number)
   11158            0 :       r->rsprintf("Time, Timestamp, Run, Run State, ");
   11159              :    else
   11160            0 :       r->rsprintf("Time, Timestamp, ");
   11161              : 
   11162            0 :    for (int i = 0, first = 1; i < hsdata->nvars; i++) {
   11163            0 :       if (hsdata->odb_index[i] < 0)
   11164            0 :          continue;
   11165            0 :       if (hsdata->num_entries[i] <= 0)
   11166            0 :          continue;
   11167            0 :       if (!first)
   11168            0 :          r->rsprintf(", ");
   11169            0 :       first = 0;
   11170            0 :       r->rsprintf("%s", hsdata->var_names[i]);
   11171              :    }
   11172            0 :    r->rsprintf("\n");
   11173              : 
   11174            0 :    int i_run = 0;
   11175              : 
   11176              :    do {
   11177              : 
   11178            0 :       if (debug)
   11179            0 :          printf("hsdata %p, t %d, irun %d\n", hsdata, (int)t, i_run);
   11180              : 
   11181              :       /* find run number/state which is valid for t */
   11182            0 :       if (hp.show_run_markers && t_run_number)
   11183            0 :          while (i_run < n_run_number-1 && t_run_number[i_run+1] <= t)
   11184            0 :             i_run++;
   11185              : 
   11186              :       //printf("irun %d\n", i_run);
   11187              : 
   11188              :       /* find index for all variables which is valid for t */
   11189            0 :       for (int i = 0; i < hsdata->nvars; i++)
   11190            0 :          while (hsdata->num_entries[i] > 0 && i_var[i] < hsdata->num_entries[i] - 1 && hsdata->t[i][i_var[i]+1] <= t)
   11191            0 :             i_var[i]++;
   11192              : 
   11193              :       /* finish if last point for all variables reached */
   11194            0 :       bool done = true;
   11195            0 :       for (int i = 0 ; i < hsdata->nvars ; i++)
   11196            0 :          if (hsdata->num_entries[i] > 0 && i_var[i] < hsdata->num_entries[i]) {
   11197            0 :             done = false;
   11198            0 :             break;
   11199              :          }
   11200              : 
   11201            0 :       if (debug) {
   11202            0 :          printf("step to time %d: ", (int)t);
   11203            0 :          for (int i = 0; i < hsdata->nvars; i++) {
   11204            0 :             printf(" [%d] %d, ", hsdata->num_entries[i], i_var[i]);
   11205              :          }
   11206            0 :          printf(" done: %d\n", done);
   11207              :       }
   11208              : 
   11209            0 :       if (done)
   11210            0 :          break;
   11211              : 
   11212              :       struct tm tms;
   11213            0 :       localtime_r(&t, &tms);
   11214              : 
   11215              :       char fmt[256];
   11216              :       //strcpy(fmt, "%c");
   11217            0 :       strcpy(fmt, "%Y.%m.%d %H:%M:%S");
   11218              :       char str[256];
   11219            0 :       strftime(str, sizeof(str), fmt, &tms);
   11220              : 
   11221            0 :       if (t_run_number && run_index>=0 && state_index>=0) {
   11222            0 :          if (t_run_number[i_run] <= t)
   11223            0 :             r->rsprintf("%s, %d, %.0f, %.0f, ", str, (int)t, hsdata->v[run_index][i_run], hsdata->v[state_index][i_run]);
   11224              :          else
   11225            0 :             r->rsprintf("%s, %d, N/A, N/A, ", str, (int)t);
   11226              :       } else
   11227            0 :          r->rsprintf("%s, %d, ", str, (int)t);
   11228              : 
   11229            0 :       if (debug) {
   11230            0 :          for (int i= 0 ; i < hsdata->nvars ; i++)
   11231            0 :             printf(" %d (%g)", i_var[i], hsdata->v[i][i_var[i]]);
   11232            0 :          printf("\n");
   11233              :       }
   11234              : 
   11235            0 :       for (int i=0, first=1 ; i<hsdata->nvars ; i++) {
   11236            0 :          if (i_var[i] < 0)
   11237            0 :             continue;
   11238            0 :          if (hsdata->odb_index[i] < 0)
   11239            0 :             continue;
   11240            0 :          if (!first)
   11241            0 :             r->rsprintf(", ");
   11242            0 :          first = 0;
   11243              :          //r->rsprintf("(%d %g)", i_var[i], hsdata->v[i][i_var[i]]);
   11244            0 :          r->rsprintf("%g", hsdata->v[i][i_var[i]]);
   11245              :       }
   11246            0 :       r->rsprintf("\n");
   11247              : 
   11248              :       /* find next t as smallest delta t */
   11249            0 :       int dt = -1;
   11250            0 :       for (int i = 0 ; i < hsdata->nvars ; i++)
   11251            0 :          if (i_var[i]>=0 && hsdata->odb_index[i]>=0 && hsdata->num_entries[i]>0 && i_var[i]<hsdata->num_entries[i]-1) {
   11252            0 :             int xdt = hsdata->t[i][i_var[i]+1] - t;
   11253            0 :             if (debug)
   11254            0 :                printf("var %d, i_var %d->%d, t %d->%d, dt %d\n", i, i_var[i], i_var[i]+1, (int)hsdata->t[i][i_var[i]], (int)hsdata->t[i][i_var[i]+1], xdt);
   11255            0 :             if (dt <= 0 || xdt < dt)
   11256            0 :                dt = xdt;
   11257              :          }
   11258              : 
   11259            0 :       if (debug)
   11260            0 :          printf("dt %d\n", dt);
   11261              : 
   11262            0 :       if (dt <= 0)
   11263            0 :          break;
   11264              : 
   11265            0 :       t += dt;
   11266              : 
   11267            0 :    } while (1);
   11268              : 
   11269            0 :    free(i_var);
   11270            0 : }
   11271              : 
   11272              : /*------------------------------------------------------------------*/
   11273              : 
   11274            0 : void show_hist_page(MVOdb* odb, Param* p, Return* r, const char *dec_path, char *buffer, int *buffer_size, int refresh)
   11275              : {
   11276              :    HNDLE hDB, hkey, hikeyp, hkeyp, hkeybutton;
   11277              :    KEY key, ikey;
   11278              :    int i, j, k, scale, index, width, size, status, labels;
   11279              :    char hgroup[256], hpanel[256], hcmd[256];
   11280            0 :    const char def_button[][NAME_LENGTH] = { "10m", "1h", "3h", "12h", "24h", "3d", "7d" };
   11281              : 
   11282            0 :    cm_get_experiment_database(&hDB, NULL);
   11283              : 
   11284            0 :    hcmd[0] = hgroup[0] = hpanel[0] = 0;
   11285              : 
   11286            0 :    if (p->getparam("group") && *p->getparam("group"))
   11287            0 :       mstrlcpy(hgroup, p->getparam("group"), sizeof(hgroup));
   11288            0 :    if (p->getparam("panel") && *p->getparam("panel"))
   11289            0 :       mstrlcpy(hpanel, p->getparam("panel"), sizeof(hpanel));
   11290            0 :    if (p->getparam("hcmd") && *p->getparam("hcmd"))
   11291            0 :       mstrlcpy(hcmd, p->getparam("hcmd"), sizeof(hcmd));
   11292              : 
   11293            0 :    if (equal_ustring(hcmd, "Reset")) {
   11294            0 :       std::string redir;
   11295              :       //sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
   11296            0 :       redir += "?cmd=oldhistory&group=";
   11297            0 :       redir += hgroup;
   11298            0 :       redir += "&panel=";
   11299            0 :       redir += hpanel;
   11300            0 :       redirect(r, redir.c_str());
   11301            0 :       return;
   11302            0 :    }
   11303              : 
   11304            0 :    if (equal_ustring(hcmd, "Query")) {
   11305            0 :       show_query_page(p, r);
   11306            0 :       return;
   11307              :    }
   11308              : 
   11309            0 :    if (equal_ustring(hcmd, "Cancel")) {
   11310              :       //sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
   11311            0 :       if (p->getparam("redir") && *p->getparam("redir"))
   11312            0 :          redirect(r, p->getparam("redir"));
   11313              :       else {
   11314            0 :          std::string redir;
   11315            0 :          redir += "?cmd=oldhistory&group=";
   11316            0 :          redir += hgroup;
   11317            0 :          redir += "&panel=";
   11318            0 :          redir += hpanel;
   11319            0 :          redirect(r, redir.c_str());
   11320            0 :       }
   11321            0 :       return;
   11322              :    }
   11323              : 
   11324            0 :    if (equal_ustring(hcmd, "Config") ||
   11325            0 :        equal_ustring(hcmd, "Save")
   11326            0 :        || equal_ustring(hcmd, "Clear history cache")
   11327            0 :        || equal_ustring(hcmd, "Refresh")) {
   11328              : 
   11329            0 :       show_hist_config_page(odb, p, r, hgroup, hpanel);
   11330            0 :       return;
   11331              :    }
   11332              : 
   11333            0 :    if (equal_ustring(hcmd, "New")) {
   11334            0 :       show_header(r, "History", "GET", "", 0);
   11335              : 
   11336            0 :       r->rsprintf("<table class=\"dialogTable\">");
   11337            0 :       r->rsprintf("<tr><th class=\"subStatusTitle\" colspan=2>New History Item</th><tr>");
   11338            0 :       r->rsprintf("<tr><td align=center colspan=2>\n");
   11339            0 :       r->rsprintf("Select group: &nbsp;&nbsp;");
   11340            0 :       r->rsprintf("<select id=\"group\" name=\"group\">\n");
   11341              : 
   11342              :       /* list existing groups */
   11343            0 :       db_find_key(hDB, 0, "/History/Display", &hkey);
   11344            0 :       i = 0;
   11345            0 :       if (hkey) {
   11346            0 :          for (i = 0;; i++) {
   11347            0 :             db_enum_link(hDB, hkey, i, &hkeyp);
   11348              : 
   11349            0 :             if (!hkeyp)
   11350            0 :                break;
   11351              : 
   11352            0 :             db_get_key(hDB, hkeyp, &key);
   11353            0 :             if (equal_ustring(hgroup, key.name))
   11354            0 :                r->rsprintf("<option selected>%s</option>\n", key.name);
   11355              :             else
   11356            0 :                r->rsprintf("<option>%s</option>\n", key.name);
   11357              :          }
   11358              :       }
   11359            0 :       if (!hkey || i == 0)
   11360            0 :          r->rsprintf("<option>Default</option>\n");
   11361            0 :       r->rsprintf("</select><p>\n");
   11362              : 
   11363            0 :       r->rsprintf("Or enter new group name: &nbsp;&nbsp;");
   11364            0 :       r->rsprintf("<input type=text size=15 maxlength=31 id=new_group name=new_group>\n");
   11365              : 
   11366            0 :       r->rsprintf("<tr><td align=center colspan=2>\n");
   11367            0 :       r->rsprintf("<br>Panel name: &nbsp;&nbsp;");
   11368            0 :       r->rsprintf("<input type=text size=15 maxlength=31 id=panel name=panel><br><br>\n");
   11369            0 :       r->rsprintf("</td></tr>\n");
   11370              : 
   11371            0 :       r->rsprintf("<tr><td align=center colspan=2>");
   11372            0 :       std::string str = "?cmd=oldhistory&hcmd=createnew";
   11373            0 :       str += "&new_group='+document.getElementById('new_group').value+'";
   11374            0 :       str += "&group='+document.getElementById('group').value+'";
   11375            0 :       str += "&panel='+document.getElementById('panel').value+'";
   11376            0 :       r->rsprintf("<input type=button value=Submit onclick=\"window.location.search='%s'\">\n", str.c_str());
   11377            0 :       r->rsprintf("</td></tr>\n");
   11378              : 
   11379            0 :       r->rsprintf("</table>\r\n");
   11380            0 :       r->rsprintf("</div>\n"); // closing for <div id="mmain">
   11381            0 :       r->rsprintf("</form>\n");
   11382            0 :       r->rsprintf("</body></html>\r\n");
   11383            0 :       return;
   11384            0 :    }
   11385              : 
   11386            0 :    if (equal_ustring(hcmd, "Delete Panel")) {
   11387            0 :       std::string path;
   11388              :       //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
   11389            0 :       path += "/History/Display/";
   11390            0 :       path += hgroup;
   11391            0 :       path += "/";
   11392            0 :       path += hpanel;
   11393            0 :       if (db_find_key(hDB, 0, path.c_str(), &hkey)==DB_SUCCESS)
   11394            0 :          db_delete_key(hDB, hkey, FALSE);
   11395              : 
   11396            0 :       redirect(r, "?cmd=oldhistory");
   11397            0 :       return;
   11398            0 :    }
   11399              : 
   11400            0 :    if (equal_ustring(hcmd, "createnew")) {
   11401              : 
   11402              :       /* strip leading/trailing spaces */
   11403            0 :       while (hpanel[0] == ' ') {
   11404              :          char str[256];
   11405            0 :          mstrlcpy(str, hpanel+1, sizeof(str));
   11406            0 :          mstrlcpy(hpanel, str, sizeof(hpanel));
   11407              :       }
   11408            0 :       while (strlen(hpanel)> 1 && hpanel[strlen(hpanel)-1] == ' ')
   11409            0 :          hpanel[strlen(hpanel)-1] = 0;
   11410              : 
   11411              :       /* use new group if present */
   11412            0 :       if (p->isparam("new_group") && *p->getparam("new_group"))
   11413            0 :          mstrlcpy(hgroup, p->getparam("new_group"), sizeof(hgroup));
   11414              : 
   11415              :       /* configure that panel */
   11416            0 :       show_hist_config_page(odb, p, r, hgroup, hpanel);
   11417            0 :       return;
   11418              :    }
   11419              : 
   11420            0 :    const char* pscale = p->getparam("scale");
   11421            0 :    if (pscale == NULL || *pscale == 0)
   11422            0 :       pscale = p->getparam("hscale");
   11423            0 :    const char* pwidth = p->getparam("width");
   11424            0 :    if (pwidth == NULL || *pwidth == 0)
   11425            0 :       pwidth = p->getparam("hwidth");
   11426            0 :    const char* pheight = p->getparam("height");
   11427            0 :    if (pheight == NULL || *pheight == 0)
   11428            0 :       pheight = p->getparam("hheight");
   11429            0 :    const char* pindex = p->getparam("index");
   11430            0 :    if (pindex == NULL || *pindex == 0)
   11431            0 :       pindex = p->getparam("hindex");
   11432              : 
   11433            0 :    labels = 1;
   11434            0 :    if (*p->getparam("labels") && atoi(p->getparam("labels")) == 0)
   11435            0 :       labels = 0;
   11436              : 
   11437            0 :    std::string bgcolor = "FFFFFF";
   11438            0 :    if (*p->getparam("bgcolor"))
   11439            0 :       bgcolor = p->xgetparam("bgcolor");
   11440              : 
   11441            0 :    std::string fgcolor = "000000";
   11442            0 :    if (*p->getparam("fgcolor"))
   11443            0 :       fgcolor = p->xgetparam("fgcolor");
   11444              : 
   11445            0 :    std::string gridcolor = "A0A0A0";
   11446            0 :    if (*p->getparam("gcolor"))
   11447            0 :       gridcolor = p->xgetparam("gcolor");
   11448              : 
   11449              :    /* evaluate scale and offset */
   11450              : 
   11451            0 :    time_t endtime = 0;
   11452            0 :    if (p->isparam("time"))
   11453            0 :       endtime = string_to_time(p->getparam("time"));
   11454            0 :    else if (p->isparam("htime"))
   11455            0 :       endtime = string_to_time(p->getparam("htime"));
   11456              : 
   11457            0 :    if (pscale && *pscale)
   11458            0 :       scale = time_to_sec(pscale);
   11459              :    else
   11460            0 :       scale = 0;
   11461              : 
   11462            0 :    index = -1;
   11463            0 :    if (pindex && *pindex)
   11464            0 :       index = atoi(pindex);
   11465              : 
   11466              : #ifdef BROKEN
   11467              :    if (equal_ustring(hcmd, "Create ELog")) {
   11468              :       std::string xurl;
   11469              :       status = db_get_value_string(hDB, 0, "/Elog/URL", 0, &xurl, FALSE);
   11470              :       if (status == DB_SUCCESS) {
   11471              :          char url[256];
   11472              :          get_elog_url(url, sizeof(url));
   11473              : 
   11474              :          /*---- use external ELOG ----*/
   11475              :          fsize = 100000;
   11476              :          char* fbuffer = (char*)M_MALLOC(fsize);
   11477              :          assert(fbuffer != NULL);
   11478              : 
   11479              :          int width = 640;
   11480              :          int height = 400;
   11481              : 
   11482              :          if (equal_ustring(pmag, "Large")) {
   11483              :             width = 1024;
   11484              :             height = 768;
   11485              :          } else if (equal_ustring(pmag, "Small")) {
   11486              :             width = 320;
   11487              :             height = 200;
   11488              :          } else if (atoi(pmag) > 0) {
   11489              :             width = atoi(pmag);
   11490              :             height = 200;
   11491              :          }
   11492              : 
   11493              :          printf("hereA\n");
   11494              :          generate_hist_graph(odb, r, hgroup, hpanel, fbuffer, &fsize, width, height, endtime, scale, index, labels, bgcolor.c_str(), fgcolor.c_str(), gridcolor.c_str());
   11495              : 
   11496              :          /* save temporary file */
   11497              :          std::string dir;
   11498              :          db_get_value_string(hDB, 0, "/Elog/Logbook Dir", 0, &dir, TRUE);
   11499              :          if (dir.length() > 0 && dir[dir.length()-1] != DIR_SEPARATOR)
   11500              :             dir += DIR_SEPARATOR_STR;
   11501              : 
   11502              :          time_t now = time(NULL);
   11503              :          localtime_r(&now, &tms);
   11504              : 
   11505              :          std::string file_name = msprintf("%02d%02d%02d_%02d%02d%02d_%s.gif",
   11506              :                   tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday,
   11507              :                   tms.tm_hour, tms.tm_min, tms.tm_sec, hpanel);
   11508              :          std::string fname = dir + file_name;
   11509              : 
   11510              :          /* save attachment */
   11511              :          fh = open(fname.c_str(), O_CREAT | O_RDWR | O_BINARY, 0644);
   11512              :          if (fh < 0) {
   11513              :             cm_msg(MERROR, "show_hist_page", "Cannot write attachment file \"%s\", open() errno %d (%s)", fname.c_str(), errno, strerror(errno));
   11514              :          } else {
   11515              :             int wr = write(fh, fbuffer, fsize);
   11516              :             if (wr != fsize) {
   11517              :                cm_msg(MERROR, "show_hist_page", "Cannot write attachment file \"%s\", write(%d) returned %d, errno %d (%s)", fname.c_str(), fsize, wr, errno, strerror(errno));
   11518              :             }
   11519              :             close(fh);
   11520              :          }
   11521              : 
   11522              :          /* redirect to ELOG */
   11523              :          if (strlen(url) > 1 && url[strlen(url)-1] != '/')
   11524              :             mstrlcat(url, "/", sizeof(url));
   11525              :          mstrlcat(url, "?cmd=New&fa=", sizeof(url));
   11526              :          mstrlcat(url, file_name, sizeof(url));
   11527              :          redirect(r, url);
   11528              : 
   11529              :          M_FREE(fbuffer);
   11530              :          return;
   11531              : 
   11532              :       } else {
   11533              :          /*---- use internal ELOG ----*/
   11534              :          std::string str = msprintf("\\HS\\%s.gif", hpanel);
   11535              :          if (p->getparam("hscale") && *p->getparam("hscale"))
   11536              :             str += msprintf("?scale=%s", p->getparam("hscale"));
   11537              :          if (p->getparam("htime") && *p->getparam("htime")) {
   11538              :             if (strchr(str.c_str(), '?'))
   11539              :                str += "&";
   11540              :             else
   11541              :                str += "?";
   11542              :             str += msprintf("time=%s", p->getparam("htime"));
   11543              :          }
   11544              :          //if (p->getparam("hoffset") && *p->getparam("hoffset")) {
   11545              :          //   if (strchr(str, '?'))
   11546              :          //      mstrlcat(str, "&", sizeof(str));
   11547              :          //   else
   11548              :          //      mstrlcat(str, "?", sizeof(str));
   11549              :          //   sprintf(str + strlen(str), "offset=%s", p->getparam("hoffset"));
   11550              :          //}
   11551              :          if (p->getparam("hwidth") && *p->getparam("hwidth")) {
   11552              :             if (strchr(str.c_str(), '?'))
   11553              :                str += "&";
   11554              :             else
   11555              :                str += "?";
   11556              :             str += msprintf("width=%s", p->getparam("hwidth"));
   11557              :          }
   11558              :          if (p->getparam("hindex") && *p->getparam("hindex")) {
   11559              :             if (strchr(str.c_str(), '?'))
   11560              :                str += "&";
   11561              :             else
   11562              :                str += "?";
   11563              :             str += msprintf("index=%s", p->getparam("hindex"));
   11564              :          }
   11565              : 
   11566              :          show_elog_new(r, hpanel, NULL, FALSE, str.c_str(), "../../EL/");
   11567              :          return;
   11568              :       }
   11569              :    }
   11570              : #endif
   11571              : 
   11572            0 :    if (equal_ustring(hcmd, "Export")) {
   11573            0 :       export_hist(odb, r, hgroup, hpanel, endtime, scale, index, labels);
   11574            0 :       return;
   11575              :    }
   11576              : 
   11577            0 :    if (strstr(dec_path, ".gif")) {
   11578            0 :       int width =  640;
   11579            0 :       int height = 400;
   11580            0 :       if (equal_ustring(pwidth, "Large")) {
   11581            0 :          width = 1024;
   11582            0 :          height = 768;
   11583            0 :       } else if (equal_ustring(pwidth, "Small")) {
   11584            0 :          width = 320;
   11585            0 :          height = 200;
   11586            0 :       } else if (atoi(pwidth) > 0) {
   11587            0 :          width = atoi(pwidth);
   11588            0 :          if (atoi(pheight) > 0)
   11589            0 :             height = atoi(pheight);
   11590              :          else
   11591            0 :             height = (int)(0.625 * width);
   11592              :       }
   11593              : 
   11594              :       //printf("dec_path [%s], buf %p, %p, width %d, height %d, endtime %ld, scale %d, index %d, labels %d\n", dec_path, buffer, buffer_size, width, height, endtime, scale, index, labels);
   11595              : 
   11596            0 :       generate_hist_graph(odb, r, hgroup, hpanel, buffer, buffer_size, width, height, endtime, scale, index, labels, bgcolor.c_str(), fgcolor.c_str(), gridcolor.c_str());
   11597              : 
   11598            0 :       return;
   11599              :    }
   11600              : 
   11601            0 :    if (history_mode && index < 0)
   11602            0 :       return;
   11603              : 
   11604            0 :    time_t now = time(NULL);
   11605              : 
   11606              :    /* evaluate offset shift */
   11607            0 :    if (equal_ustring(p->getparam("shift"), "leftmaxall")) {
   11608            0 :       if (endtime == 0)
   11609            0 :          endtime = now;
   11610            0 :       time_t last_written = 0;
   11611            0 :       status = get_hist_last_written(odb, hgroup, hpanel, endtime, index, 1, &last_written);
   11612            0 :       if (status == HS_SUCCESS)
   11613            0 :          endtime = last_written + scale/2;
   11614              :    }
   11615              : 
   11616            0 :    if (equal_ustring(p->getparam("shift"), "leftmax")) {
   11617            0 :       if (endtime == 0)
   11618            0 :          endtime = now;
   11619            0 :       time_t last_written = 0;
   11620            0 :       status = get_hist_last_written(odb, hgroup, hpanel, endtime, index, 0, &last_written);
   11621            0 :       if (status == HS_SUCCESS)
   11622            0 :          if (last_written != endtime)
   11623            0 :             endtime = last_written + scale/2;
   11624              :    }
   11625              : 
   11626            0 :    if (equal_ustring(p->getparam("shift"), "left")) {
   11627            0 :       if (endtime == 0)
   11628            0 :          endtime = now;
   11629            0 :       endtime -= scale/2;
   11630              :       //offset -= scale / 2;
   11631              :    }
   11632              : 
   11633            0 :    if (equal_ustring(p->getparam("shift"), "right")) {
   11634            0 :       if (endtime == 0)
   11635            0 :          endtime = now;
   11636            0 :       endtime += scale/2;
   11637            0 :       if (endtime > now)
   11638            0 :          endtime = now;
   11639              :    }
   11640              : 
   11641            0 :    if (equal_ustring(p->getparam("shift"), "rightmax")) {
   11642            0 :       endtime = 0;
   11643              :    }
   11644              : 
   11645            0 :    if (equal_ustring(p->getparam("shift"), "zoomin")) {
   11646            0 :       if (endtime == 0)
   11647            0 :          endtime = now;
   11648            0 :       endtime -= scale / 4;
   11649            0 :       scale /= 2;
   11650              :    }
   11651              : 
   11652            0 :    if (equal_ustring(p->getparam("shift"), "zoomout")) {
   11653            0 :       if (endtime == 0)
   11654            0 :          endtime = now;
   11655            0 :       endtime += scale / 2;
   11656            0 :       if (endtime > now)
   11657            0 :          endtime = now;
   11658            0 :       scale *= 2;
   11659              :    }
   11660              : 
   11661            0 :    int xrefresh = refresh;
   11662            0 :    if (endtime != 0)
   11663            0 :       xrefresh = 0;
   11664            0 :    show_header(r, hpanel, "GET", "", xrefresh);
   11665              : 
   11666            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
   11667            0 :    r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
   11668            0 :    show_navigation_bar(r, "History");
   11669              : 
   11670            0 :    r->rsprintf("<table class=\"mtable\">");
   11671            0 :    r->rsprintf("<tr><th class=\"mtableheader\" colspan=2>History</th></tr>");
   11672              : 
   11673              :    {
   11674              :       /* check if panel exists */
   11675            0 :       std::string path;
   11676              :       //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
   11677            0 :       path += "/History/Display/";
   11678            0 :       path += hgroup;
   11679            0 :       path += "/";
   11680            0 :       path += hpanel;
   11681            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
   11682            0 :       if (status != DB_SUCCESS && !equal_ustring(hpanel, "All") && !equal_ustring(hpanel,"")) {
   11683            0 :          r->rsprintf("<h1>Error: History panel \"%s\" in group \"%s\" does not exist</h1>\n", hpanel, hgroup);
   11684            0 :          r->rsprintf("</table>\r\n");
   11685            0 :          r->rsprintf("</div>\n"); // closing for <div id="mmain">
   11686            0 :          r->rsprintf("</form>\n");
   11687            0 :          r->rsprintf("</body></html>\r\n");
   11688            0 :          return;
   11689              :       }
   11690            0 :    }
   11691              : 
   11692              :    /* define hidden field for parameters */
   11693            0 :    if (pscale && *pscale)
   11694            0 :       r->rsprintf("<input type=hidden name=hscale id=hscale value=%d>\n", scale);
   11695              :    else {
   11696              :       /* if no scale and offset given, get it from default */
   11697            0 :       if (hpanel[0] && !equal_ustring(hpanel, "All") && hgroup[0]) {
   11698            0 :          std::string path;
   11699            0 :          path += "/History/Display/";
   11700            0 :          path += hgroup;
   11701            0 :          path += "/";
   11702            0 :          path += hpanel;
   11703            0 :          path += "/Timescale";
   11704              : 
   11705            0 :          std::string scalestr = "1h";
   11706            0 :          status = db_get_value_string(hDB, 0, path.c_str(), 0, &scalestr, TRUE);
   11707            0 :          if (status != DB_SUCCESS) {
   11708              :             /* delete old integer key */
   11709            0 :             db_find_key(hDB, 0, path.c_str(), &hkey);
   11710            0 :             if (hkey)
   11711            0 :                db_delete_key(hDB, hkey, FALSE);
   11712              : 
   11713            0 :             scalestr = "1h";
   11714            0 :             db_get_value_string(hDB, 0, path.c_str(), 0, &scalestr, TRUE);
   11715              :          }
   11716              : 
   11717            0 :          r->rsprintf("<input type=hidden name=hscale id=hscale value=%s>\n", scalestr.c_str());
   11718            0 :          scale = time_to_sec(scalestr.c_str());
   11719            0 :       }
   11720              :    }
   11721              : 
   11722            0 :    if (endtime != 0)
   11723            0 :       r->rsprintf("<input type=hidden name=htime id=htime value=%s>\n", time_to_string(endtime).c_str());
   11724            0 :    if (pwidth && *pwidth)
   11725            0 :       r->rsprintf("<input type=hidden name=hwidth id=hwidth value=%s>\n", pwidth);
   11726            0 :    if (pheight && *pheight)
   11727            0 :       r->rsprintf("<input type=hidden name=hheight id=hheight value=%s>\n", pheight);
   11728            0 :    if (pindex && *pindex)
   11729            0 :       r->rsprintf("<input type=hidden name=hindex id=hindex value=%s>\n", pindex);
   11730              : 
   11731            0 :    r->rsprintf("</td></tr>\n");
   11732              : 
   11733            0 :    if (hgroup[0] == 0) {
   11734              :       /* "New" button */
   11735            0 :       r->rsprintf("<tr><td colspan=2><input type=\"button\" name=\"New\" value=\"New\" ");
   11736            0 :       r->rsprintf("onClick=\"window.location.href='?cmd=oldhistory&hcmd=New'\"></td></tr>\n");
   11737              : 
   11738              :       /* links for history panels */
   11739            0 :       r->rsprintf("<tr><td colspan=2 style=\"text-align:left;\">\n");
   11740            0 :       if (!hpanel[0])
   11741            0 :          r->rsprintf("<b>Please select panel:</b><br>\n");
   11742              : 
   11743              :       /* table for panel selection */
   11744            0 :       r->rsprintf("<table class=\"historyTable\">");
   11745              : 
   11746              :       /* "All" link */
   11747            0 :       r->rsprintf("<tr><td colspan=2 class=\"titleCell\">\n");
   11748            0 :       if (equal_ustring(hgroup, "All"))
   11749            0 :          r->rsprintf("All &nbsp;&nbsp;");
   11750              :       else
   11751            0 :          r->rsprintf("<a href=\"?cmd=oldhistory&group=All\">ALL</a>\n");
   11752            0 :       r->rsprintf("</td></tr>\n");
   11753              : 
   11754              :       /* Setup History table links */
   11755            0 :       db_find_key(hDB, 0, "/History/Display", &hkey);
   11756            0 :       if (!hkey) {
   11757              :          /* create default panel */
   11758              :          char str[256];
   11759            0 :          strcpy(str, "System:Trigger per sec.");
   11760            0 :          strcpy(str + 2 * NAME_LENGTH, "System:Trigger kB per sec.");
   11761            0 :          db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Variables", str, 64, 2, TID_STRING);
   11762            0 :          strcpy(str, "1h");
   11763            0 :          db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Time Scale", str, NAME_LENGTH, 1, TID_STRING);
   11764              : 
   11765            0 :          strcpy(str, "1h");
   11766            0 :          db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Timescale", str, NAME_LENGTH, 1, TID_STRING);
   11767            0 :          i = 1;
   11768            0 :          db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Zero ylow", &i, sizeof(BOOL), 1, TID_BOOL);
   11769            0 :          i = 1;
   11770            0 :          db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Show run markers", &i, sizeof(BOOL), 1, TID_BOOL);
   11771              : 
   11772            0 :          strcpy(str, "");
   11773            0 :          db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Formula", str, 64, 1, TID_STRING);
   11774            0 :          db_set_value_index(hDB, 0, "/History/Display/Default/Trigger rate/Formula", str, 64, 1, TID_STRING, FALSE);
   11775              :       }
   11776              : 
   11777            0 :       db_find_key(hDB, 0, "/History/Display", &hkey);
   11778            0 :       if (hkey) {
   11779            0 :          for (i = 0;; i++) {
   11780            0 :             db_enum_link(hDB, hkey, i, &hkeyp);
   11781              : 
   11782            0 :             if (!hkeyp)
   11783            0 :                break;
   11784              : 
   11785              :             // Group key
   11786            0 :             db_get_key(hDB, hkeyp, &key);
   11787              : 
   11788              :             char enc_name[256];
   11789            0 :             mstrlcpy(enc_name, key.name, sizeof(enc_name));
   11790            0 :             urlEncode(enc_name, sizeof(enc_name));
   11791              : 
   11792            0 :             if (equal_ustring(hpanel, key.name))
   11793            0 :                r->rsprintf("<tr><td class=\"titleCell\">%s</td>\n<td>", key.name);
   11794              :             else
   11795            0 :                r->rsprintf("<tr><td class=\"titleCell\"><a href=\"?cmd=oldhistory&group=%s\">%s</a></td>\n<td>", enc_name, key.name);
   11796              : 
   11797            0 :             for (j = 0;; j++) {
   11798              :                // scan items
   11799            0 :                db_enum_link(hDB, hkeyp, j, &hikeyp);
   11800              : 
   11801            0 :                if (!hikeyp) {
   11802            0 :                   r->rsprintf("</tr>");
   11803            0 :                   break;
   11804              :                }
   11805              :                // Item key
   11806            0 :                db_get_key(hDB, hikeyp, &ikey);
   11807              : 
   11808              :                char enc_iname[256];
   11809            0 :                mstrlcpy(enc_iname, ikey.name, sizeof(enc_iname));
   11810            0 :                urlEncode(enc_iname, sizeof(enc_iname));
   11811              : 
   11812            0 :                if (equal_ustring(hpanel, ikey.name))
   11813            0 :                   r->rsprintf("<small><b>%s</b></small> &nbsp;", ikey.name);
   11814              :                else
   11815            0 :                   r->rsprintf("<small><a href=\"?cmd=oldhistory&group=%s&panel=%s\">%s</a></small> &nbsp;\n", enc_name, enc_iname, ikey.name);
   11816            0 :             }
   11817            0 :          }
   11818              :       }
   11819              : 
   11820            0 :       r->rsprintf("</table></tr>\n");
   11821              : 
   11822              :    } else {
   11823            0 :       int found = 0;
   11824              : 
   11825              :       /* show drop-down selectors */
   11826            0 :       r->rsprintf("<tr><td colspan=2>\n");
   11827              : 
   11828            0 :       r->rsprintf("Group:\n");
   11829              : 
   11830            0 :       r->rsprintf("<select title=\"Select group\" id=\"fgroup\" onChange=\"window.location.search='?cmd=oldhistory&group='+document.getElementById('fgroup').value;\">\n");
   11831              : 
   11832            0 :       db_find_key(hDB, 0, "/History/Display", &hkey);
   11833            0 :       if (hkey) {
   11834            0 :          hkeyp = 0;
   11835            0 :          for (i = 0;; i++) {
   11836            0 :             db_enum_link(hDB, hkey, i, &hikeyp);
   11837              : 
   11838            0 :             if (!hikeyp)
   11839            0 :                break;
   11840              : 
   11841            0 :             if (i == 0)
   11842            0 :                hkeyp = hikeyp;
   11843              : 
   11844              :             // Group key
   11845            0 :             db_get_key(hDB, hikeyp, &key);
   11846              : 
   11847            0 :             if (equal_ustring(key.name, hgroup)) {
   11848            0 :                r->rsprintf("<option selected value=\"%s\">%s\n", key.name, key.name);
   11849            0 :                hkeyp = hikeyp;
   11850              :             } else
   11851            0 :                r->rsprintf("<option value=\"%s\">%s\n", key.name, key.name);
   11852              :          }
   11853              : 
   11854            0 :          if (equal_ustring("ALL", hgroup)) {
   11855            0 :             r->rsprintf("<option selected value=\"%s\">%s\n", "ALL", "ALL");
   11856              :          } else {
   11857            0 :             r->rsprintf("<option value=\"%s\">%s\n", "ALL", "ALL");
   11858              :          }
   11859              : 
   11860            0 :          r->rsprintf("</select>\n");
   11861            0 :          r->rsprintf("&nbsp;&nbsp;Panel:\n");
   11862            0 :          r->rsprintf("<select title=\"Select panel\" id=\"fpanel\" ");
   11863            0 :          r->rsprintf("onChange=\"window.location.search='?cmd=oldhistory&group='+document.getElementById('fgroup').value+");
   11864            0 :          r->rsprintf("'&panel='+document.getElementById('fpanel').value;\">\n");
   11865              : 
   11866            0 :          found = 0;
   11867            0 :          if (hkeyp) {
   11868            0 :             for (i = 0;; i++) {
   11869              :                // scan panels
   11870            0 :                db_enum_link(hDB, hkeyp, i, &hikeyp);
   11871              : 
   11872            0 :                if (!hikeyp)
   11873            0 :                   break;
   11874              : 
   11875              :                // Item key
   11876            0 :                db_get_key(hDB, hikeyp, &key);
   11877              : 
   11878            0 :                if (equal_ustring(hpanel, key.name)) {
   11879            0 :                   r->rsprintf("<option selected value=\"%s\">%s\n", key.name, key.name);
   11880            0 :                   found = 1;
   11881              :                } else
   11882            0 :                   r->rsprintf("<option value=\"%s\">%s\n", key.name, key.name);
   11883              :             }
   11884              :          }
   11885              : 
   11886            0 :          if (found)
   11887            0 :             r->rsprintf("<option value=\"\">- all -\n");
   11888              :          else
   11889            0 :             r->rsprintf("<option selected value=\"\">- all -\n");
   11890              : 
   11891            0 :          r->rsprintf("</select>\n");
   11892              :       }
   11893              : 
   11894            0 :       r->rsprintf("<noscript>\n");
   11895            0 :       r->rsprintf("<input type=submit value=\"Go\">\n");
   11896            0 :       r->rsprintf("</noscript>\n");
   11897              : 
   11898            0 :       r->rsprintf("&nbsp;&nbsp;<input type=\"button\" name=\"New\" value=\"New\" ");
   11899            0 :       r->rsprintf("onClick=\"window.location.href='?cmd=oldhistory&hcmd=New&group=%s'\">\n", hgroup);
   11900              : 
   11901            0 :       r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"Reset\" onClick=\"window.location.href='?cmd=oldhistory&hcmd=Reset&group=%s&panel=%s'\">\n", hgroup, hpanel);
   11902              : 
   11903            0 :       r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"Query\" onClick=\"window.location.href='?cmd=oldhistory&hcmd=Query&group=%s&panel=%s'\">\n", hgroup, hpanel);
   11904              : 
   11905            0 :       double xendtime = endtime;
   11906            0 :       if (xendtime == 0)
   11907            0 :          xendtime = now;
   11908            0 :       double xstarttime = xendtime - scale;
   11909              :       
   11910            0 :       r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"New history\" onClick=\"window.location.href='?cmd=history&group=%s&panel=%s&A=%.0f&B=%.0f'\">\n", hgroup, hpanel, xstarttime, xendtime);
   11911              : 
   11912            0 :       r->rsprintf("</td></tr>\n");
   11913              :    }
   11914              : 
   11915              :    //printf("hgroup [%s] hpanel [%s]\n", hgroup, hpanel);
   11916              : 
   11917              :    /* check if whole group should be displayed */
   11918            0 :    if (hgroup[0] && !equal_ustring(hgroup, "ALL") && hpanel[0] == 0) {
   11919            0 :       std::string strwidth = "Small";
   11920            0 :       db_get_value_string(hDB, 0, "/History/Display Settings/Width Group", 0, &strwidth, TRUE);
   11921              : 
   11922            0 :       std::string path;
   11923            0 :       path += "/History/Display/";
   11924            0 :       path += hgroup;
   11925            0 :       db_find_key(hDB, 0, path.c_str(), &hkey);
   11926            0 :       if (hkey) {
   11927            0 :          for (i = 0 ;; i++) {     // scan group
   11928            0 :             db_enum_link(hDB, hkey, i, &hikeyp);
   11929              : 
   11930            0 :             if (!hikeyp)
   11931            0 :                break;
   11932              : 
   11933            0 :             db_get_key(hDB, hikeyp, &key);
   11934              : 
   11935              :             char enc_name[256];
   11936            0 :             mstrlcpy(enc_name, key.name, sizeof(enc_name));
   11937            0 :             urlEncode(enc_name, sizeof(enc_name));
   11938              : 
   11939            0 :             std::string ref;
   11940            0 :             ref += "graph.gif?width=";
   11941            0 :             ref += strwidth;
   11942            0 :             ref += "&cmd=oldhistory&group=";
   11943            0 :             ref += hgroup;
   11944            0 :             ref += "&panel=";
   11945            0 :             ref += enc_name;
   11946              : 
   11947            0 :             std::string ref2;
   11948            0 :             ref2 += "?cmd=oldhistory&group=";
   11949            0 :             ref2 += hgroup;
   11950            0 :             ref2 += "&panel=";
   11951            0 :             ref2 += enc_name;
   11952              : 
   11953            0 :             if (endtime != 0) {
   11954              :                char tmp[256];
   11955            0 :                sprintf(tmp, "time=%s&scale=%d", time_to_string(endtime).c_str(), scale);
   11956            0 :                ref += "&";
   11957            0 :                ref += tmp;
   11958            0 :                ref2 += "?";
   11959            0 :                ref2 += tmp;
   11960              :             }
   11961              : 
   11962            0 :             if (i % 2 == 0)
   11963            0 :                r->rsprintf("<tr><td><a href=\"%s\"><img src=\"%s\"></a>\n", ref2.c_str(), ref.c_str());
   11964              :             else
   11965            0 :                r->rsprintf("<td><a href=\"%s\"><img src=\"%s\"></a></tr>\n", ref2.c_str(), ref.c_str());
   11966            0 :          }
   11967              : 
   11968              :       } else {
   11969            0 :          r->rsprintf("Group \"%s\" not found", hgroup);
   11970              :       }
   11971            0 :    }
   11972              : 
   11973              :    /* image panel */
   11974            0 :    else if (hpanel[0] && !equal_ustring(hpanel, "All")) {
   11975              :       /* navigation links */
   11976            0 :       r->rsprintf("<tr><td>\n");
   11977              : 
   11978            0 :       std::string path;
   11979            0 :       path += "/History/Display/";
   11980            0 :       path += hgroup;
   11981            0 :       path += "/";
   11982            0 :       path += hpanel;
   11983            0 :       path += "/Buttons";
   11984            0 :       db_find_key(hDB, 0, path.c_str(), &hkeybutton);
   11985            0 :       if (hkeybutton == 0) {
   11986              :          /* create default buttons */
   11987            0 :          db_create_key(hDB, 0, path.c_str(), TID_STRING);
   11988            0 :          status = db_find_key(hDB, 0, path.c_str(), &hkeybutton);
   11989            0 :          if (status != DB_SUCCESS || !hkey) {
   11990            0 :             cm_msg(MERROR, "show_hist_page", "Cannot create history panel with invalid ODB path \"%s\"", path.c_str());
   11991            0 :             return;
   11992              :          }
   11993            0 :          db_set_data(hDB, hkeybutton, def_button, sizeof(def_button), 7, TID_STRING);
   11994              :       }
   11995              : 
   11996            0 :       r->rsprintf("<script>\n");
   11997            0 :       r->rsprintf("function histDisp(p) {\n");
   11998            0 :       r->rsprintf("  var params = '?cmd=oldhistory&group=%s&panel=%s';\n", hgroup, hpanel);
   11999            0 :       r->rsprintf("  params += '&'+p;\n");
   12000            0 :       r->rsprintf("  if (document.getElementById(\'hscale\') !== null)\n");
   12001            0 :       r->rsprintf("    params += '&hscale='+document.getElementById(\'hscale\').value;\n");
   12002            0 :       r->rsprintf("  if (document.getElementById(\'htime\') !== null)\n");
   12003            0 :       r->rsprintf("    params += '&htime='+document.getElementById(\'htime\').value;\n");
   12004            0 :       r->rsprintf("  if (document.getElementById(\'hwdith\') !== null)\n");
   12005            0 :       r->rsprintf("    params += '&hwidth='+document.getElementById(\'hwidth\').value;\n");
   12006            0 :       r->rsprintf("  if (document.getElementById(\'hindex\') !== null)\n");
   12007            0 :       r->rsprintf("    params += '&hindex='+document.getElementById(\'hindex\').value;\n");
   12008            0 :       r->rsprintf("  window.location.search = params;\n");
   12009            0 :       r->rsprintf("}\n\n");
   12010            0 :       r->rsprintf("</script>\n");
   12011              : 
   12012            0 :       db_get_key(hDB, hkeybutton, &key);
   12013              : 
   12014            0 :       for (i = 0; i < key.num_values; i++) {
   12015              :          char str[256];
   12016            0 :          size = sizeof(str);
   12017            0 :          db_get_data_index(hDB, hkeybutton, str, &size, i, TID_STRING);
   12018            0 :          r->rsprintf("<input type=\"button\" title=\"display last %s\" value=%s onclick=\"histDisp('scale=%s')\">\n", str, str, str);
   12019              :       }
   12020              : 
   12021            0 :       r->rsprintf("<input type=\"button\" value=\"<<<\" title=\"go back in time to last available data for all variables on the plot\" onclick=\"histDisp('shift=leftmaxall')\">");
   12022            0 :       r->rsprintf("<input type=\"button\" value=\"<<\" title=\"go back in time to last available data\" onclick=\"histDisp('shift=leftmax')\">");
   12023            0 :       r->rsprintf("<input type=\"button\" value=\"<\" title=\"go back in time\" onclick=\"histDisp('shift=left')\">");
   12024              : 
   12025            0 :       r->rsprintf("<input type=\"button\" value=\" + \" title=\"zoom in\" onclick=\"histDisp('shift=zoomin')\">");
   12026            0 :       r->rsprintf("<input type=\"button\" value=\" - \" title=\"zoom out\" onclick=\"histDisp('shift=zoomout')\">");
   12027              : 
   12028            0 :       if (endtime != 0) {
   12029            0 :          r->rsprintf("<input type=\"button\" value=\">\" title=\"go forward in time\" onclick=\"histDisp('shift=right')\">");
   12030            0 :          r->rsprintf("<input type=\"button\" value=\">>\" title=\"go to currently updated fresh data\" onclick=\"histDisp('shift=rightmax')\">");
   12031              :       }
   12032              : 
   12033            0 :       r->rsprintf("<td>\n");
   12034            0 :       r->rsprintf("<input type=\"button\" value=\"Large\" title=\"large display\" onclick=\"histDisp('width=Large')\">\n");
   12035            0 :       r->rsprintf("<input type=\"button\" value=\"Small\" title=\"large display\" onclick=\"histDisp('width=Small')\">\n");
   12036            0 :       r->rsprintf("<input type=\"button\" value=\"Create Elog\" title=\"large display\" onclick=\"histDisp('hcmd=Create Elog')\">\n");
   12037            0 :       r->rsprintf("<input type=\"button\" value=\"Config\" title=\"large display\" onclick=\"histDisp('hcmd=Config')\">\n");
   12038            0 :       r->rsprintf("<input type=\"button\" value=\"Export\" title=\"large display\" onclick=\"histDisp('hcmd=Export')\">\n");
   12039            0 :       r->rsprintf("</tr>\n");
   12040              : 
   12041              :       char paramstr[256];
   12042              : 
   12043            0 :       paramstr[0] = 0;
   12044            0 :       sprintf(paramstr + strlen(paramstr), "&scale=%d", scale);
   12045            0 :       if (endtime != 0)
   12046            0 :          sprintf(paramstr + strlen(paramstr), "&time=%s", time_to_string(endtime).c_str());
   12047            0 :       if (pwidth && *pwidth)
   12048            0 :          sprintf(paramstr + strlen(paramstr), "&width=%s", pwidth);
   12049              :       else {
   12050            0 :          std::string wi = "640";
   12051            0 :          db_get_value_string(hDB, 0, "/History/Display Settings/Width Individual", 0, &wi, TRUE);
   12052            0 :          sprintf(paramstr + strlen(paramstr), "&width=%s", wi.c_str());
   12053            0 :       }
   12054            0 :       if (pheight && *pheight)
   12055            0 :          sprintf(paramstr + strlen(paramstr), "&height=%s", pheight);
   12056              : 
   12057              :       /* define image map */
   12058            0 :       r->rsprintf("<map name=\"%s\">\r\n", hpanel);
   12059              : 
   12060            0 :       if (!(pindex && *pindex)) {
   12061            0 :          std::string path;
   12062            0 :          path += "/History/Display/";
   12063            0 :          path += hgroup;
   12064            0 :          path += "/";
   12065            0 :          path += hpanel;
   12066            0 :          path += "/Variables";
   12067            0 :          db_find_key(hDB, 0, path.c_str(), &hkey);
   12068            0 :          if (hkey) {
   12069            0 :             db_get_key(hDB, hkey, &key);
   12070              : 
   12071            0 :             for (i = 0; i < key.num_values; i++) {
   12072            0 :                std::string ref;
   12073              :                //if (paramstr[0]) {
   12074              :                //   sprintf(ref, "?cmd=oldhistory&group=%s&panel=%s&%s&index=%d", hgroup, hpanel, paramstr, i);
   12075              :                //} else {
   12076              :                //   sprintf(ref, "?cmd=oldhistory&group=%s&panel=%s&index=%d", hgroup, hpanel, i);
   12077              :                //}
   12078              : 
   12079            0 :                ref += "?cmd=oldhistory&group=";
   12080            0 :                ref += hgroup;
   12081            0 :                ref += "&panel=";
   12082            0 :                ref += hpanel;
   12083            0 :                if (paramstr[0]) {
   12084            0 :                   ref += "&";
   12085            0 :                   ref += paramstr;
   12086              :                }
   12087            0 :                ref += "&index=";
   12088            0 :                ref += toString(i);
   12089              : 
   12090            0 :                r->rsprintf("  <area shape=rect coords=\"%d,%d,%d,%d\" href=\"%s\">\r\n", 30, 31 + 23 * i, 150, 30 + 23 * i + 17, ref.c_str());
   12091            0 :             }
   12092              :          }
   12093            0 :       } else {
   12094            0 :          std::string ref = "?cmd=oldhistory&group=";
   12095            0 :          ref += hgroup;
   12096            0 :          ref += "&panel=";
   12097            0 :          ref += hpanel;
   12098              : 
   12099            0 :          if (paramstr[0]) {
   12100            0 :             ref += "&";
   12101            0 :             ref += paramstr;
   12102              :          }
   12103              : 
   12104            0 :          if (equal_ustring(pwidth, "Large"))
   12105            0 :             width = 1024;
   12106            0 :          else if (equal_ustring(pwidth, "Small"))
   12107            0 :             width = 320;
   12108            0 :          else if (atoi(pwidth) > 0)
   12109            0 :             width = atoi(pwidth);
   12110              :          else
   12111            0 :             width = 640;
   12112              : 
   12113            0 :          r->rsprintf("  <area shape=rect coords=\"%d,%d,%d,%d\" href=\"%s\">\r\n", 0, 0, width, 20, ref.c_str());
   12114            0 :       }
   12115              : 
   12116            0 :       r->rsprintf("</map>\r\n");
   12117              : 
   12118              :       /* Display individual panels */
   12119            0 :       if (pindex && *pindex)
   12120            0 :          sprintf(paramstr + strlen(paramstr), "&index=%s", pindex);
   12121              : 
   12122            0 :       std::string ref;
   12123              :       //sprintf(ref, "graph.gif?cmd=oldhistory&group=%s&panel=%s%s", hgroup, hpanel, paramstr);
   12124            0 :       ref += "graph.gif?cmd=oldhistory&group=";
   12125            0 :       ref += hgroup;
   12126            0 :       ref += "&panel=";
   12127            0 :       ref += hpanel;
   12128            0 :       ref += paramstr;
   12129              : 
   12130              :       /* put reference to graph */
   12131            0 :       r->rsprintf("<tr><td colspan=2><img src=\"%s\" usemap=\"#%s\"></tr>\n", ref.c_str(), hpanel);
   12132            0 :    }
   12133              : 
   12134            0 :    else if (equal_ustring(hgroup, "All")) {
   12135              :       /* Display all panels */
   12136            0 :       db_find_key(hDB, 0, "/History/Display", &hkey);
   12137            0 :       if (hkey)
   12138            0 :          for (i = 0, k = 0;; i++) {     // scan Groups
   12139            0 :             db_enum_link(hDB, hkey, i, &hkeyp);
   12140              : 
   12141            0 :             if (!hkeyp)
   12142            0 :                break;
   12143              : 
   12144            0 :             db_get_key(hDB, hkeyp, &key);
   12145              : 
   12146              :             char enc_group_name[256];
   12147            0 :             mstrlcpy(enc_group_name, key.name, sizeof(enc_group_name));
   12148            0 :             urlEncode(enc_group_name, sizeof(enc_group_name));
   12149              : 
   12150            0 :             for (j = 0;; j++, k++) {
   12151              :                // scan items
   12152            0 :                db_enum_link(hDB, hkeyp, j, &hikeyp);
   12153              : 
   12154            0 :                if (!hikeyp)
   12155            0 :                   break;
   12156              : 
   12157            0 :                db_get_key(hDB, hikeyp, &ikey);
   12158              : 
   12159              :                char enc_panel_name[256];
   12160            0 :                mstrlcpy(enc_panel_name, ikey.name, sizeof(enc_panel_name));
   12161            0 :                urlEncode(enc_panel_name, sizeof(enc_panel_name));
   12162              : 
   12163            0 :                std::string ref;
   12164            0 :                ref += "graph.gif?width=Small";
   12165            0 :                ref += "&cmd=oldhistory&group=";
   12166            0 :                ref += enc_group_name;
   12167            0 :                ref += "&panel=";
   12168            0 :                ref += enc_panel_name;
   12169              : 
   12170            0 :                std::string ref2;
   12171            0 :                ref2 += "?cmd=oldhistory&group=";
   12172            0 :                ref2 += enc_group_name;
   12173            0 :                ref2 += "&panel=";
   12174            0 :                ref2 += enc_panel_name;
   12175              : 
   12176            0 :                if (endtime != 0) {
   12177              :                   char tmp[256];
   12178            0 :                   sprintf(tmp, "time=%s&scale=%d", time_to_string(endtime).c_str(), scale);
   12179            0 :                   ref += "&";
   12180            0 :                   ref += tmp;
   12181            0 :                   ref2 += "&";
   12182            0 :                   ref2 += tmp;
   12183              :                }
   12184              : 
   12185            0 :                if (k % 2 == 0)
   12186            0 :                   r->rsprintf("<tr><td><a href=\"%s\"><img src=\"%s\"></a>\n", ref2.c_str(), ref.c_str());
   12187              :                else
   12188            0 :                   r->rsprintf("<td><a href=\"%s\"><img src=\"%s\"></a></tr>\n", ref2.c_str(), ref.c_str());
   12189            0 :             }                   // items loop
   12190            0 :          }                      // Groups loop
   12191              :    }                            // All
   12192            0 :    r->rsprintf("</table>\r\n");
   12193            0 :    r->rsprintf("</div>\n"); // closing for <div id="mmain">
   12194            0 :    r->rsprintf("</form>\n");
   12195            0 :    r->rsprintf("</body></html>\r\n");
   12196            0 : }
   12197              : 
   12198              : 
   12199              : /*------------------------------------------------------------------*/
   12200              : 
   12201            0 : void send_icon(Return* r, const char *icon)
   12202              : {
   12203              :    int length;
   12204              :    const unsigned char *picon;
   12205              :    char str[256], format[256];
   12206              :    time_t now;
   12207              : 
   12208            0 :    if (strstr(icon, "favicon.ico") != 0) {
   12209            0 :       length = sizeof(favicon_ico);
   12210            0 :       picon = favicon_ico;
   12211            0 :    } else if (strstr(icon, "favicon.png") != 0) {
   12212            0 :       length = sizeof(favicon_png);
   12213            0 :       picon = favicon_png;
   12214              :    } else
   12215            0 :       return;
   12216              : 
   12217            0 :    r->rsprintf("HTTP/1.1 200 Document follows\r\n");
   12218            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
   12219            0 :    r->rsprintf("Accept-Ranges: bytes\r\n");
   12220              : 
   12221              :    /* set expiration time to one day */
   12222            0 :    time(&now);
   12223            0 :    now += (int) (3600 * 24);
   12224              :    struct tm gmt_tms;
   12225            0 :    gmtime_r(&now, &gmt_tms);
   12226            0 :    strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT");
   12227            0 :    strftime(str, sizeof(str), format, &gmt_tms);
   12228            0 :    r->rsprintf("Expires: %s\r\n", str);
   12229              : 
   12230            0 :    if (equal_ustring(icon, "favicon.ico"))
   12231            0 :       r->rsprintf("Content-Type: image/x-icon\r\n");
   12232              :    else
   12233            0 :       r->rsprintf("Content-Type: image/png\r\n");
   12234              : 
   12235            0 :    r->rsprintf("Content-Length: %d\r\n\r\n", length);
   12236              : 
   12237            0 :    r->rmemcpy(picon, length);
   12238              : }
   12239              : 
   12240              : /*------------------------------------------------------------------*/
   12241              : 
   12242              : struct Cookies
   12243              : {
   12244              :    std::string cookie_pwd;
   12245              :    std::string cookie_wpwd;
   12246              :    std::string cookie_cpwd;
   12247              :    int refresh = 0;
   12248              :    //int expand_equipment = 0;
   12249              : };
   12250              : 
   12251              : /*------------------------------------------------------------------*/
   12252              : 
   12253            0 : void Lock(RequestTrace* t)
   12254              : {
   12255            0 :    gMutex.lock();
   12256            0 :    t->fTimeLocked = GetTimeSec();
   12257            0 : }
   12258              : 
   12259            0 : void Unlock(RequestTrace* t)
   12260              : {
   12261            0 :    t->fTimeUnlocked = GetTimeSec();
   12262            0 :    gMutex.unlock();
   12263            0 : }
   12264              : 
   12265              : /*------------------------------------------------------------------*/
   12266              : 
   12267            0 : void interprete(Param* p, Return* r, Attachment* a, const Cookies* c, const char *dec_path, RequestTrace* t)
   12268              : /********************************************************************\
   12269              : 
   12270              :  Routine: interprete
   12271              : 
   12272              :  Purpose: Main interpreter of web commands
   12273              : 
   12274              :  \********************************************************************/
   12275              : {
   12276              :    int status;
   12277              :    HNDLE hkey, hDB;
   12278              : 
   12279              :    //printf("dec_path [%s]\n", dec_path);
   12280              : 
   12281            0 :    if (strstr(dec_path, "favicon.ico") != 0 ||
   12282            0 :        strstr(dec_path, "favicon.png")) {
   12283            0 :       send_icon(r, dec_path);
   12284            0 :       return;
   12285              :    }
   12286              : 
   12287            0 :    const char* password = p->getparam("pwd");
   12288            0 :    const char* wpassword = p->getparam("wpwd");
   12289            0 :    const char* command = p->getparam("cmd");
   12290              : 
   12291              :    //printf("interprete: dec_path [%s], command [%s]\n", dec_path, command);
   12292              : 
   12293            0 :    cm_get_experiment_database(&hDB, NULL);
   12294            0 :    MVOdb* odb = gOdb;
   12295              : 
   12296            0 :    if (history_mode) {
   12297            0 :       if (equal_ustring(command, "history")) {
   12298            0 :          if (equal_ustring(command, "config")) {
   12299            0 :             return;
   12300              :          }
   12301              : 
   12302            0 :          Lock(t);
   12303            0 :          show_hist_page(odb, p, r, dec_path, NULL, NULL, c->refresh);
   12304            0 :          Unlock(t);
   12305            0 :          return;
   12306              :       }
   12307            0 :       return;
   12308              :    }
   12309              : 
   12310              :    /* check for password */
   12311            0 :    db_find_key(hDB, 0, "/Experiment/Security/Password", &hkey);
   12312            0 :    if (!password[0] && hkey) {
   12313              :       char str[256];
   12314            0 :       int size = sizeof(str);
   12315            0 :       db_get_data(hDB, hkey, str, &size, TID_STRING);
   12316              : 
   12317              :       /* check for excemption */
   12318            0 :       db_find_key(hDB, 0, "/Experiment/Security/Allowed programs/mhttpd", &hkey);
   12319            0 :       if (hkey == 0 && strcmp(c->cookie_pwd.c_str(), str) != 0) {
   12320            0 :          Lock(t);
   12321            0 :          show_password_page(r, dec_path, "");
   12322            0 :          Unlock(t);
   12323            0 :          return;
   12324              :       }
   12325              :    }
   12326              : 
   12327              :    /*---- redirect with cookie if password given --------------------*/
   12328              : 
   12329            0 :    if (password[0]) {
   12330            0 :       r->rsprintf("HTTP/1.1 302 Found\r\n");
   12331            0 :       r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
   12332              : 
   12333              :       time_t now;
   12334            0 :       time(&now);
   12335              : 
   12336            0 :       now += 3600 * 24;
   12337              : 
   12338              :       struct tm gmt_tms;
   12339            0 :       gmtime_r(&now, &gmt_tms);
   12340              : 
   12341              :       char str[256];
   12342            0 :       strftime(str, sizeof(str), "%A, %d-%b-%Y %H:00:00 GMT", &gmt_tms);
   12343              : 
   12344            0 :       r->rsprintf("Set-Cookie: midas_pwd=%s; path=/; expires=%s\r\n",
   12345              :                ss_crypt(password, "mi"), str);
   12346              : 
   12347            0 :       r->rsprintf("Location: ./\n\n<html>redir</html>\r\n");
   12348            0 :       return;
   12349              :    }
   12350              : 
   12351            0 :    if (wpassword[0]) {
   12352              :       /* check if password correct */
   12353            0 :       if (!check_web_password(r, hDB, dec_path, ss_crypt(wpassword, "mi"), p->getparam("redir")))
   12354            0 :          return;
   12355              : 
   12356            0 :       r->rsprintf("HTTP/1.1 302 Found\r\n");
   12357            0 :       r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
   12358              : 
   12359              :       time_t now;
   12360            0 :       time(&now);
   12361              : 
   12362            0 :       now += 3600 * 24;
   12363              : 
   12364              :       struct tm gmt_tms;
   12365            0 :       gmtime_r(&now, &gmt_tms);
   12366              : 
   12367              :       char str[256];
   12368            0 :       strftime(str, sizeof(str), "%A, %d-%b-%Y %H:%M:%S GMT", &gmt_tms);
   12369              : 
   12370            0 :       r->rsprintf("Set-Cookie: midas_wpwd=%s; path=/; expires=%s\r\n", ss_crypt(wpassword, "mi"), str);
   12371              : 
   12372            0 :       sprintf(str, "./%s", p->getparam("redir"));
   12373            0 :       r->rsprintf("Location: %s\n\n<html>redir</html>\r\n", str);
   12374            0 :       return;
   12375              :    }
   12376              : 
   12377              :    /*---- send sound file -------------------------------------------*/
   12378              : 
   12379            0 :    if (strlen(dec_path) > 3 &&
   12380            0 :        dec_path[strlen(dec_path)-3] == 'm' &&
   12381            0 :        dec_path[strlen(dec_path)-2] == 'p' &&
   12382            0 :        dec_path[strlen(dec_path)-1] == '3') {
   12383            0 :       if (strrchr(dec_path, '/'))
   12384            0 :          send_resource(r, strrchr(dec_path, '/')+1);
   12385              :       else
   12386            0 :          send_resource(r, dec_path);
   12387            0 :       return;
   12388              :    }
   12389              : 
   12390              :    /*---- send midas.js and midas.css -------------------------------*/
   12391              : 
   12392            0 :    if (strstr(dec_path, "midas.js")) {
   12393            0 :       send_resource(r, "midas.js");
   12394            0 :       return;
   12395              :    }
   12396              : 
   12397            0 :    if (strstr(dec_path, "midas.css")) {
   12398            0 :       send_resource(r, "midas.css");
   12399            0 :       return;
   12400              :    }
   12401              : 
   12402              :    /*---- send mhttpd.js --------------------------------------------*/
   12403              : 
   12404            0 :    if (strstr(dec_path, "mhttpd.js")) {
   12405            0 :       send_resource(r, "mhttpd.js");
   12406            0 :       return;
   12407              :    }
   12408              : 
   12409              :    /*---- send obsolete.js ------------------------------------------*/
   12410              : 
   12411            0 :    if (strstr(dec_path, "obsolete.js")) {
   12412            0 :       send_resource(r, "obsolete.js");
   12413            0 :       return;
   12414              :    }
   12415              : 
   12416              :    /*---- send the obsolete mhttpd.css ------------------------------*/
   12417              : 
   12418            0 :    if (strstr(dec_path, "mhttpd.css")) {
   12419            0 :       send_resource(r, "mhttpd.css");
   12420            0 :       return;
   12421              :    }
   12422              : 
   12423              :    /*---- send controls.js ------------------------------------------*/
   12424              : 
   12425            0 :    if (strstr(dec_path, "controls.js")) {
   12426            0 :       send_resource(r, "controls.js");
   12427            0 :       return;
   12428              :    }
   12429              : 
   12430              :    /*---- send example web page -------------------------------------*/
   12431              : 
   12432            0 :    if (equal_ustring(command, "example")) {
   12433            0 :       send_resource(r, "example.html");
   12434            0 :       return;
   12435              :    }
   12436              : 
   12437              :    /*---- send example custom page -------------------------------------*/
   12438              : 
   12439            0 :    if (equal_ustring(command, "custom_example")) {
   12440            0 :       send_resource(r, "custom_example.html");
   12441            0 :       return;
   12442              :    }
   12443              : 
   12444            0 :    if (equal_ustring(command, "plot_example")) {
   12445            0 :       send_resource(r, "plot_example.html");
   12446            0 :       return;
   12447              :    }
   12448              : 
   12449              :    /*---- script command --------------------------------------------*/
   12450              : 
   12451            0 :    if (p->getparam("script") && *p->getparam("script")) {
   12452              : 
   12453            0 :       std::string str = msprintf("%s?script=%s", dec_path, p->getparam("script"));
   12454            0 :       if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
   12455            0 :          return;
   12456              : 
   12457            0 :       std::string path;
   12458            0 :       path += "/Script/";
   12459            0 :       path += p->getparam("script");
   12460              : 
   12461            0 :       Lock(t);
   12462              : 
   12463            0 :       cm_exec_script(path.c_str());
   12464              : 
   12465            0 :       Unlock(t);
   12466              : 
   12467            0 :       if (p->isparam("redir"))
   12468            0 :          redirect2(r, p->getparam("redir"));
   12469              :       else
   12470            0 :          redirect2(r, "");
   12471              : 
   12472            0 :       return;
   12473            0 :    }
   12474              : 
   12475              :    /*---- customscript command --------------------------------------*/
   12476              : 
   12477            0 :    if (p->getparam("customscript") && *p->getparam("customscript")) {
   12478              : 
   12479            0 :       std::string str = msprintf("%s?customscript=%s", dec_path, p->getparam("customscript"));
   12480            0 :       if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
   12481            0 :          return;
   12482              : 
   12483            0 :       std::string path;
   12484            0 :       path += "/CustomScript/";
   12485            0 :       path += p->getparam("customscript");
   12486              : 
   12487            0 :       Lock(t);
   12488              : 
   12489            0 :       cm_exec_script(path.c_str());
   12490              : 
   12491            0 :       Unlock(t);
   12492              : 
   12493            0 :       if (p->isparam("redir"))
   12494            0 :          redirect2(r, p->getparam("redir"));
   12495              :       else
   12496            0 :          redirect2(r, str.c_str());
   12497              : 
   12498            0 :       return;
   12499            0 :    }
   12500              : 
   12501              :    /*---- send the new html pages -----------------------------------*/
   12502              : 
   12503            0 :    if (equal_ustring(command, "start")) {
   12504            0 :       send_resource(r, "start.html");
   12505            0 :       return;
   12506              :    }
   12507              : 
   12508            0 :    if ((equal_ustring(command, "") || equal_ustring(command, "status")) && strlen(dec_path) == 0) {
   12509            0 :       if (midas::odb::exists("/Custom/Status")) {
   12510            0 :          midas::odb custom("/Custom");
   12511              : 
   12512            0 :          std::string filename = custom["Status"];
   12513            0 :          filename = add_custom_path(filename);
   12514              : 
   12515              :          // if custom file exists, send it (like normal web server)
   12516            0 :          if (ss_file_exist(filename.c_str())) {
   12517            0 :             send_file(r, filename);
   12518            0 :             return;
   12519              :          }
   12520            0 :       } else
   12521            0 :          send_resource(r, "status.html");
   12522            0 :       return;
   12523              :    }
   12524              : 
   12525            0 :    if (equal_ustring(command, "eqtable")) {
   12526            0 :       send_resource(r, "eqtable.html");
   12527            0 :       return;
   12528              :    }
   12529              : 
   12530            0 :    if (equal_ustring(command, "newODB")) {
   12531            0 :       send_resource(r, "odb.html");
   12532            0 :       return;
   12533              :    }
   12534              : 
   12535            0 :    if (equal_ustring(command, "programs")) {
   12536            0 :       send_resource(r, "programs.html");
   12537            0 :       return;
   12538              :    }
   12539              : 
   12540            0 :    if (equal_ustring(command, "alarms")) {
   12541            0 :       send_resource(r, "alarms.html");
   12542            0 :       return;
   12543              :    }
   12544              : 
   12545            0 :    if (equal_ustring(command, "transition")) {
   12546            0 :       send_resource(r, "transition.html");
   12547            0 :       return;
   12548              :    }
   12549              : 
   12550            0 :    if (equal_ustring(command, "messages")) {
   12551            0 :       send_resource(r, "messages.html");
   12552            0 :       return;
   12553              :    }
   12554              : 
   12555            0 :    if (equal_ustring(command, "config") &&
   12556            0 :        !(dec_path[0] == 'H' && dec_path[1] == 'S' && dec_path[2] == '/')) {
   12557            0 :       send_resource(r, "config.html");
   12558            0 :       return;
   12559              :    }
   12560              : 
   12561            0 :    if (equal_ustring(command, "chat")) {
   12562            0 :       send_resource(r, "chat.html");
   12563            0 :       return;
   12564              :    }
   12565              : 
   12566            0 :    if (equal_ustring(command, "buffers")) {
   12567            0 :       send_resource(r, "buffers.html");
   12568            0 :       return;
   12569              :    }
   12570              : 
   12571            0 :    if (equal_ustring(command, "Show elog")) {
   12572            0 :       send_resource(r, "elog_show.html");
   12573            0 :       return;
   12574              :    }
   12575              : 
   12576            0 :    if (equal_ustring(command, "Query elog")) {
   12577            0 :       send_resource(r, "elog_query_form.html");
   12578            0 :       return;
   12579              :    }
   12580              : 
   12581            0 :    if (equal_ustring(command, "New elog")) {
   12582            0 :       send_resource(r, "elog_edit.html");
   12583            0 :       return;
   12584              :    }
   12585              : 
   12586            0 :    if (equal_ustring(command, "Edit elog")) {
   12587            0 :       send_resource(r, "elog_edit.html");
   12588            0 :       return;
   12589              :    }
   12590              : 
   12591            0 :    if (equal_ustring(command, "Reply Elog")) {
   12592            0 :       send_resource(r, "elog_edit.html");
   12593            0 :       return;
   12594              :    }
   12595              : 
   12596            0 :    if (equal_ustring(command, "Last elog")) {
   12597            0 :       send_resource(r, "elog_show.html");
   12598            0 :       return;
   12599              :    }
   12600              : 
   12601            0 :    if (equal_ustring(command, "Submit Query")) {
   12602            0 :       send_resource(r, "elog_query.html");
   12603            0 :       return;
   12604              :    }
   12605              : 
   12606            0 :    if (equal_ustring(dec_path, "spinning-wheel.gif")) {
   12607            0 :       send_resource(r, "spinning-wheel.gif");
   12608            0 :       return;
   12609              :    }
   12610              : 
   12611              :    /*---- java script commands --------------------------------------*/
   12612              : 
   12613            0 :    if (equal_ustring(command, "jset") ||
   12614            0 :        equal_ustring(command, "jget") ||
   12615            0 :        equal_ustring(command, "jcopy") ||
   12616            0 :        equal_ustring(command, "jpaste") ||
   12617            0 :        equal_ustring(command, "jkey") ||
   12618            0 :        equal_ustring(command, "jcreate") ||
   12619            0 :        equal_ustring(command, "jresize") ||
   12620            0 :        equal_ustring(command, "jlink") ||
   12621            0 :        equal_ustring(command, "jrename") ||
   12622            0 :        equal_ustring(command, "jreorder") ||
   12623            0 :        equal_ustring(command, "jdelete") ||
   12624            0 :        equal_ustring(command, "jmsg") ||
   12625            0 :        equal_ustring(command, "jalm") ||
   12626            0 :        equal_ustring(command, "jgenmsg") ||
   12627            0 :        equal_ustring(command, "jrpc_rev0") ||
   12628            0 :        equal_ustring(command, "jrpc_rev1") ||
   12629            0 :        equal_ustring(command, "jrpc")) {
   12630            0 :       Lock(t);
   12631            0 :       javascript_commands(p, r, c->cookie_cpwd.c_str());
   12632            0 :       Unlock(t);
   12633            0 :       return;
   12634              :    }
   12635              : 
   12636              :    /*---- history editord -------------------------------------------*/
   12637              : 
   12638            0 :    if (equal_ustring(command, "hs_edit")) {
   12639            0 :       send_resource(r, "hs_edit.html");
   12640            0 :       return;
   12641              :    }
   12642              : 
   12643              :    /*---- history command -------------------------------------------*/
   12644              : 
   12645            0 :    if (equal_ustring(command, "oldhistory")) {
   12646            0 :       Lock(t);
   12647            0 :       show_hist_page(odb, p, r, dec_path, NULL, NULL, c->refresh);
   12648            0 :       Unlock(t);
   12649            0 :       return;
   12650              :    }
   12651              : 
   12652            0 :    if (equal_ustring(command, "history")) {
   12653            0 :       send_resource(r, "history.html");
   12654            0 :       return;
   12655              :    }
   12656              : 
   12657              :    /*---- MSCB command ----------------------------------------------*/
   12658              : 
   12659            0 :    if (equal_ustring(command, "MSCB")) {
   12660            0 :       if (equal_ustring(command, "set")) {
   12661            0 :          std::string str;
   12662            0 :          str += dec_path;
   12663            0 :          str += "?";
   12664            0 :          str += add_param_to_url("cmd", command);
   12665            0 :          if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
   12666            0 :             return;
   12667            0 :       }
   12668              : 
   12669            0 :       Lock(t);
   12670              : 
   12671              : #ifdef HAVE_MSCB
   12672            0 :       show_mscb_page(p, r, c->refresh);
   12673              : #else
   12674              :       show_error(r, "MSCB support not compiled into this version of mhttpd");
   12675              : #endif
   12676              : 
   12677            0 :       Unlock(t);
   12678            0 :       return;
   12679              :    }
   12680              : 
   12681              :    /*---- help command ----------------------------------------------*/
   12682              : 
   12683            0 :    if (equal_ustring(command, "help")) {
   12684            0 :       Lock(t);
   12685            0 :       show_help_page(r, dec_path);
   12686            0 :       Unlock(t);
   12687            0 :       return;
   12688              :    }
   12689              : 
   12690              :    /*---- trigger equipment readout ---------------------------*/
   12691              : 
   12692            0 :    if (strncmp(command, "Trigger", 7) == 0) {
   12693            0 :       std::string cmd;
   12694            0 :       cmd += "?cmd=";
   12695            0 :       cmd += command;
   12696            0 :       if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), cmd.c_str())) {
   12697            0 :          return;
   12698              :       }
   12699              : 
   12700            0 :       Lock(t);
   12701              : 
   12702              :       /* extract equipment name */
   12703              :       char eq_name[NAME_LENGTH];
   12704              : 
   12705            0 :       mstrlcpy(eq_name, command + 8, sizeof(eq_name));
   12706            0 :       if (strchr(eq_name, ' '))
   12707            0 :          *strchr(eq_name, ' ') = 0;
   12708              : 
   12709              :       /* get frontend name */
   12710            0 :       std::string path;
   12711            0 :       path += "/Equipment/";
   12712            0 :       path += eq_name;
   12713            0 :       path += "/Common/Frontend name";
   12714              :       char fe_name[NAME_LENGTH];
   12715            0 :       int size = NAME_LENGTH;
   12716            0 :       db_get_value(hDB, 0, path.c_str(), fe_name, &size, TID_STRING, TRUE);
   12717              : 
   12718              :       /* and ID */
   12719            0 :       path = "";
   12720            0 :       path += "/Equipment/";
   12721            0 :       path += eq_name;
   12722            0 :       path += "/Common/Event ID";
   12723            0 :       WORD event_id = 0;
   12724            0 :       size = sizeof(event_id);
   12725            0 :       db_get_value(hDB, 0, path.c_str(), &event_id, &size, TID_WORD, TRUE);
   12726              : 
   12727            0 :       if (cm_exist(fe_name, FALSE) != CM_SUCCESS) {
   12728            0 :          std::string str;
   12729            0 :          str += "Frontend \"";
   12730            0 :          str += fe_name;
   12731            0 :          str += "\" not running!";
   12732            0 :          show_error(r, str.c_str());
   12733            0 :       } else {
   12734              :          HNDLE hconn;
   12735            0 :          status = cm_connect_client(fe_name, &hconn);
   12736            0 :          if (status != RPC_SUCCESS) {
   12737            0 :             std::string str;
   12738            0 :             str += "Cannot connect to frontend \"";
   12739            0 :             str += fe_name;
   12740            0 :             str +="\" !";
   12741            0 :             show_error(r, str.c_str());
   12742            0 :          } else {
   12743            0 :             status = rpc_client_call(hconn, RPC_MANUAL_TRIG, event_id);
   12744            0 :             if (status != CM_SUCCESS)
   12745            0 :                show_error(r, "Error triggering event");
   12746              :             else
   12747            0 :                redirect(r, "");
   12748              : 
   12749              :             //cm_disconnect_client(hconn, FALSE);
   12750              :          }
   12751              :       }
   12752              : 
   12753            0 :       Unlock(t);
   12754              : 
   12755            0 :       return;
   12756            0 :    }
   12757              : 
   12758              :    /*---- switch to next subrun -------------------------------------*/
   12759              : 
   12760            0 :    if (strncmp(command, "Next Subrun", 11) == 0) {
   12761            0 :       int i = TRUE;
   12762            0 :       db_set_value(hDB, 0, "/Logger/Next subrun", &i, sizeof(i), 1, TID_BOOL);
   12763            0 :       redirect(r, "");
   12764            0 :       return;
   12765              :    }
   12766              : 
   12767              :    /*---- cancel command --------------------------------------------*/
   12768              : 
   12769            0 :    if (equal_ustring(command, "cancel")) {
   12770            0 :       if (p->isparam("redir"))
   12771            0 :          redirect(r, p->getparam("redir"));
   12772              :       else
   12773            0 :          redirect(r, "");
   12774            0 :       return;
   12775              :    }
   12776              : 
   12777              :    /*---- set command -----------------------------------------------*/
   12778              : 
   12779            0 :    if (equal_ustring(command, "set")) {
   12780              :       char str[256];
   12781            0 :       mstrlcpy(str, "?cmd=set", sizeof(str));
   12782            0 :       if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str))
   12783            0 :          return;
   12784              : 
   12785            0 :       const char* group = p->getparam("group");
   12786            0 :       int index = atoi(p->getparam("index"));
   12787            0 :       const char* value = p->getparam("value");
   12788              : 
   12789            0 :       Lock(t);
   12790            0 :       show_set_page(p, r, group, index, value);
   12791            0 :       Unlock(t);
   12792            0 :       return;
   12793              :    }
   12794              : 
   12795              :    /*---- find command ----------------------------------------------*/
   12796              : 
   12797            0 :    if (equal_ustring(command, "find")) {
   12798            0 :       const char* value = p->getparam("value");
   12799            0 :       Lock(t);
   12800            0 :       show_find_page(r, value);
   12801            0 :       Unlock(t);
   12802            0 :       return;
   12803              :    }
   12804              : 
   12805              :    /*---- CAMAC CNAF command ----------------------------------------*/
   12806              : 
   12807            0 :    if (equal_ustring(command, "CNAF") || strncmp(dec_path, "CNAF", 4) == 0) {
   12808            0 :       if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), "?cmd=CNAF"))
   12809            0 :          return;
   12810              : 
   12811            0 :       Lock(t);
   12812            0 :       show_cnaf_page(p, r);
   12813            0 :       Unlock(t);
   12814            0 :       return;
   12815              :    }
   12816              : 
   12817              :    /*---- ELog command ----------------------------------------------*/
   12818              : 
   12819            0 :    if (equal_ustring(command, "elog")) {
   12820              :       /* redirect to external ELOG if URL present */
   12821            0 :       cm_get_experiment_database(&hDB, NULL);
   12822            0 :       BOOL external_elog = FALSE;
   12823            0 :       std::string external_elog_url;
   12824            0 :       int size = sizeof(external_elog);
   12825            0 :       status = db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, TRUE);
   12826            0 :       status = db_get_value_string(hDB, 0, "/Elog/URL", 0, &external_elog_url, TRUE);
   12827            0 :       if (external_elog && (external_elog_url.length() > 0)) {
   12828            0 :          redirect(r, external_elog_url.c_str());
   12829            0 :          return;
   12830              :       }
   12831            0 :       send_resource(r, "elog_show.html");
   12832            0 :       return;
   12833            0 :    }
   12834              : 
   12835              :    // special processing for "Elog last 7d", etc
   12836              : 
   12837              :    char cmdx[32];
   12838            0 :    mstrlcpy(cmdx, command, sizeof(cmdx));
   12839            0 :    cmdx[9] = 0;
   12840              : 
   12841            0 :    if (equal_ustring(cmdx, "Elog last")) {
   12842              :       // "Elog last 7d", etc
   12843            0 :       send_resource(r, "elog_query.html");
   12844            0 :       return;
   12845              :    }
   12846              : 
   12847            0 :    if (equal_ustring(command, "Create ELog from this page")) {
   12848            0 :       std::string redir;
   12849            0 :       redir += "?cmd=New+elog";
   12850            0 :       redir += "&odb_path=";
   12851            0 :       redir += p->getparam("odb_path");
   12852            0 :       redirect(r, redir.c_str());
   12853            0 :       return;
   12854            0 :    }
   12855              : 
   12856            0 :    if (equal_ustring(command, "Submit elog")) {
   12857            0 :       Lock(t);
   12858            0 :       submit_elog(odb, p, r, a);
   12859            0 :       Unlock(t);
   12860            0 :       return;
   12861              :    }
   12862              : 
   12863            0 :    if (equal_ustring(command, "elog_att")) {
   12864            0 :       Lock(t);
   12865            0 :       show_elog_attachment(p, r, dec_path);
   12866            0 :       Unlock(t);
   12867            0 :       return;
   12868              :    }
   12869              : 
   12870              :    /*---- accept command --------------------------------------------*/
   12871              : 
   12872            0 :    if (equal_ustring(command, "accept")) {
   12873            0 :       int refresh = atoi(p->getparam("refr"));
   12874              : 
   12875              :       /* redirect with cookie */
   12876            0 :       r->rsprintf("HTTP/1.1 302 Found\r\n");
   12877            0 :       r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
   12878            0 :       r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
   12879              : 
   12880              :       time_t now;
   12881            0 :       time(&now);
   12882              : 
   12883            0 :       now += 3600 * 24 * 365;
   12884              : 
   12885              :       struct tm gmt_tms;
   12886            0 :       gmtime_r(&now, &gmt_tms);
   12887              : 
   12888              :       char str[256];
   12889            0 :       strftime(str, sizeof(str), "%A, %d-%b-%Y %H:00:00 GMT", &gmt_tms);
   12890              : 
   12891            0 :       r->rsprintf("Set-Cookie: midas_refr=%d; path=/; expires=%s\r\n", refresh, str);
   12892            0 :       r->rsprintf("Location: ./\r\n\r\n<html>redir</html>\r\n");
   12893              : 
   12894            0 :       return;
   12895              :    }
   12896              : 
   12897              : #ifdef OBSOLETE
   12898              :    /*---- slow control display --------------------------------------*/
   12899              : 
   12900            0 :    if (equal_ustring(command, "eqtable")) {
   12901            0 :       Lock(t);
   12902            0 :       show_eqtable_page(p, r, c->refresh);
   12903            0 :       Unlock(t);
   12904            0 :       return;
   12905              :    }
   12906              : #endif
   12907              : 
   12908              :    /*---- sequencer page --------------------------------------------*/
   12909              : 
   12910            0 :    if (equal_ustring(command, "Sequencer")) {
   12911            0 :       send_resource(r, "sequencer.html");
   12912            0 :       return;
   12913              :    }
   12914              : 
   12915              :    // obsolete
   12916            0 :    if (equal_ustring(command, "seq")) {
   12917            0 :       send_resource(r, "sequencer.html");
   12918            0 :       return;
   12919              :    }
   12920              : 
   12921            0 :    if (equal_ustring(command, "start_script")) {
   12922            0 :       send_resource(r, "start_script.html");
   12923            0 :       return;
   12924              :    }
   12925              : 
   12926            0 :    if (equal_ustring(command, "load_script")) {
   12927            0 :       send_resource(r, "load_script.html");
   12928            0 :       return;
   12929              :    }
   12930              : 
   12931            0 :    if (equal_ustring(command, "edit_script")) {
   12932            0 :       send_resource(r, "edit_script.html");
   12933            0 :       return;
   12934              :    }
   12935              : 
   12936              :    /*---- show ODB --------------------------------------------------*/
   12937              : 
   12938            0 :    if (equal_ustring(command, "oldOdb")) {
   12939            0 :       int write_access = TRUE;
   12940            0 :       db_find_key(hDB, 0, "/Experiment/Security/Web Password", &hkey);
   12941            0 :       if (hkey) {
   12942              :          char str[256];
   12943            0 :          int size = sizeof(str);
   12944            0 :          db_get_data(hDB, hkey, str, &size, TID_STRING);
   12945            0 :          if (strcmp(c->cookie_wpwd.c_str(), str) == 0)
   12946            0 :             write_access = TRUE;
   12947              :          else
   12948            0 :             write_access = FALSE;
   12949              :       }
   12950              : 
   12951            0 :       std::string odb_path;
   12952            0 :       if (p->getparam("odb_path") && *p->getparam("odb_path"))
   12953            0 :          odb_path = p->getparam("odb_path");
   12954              : 
   12955            0 :       Lock(t);
   12956            0 :       show_odb_page(p, r, odb_path.c_str(), write_access);
   12957            0 :       Unlock(t);
   12958            0 :       return;
   12959            0 :    }
   12960              : 
   12961              :    /*---- New ODB browser --------------------------------------------*/
   12962              : 
   12963            0 :    if (equal_ustring(command, "odb")) {
   12964            0 :       send_resource(r, "odb.html");
   12965            0 :       return;
   12966              :    }
   12967              : 
   12968              :    /*---- ODB show open records --------------------------------------*/
   12969              : 
   12970            0 :    if (equal_ustring(command, "odb_sor")) {
   12971            0 :       send_resource(r, "odb_sor.html");
   12972            0 :       return;
   12973              :    }
   12974              : 
   12975              :    /*---- ODB show clients -------------------------------------------*/
   12976              : 
   12977            0 :    if (equal_ustring(command, "odb_scl")) {
   12978            0 :       send_resource(r, "odb_scl.html");
   12979            0 :       return;
   12980              :    }
   12981              : 
   12982              :    /*---- old ODB path ----------------------------------------------*/
   12983              : 
   12984            0 :    if ((command[0]==0) && dec_path[0]) {
   12985            0 :       if (equal_ustring(dec_path, "root")) {
   12986            0 :          std::string new_url = "./?cmd=odb";
   12987              :          //printf("redirect old odb path url [%s] to [%s]\n", dec_path, new_url.c_str());
   12988            0 :          redirect_307(r, new_url.c_str());
   12989            0 :          return;
   12990            0 :       }
   12991              :    }
   12992              : 
   12993            0 :    if ((command[0]==0) && dec_path[0]) {
   12994              :       HNDLE hkey;
   12995            0 :       status = db_find_key(hDB, 0, dec_path, &hkey);
   12996              :       //printf("try odb path [%s], status %d\n", dec_path, status);
   12997            0 :       if (status == DB_SUCCESS) {
   12998            0 :          int level = 0;
   12999            0 :          for (const char* s = dec_path; *s; s++) {
   13000            0 :             if (*s == '/')
   13001            0 :                level++;
   13002              :          }
   13003            0 :          std::string new_url;
   13004            0 :          if (level == 0) {
   13005              :             // Top-level directory like /Logger, (which appears in dec_path as "Logger")
   13006            0 :             new_url += "./";
   13007              :          } else {
   13008            0 :             for (int i=0; i<level; i++) {
   13009            0 :                if (i>0)
   13010            0 :                   new_url += "/";
   13011            0 :                new_url += "..";
   13012              :             }
   13013              :          }
   13014            0 :          new_url += "?cmd=odb";
   13015            0 :          new_url += "&odb_path=";
   13016            0 :          new_url += urlEncode(dec_path);
   13017              :          //printf("redirect old odb path url [%s] to [%s]\n", dec_path, new_url.c_str());
   13018            0 :          redirect_307(r, new_url.c_str());
   13019            0 :          return;
   13020            0 :       }
   13021              :    }
   13022              : 
   13023              :    /*---- event dump ------------------------------------------------*/
   13024              : 
   13025            0 :    if (equal_ustring(command, "event dump")) {
   13026            0 :       send_resource(r, "event_dump.html");
   13027            0 :       return;
   13028              :    }
   13029              : 
   13030              :    /*---- custom page -----------------------------------------------*/
   13031              : 
   13032            0 :    if (equal_ustring(command, "custom")) {
   13033            0 :       Lock(t);
   13034            0 :       show_custom_page(p, r, c->cookie_cpwd.c_str());
   13035            0 :       Unlock(t);
   13036            0 :       return;
   13037              :    }
   13038              : 
   13039              :    /*---- custom page accessed by direct URL that used to be under /CS/... ----*/
   13040              : 
   13041            0 :    if (db_find_key(hDB, 0, "/Custom", &hkey) == DB_SUCCESS && dec_path[0]) {
   13042            0 :       std::string odb_path;
   13043            0 :       std::string value;
   13044              :       int status;
   13045              : 
   13046            0 :       odb_path = "";
   13047            0 :       odb_path += "/Custom/Images/";
   13048            0 :       odb_path += dec_path;
   13049            0 :       odb_path += "/Background";
   13050              : 
   13051            0 :       status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
   13052              : 
   13053              :       //printf("Try custom gif [%s] status %d\n", odb_path.c_str(), status);
   13054              : 
   13055            0 :       if (status == DB_SUCCESS) {
   13056            0 :          if (strstr(dec_path, "..")) {
   13057            0 :             std::string str;
   13058            0 :             str += "Invalid custom gif name \'";
   13059            0 :             str += dec_path;
   13060            0 :             str += "\' contains \'..\'";
   13061            0 :             show_error_404(r, str.c_str());
   13062            0 :             return;
   13063            0 :          }
   13064              : 
   13065            0 :          Lock(t);
   13066            0 :          show_custom_gif(r, dec_path);
   13067            0 :          Unlock(t);
   13068            0 :          return;
   13069              :       }
   13070              : 
   13071            0 :       bool found_custom = false;
   13072              : 
   13073            0 :       odb_path = "";
   13074            0 :       odb_path += "/Custom/";
   13075            0 :       odb_path += dec_path;
   13076              : 
   13077            0 :       status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
   13078              : 
   13079              :       //printf("Try [%s] status %d\n", odb_path.c_str(), status);
   13080              : 
   13081            0 :       if (status == DB_SUCCESS) {
   13082            0 :          found_custom = true;
   13083              :       } else {
   13084            0 :          odb_path = "";
   13085            0 :          odb_path += "/Custom/";
   13086            0 :          odb_path += dec_path;
   13087            0 :          odb_path += "&";
   13088              : 
   13089            0 :          status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
   13090              : 
   13091              :          //printf("Try [%s] status %d\n", odb_path.c_str(), status);
   13092              : 
   13093            0 :          if (status == DB_SUCCESS) {
   13094            0 :             found_custom = true;
   13095              :          } else {
   13096            0 :             odb_path = "";
   13097            0 :             odb_path += "/Custom/";
   13098            0 :             odb_path += dec_path;
   13099            0 :             odb_path += "!";
   13100              : 
   13101            0 :             status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
   13102              : 
   13103              :             //printf("Try [%s] status %d\n", odb_path.c_str(), status);
   13104              : 
   13105            0 :             if (status == DB_SUCCESS) {
   13106            0 :                found_custom = true;
   13107              :             }
   13108              :          }
   13109              :       }
   13110              : 
   13111            0 :       if (found_custom) {
   13112              :          //printf("custom file: serving [%s] value [%s]\n", dec_path, value.c_str());
   13113            0 :          if (strstr(dec_path, "..")) {
   13114            0 :             std::string str;
   13115            0 :             str += "Invalid custom page name \'";
   13116            0 :             str += dec_path;
   13117            0 :             str += "\' contains \'..\'";
   13118            0 :             show_error_404(r, str.c_str());
   13119            0 :             return;
   13120            0 :          }
   13121              : 
   13122            0 :          p->setparam("page", dec_path);
   13123            0 :          Lock(t);
   13124            0 :          show_custom_page(p, r, c->cookie_cpwd.c_str());
   13125            0 :          Unlock(t);
   13126            0 :          return;
   13127              :       }
   13128            0 :    }
   13129              : 
   13130              :    /* new custom pages */
   13131            0 :    if (db_find_key(hDB, 0, "/Custom", &hkey) == DB_SUCCESS && dec_path[0]) {
   13132            0 :       std::string custom_path;
   13133            0 :       status = db_get_value_string(hDB, 0, "/Custom/Path", 0, &custom_path, TRUE);
   13134            0 :       if ((status == DB_SUCCESS) && (custom_path.length() > 0)) {
   13135            0 :          if (strstr(dec_path, "..")) {
   13136            0 :             std::string str;
   13137            0 :             str += "Invalid custom file name \'";
   13138            0 :             str += dec_path;
   13139            0 :             str += "\' contains \'..\'";
   13140            0 :             show_error_404(r, str.c_str());
   13141            0 :             return;
   13142            0 :          }
   13143              : 
   13144            0 :          std::string full_filename = add_custom_path(dec_path);
   13145              : 
   13146              :          // if custom file exists, send it (like normal web server)
   13147            0 :          if (ss_file_exist(full_filename.c_str())) {
   13148            0 :             send_file(r, full_filename);
   13149            0 :             return;
   13150              :          }
   13151            0 :       }
   13152            0 :    }
   13153              : 
   13154              :    /*---- redirect if web page --------------------------------------*/
   13155              : 
   13156              :    //if (strlen(command) > 0) {
   13157              :    //   if (send_resource(r, std::string(command) + ".html", false))
   13158              :    //      return;
   13159              :    //}
   13160              : 
   13161              :    /*---- serve url as a resource file ------------------------------*/
   13162              : 
   13163            0 :    if (strlen(p->getparam("path")) > 0) {
   13164            0 :       if (send_resource(r, p->getparam("path"), false)) {
   13165            0 :          return;
   13166              :       }
   13167              :    }
   13168              : 
   13169              :    /*---- show status -----------------------------------------------*/
   13170              : 
   13171            0 :    if (elog_mode) {
   13172            0 :       redirect(r, "EL/");
   13173            0 :       return;
   13174              :    }
   13175              : 
   13176              :    /* header */
   13177            0 :    r->rsprintf("HTTP/1.1 400 Bad Request\r\n");
   13178            0 :    r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
   13179            0 :    r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
   13180            0 :    r->rsprintf("\r\n");
   13181            0 :    r->rsprintf("Error: Invalid URL \"%s\" or query \"%s\" or command \"%s\"\n", p->getparam("path"), p->getparam("query"), command);
   13182              : }
   13183              : 
   13184              : /*------------------------------------------------------------------*/
   13185              : 
   13186            0 : void decode_query(Param* pp, const char *query_string)
   13187              : {
   13188            0 :    int len = strlen(query_string);
   13189            0 :    char *buf = (char *)malloc(len+1);
   13190            0 :    assert(buf != NULL);
   13191            0 :    memcpy(buf, query_string, len+1);
   13192            0 :    char* p = buf;
   13193            0 :    p = strtok(p, "&");
   13194            0 :    while (p != NULL) {
   13195            0 :       char *pitem = p;
   13196            0 :       p = strchr(p, '=');
   13197            0 :       if (p != NULL) {
   13198            0 :          *p++ = 0;
   13199            0 :          urlDecode(pitem); // parameter name
   13200            0 :          if (!equal_ustring(pitem, "format"))
   13201            0 :             urlDecode(p); // parameter value
   13202              : 
   13203            0 :          pp->setparam(pitem, p); // decoded query parameters
   13204              : 
   13205            0 :          p = strtok(NULL, "&");
   13206              :       }
   13207              :    }
   13208            0 :    free(buf);
   13209            0 : }
   13210              : 
   13211            0 : void decode_get(Return* rr, char *string, const Cookies* c, const char* url, const char* query_string, RequestTrace* t)
   13212              : {
   13213              :    char path[256];
   13214              : 
   13215              :    //printf("decode_get: string [%s], decode_url %d, url [%s], query_string [%s]\n", string, decode_url, url, query_string);
   13216              : 
   13217            0 :    Param* param = new Param();
   13218              : 
   13219            0 :    param->initparam();
   13220              : 
   13221            0 :    if (url)
   13222            0 :       mstrlcpy(path, url + 1, sizeof(path));     /* strip leading '/' */
   13223              :    else {
   13224            0 :       mstrlcpy(path, string + 1, sizeof(path));     /* strip leading '/' */
   13225              : 
   13226            0 :       if (strchr(path, '?'))
   13227            0 :          *strchr(path, '?') = 0;
   13228              :    }
   13229              : 
   13230            0 :    param->setparam("path", path);
   13231              : 
   13232            0 :    assert(query_string != NULL);
   13233              : 
   13234            0 :    decode_query(param, query_string);
   13235              : 
   13236            0 :    param->setparam("query", query_string);
   13237              : 
   13238              :    char dec_path[256];
   13239            0 :    mstrlcpy(dec_path, path, sizeof(dec_path));
   13240              : 
   13241            0 :    interprete(param, rr, NULL, c, dec_path, t);
   13242              : 
   13243            0 :    param->freeparam();
   13244            0 :    delete param;
   13245            0 : }
   13246              : 
   13247              : /*------------------------------------------------------------------*/
   13248              : 
   13249            0 : void decode_post(Return* rr, const char *header, const char *string, const char *boundary, int length, const Cookies* c, const char* url, RequestTrace* t)
   13250              : {
   13251            0 :    bool debug_decode_post = false;
   13252              :    
   13253            0 :    Param* param = new Param;
   13254              : 
   13255            0 :    param->initparam();
   13256              : 
   13257              :    char path[256];
   13258              : 
   13259            0 :    if (url)
   13260            0 :       mstrlcpy(path, url + 1, sizeof(path));     /* strip leading '/' */
   13261              :    else {
   13262            0 :       mstrlcpy(path, header + 1, sizeof(path));     /* strip leading '/' */
   13263            0 :       if (strchr(path, '?'))
   13264            0 :          *strchr(path, '?') = 0;
   13265            0 :       if (strchr(path, ' '))
   13266            0 :          *strchr(path, ' ') = 0;
   13267              :    }
   13268            0 :    param->setparam("path", path); // undecoded path
   13269              : 
   13270            0 :    Attachment* a = new Attachment;
   13271              : 
   13272            0 :    const char* pinit = string;
   13273              : 
   13274              :    /* return if no boundary defined */
   13275            0 :    if (!boundary[0])
   13276            0 :       return;
   13277              : 
   13278            0 :    if (strstr(string, boundary))
   13279            0 :       string = strstr(string, boundary) + strlen(boundary);
   13280              : 
   13281            0 :    if (debug_decode_post)
   13282            0 :       printf("decode_post: -->[%s]<--\n", string);
   13283              : 
   13284              :    do {
   13285              :       //printf("decode_post: [%s]\n", string);
   13286            0 :       if (strstr(string, "name=")) {
   13287            0 :          const char* pitem = strstr(string, "name=") + 5;
   13288            0 :          if (*pitem == '\"')
   13289            0 :             pitem++;
   13290              : 
   13291              :          //printf("decode_post: pitem [%s]\n", pitem);
   13292              : 
   13293            0 :          if (strncmp(pitem, "attfile", 7) == 0) {
   13294            0 :             int n = pitem[7] - '1';
   13295              : 
   13296              :             char file_name[256];
   13297            0 :             file_name[0] = 0;
   13298              : 
   13299              :             /* evaluate file attachment */
   13300            0 :             if (strstr(pitem, "filename=")) {
   13301            0 :                const char* p = strstr(pitem, "filename=") + 9;
   13302            0 :                if (*p == '\"')
   13303            0 :                   p++;
   13304            0 :                if (strstr(p, "\r\n\r\n"))
   13305            0 :                   string = strstr(p, "\r\n\r\n") + 4;
   13306            0 :                else if (strstr(p, "\r\r\n\r\r\n"))
   13307            0 :                   string = strstr(p, "\r\r\n\r\r\n") + 6;
   13308              : 
   13309            0 :                mstrlcpy(file_name, p, sizeof(file_name));
   13310              : 
   13311            0 :                char* pp = file_name;
   13312            0 :                if (strchr(pp, '\"'))
   13313            0 :                   *strchr(pp, '\"') = 0;
   13314              : 
   13315              :                /* set attachment filename */
   13316              :                char str[256];
   13317            0 :                sprintf(str, "attachment%d", n);
   13318            0 :                if (debug_decode_post)
   13319            0 :                   printf("decode_post: [%s] = [%s]\n", str, file_name);
   13320            0 :                param->setparam(str, file_name); // file_name should be decoded?
   13321              :             }
   13322              : 
   13323              :             /* find next boundary */
   13324            0 :             const char* ptmp = string;
   13325            0 :             const char* p = NULL;
   13326              :             do {
   13327            0 :                while (*ptmp != '-')
   13328            0 :                   ptmp++;
   13329              : 
   13330            0 :                p = strstr(ptmp, boundary);
   13331            0 :                if (p != NULL) {
   13332            0 :                   while (*p == '-')
   13333            0 :                      p--;
   13334            0 :                   if (*p == 10)
   13335            0 :                      p--;
   13336            0 :                   if (*p == 13)
   13337            0 :                      p--;
   13338            0 :                   p++;
   13339            0 :                   break;
   13340              :                } else
   13341            0 :                   ptmp += strlen(ptmp);
   13342              : 
   13343              :             } while (TRUE);
   13344              : 
   13345              :             /* save pointer to file */
   13346            0 :             if (file_name[0]) {
   13347            0 :                size_t size = (POINTER_T) p - (POINTER_T) string;
   13348            0 :                char* buf = (char*)malloc(size+1);
   13349            0 :                if (!buf) {
   13350            0 :                   return;
   13351              :                }
   13352            0 :                memcpy(buf, string, size);
   13353            0 :                buf[size] = 0; // make sure string is NUL terminated
   13354            0 :                a->attachment_buffer[n] = buf;
   13355            0 :                a->attachment_size[n] = size;
   13356            0 :                if (debug_decode_post)
   13357            0 :                   printf("decode_post: attachment[%d] size %d data --->[%s]<---\n", n, (int)a->attachment_size[n], a->attachment_buffer[n]);
   13358              :             }
   13359              : 
   13360            0 :             string = strstr(p, boundary) + strlen(boundary);
   13361              :          } else {
   13362            0 :             const char* p = pitem;
   13363            0 :             if (strstr(p, "\r\n\r\n"))
   13364            0 :                p = strstr(p, "\r\n\r\n") + 4;
   13365            0 :             else if (strstr(p, "\r\r\n\r\r\n"))
   13366            0 :                p = strstr(p, "\r\r\n\r\r\n") + 6;
   13367              : 
   13368            0 :             char* ppitem = (char*)strchr(pitem, '\"'); // NB: defeat "const char* string"
   13369            0 :             if (ppitem)
   13370            0 :                *ppitem = 0;
   13371              : 
   13372            0 :             char* pb = (char*)(strstr(p, boundary)); // NB: defeat "const char* string"
   13373            0 :             if (pb) {
   13374            0 :                string = pb + strlen(boundary);
   13375            0 :                *pb = 0;
   13376            0 :                char* ptmp = (char*)(p + (strlen(p) - 1)); // NB: defeat "const char* string"
   13377            0 :                while (*ptmp == '-' || *ptmp == '\n' || *ptmp == '\r')
   13378            0 :                   *ptmp-- = 0;
   13379              :             } else {
   13380            0 :                show_error(rr, "Invalid POST request");
   13381            0 :                return;
   13382              :             }
   13383            0 :             if (debug_decode_post)
   13384            0 :                printf("decode_post: [%s] = [%s]\n", pitem, p);
   13385            0 :             param->setparam(pitem, p); // in decode_post()
   13386              :          }
   13387              : 
   13388            0 :          while (*string == '-' || *string == '\n' || *string == '\r')
   13389            0 :             string++;
   13390              :       }
   13391              : 
   13392            0 :    } while ((POINTER_T) string - (POINTER_T) pinit < length);
   13393              : 
   13394              :    char dec_path[256];
   13395            0 :    mstrlcpy(dec_path, path, sizeof(dec_path));
   13396              : 
   13397            0 :    interprete(param, rr, a, c, dec_path, t);
   13398              : 
   13399            0 :    delete a;
   13400            0 :    delete param;
   13401              : }
   13402              : 
   13403              : /*------------------------------------------------------------------*/
   13404              : 
   13405            0 : INT check_odb_records(MVOdb* odb)
   13406              : {
   13407              :    HNDLE hDB, hKeyEq, hKey;
   13408            0 :    RUNINFO_STR(runinfo_str);
   13409              :    int i, status;
   13410              :    KEY key;
   13411              : 
   13412              :    /* check /Runinfo structure */
   13413            0 :    status = cm_get_experiment_database(&hDB, NULL);
   13414            0 :    assert(status == DB_SUCCESS);
   13415              : 
   13416            0 :    status = db_check_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str(), FALSE);
   13417            0 :    if (status == DB_STRUCT_MISMATCH) {
   13418            0 :       status = db_check_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str(), TRUE);
   13419            0 :       if (status == DB_SUCCESS) {
   13420            0 :          cm_msg(MINFO, "check_odb_records", "ODB subtree /Runinfo corrected successfully");
   13421              :       } else {
   13422            0 :          cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Runinfo, db_check_record() status %d", status);
   13423            0 :          return 0;
   13424              :       }
   13425            0 :    } else if (status == DB_NO_KEY) {
   13426            0 :       cm_msg(MERROR, "check_odb_records", "ODB subtree /Runinfo does not exist");
   13427            0 :       status = db_create_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str());
   13428            0 :       if (status == DB_SUCCESS) {
   13429            0 :          cm_msg(MINFO, "check_odb_records", "ODB subtree /Runinfo created successfully");
   13430              :       } else {
   13431            0 :          cm_msg(MERROR, "check_odb_records", "Cannot create ODB subtree /Runinfo, db_create_record() status %d", status);
   13432            0 :          return 0;
   13433              :       }
   13434            0 :    } else if (status != DB_SUCCESS) {
   13435            0 :       cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Runinfo, db_check_record() status %d", status);
   13436            0 :       return 0;
   13437              :    }
   13438              : 
   13439              :    /* check /Equipment/<name>/Common structures */
   13440            0 :    if (db_find_key(hDB, 0, "/equipment", &hKeyEq) == DB_SUCCESS) {
   13441            0 :       for (i = 0 ;; i++) {
   13442            0 :          db_enum_key(hDB, hKeyEq, i, &hKey);
   13443            0 :          if (!hKey)
   13444            0 :             break;
   13445            0 :          db_get_key(hDB, hKey, &key);
   13446              : 
   13447            0 :          status = db_check_record(hDB, hKey, "Common", EQUIPMENT_COMMON_STR, FALSE);
   13448            0 :          if (status == DB_STRUCT_MISMATCH) {
   13449            0 :             status = db_check_record(hDB, hKey, "Common", EQUIPMENT_COMMON_STR, TRUE);
   13450            0 :             if (status == DB_SUCCESS) {
   13451            0 :                cm_msg(MINFO, "check_odb_records", "ODB subtree /Equipment/%s/Common corrected successfully", key.name);
   13452              :             } else {
   13453            0 :                cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Equipment/%s/Common, db_check_record() status %d", key.name, status);
   13454              :             }
   13455            0 :          } else if (status != DB_SUCCESS) {
   13456            0 :             cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Equipment/%s/Common, db_check_record() status %d", key.name, status);
   13457              :          }
   13458              :       }
   13459              :    }
   13460              : 
   13461            0 :    return CM_SUCCESS;
   13462              : }
   13463              : 
   13464              : 
   13465              : /*------------------------------------------------------------------*/
   13466              : 
   13467              : std::atomic_bool _abort{false};
   13468              : 
   13469            0 : void ctrlc_handler(int sig)
   13470              : {
   13471            0 :    _abort = true;
   13472            0 : }
   13473              : 
   13474              : /*------------------------------------------------------------------*/
   13475              : 
   13476              : #ifdef HAVE_MONGOOSE6
   13477              : static std::vector<std::string> gUserAllowedHosts;
   13478              : #endif
   13479              : static std::vector<std::string> gAllowedHosts;
   13480              : #ifdef HAVE_MONGOOSE6
   13481              : static const std::string gOdbAllowedHosts = "/Experiment/Security/mhttpd hosts/Allowed hosts";
   13482              : #endif
   13483              : 
   13484              : #ifdef HAVE_MONGOOSE6
   13485            0 : static void load_allowed_hosts(HNDLE hDB, HNDLE hKey, int index, void* info)
   13486              : {
   13487            0 :    if (hKey != 0)
   13488            0 :       cm_msg(MINFO, "load_allowed_hosts", "Reloading mhttpd hosts access control list via hotlink callback");
   13489              : 
   13490            0 :    gAllowedHosts.clear();
   13491              : 
   13492              :    // copy the user allowed hosts
   13493            0 :    for (unsigned int i=0; i<gUserAllowedHosts.size(); i++)
   13494            0 :       gAllowedHosts.push_back(gUserAllowedHosts[i]);
   13495              : 
   13496            0 :    int total = 0;
   13497            0 :    int last = 0;
   13498            0 :    for (int i=0; ; i++) {
   13499            0 :       std::string s;
   13500            0 :       int status = db_get_value_string(hDB, 0, gOdbAllowedHosts.c_str(), i, &s, FALSE);
   13501              :       //printf("get %d, status %d, string [%s]\n", i, status, s.c_str());
   13502            0 :       if (status != DB_SUCCESS) {
   13503            0 :          total = i;
   13504            0 :          break;
   13505              :       }
   13506              : 
   13507            0 :       if (s.length() < 1) // skip emties
   13508            0 :          continue;
   13509              : 
   13510            0 :       if (s[0] == '#') // skip commented-out entries
   13511            0 :          continue;
   13512              : 
   13513              :       //printf("add allowed hosts %d [%s]\n", i, s.c_str());
   13514            0 :       gAllowedHosts.push_back(s);
   13515            0 :       last = i;
   13516            0 :    }
   13517              : 
   13518              :    //printf("total %d, last %d\n", total, last);
   13519              : 
   13520            0 :    if (total - last < 5) {
   13521            0 :       int new_size = last + 10;
   13522              :       //printf("new size %d\n", new_size);
   13523            0 :       int status = db_resize_string(hDB, 0, gOdbAllowedHosts.c_str(), new_size, 256);
   13524            0 :       if (status != DB_SUCCESS) {
   13525            0 :          cm_msg(MERROR, "load_allowed_hosts", "Cannot resize the allowed hosts access control list, db_resize_string(%d) status %d", new_size, status);
   13526              :       }
   13527              :    }
   13528            0 : }
   13529              : 
   13530            0 : static int init_allowed_hosts()
   13531              : {
   13532              :    HNDLE hDB;
   13533              :    HNDLE hKey;
   13534              :    int status;
   13535              : 
   13536            0 :    cm_get_experiment_database(&hDB, NULL);
   13537              : 
   13538              :    // create "allowed hosts" so we can watch it
   13539              : 
   13540            0 :    std::string s;
   13541            0 :    status = db_get_value_string(hDB, 0, gOdbAllowedHosts.c_str(), 0, &s, TRUE);
   13542              : 
   13543            0 :    if (status != DB_SUCCESS) {
   13544            0 :       cm_msg(MERROR, "init_allowed_hosts", "Cannot create the mhttpd hosts access control list, db_get_value_string() status %d", status);
   13545            0 :       return status;
   13546              :    }
   13547              : 
   13548            0 :    status = db_find_key(hDB, 0, gOdbAllowedHosts.c_str(), &hKey);
   13549              : 
   13550            0 :    if (status != DB_SUCCESS || hKey == 0) {
   13551            0 :       cm_msg(MERROR, "init_allowed_hosts", "Cannot find the mhttpd hosts access control list, db_find_key() status %d", status);
   13552            0 :       return status;
   13553              :    }
   13554              : 
   13555            0 :    load_allowed_hosts(hDB, 0, 0, NULL);
   13556              : 
   13557            0 :    status = db_watch(hDB, hKey, load_allowed_hosts, NULL);
   13558              : 
   13559            0 :    if (status != DB_SUCCESS) {
   13560            0 :       cm_msg(MERROR, "init_allowed_hosts", "Cannot watch the mhttpd hosts access control list, db_watch() status %d", status);
   13561            0 :       return status;
   13562              :    }
   13563              : 
   13564            0 :    return SUCCESS;
   13565            0 : }
   13566              : 
   13567            0 :    int check_midas_acl(const struct sockaddr *sa, int len) {
   13568              :       // access control list is empty?
   13569            0 :       if (gAllowedHosts.size() == 0)
   13570            0 :          return 1;
   13571              : 
   13572              :       char hname[NI_MAXHOST];
   13573            0 :       hname[0] = 0;
   13574              : 
   13575              :       int status;
   13576            0 :       const char* status_string = "success";
   13577              : 
   13578            0 :       status = getnameinfo(sa, len, hname, sizeof(hname), NULL, 0, 0);
   13579              : 
   13580            0 :       if (status)
   13581            0 :          status_string = gai_strerror(status);
   13582              : 
   13583              :       //printf("connection from [%s], status %d (%s)\n", hname, status, status_string);
   13584              : 
   13585            0 :       if (status != 0) {
   13586            0 :          printf("Rejecting connection from \'%s\', getnameinfo() status %d (%s)\n", hname, status, status_string);
   13587            0 :          return 0;
   13588              :       }
   13589              : 
   13590              :       /* always permit localhost */
   13591            0 :       if (strcmp(hname, "localhost.localdomain") == 0)
   13592            0 :          return 1;
   13593            0 :       if (strcmp(hname, "localhost") == 0)
   13594            0 :          return 1;
   13595              : 
   13596            0 :       for (unsigned int i=0 ; i<gAllowedHosts.size() ; i++)
   13597            0 :          if (gAllowedHosts[i] == hname) {
   13598            0 :             return 1;
   13599              :          }
   13600              : 
   13601            0 :       printf("Rejecting connection from \'%s\'\n", hname);
   13602            0 :       return 0;
   13603              :    }
   13604              : 
   13605            0 : int open_listening_socket(int port)
   13606              : {
   13607              :    int status;
   13608              :    struct sockaddr_in bind_addr;
   13609              : 
   13610              :    /* create a new socket */
   13611            0 :    int lsock = socket(AF_INET, SOCK_STREAM, 0);
   13612              : 
   13613            0 :    if (lsock == -1) {
   13614            0 :       printf("Cannot create socket, socket() errno %d (%s)\n", errno, strerror(errno));
   13615            0 :       return -1;
   13616              :    }
   13617              : 
   13618              :    /* bind local node name and port to socket */
   13619            0 :    memset(&bind_addr, 0, sizeof(bind_addr));
   13620            0 :    bind_addr.sin_family = AF_INET;
   13621            0 :    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   13622            0 :    bind_addr.sin_port = htons((short) port);
   13623              : 
   13624              :    /* try reusing address */
   13625            0 :    int flag = 1;
   13626            0 :    status = setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
   13627              : 
   13628            0 :    if (status < 0) {
   13629            0 :       printf("Cannot setsockopt(SOL_SOCKET, SO_REUSEADDR), errno %d (%s)\n", errno, strerror(errno));
   13630            0 :       return -1;
   13631              :    }
   13632              : 
   13633            0 :    status = bind(lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
   13634              : 
   13635            0 :    if (status < 0) {
   13636            0 :       printf("Cannot bind() to port %d, bind() errno %d (%s)\n", port, errno, strerror(errno));
   13637            0 :       return -1;
   13638              :    }
   13639              : 
   13640              :    /* listen for connection */
   13641            0 :    status = listen(lsock, SOMAXCONN);
   13642            0 :    if (status < 0) {
   13643            0 :       printf("Cannot listen() on port %d, errno %d (%s), bye!\n", port, errno, strerror(errno));
   13644            0 :       return -1;
   13645              :    }
   13646              : 
   13647            0 :    printf("mhttpd is listening on port %d\n", port);
   13648              : 
   13649            0 :    return lsock;
   13650              : }
   13651              : #endif
   13652              : 
   13653              : /*------------------------------------------------------------------*/
   13654              : 
   13655            0 : int try_file_mg(const char* try_dir, const char* filename, std::string& path, FILE** fpp, bool trace)
   13656              : {
   13657            0 :    if (fpp)
   13658            0 :       *fpp = NULL;
   13659            0 :    if (!try_dir)
   13660            0 :       return SS_FILE_ERROR;
   13661            0 :    if (strlen(try_dir) < 1)
   13662            0 :       return SS_FILE_ERROR;
   13663              : 
   13664            0 :    path = try_dir;
   13665            0 :    if (path[path.length()-1] != DIR_SEPARATOR)
   13666            0 :       path += DIR_SEPARATOR_STR;
   13667            0 :    path += filename;
   13668              : 
   13669            0 :    FILE* fp = fopen(path.c_str(), "r");
   13670              : 
   13671            0 :    if (trace) {
   13672            0 :       if (fp)
   13673            0 :          printf("file \"%s\": OK!\n", path.c_str());
   13674              :       else
   13675            0 :          printf("file \"%s\": not found.\n", path.c_str());
   13676              :    }
   13677              : 
   13678            0 :    if (!fp)
   13679            0 :       return SS_FILE_ERROR;
   13680            0 :    else if (fpp)
   13681            0 :       *fpp = fp;
   13682              :    else
   13683            0 :       fclose(fp);
   13684              : 
   13685            0 :    return SUCCESS;
   13686              : }
   13687              : 
   13688            0 : int find_file_mg(const char* filename, std::string& path, FILE** fpp, bool trace)
   13689              : {
   13690            0 :    std::string exptdir = cm_get_path();
   13691              : 
   13692            0 :    if (try_file_mg(".", filename, path, fpp, trace) == SUCCESS)
   13693            0 :       return SUCCESS;
   13694              : 
   13695            0 :    if (try_file_mg(getenv("MIDAS_DIR"), filename, path, fpp, trace) == SUCCESS)
   13696            0 :       return SUCCESS;
   13697              : 
   13698            0 :    if (try_file_mg(exptdir.c_str(), filename, path, fpp, trace) == SUCCESS)
   13699            0 :       return SUCCESS;
   13700              : 
   13701            0 :    if (try_file_mg(getenv("MIDASSYS"), filename, path, fpp, trace) == SUCCESS)
   13702            0 :       return SUCCESS;
   13703              : 
   13704              :    // setup default filename
   13705            0 :    try_file_mg(exptdir.c_str(), filename, path, NULL, false);
   13706            0 :    return SS_FILE_ERROR;
   13707            0 : }
   13708              : 
   13709              : #ifdef HAVE_MONGOOSE6
   13710              : #include "mongoose6.h"
   13711              : #endif
   13712              : 
   13713              : #ifdef HAVE_MONGOOSE616
   13714              : #undef closesocket
   13715              : #include "mongoose616.h"
   13716              : // cs_md5() in not in mongoose.h
   13717              : extern void cs_md5(char buf[33], ...);
   13718              : #endif
   13719              : 
   13720              : static bool verbose_mg = false;
   13721              : static bool trace_mg = false;
   13722              : static bool trace_mg_recv = false;
   13723              : static bool trace_mg_send = false;
   13724              : static bool trace_mg_verbose = false;
   13725              : #ifdef HAVE_MONGOOSE616
   13726              : static bool multithread_mg = true;
   13727              : #endif
   13728              : 
   13729              : #ifdef HAVE_MONGOOSE6
   13730              : static struct mg_mgr mgr_mg;
   13731              : #endif
   13732              : 
   13733              : struct AuthEntry {
   13734              :    std::string username;
   13735              :    std::string realm;
   13736              :    std::string password;
   13737              : };
   13738              : 
   13739              : class Auth {
   13740              : public:
   13741              :    std::string realm;
   13742              :    std::string passwd_filename;
   13743              :    std::vector<AuthEntry> passwords;
   13744              : public:
   13745              :    int Init();
   13746              : };
   13747              : 
   13748              : static bool read_passwords(Auth* auth);
   13749              : 
   13750            0 : int Auth::Init()
   13751              : {
   13752            0 :    std::string exptname = cm_get_experiment_name();
   13753              :    
   13754            0 :    if (!exptname.empty())
   13755            0 :       realm = exptname;
   13756              :    else
   13757            0 :       realm = "midas";
   13758              :    
   13759            0 :    bool ok = read_passwords(this);
   13760            0 :    if (!ok) {
   13761            0 :       cm_msg(MERROR, "mongoose", "mongoose web server password file \"%s\" has no passwords for realm \"%s\"", passwd_filename.c_str(), realm.c_str());
   13762            0 :       cm_msg(MERROR, "mongoose", "please add passwords by running: htdigest %s %s midas", passwd_filename.c_str(), realm.c_str());
   13763            0 :       return SS_FILE_ERROR;
   13764              :    }
   13765              : 
   13766            0 :    return SUCCESS;
   13767            0 : }
   13768              : 
   13769              : static Auth *gAuthMg = NULL;
   13770              : 
   13771            0 : static void xmg_mkmd5resp(const char *method, size_t method_len, const char *uri,
   13772              :                          size_t uri_len, const char *ha1, size_t ha1_len,
   13773              :                          const char *nonce, size_t nonce_len, const char *nc,
   13774              :                          size_t nc_len, const char *cnonce, size_t cnonce_len,
   13775              :                          const char *qop, size_t qop_len, char *resp) {
   13776              :   static const char colon[] = ":";
   13777              :   static const size_t one = 1;
   13778              :   char ha2[33];
   13779              : 
   13780            0 :   cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
   13781            0 :   cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
   13782              :          nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
   13783              :          colon, one, ha2, sizeof(ha2) - 1, NULL);
   13784            0 : }
   13785              : 
   13786              : /*
   13787              :  * Check for authentication timeout.
   13788              :  * Clients send time stamp encoded in nonce. Make sure it is not too old,
   13789              :  * to prevent replay attacks.
   13790              :  * Assumption: nonce is a hexadecimal number of seconds since 1970.
   13791              :  */
   13792            0 : static int xmg_check_nonce(const char *nonce) {
   13793            0 :   unsigned long now = (unsigned long) time(NULL);
   13794            0 :   unsigned long val = (unsigned long) strtoul(nonce, NULL, 16);
   13795            0 :   return now < val || now - val < 3600;
   13796              : }
   13797              : 
   13798              : /*
   13799              :  * Authenticate HTTP request against opened passwords file.
   13800              :  * Returns 1 if authenticated, 0 otherwise.
   13801              :  */
   13802              : 
   13803            0 : static void xmg_http_send_digest_auth_request(struct mg_connection *c,
   13804              :                                              const char *domain) {
   13805            0 :   mg_printf(c,
   13806              :             "HTTP/1.1 401 Unauthorized\r\n"
   13807              :             "WWW-Authenticate: Digest qop=\"auth\", "
   13808              :             "realm=\"%s\", nonce=\"%lu\"\r\n"
   13809              :             "Content-Length: 0\r\n\r\n",
   13810            0 :             domain, (unsigned long) time(NULL));
   13811            0 : }
   13812              : 
   13813            0 : static bool read_passwords(Auth* auth)
   13814              : {
   13815            0 :    std::string path;
   13816              :    FILE *fp;
   13817            0 :    int status = find_file_mg("htpasswd.txt", path, &fp, trace_mg||verbose_mg);
   13818              : 
   13819            0 :    auth->passwd_filename = path;
   13820            0 :    auth->passwords.clear();
   13821              : 
   13822            0 :    if (status != SUCCESS || fp == NULL) {
   13823            0 :       cm_msg(MERROR, "mongoose", "mongoose web server cannot find password file \"%s\"", path.c_str());
   13824            0 :       cm_msg(MERROR, "mongoose", "please create password file: touch %s", path.c_str());
   13825            0 :       return false;
   13826              :    }
   13827              : 
   13828            0 :    bool have_realm = false;
   13829              :    char buf[256];
   13830              : 
   13831              :    /*
   13832              :     * Read passwords file line by line. If should have htdigest format,
   13833              :     * i.e. each line should be a colon-separated sequence:
   13834              :     * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD
   13835              :     */
   13836            0 :    while (fgets(buf, sizeof(buf), fp) != NULL) {
   13837              :       char f_user[256];
   13838              :       char f_domain[256];
   13839              :       char f_ha1[256];
   13840              : 
   13841            0 :       if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3) {
   13842            0 :          AuthEntry e;
   13843            0 :          e.realm = f_domain;
   13844            0 :          e.username = f_user;
   13845            0 :          e.password = f_ha1;
   13846              : 
   13847            0 :          if (e.realm == auth->realm) {
   13848            0 :             have_realm = true;
   13849            0 :             auth->passwords.push_back(e);
   13850              :          }
   13851            0 :       }
   13852              :    }
   13853              : 
   13854            0 :    fclose(fp);
   13855              : 
   13856            0 :    return have_realm;
   13857            0 : }
   13858              : 
   13859              : #ifdef HAVE_MONGOOSE6
   13860            0 : std::string find_var_mg(struct mg_str *hdr, const char* var_name)
   13861              : {
   13862            0 :    assert(!"this code is untested!");
   13863              :    
   13864              :    char* buf = NULL;
   13865              :    int buf_size = 0;
   13866              : 
   13867              :    while (1) {
   13868              :       if (buf_size == 0) {
   13869              :          buf_size = 256;
   13870              :          buf = (char*)malloc(buf_size);
   13871              :          assert(buf != NULL);
   13872              :       }
   13873              :          
   13874              :       int size = mg_http_parse_header(hdr, var_name, buf, buf_size);
   13875              : 
   13876              :       if (size <= 0) {
   13877              :          free(buf);
   13878              :          return "";
   13879              :       }
   13880              : 
   13881              :       if (size < buf_size) {
   13882              :          std::string s = buf;
   13883              :          free(buf);
   13884              :          return s;
   13885              :       }
   13886              : 
   13887              :       buf_size = buf_size*2 + 16;
   13888              :       buf = (char*)realloc(buf, buf_size);
   13889              :       assert(buf != NULL);
   13890              :    }
   13891              : }
   13892              : #endif
   13893              : 
   13894              : #ifdef HAVE_MONGOOSE616
   13895            0 : std::string find_var_mg(struct mg_str *hdr, const char* var_name)
   13896              : {
   13897            0 :    char* buf = NULL;
   13898            0 :    int buf_size = 0;
   13899            0 :    int size = mg_http_parse_header2(hdr, var_name, &buf, buf_size);
   13900            0 :    if (size <= 0)
   13901            0 :       return "";
   13902            0 :    assert(buf != NULL);
   13903            0 :    std::string s = buf;
   13904            0 :    free(buf);
   13905            0 :    return s;
   13906            0 : }
   13907              : #endif
   13908              : 
   13909            0 : static std::string check_digest_auth(struct http_message *hm, Auth* auth)
   13910              : {
   13911              :    char expected_response[33];
   13912              : 
   13913              :    //printf("HereA!\n");
   13914              : 
   13915              :    /* Parse "Authorization:" header, fail fast on parse error */
   13916            0 :    struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
   13917              : 
   13918            0 :    if (!hdr)
   13919            0 :       return "";
   13920              : 
   13921              :    //printf("HereB!\n");
   13922              : 
   13923            0 :    std::string user     = find_var_mg(hdr, "username");
   13924            0 :    std::string cnonce   = find_var_mg(hdr, "cnonce");
   13925            0 :    std::string response = find_var_mg(hdr, "response");
   13926            0 :    std::string uri      = find_var_mg(hdr, "uri");
   13927            0 :    std::string qop      = find_var_mg(hdr, "qop");
   13928            0 :    std::string nc       = find_var_mg(hdr, "nc");
   13929            0 :    std::string nonce    = find_var_mg(hdr, "nonce");
   13930              : 
   13931            0 :    if (user.length()<1) return "";
   13932            0 :    if (cnonce.length()<1) return "";
   13933            0 :    if (response.length()<1) return "";
   13934            0 :    if (uri.length()<1) return "";
   13935            0 :    if (qop.length()<1) return "";
   13936            0 :    if (nc.length()<1) return "";
   13937            0 :    if (nonce.length()<1) return "";
   13938              : 
   13939            0 :    if (xmg_check_nonce(nonce.c_str()) == 0) return "";
   13940              :    //printf("HereB8!\n");
   13941              : 
   13942              :    //printf("HereC!\n");
   13943              : 
   13944            0 :    const char* uri_end = strchr(hm->uri.p, ' ');
   13945            0 :    if (!uri_end) return "";
   13946              : 
   13947            0 :    size_t uri_length = uri_end - hm->uri.p;
   13948              : 
   13949            0 :    if (uri_length != uri.length())
   13950            0 :       return "";
   13951              : 
   13952            0 :    int cmp = strncmp(hm->uri.p, uri.c_str(), uri_length);
   13953              : 
   13954              :    //printf("check URI: message %d %d [%d] authorization [%s]\n", (int)hm->uri.len, uri_length, cmp, uri);
   13955              : 
   13956            0 :    if (cmp != 0)
   13957            0 :       return "";
   13958              : 
   13959            0 :    for (unsigned i=0; i<auth->passwords.size(); i++) {
   13960            0 :       AuthEntry* e = &auth->passwords[i];
   13961            0 :       if (e->username != user)
   13962            0 :          continue;
   13963            0 :       if (e->realm != auth->realm)
   13964            0 :          continue;
   13965            0 :       const char* f_ha1 = e->password.c_str();
   13966            0 :       int uri_len = hm->uri.len;
   13967            0 :       if (hm->uri.p[uri_len] == '?')
   13968            0 :          uri_len += hm->query_string.len + 1; // "+1" accounts for the "?" character
   13969            0 :       xmg_mkmd5resp(hm->method.p, hm->method.len,
   13970              :                     hm->uri.p, uri_len,
   13971              :                     f_ha1, strlen(f_ha1),
   13972              :                     nonce.c_str(), nonce.length(),
   13973              :                     nc.c_str(), nc.length(),
   13974              :                     cnonce.c_str(), cnonce.length(),
   13975              :                     qop.c_str(), qop.length(),
   13976              :                     expected_response);
   13977            0 :       int cmp = strcasecmp(response.c_str(), expected_response);
   13978              :       //printf("digest_auth: expected %s, got %s, cmp %d\n", expected_response, response.c_str(), cmp);
   13979            0 :       if (cmp == 0) {
   13980            0 :          return e->username;
   13981              :       }
   13982              :     }
   13983              : 
   13984            0 :    return "";
   13985            0 : }
   13986              : 
   13987              : #ifdef HAVE_MONGOOSE616
   13988              : 
   13989              : struct HostlistCacheEntry
   13990              : {
   13991              :    time_t time_created = 0;
   13992              :    time_t time_last_used = 0;
   13993              :    int count_used = 0;
   13994              :    bool ipv4 = false;
   13995              :    bool ipv6 = false;
   13996              :    uint32_t ipv4addr = 0;
   13997              :    struct in6_addr ipv6addr;
   13998              :    std::string hostname;
   13999              :    int gai_status = 0;
   14000              :    std::string gai_strerror;
   14001              :    bool ok = false;
   14002              : };
   14003              : 
   14004              : static std::vector<HostlistCacheEntry*> gHostlistCache;
   14005              : 
   14006            0 : static void print_hostlist_cache()
   14007              : {
   14008            0 :    time_t now = time(NULL);
   14009              :    
   14010            0 :    for (unsigned i=0; i<gHostlistCache.size(); i++) {
   14011            0 :       HostlistCacheEntry* e = gHostlistCache[i];
   14012            0 :       if (!e) {
   14013              :          // empty slot
   14014            0 :          continue;
   14015              :       }
   14016              : 
   14017            0 :       printf("%3d: %s \"%s\", ok %d, count_used %d, age created: %d, last_used %d",
   14018              :              i,
   14019            0 :              e->ipv4?"IPv4":(e->ipv6?"IPv6":"????"),
   14020              :              e->hostname.c_str(),
   14021            0 :              e->ok,
   14022              :              e->count_used,
   14023            0 :              (int)(now - e->time_created),
   14024            0 :              (int)(now - e->time_last_used));
   14025              : 
   14026            0 :       if (e->gai_status) {
   14027            0 :          printf(", getnameinfo() status %d (%s)", e->gai_status, e->gai_strerror.c_str());
   14028              :       }
   14029              : 
   14030            0 :       printf("\n");
   14031              :    }
   14032            0 : }
   14033              : 
   14034            0 : static bool mongoose_check_hostlist(const union socket_address *sa)
   14035              : {
   14036            0 :    time_t now = time(NULL);
   14037            0 :    bool ipv4 = false;
   14038            0 :    bool ipv6 = false;
   14039            0 :    uint32_t ipv4addr = 0;
   14040              :    struct in6_addr ipv6addr;
   14041              : 
   14042            0 :    if (sa->sa.sa_family == AF_INET) {
   14043            0 :       ipv4 = true;
   14044            0 :       ipv4addr = sa->sin.sin_addr.s_addr;
   14045            0 :    } else if (sa->sa.sa_family == AF_INET6) {
   14046            0 :       ipv6 = true;
   14047            0 :       memcpy(&ipv6addr, &sa->sin6.sin6_addr, sizeof(ipv6addr));
   14048              :    } else {
   14049            0 :       printf("Rejecting connection from unknown address family %d (AF_xxx)\n", sa->sa.sa_family);
   14050            0 :       return false;
   14051              :    }
   14052              :    
   14053            0 :    for (unsigned i=0; i<gHostlistCache.size(); i++) {
   14054            0 :       HostlistCacheEntry* e = gHostlistCache[i];
   14055            0 :       if (!e) {
   14056              :          // empty slot
   14057            0 :          continue;
   14058              :       }
   14059              :       
   14060            0 :       if ((ipv4 == e->ipv4) && (ipv4addr == e->ipv4addr)) {
   14061              :          // IPv4 address match
   14062            0 :          e->time_last_used = now;
   14063            0 :          e->count_used++;
   14064            0 :          return e->ok;
   14065              :       }
   14066              : 
   14067            0 :       if ((ipv6 == e->ipv6) && (memcmp(&ipv6addr, &e->ipv6addr, sizeof(ipv6addr)) == 0)) {
   14068              :          // IPv6 address match
   14069            0 :          e->time_last_used = now;
   14070            0 :          e->count_used++;
   14071            0 :          return e->ok;
   14072              :       }
   14073              : 
   14074              :       // not this one. maybe expire old entries?
   14075              : 
   14076            0 :       if (e->time_last_used < now - 24*60*60) {
   14077            0 :          printf("hostlist: expire \"%s\", ok %d, age %d, count_used: %d\n", e->hostname.c_str(), e->ok, (int)(now - e->time_last_used), e->count_used);
   14078            0 :          gHostlistCache[i] = NULL;
   14079            0 :          delete e;
   14080              :       }
   14081              :    }
   14082              : 
   14083              :    // not found in cache
   14084              : 
   14085            0 :    assert(ipv4 || ipv6);
   14086              : 
   14087            0 :    HostlistCacheEntry* e = new HostlistCacheEntry;
   14088              : 
   14089            0 :    bool found = false;
   14090            0 :    for (unsigned i=0; i<gHostlistCache.size(); i++) {
   14091            0 :       if (gHostlistCache[i] == NULL) {
   14092            0 :          gHostlistCache[i] = e;
   14093            0 :          found = true;
   14094              :       }
   14095              :    }
   14096            0 :    if (!found) {
   14097            0 :       gHostlistCache.push_back(e);
   14098              :    }
   14099              :    
   14100            0 :    e->time_created = now;
   14101            0 :    e->time_last_used = now;
   14102            0 :    e->count_used = 1;
   14103            0 :    e->ipv4 = ipv4;
   14104            0 :    e->ipv6 = ipv6;
   14105            0 :    if (ipv4)
   14106            0 :       e->ipv4addr = ipv4addr;
   14107            0 :    if (ipv6)
   14108            0 :       memcpy(&e->ipv6addr, &ipv6addr, sizeof(ipv6addr));
   14109            0 :    e->ok = false;
   14110              : 
   14111              :    char hname[NI_MAXHOST];
   14112            0 :    hname[0] = 0;
   14113              :    
   14114            0 :    e->gai_status = getnameinfo(&sa->sa, sizeof(*sa), hname, sizeof(hname), NULL, 0, 0);
   14115              :    
   14116            0 :    if (e->gai_status) {
   14117            0 :       e->gai_strerror = gai_strerror(e->gai_status);
   14118              : 
   14119            0 :       printf("Rejecting connection from \'%s\', getnameinfo() status %d (%s)\n", hname, e->gai_status, e->gai_strerror.c_str());
   14120              : 
   14121            0 :       e->ok = false;
   14122            0 :       return e->ok;
   14123              :    }
   14124              :    
   14125            0 :    printf("connection from \"%s\"\n", hname);
   14126              :    
   14127            0 :    e->hostname = hname;
   14128              : 
   14129              :    /* always permit localhost */
   14130            0 :    if (e->hostname == "localhost.localdomain")
   14131            0 :       e->ok = true;
   14132            0 :    else if (e->hostname == "localhost")
   14133            0 :       e->ok = true;
   14134              :    else {
   14135            0 :       for (unsigned int i=0 ; i<gAllowedHosts.size() ; i++) {
   14136            0 :          if (e->hostname == gAllowedHosts[i]) {
   14137            0 :             e->ok = true;
   14138              :          }
   14139              :       }
   14140              :    }
   14141              : 
   14142            0 :    if (!e->ok) {
   14143            0 :       printf("Rejecting connection from \'%s\'\n", hname);
   14144              :    }
   14145              : 
   14146            0 :    print_hostlist_cache();
   14147              : 
   14148            0 :    return e->ok;
   14149              : }
   14150              : 
   14151              : #endif
   14152              : 
   14153            0 : static std::string mgstr(const mg_str* s)
   14154              : {
   14155            0 :    return std::string(s->p, s->len);
   14156              : }
   14157              : 
   14158            0 : static const std::string find_header_mg(const struct http_message *msg, const char* name)
   14159              : {
   14160            0 :    size_t nlen = strlen(name);
   14161            0 :    for (int i=0; i<MG_MAX_HTTP_HEADERS; i++) {
   14162            0 :       if (msg->header_names[i].len != nlen)
   14163            0 :          continue;
   14164            0 :       if (strncmp(msg->header_names[i].p, name, nlen) != 0)
   14165            0 :          continue;
   14166            0 :       return mgstr(&msg->header_values[i]);
   14167              :    }
   14168            0 :    return "";
   14169              : }
   14170              : 
   14171            0 : static const std::string find_cookie_mg(const struct http_message *msg, const char* cookie_name)
   14172              : {
   14173            0 :    const std::string cookies = find_header_mg(msg, "Cookie");
   14174            0 :    if (cookies.length() < 1)
   14175            0 :       return "";
   14176            0 :    const char* p = strstr(cookies.c_str(), cookie_name);
   14177            0 :    if (!p)
   14178            0 :       return "";
   14179            0 :    const char* v = p+strlen(cookie_name);
   14180            0 :    if (*v != '=')
   14181            0 :       return "";
   14182            0 :    v++;
   14183              :    //printf("cookie [%s] value [%s]\n", cookie_name, v);
   14184            0 :    return v;
   14185            0 : }
   14186              : 
   14187              : // Generic event handler
   14188              : 
   14189            0 : static void handle_event_mg(struct mg_connection *nc, int ev, void *ev_data)
   14190              : {
   14191            0 :    struct mbuf *io = &nc->recv_mbuf;
   14192            0 :    switch (ev) {
   14193            0 :    case MG_EV_POLL: // periodic call from loop_mg() via mg_mgr_poll()
   14194            0 :       break;
   14195            0 :    case MG_EV_ACCEPT:
   14196            0 :       if (trace_mg)
   14197            0 :          printf("handle_event_mg: nc %p, ev %d, ev_data %p -> accept\n", nc, ev, ev_data);
   14198            0 :       break;
   14199            0 :    case MG_EV_RECV:
   14200            0 :       if (trace_mg)
   14201            0 :          printf("handle_event_mg: nc %p, ev %d, ev_data %p -> recv %d, buffered %d bytes\n", nc, ev, ev_data, *(int*)ev_data, (int)io->len);
   14202              : #if 0
   14203              :       // This event handler implements simple TCP echo server
   14204              :       mg_send(nc, io->buf, io->len);  // Echo received data back
   14205              :       mbuf_remove(io, io->len);      // Discard data from recv buffer
   14206              : #endif
   14207            0 :       break;
   14208            0 :    case MG_EV_SEND:
   14209            0 :       if (trace_mg)
   14210            0 :          printf("handle_event_mg: nc %p, ev %d, ev_data %p -> send %d bytes\n", nc, ev, ev_data, *(int*)ev_data);
   14211            0 :       break;
   14212            0 :    case MG_EV_CLOSE:
   14213            0 :       if (trace_mg)
   14214            0 :          printf("handle_event_mg: nc %p, ev %d, ev_data %p -> close\n", nc, ev, ev_data);
   14215            0 :       break;
   14216            0 :    default:
   14217            0 :       if (trace_mg)
   14218            0 :          printf("handle_event_mg: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
   14219            0 :       break;
   14220              :    }
   14221            0 : }
   14222              : 
   14223            0 : void decode_cookies(Cookies *c, const http_message* msg)
   14224              : {
   14225              :    // extract password cookies
   14226              : 
   14227              :    char cookie_pwd[256]; // general access password
   14228              :    char cookie_wpwd[256]; // "write mode" password
   14229              :    char cookie_cpwd[256]; // custom page and javascript password
   14230              : 
   14231            0 :    cookie_pwd[0] = 0;
   14232            0 :    cookie_wpwd[0] = 0;
   14233            0 :    cookie_cpwd[0] = 0;
   14234              : 
   14235            0 :    std::string s = find_cookie_mg(msg, "midas_pwd");
   14236            0 :    if (s.length() > 0) {
   14237            0 :       mstrlcpy(cookie_pwd, s.c_str(), sizeof(cookie_pwd));
   14238            0 :       cookie_pwd[strcspn(cookie_pwd, " ;\r\n")] = 0;
   14239              :    }
   14240              : 
   14241            0 :    s = find_cookie_mg(msg, "midas_wpwd");
   14242            0 :    if (s.length()) {
   14243            0 :       mstrlcpy(cookie_wpwd, s.c_str(), sizeof(cookie_pwd));
   14244            0 :       cookie_wpwd[strcspn(cookie_wpwd, " ;\r\n")] = 0;
   14245              :    }
   14246              : 
   14247            0 :    s = find_cookie_mg(msg, "cpwd");
   14248            0 :    if (s.length()) {
   14249            0 :       mstrlcpy(cookie_cpwd, s.c_str(), sizeof(cookie_pwd));
   14250            0 :       cookie_cpwd[strcspn(cookie_cpwd, " ;\r\n")] = 0;
   14251              :    }
   14252              : 
   14253              :    // extract refresh rate
   14254            0 :    c->refresh = DEFAULT_REFRESH;
   14255            0 :    s = find_cookie_mg(msg, "midas_refr");
   14256            0 :    if (s.length() > 0)
   14257            0 :       c->refresh = atoi(s.c_str());
   14258              : 
   14259              :    // extract equipment expand flag
   14260              :    //c->expand_equipment = 0;
   14261              :    //s = find_cookie_mg(msg, "midas_expeq");
   14262              :    //if (s.length() > 0)
   14263              :    //   c->expand_equipment = atoi(s.c_str());
   14264              : 
   14265            0 :    c->cookie_pwd  = cookie_pwd;
   14266            0 :    c->cookie_wpwd = cookie_wpwd;
   14267            0 :    c->cookie_cpwd = cookie_cpwd;
   14268            0 : }
   14269              : 
   14270              : #define RESPONSE_SENT   1
   14271              : #define RESPONSE_QUEUED 2
   14272              : #define RESPONSE_501    3
   14273              : 
   14274            0 : static int handle_decode_get(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
   14275              : {
   14276            0 :    Cookies cookies;
   14277              : 
   14278            0 :    decode_cookies(&cookies, msg);
   14279              : 
   14280              :    // lock shared structures
   14281              : 
   14282              : #ifdef HAVE_MONGOOSE6
   14283            0 :    int status = ss_mutex_wait_for(request_mutex, 0);
   14284            0 :    assert(status == SS_SUCCESS);
   14285              : #endif
   14286              : 
   14287              :    //t->fTimeLocked = GetTimeSec();
   14288              : 
   14289              :    // prepare return buffer
   14290              : 
   14291            0 :    Return *rr = new Return();
   14292              : 
   14293            0 :    rr->zero();
   14294              : 
   14295              :    // call midas
   14296              : 
   14297            0 :    decode_get(rr, NULL, &cookies, uri, query_string, t);
   14298              : 
   14299            0 :    if (trace_mg)
   14300            0 :       printf("handle_decode_get: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
   14301              : 
   14302            0 :    t->fTimeProcessed = GetTimeSec();
   14303              : 
   14304            0 :    if (rr->return_length == -1) {
   14305            0 :       delete rr;
   14306              : #ifdef HAVE_MONGOOSE6
   14307              :       //t->fTimeUnlocked = GetTimeSec();
   14308            0 :       ss_mutex_release(request_mutex);
   14309              : #endif
   14310            0 :       return RESPONSE_501;
   14311              :    }
   14312              : 
   14313            0 :    if (rr->return_length == 0)
   14314            0 :       rr->return_length = strlen(rr->return_buffer);
   14315              : 
   14316              :    //t->fTimeUnlocked = GetTimeSec();
   14317              : 
   14318              : #ifdef HAVE_MONGOOSE6
   14319            0 :    ss_mutex_release(request_mutex);
   14320              : #endif
   14321              : 
   14322            0 :    mg_send(nc, rr->return_buffer, rr->return_length);
   14323              : 
   14324            0 :    if (!strstr(rr->return_buffer, "Content-Length")) {
   14325              :       // cannot do pipelined http if response generated by mhttpd
   14326              :       // decode_get() has no Content-Length header.
   14327              :       // must close the connection.
   14328            0 :       nc->flags |= MG_F_SEND_AND_CLOSE;
   14329              :    }
   14330              : 
   14331            0 :    t->fTimeSent = GetTimeSec();
   14332              : 
   14333            0 :    delete rr;
   14334              : 
   14335            0 :    return RESPONSE_SENT;
   14336            0 : }
   14337              : 
   14338              : #ifdef HAVE_MONGOOSE616
   14339              : 
   14340              : static uint32_t s_ncseqno = 1;
   14341              : 
   14342              : struct MongooseNcUserData
   14343              : {
   14344              :    uint32_t ncseqno = 0;
   14345              : 
   14346            0 :    MongooseNcUserData() // ctor
   14347            0 :    {
   14348            0 :       ncseqno = s_ncseqno++;
   14349              :       //printf("MongooseNcUserData::ctor! ncseqno %d\n", ncseqno);
   14350            0 :    }
   14351              : 
   14352            0 :    ~MongooseNcUserData() // ctor
   14353              :    {
   14354              :       //printf("MongooseNcUserData::dtor! ncseqno %d\n", ncseqno);
   14355            0 :    }
   14356              : };
   14357              : 
   14358            0 : static uint32_t GetNcSeqno(const mg_connection* nc)
   14359              : {
   14360            0 :    if (nc == NULL)
   14361            0 :       return 0;
   14362            0 :    if (nc->user_data == NULL)
   14363            0 :       return 0;
   14364            0 :    const MongooseNcUserData* ncud = (const MongooseNcUserData*)nc->user_data;
   14365            0 :    return ncud->ncseqno;
   14366              : }
   14367              : 
   14368              : static uint32_t s_wseqno = 1;
   14369              : 
   14370              : struct MongooseWorkObject
   14371              : {
   14372              :    uint32_t wseqno = 0;
   14373              :    mg_connection* nc = NULL;
   14374              :    uint32_t wncseqno = 0;
   14375              :    bool http_get  = false;
   14376              :    bool http_post = false;
   14377              :    bool mjsonrpc  = false;
   14378              :    Cookies cookies;
   14379              :    std::string origin;
   14380              :    std::string uri;
   14381              :    std::string query_string;
   14382              :    std::string post_body;
   14383              :    std::string post_boundary;
   14384              :    RequestTrace* t = NULL;
   14385              :    bool send_done = false;
   14386              : 
   14387            0 :    MongooseWorkObject(mg_connection* xnc) // ctor
   14388            0 :    {
   14389            0 :       wseqno = s_wseqno++;
   14390            0 :       nc = xnc;
   14391            0 :       wncseqno = GetNcSeqno(nc);
   14392              : 
   14393              :       //printf("MongooseWorkObject::ctor! wseqno %d, nc %p, wncseqno %d\n", wseqno, nc, wncseqno);
   14394            0 :    }
   14395              : 
   14396            0 :    ~MongooseWorkObject() // dtor
   14397              :    {
   14398              :       //printf("MongooseWorkObject::dtor! wseqno %d, nc %p, wncseqno %d\n", wseqno, nc, wncseqno);
   14399              : 
   14400              :       // poison pointers
   14401            0 :       nc = NULL;
   14402            0 :       t = NULL;
   14403            0 :    }
   14404              : };
   14405              : 
   14406              : struct MongooseThreadObject
   14407              : {
   14408              :    std::atomic_bool fIsRunning{false};
   14409              :    std::thread* fThread = NULL; // thread
   14410              :    void*        fNc     = NULL; // thread is attached to this network connection
   14411              :    std::mutex   fMutex;
   14412              :    std::deque<MongooseWorkObject*> fQueue;
   14413              :    std::condition_variable fNotify;
   14414              : 
   14415              :    //MongooseThreadObject() // ctor
   14416              :    //{
   14417              :    //   printf("MongooseThreadObject %p created!\n", this);
   14418              :    //}
   14419              : 
   14420              :    //~MongooseThreadObject() // dtor
   14421              :    //{
   14422              :    //   printf("MongooseThreadObject %p destroyed!\n", this);
   14423              :    //}
   14424              : };
   14425              : 
   14426              : static std::vector<MongooseThreadObject*> gMongooseThreads;
   14427              : 
   14428              : static void mongoose_thread(MongooseThreadObject*);
   14429              : 
   14430            0 : MongooseThreadObject* FindThread(void* nc)
   14431              : {
   14432              :    //printf("FindThread: nc %p, thread %s\n", nc, ss_tid_to_string(ss_gettid()).c_str());
   14433              : 
   14434            0 :    MongooseThreadObject* last_not_connected = NULL;
   14435              :    
   14436            0 :    for (auto it : gMongooseThreads) {
   14437            0 :       MongooseThreadObject* to = it;
   14438            0 :       if (to->fNc == nc) {
   14439              :          //printf("to %p, nc %p: found thread\n", to, nc);
   14440            0 :          return to;
   14441              :       }
   14442            0 :       if (to->fNc == NULL) {
   14443            0 :          last_not_connected = to;
   14444              :       }
   14445              :    }
   14446              : 
   14447            0 :    if (last_not_connected) {
   14448            0 :       MongooseThreadObject* to = last_not_connected;
   14449            0 :       to->fNc = nc;
   14450              :       //printf("to %p, nc %p: reusing thread\n", to, nc);
   14451            0 :       return to;
   14452              :    }
   14453              : 
   14454            0 :    MongooseThreadObject* to = new MongooseThreadObject();
   14455              : 
   14456            0 :    to->fNc = nc;
   14457              : 
   14458              :    //printf("to %p, nc %p: new thread\n", to, nc);
   14459              : 
   14460            0 :    gMongooseThreads.push_back(to);
   14461              : 
   14462            0 :    printf("Mongoose web server is using %d threads   \r", (int)gMongooseThreads.size());
   14463            0 :    fflush(stdout);
   14464              : 
   14465            0 :    to->fThread = new std::thread(mongoose_thread, to);
   14466              : 
   14467            0 :    return to;
   14468              : }
   14469              : 
   14470            0 : void FreeThread(void* nc)
   14471              : {
   14472              :    //printf("FreeThread, nc %p\n", nc);
   14473              : 
   14474            0 :    for (auto it : gMongooseThreads) {
   14475            0 :       MongooseThreadObject* to = it;
   14476            0 :       if (to->fNc == nc) {
   14477              :          //printf("to %p, nc %p: connection closed\n", to, nc);
   14478            0 :          to->fNc = NULL;
   14479            0 :          return;
   14480              :       }
   14481              :    }
   14482              : 
   14483              :    //printf("to %p, nc %p: connection closed, but no thread\n", nullptr, nc);
   14484              : }
   14485              : 
   14486            0 : static void mongoose_queue(mg_connection* nc, MongooseWorkObject* w)
   14487              : {
   14488            0 :    w->nc = nc;
   14489            0 :    MongooseThreadObject* to = FindThread(nc);
   14490            0 :    assert(to->fNc == nc);
   14491            0 :    to->fMutex.lock();
   14492            0 :    to->fQueue.push_back(w);
   14493            0 :    to->fMutex.unlock();
   14494            0 :    to->fNotify.notify_one();
   14495            0 : }
   14496              : 
   14497              : static void mongoose_send(mg_connection* nc, MongooseWorkObject* w, const char* p1, size_t s1, const char* p2, size_t s2, bool close_flag = false);
   14498              : 
   14499            0 : static int queue_decode_get(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
   14500              : {
   14501            0 :    MongooseWorkObject* w = new MongooseWorkObject(nc);
   14502            0 :    w->http_get = true;
   14503            0 :    decode_cookies(&w->cookies, msg);
   14504            0 :    w->uri = uri;
   14505            0 :    w->query_string = query_string;
   14506            0 :    w->t = t;
   14507              : 
   14508            0 :    mongoose_queue(nc, w);
   14509              : 
   14510            0 :    return RESPONSE_QUEUED;
   14511              : }
   14512              : 
   14513            0 : static int queue_decode_post(struct mg_connection *nc, const http_message* msg, const char* boundary, const char* uri, const char* query_string, RequestTrace* t)
   14514              : {
   14515            0 :    MongooseWorkObject* w = new MongooseWorkObject(nc);
   14516            0 :    w->http_post = true;
   14517            0 :    decode_cookies(&w->cookies, msg);
   14518            0 :    w->uri = uri;
   14519            0 :    w->query_string = query_string;
   14520            0 :    w->post_body = mgstr(&msg->body);
   14521            0 :    w->post_boundary = boundary;
   14522            0 :    w->t = t;
   14523              : 
   14524            0 :    mongoose_queue(nc, w);
   14525              : 
   14526            0 :    return RESPONSE_QUEUED;
   14527              : }
   14528              : 
   14529            0 : static int queue_mjsonrpc(struct mg_connection *nc, const std::string& origin, const std::string& post_body, RequestTrace* t)
   14530              : {
   14531            0 :    MongooseWorkObject* w = new MongooseWorkObject(nc);
   14532            0 :    w->mjsonrpc = true;
   14533            0 :    w->origin = origin;
   14534            0 :    w->post_body = post_body;
   14535            0 :    w->t = t;
   14536              : 
   14537            0 :    mongoose_queue(nc, w);
   14538              : 
   14539            0 :    return RESPONSE_QUEUED;
   14540              : }
   14541              : 
   14542            0 : static int thread_http_get(mg_connection *nc, MongooseWorkObject *w)
   14543              : {
   14544              :    // lock shared structures
   14545              : 
   14546              :    //int status = ss_mutex_wait_for(request_mutex, 0);
   14547              :    //assert(status == SS_SUCCESS);
   14548              : 
   14549              :    //w->t->fTimeLocked = GetTimeSec();
   14550              : 
   14551              :    // prepare return buffer
   14552              : 
   14553            0 :    Return *rr = new Return();
   14554              : 
   14555            0 :    rr->zero();
   14556              : 
   14557              :    // call midas
   14558              : 
   14559            0 :    decode_get(rr, NULL, &w->cookies, w->uri.c_str(), w->query_string.c_str(), w->t);
   14560              : 
   14561            0 :    if (trace_mg)
   14562            0 :       printf("handle_decode_get: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
   14563              : 
   14564            0 :    w->t->fTimeProcessed = GetTimeSec();
   14565              : 
   14566            0 :    if (rr->return_length == -1) {
   14567            0 :       delete rr;
   14568              :       //w->t->fTimeUnlocked = GetTimeSec();
   14569              :       //ss_mutex_release(request_mutex);
   14570            0 :       return RESPONSE_501;
   14571              :    }
   14572              : 
   14573            0 :    if (rr->return_length == 0)
   14574            0 :       rr->return_length = strlen(rr->return_buffer);
   14575              : 
   14576              :    //w->t->fTimeUnlocked = GetTimeSec();
   14577              : 
   14578              :    //ss_mutex_release(request_mutex);
   14579              : 
   14580            0 :    bool close_flag = false;
   14581              : 
   14582            0 :    if (!strstr(rr->return_buffer, "Content-Length")) {
   14583              :       // cannot do pipelined http if response generated by mhttpd
   14584              :       // decode_get() has no Content-Length header.
   14585              :       // must close the connection.
   14586            0 :       close_flag = true;
   14587              :    }
   14588              : 
   14589            0 :    mongoose_send(nc, w, rr->return_buffer, rr->return_length, NULL, 0, close_flag);
   14590              : 
   14591            0 :    w->t->fTimeSent = GetTimeSec();
   14592              : 
   14593            0 :    delete rr;
   14594              : 
   14595            0 :    return RESPONSE_SENT;
   14596              : }
   14597              : 
   14598            0 : static int thread_http_post(mg_connection *nc, MongooseWorkObject *w)
   14599              : {
   14600            0 :    const char* post_data = w->post_body.c_str();
   14601            0 :    int post_data_len = w->post_body.length();
   14602              : 
   14603              :    // lock shared strctures
   14604              : 
   14605              :    //int status = ss_mutex_wait_for(request_mutex, 0);
   14606              :    //assert(status == SS_SUCCESS);
   14607              : 
   14608              :    // prepare return buffer
   14609              : 
   14610            0 :    Return* rr = new Return;
   14611              : 
   14612            0 :    rr->zero();
   14613              : 
   14614              :    //printf("post_data_len %d, data [%s], boundary [%s]\n", post_data_len, post_data, boundary);
   14615              : 
   14616            0 :    decode_post(rr, NULL, (char*)post_data, w->post_boundary.c_str(), post_data_len, &w->cookies, w->uri.c_str(), w->t);
   14617              : 
   14618            0 :    if (trace_mg)
   14619            0 :       printf("handle_decode_post: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
   14620              : 
   14621            0 :    if (rr->return_length == -1) {
   14622              :       //ss_mutex_release(request_mutex);
   14623            0 :       delete rr;
   14624            0 :       return RESPONSE_501;
   14625              :    }
   14626              : 
   14627            0 :    if (rr->return_length == 0)
   14628            0 :       rr->return_length = strlen(rr->return_buffer);
   14629              : 
   14630              :    //ss_mutex_release(request_mutex);
   14631              : 
   14632            0 :    bool close_flag = false;
   14633            0 :    if (!strstr(rr->return_buffer, "Content-Length")) {
   14634              :       // cannot do pipelined http if response generated by mhttpd
   14635              :       // decode_get() has no Content-Length header.
   14636              :       // must close the connection.
   14637            0 :       close_flag = true;
   14638              :    }
   14639              : 
   14640            0 :    mongoose_send(nc, w, rr->return_buffer, rr->return_length, NULL, 0, close_flag);
   14641              : 
   14642            0 :    delete rr;
   14643              : 
   14644            0 :    return RESPONSE_SENT;
   14645              : 
   14646              : }
   14647              : 
   14648            0 : static int thread_mjsonrpc(mg_connection *nc, MongooseWorkObject *w)
   14649              : {
   14650            0 :    w->t->fRPC = w->post_body;
   14651              : 
   14652              :    //int status = ss_mutex_wait_for(request_mutex, 0);
   14653              :    //assert(status == SS_SUCCESS);
   14654              : 
   14655              :    //gMutex.lock();
   14656              :    //w->t->fTimeLocked = GetTimeSec();
   14657              : 
   14658            0 :    MJsonNode* reply = mjsonrpc_decode_post_data(w->post_body.c_str());
   14659              : 
   14660              :    //w->t->fTimeUnlocked = GetTimeSec();
   14661              :    //gMutex.unlock();
   14662              :    
   14663              :    //ss_mutex_release(request_mutex);
   14664              : 
   14665            0 :    if (reply->GetType() == MJSON_ARRAYBUFFER) {
   14666              :       const char* ptr;
   14667              :       size_t size;
   14668            0 :       reply->GetArrayBuffer(&ptr, &size);
   14669              :       
   14670            0 :       std::string headers;
   14671            0 :       headers += "HTTP/1.1 200 OK\n";
   14672            0 :       if (w->origin.length() > 0)
   14673            0 :          headers += "Access-Control-Allow-Origin: " + w->origin + "\n";
   14674              :       else
   14675            0 :          headers += "Access-Control-Allow-Origin: *\n";
   14676            0 :       headers += "Access-Control-Allow-Credentials: true\n";
   14677            0 :       headers += "Content-Length: " + toString(size) + "\n";
   14678            0 :       headers += "Content-Type: application/octet-stream\n";
   14679              :       //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   14680              :       
   14681              :       //printf("sending headers: %s\n", headers.c_str());
   14682              :       //printf("sending reply: %s\n", reply_string.c_str());
   14683              :       
   14684            0 :       std::string send = headers + "\n";
   14685              :       
   14686            0 :       w->t->fTimeProcessed = GetTimeSec();
   14687              :       
   14688            0 :       mongoose_send(nc, w, send.c_str(), send.length(), ptr, size);
   14689              :       
   14690            0 :       w->t->fTimeSent = GetTimeSec();
   14691              :       
   14692            0 :       delete reply;
   14693              :       
   14694            0 :       return RESPONSE_SENT;
   14695            0 :    }
   14696              :    
   14697            0 :    std::string reply_string = reply->Stringify();
   14698            0 :    int reply_length = reply_string.length();
   14699              :    
   14700            0 :    std::string headers;
   14701            0 :    headers += "HTTP/1.1 200 OK\n";
   14702            0 :    if (w->origin.length() > 0)
   14703            0 :       headers += "Access-Control-Allow-Origin: " + w->origin + "\n";
   14704              :    else
   14705            0 :       headers += "Access-Control-Allow-Origin: *\n";
   14706            0 :    headers += "Access-Control-Allow-Credentials: true\n";
   14707            0 :    headers += "Content-Length: " + toString(reply_length) + "\n";
   14708            0 :    headers += "Content-Type: application/json\n";
   14709              :    //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   14710              : 
   14711            0 :    if (trace_mg_verbose) {
   14712            0 :       printf("-----------------------\nSending headers: %s", headers.c_str());
   14713            0 :       std::string r = reply_string.substr(0, 128);
   14714            0 :       printf("-----------------------\nSending reply (%d bytes): %s\n\n\n", (int)reply_string.size(), r.c_str());
   14715            0 :    }
   14716              : 
   14717            0 :    std::string send = headers + "\n" + reply_string;
   14718              :    
   14719            0 :    w->t->fTimeProcessed = GetTimeSec();
   14720              :    
   14721            0 :    mongoose_send(nc, w, send.c_str(), send.length(), NULL, 0);
   14722              :    
   14723            0 :    w->t->fTimeSent = GetTimeSec();
   14724              :    
   14725            0 :    delete reply;
   14726              :    
   14727            0 :    return RESPONSE_SENT;
   14728            0 : }
   14729              : 
   14730            0 : static int thread_work_function(mg_connection *nc, MongooseWorkObject *w)
   14731              : {
   14732            0 :    if (w->http_get)
   14733            0 :       return thread_http_get(nc, w);
   14734            0 :    else if (w->http_post)
   14735            0 :       return thread_http_post(nc, w);
   14736            0 :    else if (w->mjsonrpc)
   14737            0 :       return thread_mjsonrpc(nc, w);
   14738              :    else
   14739            0 :       return RESPONSE_501;
   14740              : }
   14741              : 
   14742              : #endif
   14743              : 
   14744            0 : static int handle_decode_post(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
   14745              : {
   14746              : 
   14747              :    char boundary[256];
   14748            0 :    boundary[0] = 0;
   14749            0 :    const std::string ct = find_header_mg(msg, "Content-Type");
   14750            0 :    if (ct.length() > 0) {
   14751            0 :       const char* s = strstr(ct.c_str(), "boundary=");
   14752            0 :       if (s)
   14753            0 :          mstrlcpy(boundary, s+9, sizeof(boundary));
   14754              :    }
   14755              : 
   14756              : #ifdef HAVE_MONGOOSE616
   14757            0 :    if (multithread_mg)
   14758            0 :       return queue_decode_post(nc, msg, boundary, uri, query_string, t);
   14759              : #endif
   14760              : 
   14761            0 :    Cookies cookies;
   14762              : 
   14763            0 :    decode_cookies(&cookies, msg);
   14764              : 
   14765            0 :    const char* post_data = msg->body.p;
   14766            0 :    int post_data_len = msg->body.len;
   14767              : 
   14768              :    // lock shared strctures
   14769              : 
   14770              : #ifdef HAVE_MONGOOSE6
   14771            0 :    int status = ss_mutex_wait_for(request_mutex, 0);
   14772            0 :    assert(status == SS_SUCCESS);
   14773              : #endif
   14774              : 
   14775              :    // prepare return buffer
   14776              : 
   14777            0 :    Return* rr = new Return;
   14778              : 
   14779            0 :    rr->zero();
   14780              : 
   14781              :    //printf("post_data_len %d, data [%s], boundary [%s]\n", post_data_len, post_data, boundary);
   14782              : 
   14783            0 :    decode_post(rr, NULL, (char*)post_data, boundary, post_data_len, &cookies, uri, t);
   14784              : 
   14785            0 :    if (trace_mg)
   14786            0 :       printf("handle_decode_post: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
   14787              : 
   14788            0 :    if (rr->return_length == -1) {
   14789              : #ifdef HAVE_MONGOOSE6
   14790            0 :       ss_mutex_release(request_mutex);
   14791              : #endif
   14792            0 :       delete rr;
   14793            0 :       return RESPONSE_501;
   14794              :    }
   14795              : 
   14796            0 :    if (rr->return_length == 0)
   14797            0 :       rr->return_length = strlen(rr->return_buffer);
   14798              : 
   14799              : #ifdef HAVE_MONGOOSE6
   14800            0 :    ss_mutex_release(request_mutex);
   14801              : #endif
   14802              : 
   14803            0 :    mg_send(nc, rr->return_buffer, rr->return_length);
   14804              : 
   14805            0 :    if (!strstr(rr->return_buffer, "Content-Length")) {
   14806              :       // cannot do pipelined http if response generated by mhttpd
   14807              :       // decode_get() has no Content-Length header.
   14808              :       // must close the connection.
   14809            0 :       nc->flags |= MG_F_SEND_AND_CLOSE;
   14810              :    }
   14811              : 
   14812            0 :    delete rr;
   14813              : 
   14814            0 :    return RESPONSE_SENT;
   14815            0 : }
   14816              : 
   14817            0 : static int handle_http_get(struct mg_connection *nc, const http_message* msg, const char* uri, RequestTrace* t)
   14818              : {
   14819            0 :    std::string query_string = mgstr(&msg->query_string);
   14820              : 
   14821            0 :    if (trace_mg||verbose_mg)
   14822            0 :       printf("handle_http_get: uri [%s], query [%s]\n", uri, query_string.c_str());
   14823              : 
   14824            0 :    if (query_string == "mjsonrpc_schema") {
   14825            0 :       MJsonNode* s = mjsonrpc_get_schema();
   14826            0 :       std::string reply = s->Stringify();
   14827            0 :       delete s;
   14828              : 
   14829            0 :       int reply_length = reply.length();
   14830              : 
   14831            0 :       const std::string origin_header = find_header_mg(msg, "Origin");
   14832              : 
   14833            0 :       std::string headers;
   14834            0 :       headers += "HTTP/1.1 200 OK\n";
   14835            0 :       if (origin_header.length() > 0)
   14836            0 :          headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
   14837              :       else
   14838            0 :          headers += "Access-Control-Allow-Origin: *\n";
   14839            0 :       headers += "Access-Control-Allow-Credentials: true\n";
   14840            0 :       headers += "Content-Length: " + toString(reply_length) + "\n";
   14841            0 :       headers += "Content-Type: application/json\n";
   14842              :       //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   14843              : 
   14844              :       //printf("sending headers: %s\n", headers.c_str());
   14845              :       //printf("sending reply: %s\n", reply.c_str());
   14846              : 
   14847            0 :       std::string send = headers + "\n" + reply;
   14848              : 
   14849            0 :       t->fTimeProcessed = GetTimeSec();
   14850              : 
   14851            0 :       mg_send(nc, send.c_str(), send.length());
   14852              : 
   14853            0 :       t->fTimeSent = GetTimeSec();
   14854              : 
   14855            0 :       return RESPONSE_SENT;
   14856            0 :    }
   14857              : 
   14858            0 :    if (query_string == "mjsonrpc_schema_text") {
   14859            0 :       MJsonNode* s = mjsonrpc_get_schema();
   14860            0 :       std::string reply = mjsonrpc_schema_to_text(s);
   14861            0 :       delete s;
   14862              : 
   14863            0 :       int reply_length = reply.length();
   14864              : 
   14865            0 :       const std::string origin_header = find_header_mg(msg, "Origin");
   14866              : 
   14867            0 :       std::string headers;
   14868            0 :       headers += "HTTP/1.1 200 OK\n";
   14869            0 :       if (origin_header.length() > 0)
   14870            0 :          headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
   14871              :       else
   14872            0 :          headers += "Access-Control-Allow-Origin: *\n";
   14873            0 :       headers += "Access-Control-Allow-Credentials: true\n";
   14874            0 :       headers += "Content-Length: " + toString(reply_length) + "\n";
   14875            0 :       headers += "Content-Type: text/plain\n";
   14876              :       //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   14877              : 
   14878              :       //printf("sending headers: %s\n", headers.c_str());
   14879              :       //printf("sending reply: %s\n", reply.c_str());
   14880              : 
   14881            0 :       std::string send = headers + "\n" + reply;
   14882              : 
   14883            0 :       t->fTimeProcessed = GetTimeSec();
   14884              : 
   14885            0 :       mg_send(nc, send.c_str(), send.length());
   14886              : 
   14887            0 :       t->fTimeSent = GetTimeSec();
   14888              : 
   14889            0 :       return RESPONSE_SENT;
   14890            0 :    }
   14891              : 
   14892              : #ifdef HAVE_MONGOOSE616
   14893            0 :    if (multithread_mg)
   14894            0 :       return queue_decode_get(nc, msg, uri, query_string.c_str(), t);
   14895              : #endif
   14896              : 
   14897            0 :    return handle_decode_get(nc, msg, uri, query_string.c_str(), t);
   14898            0 : }
   14899              : 
   14900            0 : static int handle_http_post(struct mg_connection *nc, const http_message* msg, const char* uri, RequestTrace* t)
   14901              : {
   14902            0 :    std::string query_string = mgstr(&msg->query_string);
   14903            0 :    std::string post_data = mgstr(&msg->body);
   14904              : 
   14905            0 :    if (trace_mg||verbose_mg)
   14906            0 :       printf("handle_http_post: uri [%s], query [%s], post data %d bytes\n", uri, query_string.c_str(), (int)post_data.length());
   14907            0 :    if (trace_mg_verbose)
   14908            0 :       printf("handle_http_post: post data = \n%s\n", post_data.c_str());
   14909              : 
   14910            0 :    if (query_string.substr(0, 8) == "mjsonrpc") { // ignore any parameter after "mjsonrpc"
   14911            0 :       const std::string origin_header = find_header_mg(msg, "Origin");
   14912            0 :       const std::string ctype_header = find_header_mg(msg, "Content-Type");
   14913              : 
   14914            0 :       if (strstr(ctype_header.c_str(), "application/json") == NULL) {
   14915            0 :          std::string headers;
   14916            0 :          headers += "HTTP/1.1 415 Unsupported Media Type\n";
   14917              :          //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   14918              : 
   14919              :          //printf("sending headers: %s\n", headers.c_str());
   14920              :          //printf("sending reply: %s\n", reply.c_str());
   14921              : 
   14922            0 :          if (trace_mg_verbose)
   14923            0 :             printf("handle_http_post: unsupported media type \"%s\"\n", ctype_header.c_str());
   14924              : 
   14925            0 :          std::string send = headers + "\n";
   14926              : 
   14927            0 :          t->fTimeProcessed = GetTimeSec();
   14928              : 
   14929            0 :          mg_send(nc, send.c_str(), send.length());
   14930              : 
   14931            0 :          t->fTimeSent = GetTimeSec();
   14932              : 
   14933            0 :          return RESPONSE_SENT;
   14934            0 :       }
   14935              : 
   14936              : #ifdef HAVE_MONGOOSE616
   14937            0 :       if (multithread_mg)
   14938            0 :          return queue_mjsonrpc(nc, origin_header, post_data, t);
   14939              : #endif
   14940              : 
   14941              :       //printf("post body: %s\n", post_data.c_str());
   14942              : 
   14943            0 :       t->fRPC = post_data;
   14944              : 
   14945              : #ifdef HAVE_MONGOOSE6
   14946            0 :       int status = ss_mutex_wait_for(request_mutex, 0);
   14947            0 :       assert(status == SS_SUCCESS);
   14948              : #endif
   14949              : 
   14950              :       //t->fTimeLocked = GetTimeSec();
   14951              : 
   14952            0 :       MJsonNode* reply = mjsonrpc_decode_post_data(post_data.c_str());
   14953              : 
   14954              :       //t->fTimeUnlocked = GetTimeSec();
   14955              :       
   14956              : #ifdef HAVE_MONGOOSE6
   14957            0 :       ss_mutex_release(request_mutex);
   14958              : #endif
   14959              : 
   14960            0 :       if (reply->GetType() == MJSON_ARRAYBUFFER) {
   14961              :          const char* ptr;
   14962              :          size_t size;
   14963            0 :          reply->GetArrayBuffer(&ptr, &size);
   14964              : 
   14965            0 :          std::string headers;
   14966            0 :          headers += "HTTP/1.1 200 OK\n";
   14967            0 :          if (origin_header.length() > 0)
   14968            0 :             headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
   14969              :          else
   14970            0 :             headers += "Access-Control-Allow-Origin: *\n";
   14971            0 :          headers += "Access-Control-Allow-Credentials: true\n";
   14972            0 :          headers += "Content-Length: " + toString(size) + "\n";
   14973            0 :          headers += "Content-Type: application/octet-stream\n";
   14974              :          //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   14975              : 
   14976              :          //printf("sending headers: %s\n", headers.c_str());
   14977              :          //printf("sending reply: %s\n", reply_string.c_str());
   14978              : 
   14979            0 :          std::string send = headers + "\n";
   14980              : 
   14981            0 :          t->fTimeProcessed = GetTimeSec();
   14982              : 
   14983            0 :          mg_send(nc, send.c_str(), send.length());
   14984            0 :          mg_send(nc, ptr, size);
   14985              : 
   14986            0 :          t->fTimeSent = GetTimeSec();
   14987              : 
   14988            0 :          delete reply;
   14989              : 
   14990            0 :          return RESPONSE_SENT;
   14991            0 :       }
   14992              : 
   14993            0 :       std::string reply_string = reply->Stringify();
   14994            0 :       int reply_length = reply_string.length();
   14995              : 
   14996            0 :       std::string headers;
   14997            0 :       headers += "HTTP/1.1 200 OK\n";
   14998            0 :       if (origin_header.length() > 0)
   14999            0 :          headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
   15000              :       else
   15001            0 :          headers += "Access-Control-Allow-Origin: *\n";
   15002            0 :       headers += "Access-Control-Allow-Credentials: true\n";
   15003            0 :       headers += "Content-Length: " + toString(reply_length) + "\n";
   15004            0 :       headers += "Content-Type: application/json\n";
   15005              :       //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   15006              : 
   15007              :       //printf("sending headers: %s\n", headers.c_str());
   15008              :       //printf("sending reply: %s\n", reply_string.c_str());
   15009              : 
   15010            0 :       std::string send = headers + "\n" + reply_string;
   15011              : 
   15012            0 :       t->fTimeProcessed = GetTimeSec();
   15013              : 
   15014            0 :       mg_send(nc, send.c_str(), send.length());
   15015              : 
   15016            0 :       t->fTimeSent = GetTimeSec();
   15017              : 
   15018            0 :       delete reply;
   15019              : 
   15020            0 :       return RESPONSE_SENT;
   15021            0 :    }
   15022              : 
   15023            0 :    return handle_decode_post(nc, msg, uri, query_string.c_str(), t);
   15024            0 : }
   15025              : 
   15026            0 : static void handle_http_options_cors(struct mg_connection *nc, const http_message* msg, RequestTrace* t)
   15027              : {
   15028              :    //
   15029              :    // JSON-RPC CORS pre-flight request, see
   15030              :    // https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
   15031              :    //
   15032              :    // OPTIONS /resources/post-here/ HTTP/1.1
   15033              :    // Host: bar.other
   15034              :    // User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
   15035              :    // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
   15036              :    // Accept-Language: en-us,en;q=0.5
   15037              :    // Accept-Encoding: gzip,deflate
   15038              :    // Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
   15039              :    // Connection: keep-alive
   15040              :    // Origin: http://foo.example
   15041              :    // Access-Control-Request-Method: POST
   15042              :    // Access-Control-Request-Headers: X-PINGOTHER
   15043              :    //
   15044              :    // HTTP/1.1 200 OK
   15045              :    // Date: Mon, 01 Dec 2008 01:15:39 GMT
   15046              :    // Server: Apache/2.0.61 (Unix)
   15047              :    // Access-Control-Allow-Origin: http://foo.example
   15048              :    // Access-Control-Allow-Methods: POST, GET, OPTIONS
   15049              :    // Access-Control-Allow-Headers: X-PINGOTHER
   15050              :    // Access-Control-Max-Age: 1728000
   15051              :    // Vary: Accept-Encoding, Origin
   15052              :    // Content-Encoding: gzip
   15053              :    // Content-Length: 0
   15054              :    // Keep-Alive: timeout=2, max=100
   15055              :    // Connection: Keep-Alive
   15056              :    // Content-Type: text/plain
   15057              :    //
   15058              : 
   15059            0 :    const std::string origin_header = find_header_mg(msg, "Origin");
   15060              : 
   15061            0 :    if (trace_mg||verbose_mg)
   15062            0 :       printf("handle_http_options_cors: origin [%s]\n", origin_header.c_str());
   15063              : 
   15064            0 :    std::string headers;
   15065            0 :    headers += "HTTP/1.1 200 OK\n";
   15066              :    //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
   15067            0 :    if (origin_header.length() > 0)
   15068            0 :       headers += "Access-Control-Allow-Origin: " + origin_header + "\n";
   15069              :    else
   15070            0 :       headers += "Access-Control-Allow-Origin: *\n";
   15071            0 :    headers += "Access-Control-Allow-Headers: Content-Type\n";
   15072            0 :    headers += "Access-Control-Allow-Credentials: true\n";
   15073            0 :    headers += "Access-Control-Max-Age: 120\n";
   15074            0 :    headers += "Content-Length: 0\n";
   15075            0 :    headers += "Content-Type: text/plain\n";
   15076              :    //printf("sending headers: %s\n", headers.c_str());
   15077              :    //printf("sending reply: %s\n", reply.c_str());
   15078              : 
   15079            0 :    std::string send = headers + "\n";
   15080              : 
   15081            0 :    t->fTimeProcessed = GetTimeSec();
   15082              : 
   15083            0 :    mg_send(nc, send.c_str(), send.length());
   15084              : 
   15085            0 :    t->fTimeSent = GetTimeSec();
   15086            0 : }
   15087              : 
   15088              : // HTTP event handler
   15089              : 
   15090              : static bool mongoose_passwords_enabled(const struct mg_connection *nc);
   15091              : 
   15092              : #ifdef HAVE_MONGOOSE616
   15093              : static MVOdb* gProxyOdb = NULL;
   15094              : #endif
   15095              : 
   15096            0 : static void handle_http_message(struct mg_connection *nc, http_message* msg)
   15097              : {
   15098            0 :    std::string method = mgstr(&msg->method);
   15099            0 :    std::string query_string = mgstr(&msg->query_string);
   15100            0 :    std::string uri_encoded = mgstr(&msg->uri);
   15101            0 :    std::string uri = UrlDecode(uri_encoded.c_str());
   15102              : 
   15103            0 :    if (trace_mg)
   15104            0 :       printf("handle_http_message: method [%s] uri [%s] proto [%s]\n", method.c_str(), uri.c_str(), mgstr(&msg->proto).c_str());
   15105              : 
   15106            0 :    RequestTrace* t = new RequestTrace;
   15107            0 :    t->fTimeReceived = GetTimeSec();
   15108            0 :    t->fMethod = method;
   15109            0 :    t->fUri = uri;
   15110            0 :    t->fQuery = query_string;
   15111              : 
   15112              :    // process OPTIONS for Cross-origin (CORS) preflight request
   15113              :    // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
   15114            0 :    if (method == "OPTIONS" && query_string == "mjsonrpc" && mg_get_http_header(msg, "Access-Control-Request-Method") != NULL) {
   15115            0 :       handle_http_options_cors(nc, msg, t);
   15116            0 :       t->fCompleted = true;
   15117            0 :       gTraceBuf->AddTraceMTS(t);
   15118            0 :       return;
   15119              :    }
   15120              : 
   15121            0 :    if (gAuthMg && mongoose_passwords_enabled(nc)) {
   15122            0 :       std::string username = check_digest_auth(msg, gAuthMg);
   15123              : 
   15124              :       // Cannot re-read the password file - it is not thread safe to do so
   15125              :       // unless I lock gAuthMg for each call check_digest_auth() and if I do so,
   15126              :       // I will serialize (single-thread) all the http requests and defeat
   15127              :       // the whole point of multithreading the web server. K.O.
   15128              :       //
   15129              :       //// if auth failed, reread password file - maybe user added or password changed
   15130              :       //if (username.length() < 1) {
   15131              :       //   bool ok = read_passwords(&gAuthMg);
   15132              :       //   if (ok)
   15133              :       //      username = check_digest_auth(msg, &gAuthMg);
   15134              :       //}
   15135              : 
   15136            0 :       if (trace_mg)
   15137            0 :          printf("handle_http_message: auth user: \"%s\"\n", username.c_str());
   15138              : 
   15139            0 :       if (username.length() == 0) {
   15140            0 :          if (trace_mg||verbose_mg)
   15141            0 :             printf("handle_http_message: method [%s] uri [%s] query [%s] proto [%s], sending auth request for realm \"%s\"\n", method.c_str(), uri.c_str(), query_string.c_str(), mgstr(&msg->proto).c_str(), gAuthMg->realm.c_str());
   15142              : 
   15143            0 :          xmg_http_send_digest_auth_request(nc, gAuthMg->realm.c_str());
   15144            0 :          t->fCompleted = true;
   15145            0 :          gTraceBuf->AddTraceMTS(t);
   15146            0 :          return;
   15147              :       }
   15148            0 :       t->fAuthOk = true;
   15149            0 :    } else {
   15150            0 :       t->fAuthOk = true;
   15151              :    }
   15152              : 
   15153              : #ifdef HAVE_MONGOOSE616
   15154            0 :    if (gProxyOdb && starts_with(uri, "/proxy/")) {
   15155            0 :       std::string::size_type p1 = uri.find("/", 1);
   15156            0 :       if (p1 == uri.length()-1) {
   15157            0 :          std::string response = "404 Not Found (Proxy name is missing)";
   15158            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15159            0 :          mg_send(nc, response.c_str(), response.length());
   15160            0 :          delete t;
   15161            0 :          return;
   15162            0 :       }
   15163            0 :       std::string::size_type p2 = uri.find("/", p1+1);
   15164            0 :       if (p2 == std::string::npos) {
   15165            0 :          std::string response = "404 Not Found (Proxy URL should end with a slash)";
   15166            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15167            0 :          mg_send(nc, response.c_str(), response.length());
   15168            0 :          delete t;
   15169            0 :          return;
   15170            0 :       }
   15171            0 :       std::string p = uri.substr(p1+1, p2-p1-1);
   15172              :       //printf("uri [%s], p1: %d, p2: %d, substr: [%s]\n", uri.c_str(), (int)p1, (int)p2, p.c_str());
   15173            0 :       if (p.length() < 1) {
   15174            0 :          std::string response = "404 Not Found (Double-slash or Proxy name is too short)";
   15175            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15176            0 :          mg_send(nc, response.c_str(), response.length());
   15177            0 :          delete t;
   15178            0 :          return;
   15179            0 :       }
   15180            0 :       std::string destination;
   15181            0 :       gProxyOdb->RS(p.c_str(), &destination);
   15182            0 :       if (destination.length() < 1) {
   15183            0 :          std::string response = "404 Not Found (Proxy not found in ODB)";
   15184            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15185            0 :          mg_send(nc, response.c_str(), response.length());
   15186            0 :          delete t;
   15187            0 :          return;
   15188            0 :       } else if (destination[0] == '#') {
   15189            0 :          std::string response = "404 Not Found (Proxy commented-out in ODB)";
   15190            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15191            0 :          mg_send(nc, response.c_str(), response.length());
   15192            0 :          delete t;
   15193            0 :          return;
   15194            0 :       } else if (ends_with_char(destination, '/')) {
   15195            0 :          std::string response = "404 Not Found (Proxy address should not end with a slash)";
   15196            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15197            0 :          mg_send(nc, response.c_str(), response.length());
   15198            0 :          delete t;
   15199            0 :          return;
   15200            0 :       } else if (!starts_with(destination, "http")) {
   15201            0 :          std::string response = "404 Not Found (Proxy address does not start with http";
   15202            0 :          mg_send_head(nc, 404, response.length(), NULL);
   15203            0 :          mg_send(nc, response.c_str(), response.length());
   15204            0 :          delete t;
   15205            0 :          return;
   15206            0 :       } else {
   15207            0 :          std::string m;
   15208            0 :          m += "/proxy";
   15209            0 :          m += "/";
   15210            0 :          m += p;
   15211            0 :          mg_str mount = mg_mk_str(m.c_str());
   15212            0 :          mg_str upstream = mg_mk_str(destination.c_str());
   15213            0 :          if (verbose_mg||trace_mg) {
   15214            0 :             printf("proxy: uri [%s] mount [%s] upstream [%s]\n", uri.c_str(), mgstr(&mount).c_str(), mgstr(&upstream).c_str());
   15215              :          }
   15216            0 :          mg_http_reverse_proxy(nc, msg, mount, upstream);
   15217            0 :          delete t;
   15218            0 :          return;
   15219            0 :       }
   15220            0 :    }
   15221              : #endif
   15222              : 
   15223            0 :    int response = RESPONSE_501;
   15224              : 
   15225            0 :    if (method == "GET")
   15226            0 :       response = handle_http_get(nc, msg, uri.c_str(), t);
   15227            0 :    else if (method == "POST")
   15228            0 :       response = handle_http_post(nc, msg, uri.c_str(), t);
   15229              : 
   15230            0 :    if (response == RESPONSE_501) {
   15231            0 :       if (trace_mg||verbose_mg)
   15232            0 :          printf("handle_http_message: sending 501 Not Implemented error\n");
   15233              : 
   15234            0 :       std::string response = "501 Not Implemented";
   15235            0 :       mg_send_head(nc, 501, response.length(), NULL); // 501 Not Implemented
   15236            0 :       mg_send(nc, response.c_str(), response.length());
   15237            0 :    }
   15238              : 
   15239            0 :    if (response != RESPONSE_QUEUED) {
   15240            0 :       t->fCompleted = true;
   15241            0 :       gTraceBuf->AddTraceMTS(t);
   15242              :    }
   15243            0 : }
   15244              : 
   15245              : #ifdef HAVE_MONGOOSE6
   15246              : 
   15247            0 : static void handle_http_event_mg(struct mg_connection *nc, int ev, void *ev_data)
   15248              : {
   15249            0 :    switch (ev) {
   15250            0 :    case MG_EV_HTTP_REQUEST:
   15251            0 :       if (trace_mg)
   15252            0 :          printf("handle_http_event_mg: nc %p, ev %d, ev_data %p -> http request\n", nc, ev, ev_data);
   15253            0 :       handle_http_message(nc, (http_message*)ev_data);
   15254            0 :       break;
   15255            0 :    default:
   15256            0 :       if (trace_mg)
   15257            0 :          printf("handle_http_event_mg: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
   15258            0 :       break;
   15259              :    }
   15260            0 : }
   15261              : 
   15262            0 : static void handle_http_redirect(struct mg_connection *nc, int ev, void *ev_data)
   15263              : {
   15264            0 :    switch (ev) {
   15265            0 :    case MG_EV_HTTP_REQUEST:
   15266              :       {
   15267            0 :          http_message* msg = (http_message*)ev_data;
   15268            0 :          if (trace_mg)
   15269            0 :             printf("handle_http_redirect: nc %p, ev %d, ev_data %p -> http request\n", nc, ev, ev_data);
   15270              : 
   15271            0 :          mg_printf(nc, "HTTP/1.1 302 Found\r\nLocation: https://%s%s\r\n\r\n",
   15272            0 :                    ((std::string*)(nc->user_data))->c_str(),
   15273            0 :                    mgstr(&msg->uri).c_str());
   15274            0 :          nc->flags |= MG_F_SEND_AND_CLOSE;
   15275              :       }
   15276            0 :       break;
   15277            0 :    default:
   15278            0 :       if (trace_mg)
   15279            0 :          printf("handle_http_redirect: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
   15280              :    }
   15281            0 : }
   15282              : 
   15283              : #endif
   15284              : 
   15285              : #ifdef HAVE_MONGOOSE616
   15286              : 
   15287              : // from mongoose examples/multithreaded/multithreaded.c
   15288              : 
   15289              : //static sock_t s_sock[2];
   15290              : static std::atomic_bool s_shutdown{false};
   15291              : static struct mg_mgr s_mgr;
   15292              : static std::atomic_int s_rseqno{1};
   15293              : static std::mutex s_mg_broadcast_mutex;
   15294              : 
   15295              : #if 0
   15296              : // This info is passed to the worker thread
   15297              : struct work_request {
   15298              :    void* nc;
   15299              :    MongooseWorkObject* w;
   15300              : };
   15301              : #endif
   15302              : 
   15303              : // This info is passed by the worker thread to mg_broadcast
   15304              : struct work_result {
   15305              :    mg_connection* nc = NULL;
   15306              :    uint32_t check = 0x12345678;
   15307              :    int rseqno = 0;
   15308              :    MongooseWorkObject* w = NULL;
   15309              :    const char* p1 = NULL;
   15310              :    size_t s1 = 0;
   15311              :    const char* p2 = NULL;
   15312              :    size_t s2 = 0;
   15313              :    bool close_flag = false;
   15314              :    bool send_501 = false;
   15315              : };
   15316              : 
   15317              : #if 0
   15318              : static void mongoose_queue(void *nc, MongooseWorkObject *w)
   15319              : {
   15320              :    struct work_request req = {nc, w};
   15321              : 
   15322              :    //printf("nc: %p: wseqno: %d, queue work object!\n", nc, w->wseqno);
   15323              :          
   15324              :    if (write(s_sock[0], &req, sizeof(req)) < 0) {
   15325              :       fprintf(stderr, "mongoose_queue: Error: write(s_sock(0)) error %d (%s)\n", errno, strerror(errno));
   15326              :       abort();
   15327              :    }
   15328              : }
   15329              : #endif
   15330              : 
   15331            0 : static void on_work_complete(struct mg_connection *nc, int ev, void *ev_data)
   15332              : {
   15333              :    (void) ev;
   15334            0 :    struct work_result *res = (struct work_result *)ev_data;
   15335              : 
   15336            0 :    assert(res != NULL);
   15337            0 :    assert(res->w != NULL);
   15338              : 
   15339              :    //printf("nc: %p, ncseqno: %d, wseqno: %d, rseqno: %d, check 0x%08x, offered nc %p ncseqno %d, flags 0x%08x\n", res->nc, GetNcSeqno(res->nc), res->w->wseqno, res->rseqno, res->check, nc, GetNcSeqno(nc), (int)nc->flags);
   15340              : 
   15341              :    // check for correct connection, note that nc objects are reused and after a socket is closed
   15342              :    // and a new one is opened, the same nc address and the same nc->sock can now refer to a completely
   15343              :    // different tcp connection. So we check ncseqno instead of nc. K.O.
   15344              : 
   15345              :    //if (res->nc != nc)
   15346              :    //return;
   15347              :    
   15348            0 :    if (GetNcSeqno(nc) != res->w->wncseqno)
   15349            0 :       return;
   15350              :    
   15351              :    //printf("nc: %p, ncseqno: %d, wseqno: %d, wncseqno %d, on_work_complete: rseqno: %d, send_501: %d, s1 %d, s2: %d, close_flag: %d\n", res->nc, GetNcSeqno(nc), res->w->wseqno, res->w->wncseqno, res->rseqno, res->send_501, (int)res->s1, (int)res->s2, res->close_flag);
   15352              : 
   15353            0 :    if (res->send_501) {
   15354            0 :       std::string response = "501 Not Implemented";
   15355            0 :       mg_send_head(nc, 501, response.length(), NULL); // 501 Not Implemented
   15356            0 :       mg_send(nc, response.c_str(), response.length());
   15357            0 :    }
   15358              : 
   15359            0 :    if (res->s1 > 0)
   15360            0 :       mg_send(nc, res->p1, res->s1);
   15361              : 
   15362            0 :    if (res->s2 > 0)
   15363            0 :       mg_send(nc, res->p2, res->s2);
   15364              : 
   15365            0 :    if (res->close_flag) {
   15366              :       // cannot do pipelined http if response generated by mhttpd
   15367              :       // decode_get() has no Content-Length header.
   15368              :       // must close the connection.
   15369            0 :       nc->flags |= MG_F_SEND_AND_CLOSE;
   15370              :    }
   15371              : 
   15372            0 :    res->w->send_done = true;
   15373              : }
   15374              : 
   15375            0 : static void mongoose_send(mg_connection* nc, MongooseWorkObject* w, const char* p1, size_t s1, const char* p2, size_t s2, bool close_flag)
   15376              : {
   15377              :    //printf("nc: %p: send %d and %d\n", nc, (int)s1, (int)s2);
   15378            0 :    struct work_result res;
   15379            0 :    res.nc = nc;
   15380            0 :    res.w  = w;
   15381            0 :    res.rseqno = s_rseqno++; // thread-asfe, s_rseqno is std::atomic_int
   15382            0 :    res.p1 = p1;
   15383            0 :    res.s1 = s1;
   15384            0 :    res.p2 = p2;
   15385            0 :    res.s2 = s2;
   15386            0 :    res.close_flag = close_flag;
   15387            0 :    res.send_501 = false;
   15388              :    //printf("nc: %p: call mg_broadcast()\n", nc);
   15389              : 
   15390              :    // NB: mg_broadcast() is advertised as thread-safe, but it is not.
   15391              :    //
   15392              :    // in mongoose 6.16, mg_brodacast() and mg_mgr_handle_ctl_sock() have several problems:
   15393              :    //
   15394              :    // a) "wrong thread" read from mgr->ctl[0], defeating the handshake
   15395              :    //
   15396              :    // b) "lost messages". if more than one message is written to mgr->ctl[0], the second message
   15397              :    //    will be "eaten" by mg_mgr_handle_ctl_sock() because of mistatch between number of bytes read and written
   15398              :    //    in the two functions. mg_mgr_handle_ctl_sock() always reads about 8000 bytes while mg_broadcast()
   15399              :    //    writes 8 bytes per message, (per examples/multithreaded/multithreaded.c. mhttpd messages are a bit longer).
   15400              :    //    So if multiple messages are present in the msg->ctl[0] pipe, the read call (of about 8000 bytes)
   15401              :    //    in mg_mgr_handle_ctl_sock() will return several messages (last message may be truncated)
   15402              :    //    but only the first message will be processed by the code. any additional messages are ignored.
   15403              :    //
   15404              :    // Problems (a) and (b) are easy to fix by using a mutex to serialize mg_broadcast().
   15405              :    //
   15406              :    // c) if the mg_broadcast() message contains pointers to the data buffer to be sent out,
   15407              :    //    the caller of mg_broadcast() should not free these data buffers until mg_send() is called
   15408              :    //    in "on_work_complete()". In theory, the caller of mg_broadcast() could wait until on_work_complete()
   15409              :    //    sets a "done" flag. In practice, if the corresponding network connection is closed before
   15410              :    //    mg_mgr_handle_ctl_sock() has looped over it, on_work_complete() will never run
   15411              :    //    and the "done" flag will never be set. (Of course, network connections are permitted to close
   15412              :    //    at any time without warning, but) the firefox browser closes the network connections "a lot"
   15413              :    //    especially when user pressed the "page reload" button at the moment when HTTP transations
   15414              :    //    are "in flight". (google-chrome tends to permit these "lame duck" transactions to complete and mongoose
   15415              :    //    does not see unexpected socket closures, at least not as many).
   15416              :    //
   15417              :    // To fix problem (c) I need to know when mg_mgr_handle_ctl_sock()'s loop over network connections
   15418              :    // has completed (two cases: (a) my on_work_complete() was hopefully called and finished,
   15419              :    // and (b) the "right" network connection was already closed (for whatever reason) and my on_work_complete()
   15420              :    // was never called).
   15421              :    //
   15422              :    // My solution is to change the handshake between mg_broadcast() and mg_mgr_handle_ctl_sock() by sending
   15423              :    // the handshake reply after looping over the network connections instead of after reading the message
   15424              :    // from msg->ctl[1].
   15425              :    //
   15426              :    // This requires a modification to the code in mongoose.c. If this change is lost/undone, nothing will work.
   15427              :    //
   15428              : 
   15429            0 :    s_mg_broadcast_mutex.lock();
   15430            0 :    mg_broadcast(&s_mgr, on_work_complete, (void *)&res, sizeof(res));
   15431            0 :    s_mg_broadcast_mutex.unlock();
   15432            0 : }
   15433              : 
   15434            0 : static void mongoose_send_501(mg_connection* nc, MongooseWorkObject* w)
   15435              : {
   15436            0 :    struct work_result res;
   15437            0 :    res.nc = nc;
   15438            0 :    res.w  = w;
   15439            0 :    res.rseqno = s_rseqno++; // thread-asfe, s_rseqno is std::atomic_int
   15440            0 :    res.p1 = 0;
   15441            0 :    res.s1 = 0;
   15442            0 :    res.p2 = 0;
   15443            0 :    res.s2 = 0;
   15444            0 :    res.close_flag = false;
   15445            0 :    res.send_501 = true;
   15446              :    //printf("nc: %p, call mg_broadcast()\n", nc);
   15447              : 
   15448            0 :    s_mg_broadcast_mutex.lock();
   15449            0 :    mg_broadcast(&s_mgr, on_work_complete, (void *)&res, sizeof(res));
   15450            0 :    s_mg_broadcast_mutex.unlock();
   15451            0 : }
   15452              : 
   15453              : #if 0
   15454              : void *worker_thread_proc(void *param)
   15455              : {
   15456              :    //struct mg_mgr *mgr = (struct mg_mgr *) param;
   15457              :    struct work_request req = {0};
   15458              :    
   15459              :    while ((! _abort) && (! s_shutdown)) {
   15460              :       int rd = read(s_sock[1], &req, sizeof(req));
   15461              :       if (rd == 0) {
   15462              :          // socket closed, shutdown the thread
   15463              :          break;
   15464              :       }
   15465              :       if (rd < 0) {
   15466              :          if (_abort || s_shutdown) {
   15467              :             return NULL;
   15468              :          }
   15469              :          fprintf(stderr, "worker_thread_proc: Error: read(s_sock(1)) returned %d, error %d (%s)\n", rd, errno, strerror(errno));
   15470              :          abort();
   15471              :          return NULL;
   15472              :       }
   15473              :       
   15474              :       //printf("nc: %p: received request!\n", req.nc);
   15475              : 
   15476              :       int response = thread_work_function(req.nc, req.w);
   15477              : 
   15478              :       if (response == RESPONSE_501) {
   15479              :          if (trace_mg||verbose_mg)
   15480              :             printf("handle_http_message: sending 501 Not Implemented error\n");
   15481              :          mongoose_send_501(req.nc, req.w);
   15482              :       }
   15483              : 
   15484              :       req.w->t->fCompleted = true;
   15485              :       gTraceBuf->AddTraceMTS(req.w->t);
   15486              : 
   15487              :       //printf("nc: %p: wseqno: %d, delete work object!\n", req.nc, req.w->wseqno);
   15488              : 
   15489              :       delete req.w;
   15490              :       req.w = NULL;
   15491              :    }
   15492              :    return NULL;
   15493              : }
   15494              : #endif
   15495              : 
   15496            0 : static void mongoose_thread(MongooseThreadObject* to)
   15497              : {
   15498              :    //printf("to %p, nc %p: thread %p started!\n", to, to->fNc, to->fThread);
   15499              : 
   15500            0 :    std::unique_lock<std::mutex> ulm(to->fMutex, std::defer_lock);
   15501              : 
   15502            0 :    to->fIsRunning = true;
   15503              :    
   15504            0 :    while ((! _abort) && (! s_shutdown)) {
   15505            0 :       MongooseWorkObject *w = NULL;
   15506              : 
   15507            0 :       ulm.lock();
   15508            0 :       while (to->fQueue.empty()) {
   15509              :          //printf("to %p, nc %p, thread %p: waiting!\n", to, to->fNc, to->fThread);
   15510            0 :          to->fNotify.wait(ulm);
   15511            0 :          if (_abort || s_shutdown) {
   15512            0 :             break;
   15513              :          }
   15514              :       }
   15515              : 
   15516            0 :       if (_abort || s_shutdown) {
   15517            0 :          break;
   15518              :       }
   15519              :       
   15520            0 :       w = to->fQueue.front();
   15521            0 :       to->fQueue.pop_front();
   15522            0 :       ulm.unlock();
   15523              : 
   15524              :       //printf("to %p, nc %p: wseqno: %d, received request!\n", to, w->nc, w->wseqno);
   15525              : 
   15526            0 :       int response = thread_work_function(w->nc, w);
   15527              : 
   15528            0 :       if (response == RESPONSE_501) {
   15529            0 :          if (trace_mg||verbose_mg)
   15530            0 :             printf("handle_http_message: sending 501 Not Implemented error\n");
   15531            0 :          mongoose_send_501(w->nc, w);
   15532              :       }
   15533              : 
   15534              :       // mg_broadcast() called on_work_complete() on the main thread and waited until it finished
   15535              : 
   15536            0 :       if (!w->send_done) {
   15537              :          // NB: careful here, if connection nc was closed, pointer nc points to nowhere! do not dereference it!
   15538              :          // NB: stay quiet about it, nothing special about network connctions closing and opening as they wish.
   15539              :          //printf("to %p, nc %p: wseqno: %d, wncseqno: %d, request was not sent, maybe nc was closed while we were thinking\n", to, w->nc, w->wseqno, w->wncseqno);
   15540              :       }
   15541              : 
   15542            0 :       w->t->fCompleted = true;
   15543            0 :       gTraceBuf->AddTraceMTS(w->t);
   15544              : 
   15545              :       //printf("nc: %p: wseqno: %d, delete work object!\n", w->nc, w->wseqno);
   15546              : 
   15547            0 :       delete w;
   15548              :    }
   15549              : 
   15550            0 :    to->fIsRunning = false;
   15551              : 
   15552              :    //printf("to %p, nc %p: thread %p finished!\n", to, to->fNc, to->fThread);
   15553            0 : }
   15554              : 
   15555              : static bool mongoose_hostlist_enabled(const struct mg_connection *nc);
   15556              : 
   15557            0 : static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
   15558              : {
   15559              :    (void) nc;
   15560              :    (void) ev_data;
   15561              : 
   15562              :    //if (trace_mg && ev != 0) {
   15563              :    //   printf("ev_handler: connection %p, event %d\n", nc, ev);
   15564              :    //}
   15565              :    
   15566            0 :    switch (ev) {
   15567            0 :    case 0:
   15568            0 :       break;
   15569            0 :    default: { 
   15570            0 :       if (trace_mg) {
   15571            0 :          printf("ev_handler: connection %p, event %d\n", nc, ev);
   15572              :       }
   15573            0 :       break;
   15574              :    }
   15575            0 :    case MG_EV_ACCEPT:
   15576            0 :       assert(nc->user_data == NULL);
   15577            0 :       nc->user_data = new MongooseNcUserData();
   15578              : 
   15579            0 :       if (trace_mg) {
   15580            0 :          printf("ev_handler: connection %p, MG_EV_ACCEPT, user_data %p, ncseqno %d\n", nc, nc->user_data, GetNcSeqno(nc));
   15581              :       }
   15582            0 :       if (s_shutdown) {
   15583              :          //printf("XXX nc %p!\n", nc);
   15584            0 :          nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   15585            0 :       } else if (mongoose_hostlist_enabled(nc)) {
   15586            0 :          if (!mongoose_check_hostlist(&nc->sa)) {
   15587            0 :             nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   15588              :          }
   15589              :       }
   15590            0 :       break;
   15591            0 :    case MG_EV_RECV:
   15592            0 :       if (trace_mg_recv) {
   15593            0 :          printf("ev_handler: connection %p, MG_EV_RECV, %d bytes\n", nc, *(int*)ev_data);
   15594              :       }
   15595            0 :       if (s_shutdown) {
   15596              :          //printf("RRR nc %p!\n", nc);
   15597            0 :          nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   15598              :       }
   15599            0 :       break;
   15600            0 :    case MG_EV_SEND:
   15601            0 :       if (trace_mg_send) {
   15602            0 :          printf("ev_handler: connection %p, MG_EV_SEND, %d bytes\n", nc, *(int*)ev_data);
   15603              :       }
   15604            0 :       break;
   15605            0 :    case MG_EV_HTTP_CHUNK: {
   15606            0 :       if (trace_mg) {
   15607            0 :          printf("ev_handler: connection %p, MG_EV_HTTP_CHUNK\n", nc);
   15608              :       }
   15609            0 :       if (s_shutdown) {
   15610              :          //printf("RRR1 nc %p!\n", nc);
   15611            0 :          nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   15612              :       }
   15613            0 :       break;
   15614              :    }
   15615            0 :    case MG_EV_HTTP_REQUEST: {
   15616            0 :       struct http_message* msg = (struct http_message*)ev_data;
   15617            0 :       if (trace_mg) {
   15618            0 :          printf("ev_handler: connection %p, MG_EV_HTTP_REQUEST \"%s\" \"%s\"\n", nc, mgstr(&msg->method).c_str(), mgstr(&msg->uri).c_str());
   15619              :       }
   15620            0 :       if (s_shutdown) {
   15621              :          //printf("RRR2 nc %p!\n", nc);
   15622            0 :          nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   15623              :       } else {
   15624            0 :          handle_http_message(nc, msg);
   15625              :       }
   15626            0 :       break;
   15627              :    }
   15628            0 :    case MG_EV_CLOSE: {
   15629            0 :       if (trace_mg) {
   15630            0 :          printf("ev_handler: connection %p, MG_EV_CLOSE, user_data %p, ncseqno %d\n", nc, nc->user_data, GetNcSeqno(nc));
   15631              :       }
   15632              :       //printf("CCC nc %p!\n", nc);
   15633            0 :       FreeThread(nc);
   15634            0 :       if (nc->user_data) {
   15635            0 :          MongooseNcUserData* ncud = (MongooseNcUserData*)nc->user_data;
   15636            0 :          nc->user_data = NULL;
   15637            0 :          delete ncud;
   15638            0 :          ncud = NULL;
   15639              :       }
   15640              :    }
   15641              :    }
   15642            0 : }
   15643              : 
   15644              : #define FLAG_HTTPS     MG_F_USER_1
   15645              : #define FLAG_PASSWORDS MG_F_USER_2
   15646              : #define FLAG_HOSTLIST  MG_F_USER_3
   15647              : 
   15648            0 : static bool mongoose_passwords_enabled(const struct mg_connection *nc)
   15649              : {
   15650            0 :    int flags = 0;
   15651            0 :    if (nc && nc->listener) {
   15652            0 :       flags = nc->listener->flags;
   15653              :    }
   15654              :    //printf("mongoose_passwords_enabled: nc %p, listener %p, flags 0x%lx, user_data %p, flags 0x%x\n", nc, nc->listener, nc->listener->flags, nc->listener->user_data, flags);
   15655            0 :    return flags & FLAG_PASSWORDS;
   15656              : }
   15657              : 
   15658            0 : static bool mongoose_hostlist_enabled(const struct mg_connection *nc)
   15659              : {
   15660            0 :    int flags = 0;
   15661            0 :    if (nc && nc->listener) {
   15662            0 :       flags = nc->listener->flags;
   15663              :    }
   15664              :    //printf("mongoose_hostlist_enabled: nc %p, listener %p, flags 0x%lx, user_data %p, flags 0x%x\n", nc, nc->listener, nc->listener->flags, nc->listener->user_data, flags);
   15665            0 :    return flags & FLAG_HOSTLIST;
   15666              : }
   15667              : 
   15668            0 : static int mongoose_listen(const char* address, int flags)
   15669              : {
   15670              : #if MG_ENABLE_SSL
   15671              : #else
   15672            0 :    if (flags & FLAG_HTTPS) {
   15673            0 :       cm_msg(MERROR, "mongoose_listen", "https port \"%s\" requested, but mhttpd compiled without MG_ENABLE_SSL", address);
   15674            0 :       return SS_SOCKET_ERROR;
   15675              :    }
   15676              : #endif
   15677              : 
   15678            0 :    struct mg_connection *nc = mg_bind(&s_mgr, address, ev_handler);
   15679            0 :    if (nc == NULL) {
   15680            0 :       cm_msg(MERROR, "mongoose_listen", "Cannot mg_bind address \"%s\"", address);
   15681            0 :       return SS_SOCKET_ERROR;
   15682              :    }
   15683              : 
   15684            0 :    if (flags & FLAG_HTTPS) {
   15685              : #if MG_ENABLE_SSL
   15686              :       std::string cert_file;
   15687              : 
   15688              :       int status = find_file_mg("ssl_cert.pem", cert_file, NULL, trace_mg);
   15689              : 
   15690              :       if (status != SUCCESS) {
   15691              :          cm_msg(MERROR, "mongoose_listen", "cannot find SSL certificate file \"%s\"", cert_file.c_str());
   15692              :          cm_msg(MERROR, "mongoose_listen", "please create SSL certificate file using openssl: cd $MIDASSYS; openssl req -new -nodes -newkey rsa:2048 -sha256 -out ssl_cert.csr -keyout ssl_cert.key -subj \"/C=/ST=/L=/O=midas/OU=mhttpd/CN=localhost\"; openssl x509 -req -days 365 -sha256 -in ssl_cert.csr -signkey ssl_cert.key -out ssl_cert.pem; cat ssl_cert.key >> ssl_cert.pem");
   15693              :          cm_msg(MERROR, "mongoose_listen", "or using certbot (recommened): setup certbot per Let's Encrypt instructions, certificates are typically saved in /etc/letsencrypt/live/$HOSTNAME/, copy fullchain.pem and privkey.pem to $MIDASSYS; cd $MIDASSYS; cat fullchain.pem privkey.pem > ssl_cert.pem");
   15694              :          return SS_FILE_ERROR;
   15695              :       }
   15696              : 
   15697              :       printf("Mongoose web server will use https certificate file \"%s\"\n", cert_file.c_str());
   15698              : 
   15699              :       const char* errmsg = mg_set_ssl(nc, cert_file.c_str(), NULL);
   15700              :       if (errmsg) {
   15701              :          cm_msg(MERROR, "mongoose_listen", "Cannot enable https with certificate file \"%s\", error: %s", cert_file.c_str(), errmsg);
   15702              :          return SS_SOCKET_ERROR;
   15703              :       }
   15704              : 
   15705              :       // NB: where is the warning that the SSL certificate has expired?!? K.O.
   15706              : #else
   15707            0 :       abort(); // cannot happen!
   15708              : #endif
   15709              :    }
   15710              : 
   15711            0 :    mg_set_protocol_http_websocket(nc);
   15712              : 
   15713            0 :    nc->flags |= flags;
   15714              : 
   15715            0 :    printf("Listening on \"%s://%s\", passwords %s, hostlist %s\n", (flags&FLAG_HTTPS)?"https":"http", address, (flags&FLAG_PASSWORDS)?"enabled":"OFF", (flags&FLAG_HOSTLIST)?"enabled":"OFF");
   15716              : 
   15717            0 :    return SUCCESS;
   15718              : }
   15719              : 
   15720            0 : static int mongoose_init(MVOdb* odb, bool no_passwords, bool no_hostlist, const std::vector<std::string>& user_hostlist)
   15721              : {
   15722            0 :    bool enable_localhost_port = true;
   15723            0 :    int  localhost_port           = 8080;
   15724            0 :    bool localhost_port_passwords = false;
   15725              : 
   15726            0 :    bool enable_insecure_port     = false;
   15727            0 :    int  insecure_port            = 8081;
   15728            0 :    bool insecure_port_passwords  = true;
   15729            0 :    bool insecure_port_hostlist   = true;
   15730              : 
   15731            0 :    bool enable_https_port        = false;
   15732            0 :    int  https_port               = 8443;
   15733            0 :    bool https_port_passwords     = true;
   15734            0 :    bool https_port_hostlist      = false;
   15735              : 
   15736            0 :    std::vector<std::string> hostlist;
   15737            0 :    hostlist.push_back("localhost");
   15738              : 
   15739            0 :    bool enable_ipv6 = true;
   15740              : 
   15741            0 :    odb->RB("Enable localhost port", &enable_localhost_port, true);
   15742            0 :    odb->RI("localhost port", &localhost_port, true);
   15743            0 :    odb->RB("localhost port passwords", &localhost_port_passwords, true);
   15744            0 :    odb->RB("Enable insecure port", &enable_insecure_port, true);
   15745            0 :    odb->RI("insecure port", &insecure_port, true);
   15746            0 :    odb->RB("insecure port passwords", &insecure_port_passwords, true);
   15747            0 :    odb->RB("insecure port host list", &insecure_port_hostlist, true);
   15748            0 :    odb->RB("Enable https port", &enable_https_port, true);
   15749            0 :    odb->RI("https port", &https_port, true);
   15750            0 :    odb->RB("https port passwords", &https_port_passwords, true);
   15751            0 :    odb->RB("https port host list", &https_port_hostlist, true);
   15752            0 :    odb->RSA("Host list", &hostlist, true, 10, 256);
   15753            0 :    odb->RB("Enable IPv6", &enable_ipv6, true);
   15754              : 
   15755              :    // populate the MIME.types table
   15756            0 :    gProxyOdb = odb->Chdir("Proxy", true);
   15757            0 :    std::string proxy_example = "#http://localhost:8080";
   15758            0 :    gProxyOdb->RS("example", &proxy_example, true);
   15759              :    
   15760              :    // populate the MIME.types table
   15761            0 :    SaveMimetypes(odb->Chdir("mime.types", true));
   15762              :    
   15763            0 :    if (!no_passwords
   15764            0 :        && ((enable_localhost_port && localhost_port_passwords)
   15765            0 :            || (enable_insecure_port && insecure_port_passwords)
   15766            0 :            || (enable_https_port && https_port_passwords))) {
   15767            0 :       gAuthMg = new Auth();
   15768            0 :       int status = gAuthMg->Init();
   15769            0 :       if (status != SUCCESS) {
   15770            0 :          printf("mongoose_init: Error: Cannot initialize authorization object!\n");
   15771            0 :          return status;
   15772              :       }
   15773            0 :       printf("HTTP Digest authentication with realm \"%s\" and password file \"%s\"\n", gAuthMg->realm.c_str(), gAuthMg->passwd_filename.c_str());
   15774            0 :    } else {
   15775            0 :       printf("Password protection is off\n");
   15776              :    }
   15777              : 
   15778            0 :    if (!no_hostlist
   15779            0 :        && ((enable_insecure_port && insecure_port_hostlist)
   15780            0 :            || (enable_https_port && https_port_hostlist))) {
   15781            0 :       gAllowedHosts.clear();
   15782              : 
   15783              :       // copy the user allowed hosts
   15784            0 :       for (unsigned int i=0; i<user_hostlist.size(); i++)
   15785            0 :          gAllowedHosts.push_back(user_hostlist[i]);
   15786              : 
   15787            0 :       for (unsigned i=0; i<hostlist.size(); i++) {
   15788            0 :          std::string s = hostlist[i];
   15789            0 :          if (s.length() < 1) // skip emties
   15790            0 :             continue;
   15791              : 
   15792            0 :          if (s[0] == '#') // skip commented-out entries
   15793            0 :             continue;
   15794              : 
   15795              :          //printf("add allowed hosts %d [%s]\n", i, s.c_str());
   15796            0 :          gAllowedHosts.push_back(s);
   15797            0 :       }
   15798              : 
   15799            0 :       printf("Hostlist active, connections will be accepted only from: ");
   15800            0 :       for (unsigned i=0; i<gAllowedHosts.size(); i++) {
   15801            0 :          if (i>0)
   15802            0 :             printf(", ");
   15803            0 :          printf("%s", gAllowedHosts[i].c_str());
   15804              :       }
   15805            0 :       printf("\n");
   15806            0 :    } else {
   15807            0 :       printf("Hostlist off, connections from anywhere will be accepted\n");
   15808              :    }
   15809              : 
   15810            0 :    mg_mgr_init(&s_mgr, NULL);
   15811              : 
   15812            0 :    bool listen_failed = false;
   15813              : 
   15814            0 :    if (enable_localhost_port) {
   15815              :       char str[256];
   15816            0 :       sprintf(str, "localhost:%d", localhost_port);
   15817            0 :       int status = mongoose_listen(str, 0);
   15818            0 :       if (status != SUCCESS)
   15819            0 :          listen_failed = true;
   15820            0 :       if (enable_ipv6) {
   15821            0 :          sprintf(str, "[::1]:%d", localhost_port);
   15822            0 :          status = mongoose_listen(str, 0);
   15823            0 :          if (status != SUCCESS)
   15824            0 :             listen_failed = true;
   15825              :       }
   15826              :    }
   15827              : 
   15828            0 :    if (enable_insecure_port) {
   15829              :       char str[256];
   15830            0 :       int flags = 0;
   15831            0 :       if (insecure_port_passwords)
   15832            0 :          flags |= FLAG_PASSWORDS;
   15833            0 :       if (insecure_port_hostlist)
   15834            0 :          flags |= FLAG_HOSTLIST;
   15835            0 :       if (enable_ipv6) {
   15836            0 :          sprintf(str, "[::]:%d", insecure_port);
   15837            0 :          int status = mongoose_listen(str, flags);
   15838            0 :          if (status != SUCCESS)
   15839            0 :             listen_failed = true;
   15840              :       } else {
   15841            0 :          sprintf(str, "%d", insecure_port);
   15842            0 :          int status = mongoose_listen(str, flags);
   15843            0 :          if (status != SUCCESS)
   15844            0 :             listen_failed = true;
   15845              :       }
   15846              :    }
   15847              : 
   15848            0 :    if (enable_https_port) {
   15849              :       char str[256];
   15850            0 :       int flags = 0;
   15851            0 :       if (https_port_passwords)
   15852            0 :          flags |= FLAG_PASSWORDS;
   15853            0 :       if (https_port_hostlist)
   15854            0 :          flags |= FLAG_HOSTLIST;
   15855            0 :       flags |= FLAG_HTTPS;
   15856            0 :       if (enable_ipv6) {
   15857            0 :          sprintf(str, "[::]:%d", https_port);
   15858            0 :          int status = mongoose_listen(str, flags);
   15859            0 :          if (status != SUCCESS)
   15860            0 :             listen_failed = true;
   15861              :       } else {
   15862            0 :          sprintf(str, "%d", https_port);
   15863            0 :          int status = mongoose_listen(str, flags);
   15864            0 :          if (status != SUCCESS)
   15865            0 :             listen_failed = true;
   15866              :       }
   15867              :    }
   15868              : 
   15869            0 :    if (listen_failed) {
   15870            0 :       cm_msg(MERROR, "mongoose_init", "Failed to listen on a TCP port enabled in ODB /WebServer");
   15871            0 :       return SS_SOCKET_ERROR;
   15872              :    }
   15873              : 
   15874            0 :    return SUCCESS;
   15875            0 : }
   15876              : 
   15877            0 : static void mongoose_poll(int msec = 200)
   15878              : {
   15879            0 :    mg_mgr_poll(&s_mgr, msec);
   15880            0 : }
   15881              : 
   15882            0 : static void mongoose_cleanup()
   15883              : {
   15884            0 :    printf("Mongoose web server shutting down\n");
   15885              : 
   15886            0 :    s_shutdown = true;
   15887              : 
   15888              :    // close listener sockets
   15889            0 :    if (s_mgr.active_connections) {
   15890            0 :       struct mg_connection* nc = s_mgr.active_connections;
   15891            0 :       while (nc) {
   15892              :          //printf("nc %p, next %p, user_data %p, listener %p, flags %lu\n", nc, nc->next, nc->user_data, nc->listener, nc->flags);
   15893            0 :          if (nc->flags & MG_F_LISTENING) {
   15894            0 :             nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   15895              :          }
   15896            0 :          nc = nc->next;
   15897              :       }
   15898              :    }
   15899              : 
   15900              :    // tell threads to shut down
   15901            0 :    for (auto it : gMongooseThreads) {
   15902            0 :       MongooseThreadObject* to = it;
   15903            0 :       to->fNotify.notify_one();
   15904              :    }
   15905              : 
   15906              :    // wait until all threads stop
   15907            0 :    for (int i=0; i<10; i++) {
   15908            0 :       int count_running = 0;
   15909            0 :       for (auto it : gMongooseThreads) {
   15910            0 :          MongooseThreadObject* to = it;
   15911              :          //printf("AAA6C %p thread %p running %d!\n", to, to->fThread, to->fIsRunning);
   15912            0 :          if (to->fIsRunning) {
   15913            0 :             count_running++;
   15914              :          }
   15915              :       }
   15916            0 :       printf("Mongoose web server shutting down, %d threads still running\n", count_running);
   15917            0 :       if (count_running == 0)
   15918            0 :          break;
   15919            0 :       mongoose_poll(1000);
   15920              :    }
   15921              : 
   15922              :    // delete thread objects
   15923            0 :    for (auto it : gMongooseThreads) {
   15924            0 :       MongooseThreadObject* to = it;
   15925              :       //printf("AAA7B %p thread %p running %d!\n", to, to->fThread, to->fIsRunning);
   15926            0 :       if (to->fIsRunning) {
   15927            0 :          cm_msg(MERROR, "mongoose", "thread failed to shut down");
   15928            0 :          continue;
   15929              :       }
   15930            0 :       to->fThread->join();
   15931            0 :       delete to->fThread;
   15932            0 :       delete to;
   15933              :    }
   15934            0 :    gMongooseThreads.clear();
   15935              : 
   15936            0 :    mg_mgr_free(&s_mgr);
   15937              :    
   15938              :    //closesocket(s_sock[0]);
   15939              :    //closesocket(s_sock[1]);
   15940              : 
   15941              :    // make leak sanitizer happy!
   15942            0 :    for (auto e : gHostlistCache) {
   15943            0 :       delete e;
   15944              :    }
   15945            0 :    gHostlistCache.clear();
   15946            0 :    if (gProxyOdb) {
   15947            0 :       delete gProxyOdb;
   15948            0 :       gProxyOdb = NULL;
   15949              :    }
   15950            0 :    if (gMimeTypesOdb) {
   15951            0 :       delete gMimeTypesOdb;
   15952            0 :       gMimeTypesOdb = NULL;
   15953              :    }
   15954              : 
   15955            0 :    printf("Mongoose web server shut down\n");
   15956            0 : }
   15957              : 
   15958              : #endif
   15959              : 
   15960              : #ifdef HAVE_MONGOOSE6
   15961              : 
   15962            0 : static bool mongoose_passwords_enabled(const struct mg_connection *nc)
   15963              : {
   15964            0 :    return true;
   15965              : }
   15966              : 
   15967            0 : int start_mg(int user_http_port, int user_https_port, int socket_priviledged_port, int verbose)
   15968              : {
   15969              :    HNDLE hDB;
   15970              :    int size;
   15971              :    int status;
   15972              : 
   15973              :    //if (verbose)
   15974              :    //   trace_mg = true;
   15975              : 
   15976            0 :    if (verbose)
   15977            0 :       verbose_mg = true;
   15978              : 
   15979            0 :    status = cm_get_experiment_database(&hDB, NULL);
   15980            0 :    assert(status == CM_SUCCESS);
   15981              : 
   15982            0 :    int http_port = 8080;
   15983            0 :    int https_port = 8443;
   15984            0 :    int http_redirect_to_https = 1;
   15985              : 
   15986            0 :    size = sizeof(http_port);
   15987            0 :    db_get_value(hDB, 0, "/Experiment/midas http port", &http_port, &size, TID_INT, TRUE);
   15988              : 
   15989            0 :    size = sizeof(https_port);
   15990            0 :    db_get_value(hDB, 0, "/Experiment/midas https port", &https_port, &size, TID_INT, TRUE);
   15991              : 
   15992            0 :    size = sizeof(http_redirect_to_https);
   15993            0 :    db_get_value(hDB, 0, "/Experiment/http redirect to https", &http_redirect_to_https, &size, TID_BOOL, TRUE);
   15994              : 
   15995            0 :    bool need_cert_file = false;
   15996            0 :    bool need_password_file = false;
   15997              : 
   15998            0 :    if (user_http_port)
   15999            0 :       http_port = user_http_port;
   16000              : 
   16001            0 :    if (user_https_port)
   16002            0 :       https_port = user_https_port;
   16003              : 
   16004            0 :    if (https_port) {
   16005            0 :       need_cert_file = true;
   16006            0 :       need_password_file = true;
   16007              :    }
   16008              : 
   16009            0 :    if (!https_port)
   16010            0 :       http_redirect_to_https = 0;
   16011              : 
   16012            0 :    if (http_port && !http_redirect_to_https) {
   16013              :       // no passwords serving over http unless
   16014              :       // http is just a redict to https
   16015            0 :       need_password_file = false;
   16016              :    }
   16017              : 
   16018            0 :    if (socket_priviledged_port >= 0) {
   16019              :       // no passwords if serving unencrypted http on port 80
   16020            0 :       need_password_file = false;
   16021            0 :       printf("Mongoose web server password portection is disabled: serving unencrypted http on port 80\n");
   16022              :    }
   16023              : 
   16024            0 :    bool have_at_least_one_port = false;
   16025              : 
   16026            0 :    std::string cert_file;
   16027              : 
   16028            0 :    if (need_cert_file) {
   16029            0 :       status = find_file_mg("ssl_cert.pem", cert_file, NULL, trace_mg);
   16030              : 
   16031            0 :       if (status != SUCCESS) {
   16032            0 :          cm_msg(MERROR, "mongoose", "cannot find SSL certificate file \"%s\"", cert_file.c_str());
   16033            0 :          cm_msg(MERROR, "mongoose", "please create SSL certificate file: cd $MIDASSYS; openssl req -new -nodes -newkey rsa:2048 -sha256 -out ssl_cert.csr -keyout ssl_cert.key -subj \"/C=/ST=/L=/O=midas/OU=mhttpd/CN=localhost\"; openssl x509 -req -days 365 -sha256 -in ssl_cert.csr -signkey ssl_cert.key -out ssl_cert.pem; cat ssl_cert.key >> ssl_cert.pem");
   16034            0 :          return SS_FILE_ERROR;
   16035              :       }
   16036              : 
   16037            0 :       printf("Mongoose web server will use SSL certificate file \"%s\"\n", cert_file.c_str());
   16038              :    }
   16039              : 
   16040            0 :    if (need_password_file) {
   16041            0 :       gAuthMg = new Auth();
   16042            0 :       status = gAuthMg->Init();
   16043            0 :       if (status != SUCCESS) {
   16044            0 :          printf("Error: Cannot initialize authorization object!\n");
   16045            0 :          return status;
   16046              :       }
   16047            0 :       printf("Mongoose web server will use authentication realm \"%s\", password file \"%s\"\n", gAuthMg->realm.c_str(), gAuthMg->passwd_filename.c_str());
   16048              :    } else {
   16049            0 :       printf("Mongoose web server will not use password protection\n");
   16050              :    }
   16051              : 
   16052            0 :    if (trace_mg)
   16053            0 :       printf("start_mg!\n");
   16054              : 
   16055              : #ifndef OS_WINNT
   16056            0 :    signal(SIGPIPE, SIG_IGN);
   16057              : #endif
   16058              : 
   16059            0 :    if (!gTraceBuf) {
   16060            0 :       gTraceBuf = new RequestTraceBuf;
   16061              :    }
   16062              : 
   16063            0 :    if (!request_mutex) {
   16064            0 :       status = ss_mutex_create(&request_mutex, FALSE);
   16065            0 :       assert(status==SS_SUCCESS || status==SS_CREATED);
   16066              :    }
   16067              : 
   16068            0 :    mg_mgr_init(&mgr_mg, NULL);
   16069              : 
   16070              :    // use socket bound to priviledged port (setuid-mode)
   16071            0 :    if (socket_priviledged_port >= 0) {
   16072            0 :       struct mg_connection* nc = mg_add_sock(&mgr_mg, socket_priviledged_port, handle_event_mg);
   16073            0 :       if (nc == NULL) {
   16074            0 :          cm_msg(MERROR, "mongoose", "Cannot create mg_connection for set-uid-root privileged port");
   16075            0 :          return SS_SOCKET_ERROR;
   16076              :       }
   16077              : 
   16078            0 :       nc->flags |= MG_F_LISTENING;
   16079              : #ifdef MG_ENABLE_THREADS
   16080            0 :       mg_enable_multithreading(nc);
   16081              : #endif
   16082            0 :       mg_set_protocol_http_websocket(nc);
   16083            0 :       mg_register_http_endpoint(nc, "/", handle_http_event_mg);
   16084              : 
   16085            0 :       have_at_least_one_port = true;
   16086            0 :       printf("mongoose web server is listening on the set-uid-root privileged port\n");
   16087              :    }
   16088              : 
   16089            0 :    if (http_port != 80) { // port 80 is already handled by socket_priviledged_port
   16090              :       char str[256];
   16091            0 :       sprintf(str, "%d", http_port);
   16092            0 :       struct mg_connection* nc = mg_bind(&mgr_mg, str, handle_event_mg);
   16093            0 :       if (nc == NULL) {
   16094            0 :          cm_msg(MERROR, "mongoose", "Cannot bind to port %d", http_port);
   16095            0 :          return SS_SOCKET_ERROR;
   16096              :       }
   16097              : 
   16098              : #ifdef MG_ENABLE_THREADS
   16099            0 :       mg_enable_multithreading(nc);
   16100              : #endif
   16101            0 :       mg_set_protocol_http_websocket(nc);
   16102              : 
   16103            0 :       if (http_redirect_to_https) {
   16104            0 :          std::string hostname = ss_gethostname();
   16105              :          char str[256];
   16106            0 :          sprintf(str, "%d", https_port);
   16107            0 :          std::string s = hostname + ":" + std::string(str);
   16108            0 :          nc->user_data = new std::string(s);
   16109            0 :          mg_register_http_endpoint(nc, "/", handle_http_redirect);
   16110            0 :          printf("mongoose web server is redirecting HTTP port %d to https://%s\n", http_port, s.c_str());
   16111            0 :       } else {
   16112            0 :          mg_register_http_endpoint(nc, "/", handle_http_event_mg);
   16113              :       }
   16114              : 
   16115            0 :       have_at_least_one_port = true;
   16116            0 :       printf("mongoose web server is listening on the HTTP port %d\n", http_port);
   16117              :    }
   16118              : 
   16119            0 :    if (https_port) {
   16120              : #ifdef MG_ENABLE_SSL
   16121              :       char str[256];
   16122              :       sprintf(str, "%d", https_port);
   16123              :       struct mg_connection* nc = mg_bind(&mgr_mg, str, handle_event_mg);
   16124              :       if (nc == NULL) {
   16125              :          cm_msg(MERROR, "mongoose", "Cannot bind to port %d", https_port);
   16126              :          return SS_SOCKET_ERROR;
   16127              :       }
   16128              : 
   16129              :       mg_set_ssl(nc, cert_file.c_str(), NULL);
   16130              : #ifdef MG_ENABLE_THREADS
   16131              :       mg_enable_multithreading(nc);
   16132              : #endif
   16133              :       mg_set_protocol_http_websocket(nc);
   16134              :       mg_register_http_endpoint(nc, "/", handle_http_event_mg);
   16135              : 
   16136              :       have_at_least_one_port = true;
   16137              :       printf("mongoose web server is listening on the HTTPS port %d\n", https_port);
   16138              : #else
   16139            0 :       cm_msg(MERROR, "mongoose", "https port %d requested, but mhttpd compiled without MG_ENABLE_SSL", https_port);
   16140            0 :       return SS_SOCKET_ERROR;
   16141              : #endif
   16142              :    }
   16143              : 
   16144            0 :    if (!have_at_least_one_port) {
   16145            0 :       cm_msg(MERROR, "mongoose", "cannot start: no ports defined");
   16146            0 :       return SS_FILE_ERROR;
   16147              :    }
   16148              : 
   16149            0 :    return SUCCESS;
   16150            0 : }
   16151              : 
   16152            0 : int stop_mg()
   16153              : {
   16154            0 :    if (trace_mg)
   16155            0 :       printf("stop_mg!\n");
   16156              : 
   16157              :    // Stop the server.
   16158            0 :    mg_mgr_free(&mgr_mg);
   16159              : 
   16160            0 :    if (trace_mg)
   16161            0 :       printf("stop_mg done!\n");
   16162            0 :    return SUCCESS;
   16163              : }
   16164              : 
   16165            0 : int loop_mg()
   16166              : {
   16167            0 :    int status = SUCCESS;
   16168              : 
   16169              :    /* establish Ctrl-C handler - will set _abort to TRUE */
   16170            0 :    ss_ctrlc_handler(ctrlc_handler);
   16171              : 
   16172            0 :    while (!_abort) {
   16173              : 
   16174              :       /* cm_yield() is not thread safe, need to take a lock */
   16175              : 
   16176              : #ifdef HAVE_MONGOOSE6
   16177            0 :       status = ss_mutex_wait_for(request_mutex, 0);
   16178              : #endif
   16179            0 :       gMutex.lock();
   16180              : 
   16181              :       /* check for shutdown message */
   16182            0 :       status = cm_yield(0);
   16183            0 :       if (status == RPC_SHUTDOWN)
   16184            0 :          break;
   16185              : 
   16186            0 :       gMutex.unlock();
   16187              : #ifdef HAVE_MONGOOSE6
   16188            0 :       status = ss_mutex_release(request_mutex);
   16189              : #endif
   16190              : 
   16191              :       //ss_sleep(10);
   16192              : 
   16193            0 :       mg_mgr_poll(&mgr_mg, 10);
   16194              :    }
   16195              : 
   16196            0 :    return status;
   16197              : }
   16198              : #endif
   16199              : 
   16200            0 : static MJsonNode* get_http_trace(const MJsonNode* params)
   16201              : {
   16202            0 :    if (!params) {
   16203            0 :       MJSO *doc = MJSO::I();
   16204            0 :       doc->D("get current value of mhttpd http_trace");
   16205            0 :       doc->P(NULL, 0, "there are no input parameters");
   16206            0 :       doc->R(NULL, MJSON_INT, "current value of http_trace");
   16207            0 :       return doc;
   16208              :    }
   16209              : 
   16210            0 :    return mjsonrpc_make_result("http_trace", MJsonNode::MakeInt(http_trace));
   16211              : }
   16212              : 
   16213            0 : static MJsonNode* set_http_trace(const MJsonNode* params)
   16214              : {
   16215            0 :    if (!params) {
   16216            0 :       MJSO* doc = MJSO::I();
   16217            0 :       doc->D("set new value of mhttpd http_trace");
   16218            0 :       doc->P(NULL, MJSON_INT, "new value of http_trace");
   16219            0 :       doc->R(NULL, MJSON_INT, "new value of http_trace");
   16220            0 :       return doc;
   16221              :    }
   16222              : 
   16223            0 :    http_trace = params->GetInt();
   16224            0 :    return mjsonrpc_make_result("http_trace", MJsonNode::MakeInt(http_trace));
   16225              : }
   16226              : 
   16227            0 : static void add_rpc_functions()
   16228              : {
   16229            0 :    mjsonrpc_add_handler("set_http_trace", set_http_trace);
   16230            0 :    mjsonrpc_add_handler("get_http_trace", get_http_trace);
   16231            0 : }
   16232              : 
   16233              : /*------------------------------------------------------------------*/
   16234              : 
   16235            0 : int main(int argc, const char *argv[])
   16236              : {
   16237              :    int status;
   16238            0 :    int daemon = FALSE;
   16239              : #ifdef HAVE_MONGOOSE6
   16240            0 :    int user_http_port = 0;
   16241            0 :    int user_https_port = 0;
   16242              : #endif
   16243              : #ifdef HAVE_MONGOOSE616
   16244            0 :    bool no_passwords = false;
   16245            0 :    bool no_hostlist  = false;
   16246              : #endif
   16247            0 :    const char *myname = "mhttpd";
   16248              : 
   16249            0 :    setbuf(stdout, NULL);
   16250            0 :    setbuf(stderr, NULL);
   16251              : #ifdef SIGPIPE
   16252              :    /* avoid getting killed by "Broken pipe" signals */
   16253            0 :    signal(SIGPIPE, SIG_IGN);
   16254              : #endif
   16255              : 
   16256              : #ifdef HAVE_MONGOOSE6
   16257              :    //
   16258              :    // if running setuid-root, unconditionally bind to port 80.
   16259              :    //
   16260              : 
   16261            0 :    int socket_priviledged_port = -1;
   16262              : 
   16263              : #ifdef OS_UNIX
   16264              :    // in setuid-root mode bind to priviledged port
   16265            0 :    if (getuid() != geteuid()) {
   16266            0 :       int port80 = 80;
   16267              : 
   16268            0 :       printf("mhttpd is running in setuid-root mode.\n");
   16269              : 
   16270            0 :       socket_priviledged_port = open_listening_socket(port80);
   16271            0 :       if (socket_priviledged_port < 0) {
   16272            0 :          printf("Cannot open listening socket on TCP port %d, aborting.\n", port80);
   16273            0 :          exit(1);
   16274              :       }
   16275              : 
   16276              :       // give up root privilege
   16277            0 :       status = setuid(getuid());
   16278            0 :       if (status != 0) {
   16279            0 :          printf("Cannot give up root privelege, aborting.\n");
   16280            0 :          exit(1);
   16281              :       }
   16282            0 :       status = setuid(getuid());
   16283            0 :       if (status != 0) {
   16284            0 :          printf("Cannot give up root privelege, aborting.\n");
   16285            0 :          exit(1);
   16286              :       }
   16287              :    }
   16288              : #endif
   16289              : #endif
   16290              : 
   16291              :    char midas_hostname[256];
   16292              :    char midas_expt[256];
   16293              : 
   16294              :    /* get default from environment */
   16295            0 :    cm_get_environment(midas_hostname, sizeof(midas_hostname), midas_expt, sizeof(midas_expt));
   16296              : 
   16297              :    /* parse command line parameters */
   16298              : #ifdef HAVE_MONGOOSE6
   16299            0 :    gUserAllowedHosts.clear();
   16300              : #else
   16301            0 :    std::vector<std::string> user_hostlist;
   16302              : #endif
   16303            0 :    for (int i = 1; i < argc; i++) {
   16304            0 :       if (argv[i][0] == '-' && argv[i][1] == 'D')
   16305            0 :          daemon = TRUE;
   16306            0 :       else if (argv[i][0] == '-' && argv[i][1] == 'v')
   16307            0 :          verbose = TRUE;
   16308            0 :       else if (argv[i][0] == '-' && argv[i][1] == 'E')
   16309            0 :          elog_mode = TRUE;
   16310            0 :       else if (argv[i][0] == '-' && argv[i][1] == 'H') {
   16311            0 :          history_mode = TRUE;
   16312              : #ifdef HAVE_MONGOOSE6
   16313            0 :       } else if (strcmp(argv[i], "--http") == 0) {
   16314            0 :          if (argv[i+1]) {
   16315            0 :             user_http_port = atoi(argv[i+1]);
   16316              :          }
   16317            0 :       } else if (strcmp(argv[i], "--https") == 0) {
   16318            0 :          if (argv[i+1]) {
   16319            0 :             user_https_port = atoi(argv[i+1]);
   16320              :          }
   16321              : #endif
   16322            0 :       } else if (strcmp(argv[i], "--trace-mg") == 0) {
   16323            0 :          trace_mg = true;
   16324            0 :          trace_mg_recv = true;
   16325            0 :          trace_mg_send = true;
   16326            0 :       } else if (strcmp(argv[i], "--trace-mg-verbose") == 0) {
   16327            0 :          trace_mg_verbose = true;
   16328            0 :       } else if (strcmp(argv[i], "--no-trace-mg-recv") == 0) {
   16329            0 :          trace_mg_recv = false;
   16330            0 :       } else if (strcmp(argv[i], "--no-trace-mg-send") == 0) {
   16331            0 :          trace_mg_send = false;
   16332            0 :       } else if (strcmp(argv[i], "--verbose-mg") == 0) {
   16333            0 :          verbose_mg = true;
   16334              : #ifdef HAVE_MONGOOSE616
   16335            0 :       } else if (strcmp(argv[i], "--no-multithread") == 0) {
   16336            0 :          multithread_mg = false;
   16337            0 :       } else if (strcmp(argv[i], "--no-passwords") == 0) {
   16338            0 :          no_passwords = true;
   16339            0 :       } else if (strcmp(argv[i], "--no-hostlist") == 0) {
   16340            0 :          no_hostlist = true;
   16341              : #endif
   16342            0 :       } else if (argv[i][0] == '-') {
   16343            0 :          if (i + 1 >= argc || argv[i + 1][0] == '-')
   16344            0 :             goto usage;
   16345            0 :          if (argv[i][1] == 'h')
   16346            0 :             mstrlcpy(midas_hostname, argv[++i], sizeof(midas_hostname));
   16347            0 :          else if (argv[i][1] == 'e')
   16348            0 :             mstrlcpy(midas_expt, argv[++i], sizeof(midas_hostname));
   16349            0 :          else if (argv[i][1] == 'a') {
   16350              : #ifdef HAVE_MONGOOSE6
   16351            0 :             gUserAllowedHosts.push_back(argv[++i]);
   16352              : #else
   16353            0 :             user_hostlist.push_back(argv[++i]);
   16354              : #endif
   16355            0 :          } else if (argv[i][1] == 'p') {
   16356            0 :             printf("Option \"-p port_number\" for the old web server is obsolete.\n");
   16357            0 :             printf("mongoose web server is the new default, port number is set in ODB or with \"--http port_number\".\n");
   16358            0 :             printf("To run the obsolete old web server, please use \"--oldserver\" switch.\n");
   16359            0 :             return 1;
   16360              :          } else {
   16361            0 :           usage:
   16362            0 :             printf("usage: %s [-h Hostname[:port]] [-e Experiment] [-v] [-D] [-a Hostname]\n\n", argv[0]);
   16363            0 :             printf("       -a add hostname to the hostlist of hosts allowed to connect to mhttpd\n");
   16364            0 :             printf("       -e experiment to connect to\n");
   16365            0 :             printf("       -h connect to midas server (mserver) on given host\n");
   16366            0 :             printf("       -v display verbose HTTP communication\n");
   16367            0 :             printf("       -D become a daemon\n");
   16368            0 :             printf("       -E only display ELog system\n");
   16369            0 :             printf("       -H only display history plots\n");
   16370              : #ifdef HAVE_MONGOOSE6
   16371            0 :             printf("       --http port - bind to specified HTTP port (default is ODB \"/Experiment/midas http port\")\n");
   16372            0 :             printf("       --https port - bind to specified HTTP port (default is ODB \"/Experiment/midas https port\")\n");
   16373              : #endif
   16374            0 :             printf("       --verbose-mg - trace mongoose web requests\n");
   16375            0 :             printf("       --trace-mg - trace mongoose events\n");
   16376            0 :             printf("       --no-trace-mg-recv - do not trace mongoose recv events\n");
   16377            0 :             printf("       --no-trace-mg-send - dop not trace mongoose send events\n");
   16378              : #ifdef HAVE_MONGOOSE616
   16379            0 :             printf("       --no-multithread - disable mongoose multithreading\n");
   16380            0 :             printf("       --no-passwords - disable password protection\n");
   16381            0 :             printf("       --no-hostlist - disable access control host list\n");
   16382              : #endif
   16383            0 :             return 0;
   16384              :          }
   16385              :       }
   16386              :    }
   16387              : 
   16388            0 :    if (daemon) {
   16389            0 :       printf("Becoming a daemon...\n");
   16390            0 :       ss_daemon_init(FALSE);
   16391              :    }
   16392              : 
   16393              : #ifdef OS_LINUX
   16394              :    /* write PID file */
   16395            0 :    FILE *f = fopen("/var/run/mhttpd.pid", "w");
   16396            0 :    if (f != NULL) {
   16397            0 :       fprintf(f, "%d", ss_getpid());
   16398            0 :       fclose(f);
   16399              :    }
   16400              : #endif
   16401              : 
   16402            0 :    if (history_mode)
   16403            0 :       myname = "mhttpd_history";
   16404              : 
   16405              :    /*---- connect to experiment ----*/
   16406            0 :    status = cm_connect_experiment1(midas_hostname, midas_expt, myname, NULL,
   16407              :                                    DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
   16408            0 :    if (status == CM_WRONG_PASSWORD)
   16409            0 :       return 1;
   16410            0 :    else if (status == DB_INVALID_HANDLE) {
   16411            0 :       std::string s = cm_get_error(status);
   16412            0 :       puts(s.c_str());
   16413            0 :    } else if (status != CM_SUCCESS) {
   16414            0 :       std::string s = cm_get_error(status);
   16415            0 :       puts(s.c_str());
   16416            0 :       return 1;
   16417            0 :    }
   16418              : 
   16419              :    /* mhttpd needs the watchdog thread until we are sure
   16420              :     * we do not have any long sleeps anywhere in the mhttpd code.
   16421              :     * this includes reads from the history files or databases,
   16422              :     * that can take arbitrary long time */
   16423            0 :    cm_start_watchdog_thread();
   16424              : 
   16425              :    /* Get ODB handles */
   16426              : 
   16427              :    HNDLE hDB;
   16428              : 
   16429            0 :    cm_get_experiment_database(&hDB, NULL);
   16430              : 
   16431            0 :    MVOdb *odb = MakeMidasOdb(hDB);
   16432            0 :    gOdb = odb;
   16433              : 
   16434              :    /* do ODB record checking */
   16435            0 :    if (!check_odb_records(odb)) {
   16436              :       // check_odb_records() fails with nothing printed to the terminal
   16437              :       // because mhttpd does not print cm_msg(MERROR, ...) messages to the terminal.
   16438              :       // At least print something!
   16439            0 :       printf("check_odb_records() failed, see messages and midas.log, bye!\n");
   16440            0 :       cm_disconnect_experiment();
   16441            0 :       return 1;
   16442              :    }
   16443              : 
   16444              : #ifdef HAVE_MONGOOSE6
   16445            0 :    if (init_allowed_hosts() != SUCCESS) {
   16446            0 :       printf("init_allowed_hosts() failed, see messages and midas.log, bye!\n");
   16447            0 :       cm_disconnect_experiment();
   16448            0 :       return 1;
   16449              :    }
   16450              : 
   16451            0 :    if (verbose) {
   16452            0 :       if (gAllowedHosts.size() > 0) {
   16453            0 :          printf("mhttpd allowed hosts list: ");
   16454            0 :          for (unsigned int i=0; i<gAllowedHosts.size(); i++) {
   16455            0 :             if (i>0)
   16456            0 :                printf(", ");
   16457            0 :             printf("%s", gAllowedHosts[i].c_str());
   16458              :          }
   16459            0 :          printf("\n");
   16460              :       } else {
   16461            0 :          printf("mhttpd allowed hosts list is empty\n");
   16462              :       }
   16463              :    }
   16464              : 
   16465              :    // populate the MIME.types table
   16466            0 :    SaveMimetypes(odb->Chdir("WebServer/mime.types", true));
   16467              : #endif
   16468              : 
   16469              :    /* initialize odb entries needed for mhttpd and midas web pages */
   16470            0 :    init_mhttpd_odb(odb);
   16471              : 
   16472              :    /* initialize menu buttons */
   16473            0 :    init_menu_buttons(odb);
   16474              : 
   16475              :    /* initialize elog odb entries */
   16476            0 :    init_elog_odb();
   16477              : 
   16478              :    /* initialize the JSON RPC handlers */
   16479            0 :    mjsonrpc_init();
   16480            0 :    mjsonrpc_set_std_mutex(&gMutex);
   16481              : 
   16482            0 :    add_rpc_functions();
   16483              : 
   16484              : #ifdef HAVE_MONGOOSE6
   16485            0 :    status = start_mg(user_http_port, user_https_port, socket_priviledged_port, verbose);
   16486            0 :    if (status != SUCCESS) {
   16487              :       // At least print something!
   16488            0 :       printf("could not start the mongoose web server, see messages and midas.log, bye!\n");
   16489            0 :       cm_disconnect_experiment();
   16490            0 :       return 1;
   16491              :    }
   16492              : #endif
   16493              :    
   16494              : #ifdef HAVE_MONGOOSE616
   16495              : 
   16496              : #ifdef SIGPIPE
   16497              : #ifdef SIG_IGN
   16498            0 :    signal(SIGPIPE, SIG_IGN);
   16499              : #endif
   16500              : #endif
   16501              : 
   16502            0 :    if (!gTraceBuf) {
   16503            0 :       gTraceBuf = new RequestTraceBuf;
   16504              :    }
   16505              : 
   16506              :    //if (!request_mutex) {
   16507              :    //   status = ss_mutex_create(&request_mutex, FALSE);
   16508              :    //   assert(status==SS_SUCCESS || status==SS_CREATED);
   16509              :    //}
   16510              : 
   16511              :    /* establish Ctrl-C handler - will set _abort to TRUE */
   16512            0 :    ss_ctrlc_handler(ctrlc_handler);
   16513              : 
   16514            0 :    MVOdb* o = odb->Chdir("WebServer", true);
   16515            0 :    status = mongoose_init(o, no_passwords, no_hostlist, user_hostlist);
   16516            0 :    if (status != SUCCESS) {
   16517              :       // At least print something!
   16518            0 :       printf("Error: Could not start the mongoose web server, see messages and midas.log, bye!\n");
   16519            0 :       cm_disconnect_experiment();
   16520            0 :       return 1;
   16521              :    }
   16522              : 
   16523            0 :    delete o;
   16524              : #endif
   16525              : 
   16526              : #ifdef HAVE_MONGOOSE6
   16527            0 :    loop_mg();
   16528            0 :    stop_mg();
   16529              : #endif
   16530              : 
   16531              : #ifdef HAVE_MONGOOSE616
   16532            0 :    while (!_abort) {
   16533              : 
   16534              :       /* cm_yield() is not thread safe, need to take a lock */
   16535              : 
   16536              :       //status = ss_mutex_wait_for(request_mutex, 0);
   16537            0 :       gMutex.lock();
   16538              : 
   16539              :       /* check for shutdown message */
   16540            0 :       status = cm_yield(0);
   16541            0 :       if (status == RPC_SHUTDOWN)
   16542            0 :          break;
   16543              : 
   16544            0 :       gMutex.unlock();
   16545              :       //status = ss_mutex_release(request_mutex);
   16546              : 
   16547              :       //ss_sleep(10);
   16548              : 
   16549            0 :       mongoose_poll(10);
   16550              :    }
   16551              : 
   16552            0 :    mongoose_cleanup();
   16553              : #endif
   16554              : 
   16555            0 :    if (gMh) {
   16556            0 :       delete gMh;
   16557            0 :       gMh = NULL;
   16558            0 :       gMhkey = 0;
   16559              :    }
   16560              : 
   16561            0 :    mjsonrpc_exit();
   16562            0 :    cm_disconnect_experiment();
   16563            0 :    return 0;
   16564            0 : }
   16565              : 
   16566              : /* emacs
   16567              :  * Local Variables:
   16568              :  * tab-width: 8
   16569              :  * c-basic-offset: 3
   16570              :  * indent-tabs-mode: nil
   16571              :  * End:
   16572              :  */
        

Generated by: LCOV version 2.0-1