MIDAS
Loading...
Searching...
No Matches
mhttpd.cxx
Go to the documentation of this file.
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
42static MUTEX_T* request_mutex = NULL;
43#endif
44
45#define OBSOLETE 1 // obsolete code scheduled for removal. K.O.
46
47static std::mutex gMutex;
48static MVOdb* gOdb = NULL;
49
50/*------------------------------------------------------------------*/
51
52#define MAX_GROUPS 32
53#define MAX_VARS 100
54
55/*------------------------------------------------------------------*/
56
57static std::string toString(int i)
58{
59 char buf[256];
60 sprintf(buf, "%d", i);
61 return buf;
62}
63
64/*------------------------------------------------------------------*/
65
67{
68public:
70 size_t attachment_size[3];
71public:
72 Attachment() // ctor
73 {
74 for (int i=0; i<3; i++) {
75 attachment_buffer[i] = NULL;
76 attachment_size[i] = 0;
77 }
78 }
79 ~Attachment() // dtor
80 {
81 for (int i=0; i<3; i++) {
82 clear(i);
83 }
84 }
85 void clear(int i)
86 {
87 if (attachment_size[i]) {
88 attachment_size[i] = 0;
89 free(attachment_buffer[i]);
90 attachment_buffer[i] = NULL;
91 }
92 }
93};
97
98// month name from midas.c
99extern const char *mname[];
100
101static 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
116static const char default_system_list[20][NAME_LENGTH] = {
117 "General",
118 "DAQ",
119 "Detector",
120 "Electronics",
121 "Target",
122 "Beamline"
123};
124
126 std::string ext;
127 std::string mimetype;
128};
129
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
179static MVOdb* gMimeTypesOdb = NULL;
180
181static std::string GetMimetype(const std::string& ext)
182{
183 if (gMimeTypesOdb) {
184 std::string mimetype;
185 gMimeTypesOdb->RS(ext.c_str(), &mimetype);
186 if (mimetype.length() > 0) {
187 //printf("GetMimetype: %s -> %s from ODB\n", ext.c_str(), mimetype.c_str());
188 return mimetype;
189 }
190 }
191
192 for (int i=0; gMimetypeTable[i].ext[0]; i++) {
193 if (ext == gMimetypeTable[i].ext) {
194 //printf("GetMimetype: %s -> %s from built-in table\n", ext.c_str(), gMimetypeTable[i].mimetype.c_str());
195 return gMimetypeTable[i].mimetype;
196 }
197 }
198
199 //printf("GetMimetype: %s -> not found\n", ext.c_str());
200 return "";
201}
202
203static void SaveMimetypes(MVOdb* odb)
204{
205 gMimeTypesOdb = odb;
206
207 for (int i=0; gMimetypeTable[i].ext.length() > 0; i++) {
208 std::string tmp = gMimetypeTable[i].mimetype;
209 gMimeTypesOdb->RS(gMimetypeTable[i].ext.c_str(), &tmp, true);
210 }
211}
212
213#define HTTP_ENCODING "UTF-8"
214
215/*------------------------------------------------------------------*/
216
217const 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
282const 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
327class Param;
328class Return;
329void show_hist_page(MVOdb* odb, Param* p, Return* r, const char *dec_path, char *buffer, int *buffer_size, int refresh);
330int 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);
333void 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);
335void get_elog_url(char *url, int len);
336void show_header(Return* r, const char *title, const char *method, const char *path, int refresh);
337void show_navigation_bar(Return* r, const char *cur_page);
338
339/*------------------------------------------------------------------*/
340
341char *stristr(const char *str, const char *pattern)
342{
343 char c1, c2, *ps, *pp;
344
345 if (str == NULL || pattern == NULL)
346 return NULL;
347
348 while (*str) {
349 ps = (char *) str;
350 pp = (char *) pattern;
351 c1 = *ps;
352 c2 = *pp;
353 if (toupper(c1) == toupper(c2)) {
354 while (*pp) {
355 c1 = *ps;
356 c2 = *pp;
357
358 if (toupper(c1) != toupper(c2))
359 break;
360
361 ps++;
362 pp++;
363 }
364
365 if (!*pp)
366 return (char *) str;
367 }
368 str++;
369 }
370
371 return NULL;
372}
373
374/*------------------------------------------------------------------*/
375
376static double GetTimeSec()
377{
378 struct timeval tv;
379 gettimeofday(&tv, NULL);
380 return tv.tv_sec*1.0 + tv.tv_usec/1000000.0;
381}
382
383/*------------------------------------------------------------------*/
384
386{
387public:
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
401public:
402 RequestTrace() // ctor
403 {
404 fTimeReceived = 0;
405 fTimeLocked = 0;
406 fTimeUnlocked = 0;
407 fTimeProcessed = 0;
408 fTimeSent = 0;
409 fCompleted = false;
410 fAuthOk = false;
411 }
412
413 void PrintTrace0() const
414 {
415 printf("%.3f ", fTimeReceived);
416 printf("%.3f ", fTimeLocked-fTimeReceived);
417 printf("%.3f ", fTimeUnlocked-fTimeLocked);
418 printf("%.3f ", fTimeProcessed-fTimeUnlocked);
419 printf("%.3f ", fTimeSent-fTimeProcessed);
420 printf("A ");
421 printf("%d ", fAuthOk);
422 printf("T ");
423 printf("%.3f ", fTimeSent-fTimeReceived);
424 printf("%.3f ", fTimeLocked-fTimeReceived);
425 printf("%.3f ", fTimeProcessed-fTimeLocked);
426 printf("M %s ", fMethod.c_str());
427 printf("URL %s ", fUri.c_str());
428 if (fRPC.length() > 0) {
429 printf("RPC %s ", fRPC.c_str());
430 }
431 printf("\n");
432 };
433};
434
435static int http_trace = 0;
436
438{
439public:
441 std::vector<RequestTrace*> fBuf;
442
443public:
445 {
446 int status;
448 assert(status==SS_SUCCESS || status==SS_CREATED);
449 }
450
451 //RequestTrace* NewTrace() // RequestTrace factory
452 //{
453 // RequestTrace* t = new RequestTrace;
454 // fBuf.push_back(t);
455 // return t;
456 //}
457
459 {
460 fBuf.push_back(t);
461 }
462
464 {
466 if (http_trace) {
467 t->PrintTrace0();
468 }
469 delete t;
470 //AddTrace(t);
472 }
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
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
514{
515public:
516
519
522
523public:
524 Return() // ctor
525 {
527 return_buffer = (char*)malloc(return_size);
528 assert(return_buffer != NULL);
529
530 strlen_retbuf = 0;
531 return_length = 0;
532 }
533
534 ~Return() // dtor
535 {
536 if (return_buffer)
537 free(return_buffer);
538 return_buffer = NULL;
539 return_size = 0;
540 strlen_retbuf = 0;
541 return_length = 0;
542 }
543
544 void reset()
545 {
546 strlen_retbuf = 0;
547 }
548
549 void zero()
550 {
551 memset(return_buffer, 0, return_size);
552 strlen_retbuf = 0;
553 return_length = 0;
554 }
555
556int return_grow(size_t len)
557{
558 //printf("size %d, grow %d, room %d\n", return_size, len, return_size - strlen_retbuf);
559
560 for (int i=0; i<1000; i++) { // infinite loop with protection against infinite looping
561 if (strlen_retbuf + len < return_size-40)
562 return SUCCESS;
563
564 return_size *= 2;
565 return_buffer = (char*)realloc(return_buffer, return_size);
566
568
569 //printf("new size %d\n", return_size);
570 }
571
572 assert(!"Cannot happen!"); // does not return
573 return 0;
574}
575
576/*------------------------------------------------------------------*/
577
578void rmemcpy(const void *buf, int len)
579{
580 return_grow(len);
581 memcpy(return_buffer + strlen_retbuf, buf, len);
582 strlen_retbuf += len;
584}
585
586/*------------------------------------------------------------------*/
587
588void rread(const char* filename, int fh, int len)
589{
590 return_grow(len);
591 int rd = read(fh, return_buffer + strlen_retbuf, len);
592 if (rd != len) {
593 cm_msg(MERROR, "rread", "Cannot read file \'%s\', read of %d returned %d, errno %d (%s)", filename, len, rd, errno, strerror(errno));
594 memset(return_buffer + strlen_retbuf, 0, len);
595 }
596 strlen_retbuf += len;
598}
599
600/*------------------------------------------------------------------*/
601
602void rsputs(const char *str)
603{
604 size_t len = strlen(str);
605
606 return_grow(len);
607
608 if (strlen_retbuf + len > return_size-40) {
609 strcpy(return_buffer, "<H1>Error: return buffer too small</H1>");
611 } else {
613 strlen_retbuf += len;
614 }
615
617}
618
619/*------------------------------------------------------------------*/
620
621void 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);
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;
662 }
663
665}
666
667/*------------------------------------------------------------------*/
668
669void rsprintf(const char *format, ...) MATTRPRINTF(2,3)
670{
671 va_list argptr;
672 char str[10000];
673
674 va_start(argptr, format);
675 vsprintf(str, (char *) format, argptr);
676 va_end(argptr);
677
678 // catch array overrun. better too late than never...
679 assert(strlen(str) < sizeof(str));
680
681 return_grow(strlen(str));
682
684 strcpy(return_buffer, "<H1>Error: return buffer too small</H1>");
685 else
687
688 strlen_retbuf += strlen(str);
690}
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
701class Param
702{
703public:
707
708public:
709 Param() // ctor
710 {
711 initparam();
712 }
713
714 ~Param() // dtor
715 {
716 freeparam();
717 }
718
720 {
721 memset(_param, 0, sizeof(_param));
722 memset(_value, 0, sizeof(_value));
723 _text[0] = 0;
724 }
725
726 void setparam(const char *param, const char *value)
727 {
728 int i;
729
730 if (equal_ustring(param, "text")) {
731 if (strlen(value) >= TEXT_SIZE)
732 printf("Error: parameter value too big\n");
733
734 mstrlcpy(_text, value, TEXT_SIZE);
735 _text[TEXT_SIZE - 1] = 0;
736 return;
737 }
738
739 for (i = 0; i < MAX_PARAM; i++)
740 if (_param[i][0] == 0)
741 break;
742
743 if (i < MAX_PARAM) {
744 mstrlcpy(_param[i], param, PARAM_LENGTH);
745
746 int size = strlen(value)+1;
747 _value[i] = (char*)malloc(size);
748 mstrlcpy(_value[i], value, size);
749 _value[i][strlen(value)] = 0;
750
751 } else {
752 printf("Error: parameter array too small\n");
753 }
754 }
755
757 {
758 int i;
759
760 for (i=0 ; i<MAX_PARAM ; i++)
761 if (_value[i] != NULL) {
762 free(_value[i]);
763 _value[i] = NULL;
764 }
765 }
766
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 const char *getparam(const char *param)
777 {
778 int i;
779
780 if (equal_ustring(param, "text"))
781 return _text;
782
783 for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
785 break;
786
787 if (i == MAX_PARAM)
788 return NULL;
789
790 if (_value[i] == NULL)
791 return "";
792
793 return _value[i];
794 }
795
796 std::string xgetparam(const char *param)
797 {
798 const char* s = getparam(param);
799 if (s)
800 return s;
801 else
802 return "";
803 }
804
805 BOOL isparam(const char *param)
806 {
807 int i;
808
809 for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
811 break;
812
813 if (i < MAX_PARAM && _param[i][0])
814 return TRUE;
815
816 return FALSE;
817 }
818
819 void unsetparam(const char *param)
820 {
821 int i;
822
823 for (i = 0; i < MAX_PARAM; i++)
825 break;
826
827 if (i < MAX_PARAM) {
828 _param[i][0] = 0;
829 _value[i][0] = 0;
830 }
831 }
832};
833
834/*------------------------------------------------------------------*/
835
836const char *mhttpd_revision(void)
837{
838 return cm_get_revision();
839}
840
841/*------------------------------------------------------------------*/
842
843static std::string UrlDecode(const char* p)
844/********************************************************************\
845 Decode the given string in-place by expanding %XX escapes
846\********************************************************************/
847{
848 std::string s;
849
850 //printf("URL decode: [%s] --> ", p);
851
852 while (*p) {
853 if (*p == '%') {
854 /* Escape: next 2 chars are hex representation of the actual character */
855 p++;
856 if (isxdigit(p[0]) && isxdigit(p[1])) {
857 int i = 0;
858 char str[3];
859 str[0] = p[0];
860 str[1] = p[1];
861 str[2] = 0;
862 sscanf(str, "%02X", &i);
863
864 s += (char) i;
865 p += 2;
866 } else
867 s += '%';
868 } else if (*p == '+') {
869 /* convert '+' to ' ' */
870 s += ' ';
871 p++;
872 } else {
873 s += *p++;
874 }
875 }
876
877 //printf("[%s]\n", s.c_str());
878
879 return s;
880}
881
882static 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 pD = p;
894 while (*p) {
895 if (*p == '%') {
896 /* Escape: next 2 chars are hex representation of the actual character */
897 p++;
898 if (isxdigit(p[0]) && isxdigit(p[1])) {
899 str[0] = p[0];
900 str[1] = p[1];
901 str[2] = 0;
902 sscanf(str, "%02X", &i);
903
904 *pD++ = (char) i;
905 p += 2;
906 } else
907 *pD++ = '%';
908 } else if (*p == '+') {
909 /* convert '+' to ' ' */
910 *pD++ = ' ';
911 p++;
912 } else {
913 *pD++ = *p++;
914 }
915 }
916 *pD = '\0';
917
918 //printf("[%s]\n", px);
919}
920
921static 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 int len = strlen(ps);
929 char *str = (char*)malloc(len*3 + 10); // at worst, each input character is expanded into 3 output characters
930
931 pd = str;
932 p = ps;
933 while (*p) {
934 if (isalnum(*p)) {
935 *pd++ = *p++;
936 } else {
937 sprintf(pd, "%%%02X", (*p)&0xFF);
938 pd += 3;
939 p++;
940 }
941 }
942 *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 mstrlcpy(ps, str, ps_size);
954 free(str);
955}
956
957static 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 std::string encoded;
964
965 const char* p = text;
966 while (*p) {
967 if (isalnum(*p)) {
968 encoded += *p++;
969 } else {
970 char buf[16];
971 sprintf(buf, "%%%02X", (*p)&0xFF);
972 encoded += buf;
973 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 return encoded;
987}
988
989/*------------------------------------------------------------------*/
990
991std::vector<std::string> get_resource_paths()
992{
993 HNDLE hDB;
994 int status;
995
997
998 std::vector<std::string> paths;
999
1000 // add /Experiment/Resources
1001 std::string buf;
1002 status = db_get_value_string(hDB, 0, "/Experiment/Resources", 0, &buf, TRUE);
1003 if (status == DB_SUCCESS && buf.length() > 0)
1004 paths.push_back(buf);
1005
1006 // add "/Logger/History/IMAGE/History dir"
1007 paths.push_back(cm_get_history_path("IMAGE"));
1008
1009 // add /Logger/Data dir
1010 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &buf, TRUE);
1011 if (status == DB_SUCCESS && buf.length() > 0)
1012 paths.push_back(buf);
1013
1014 std::string cwd = ss_getcwd();
1015 if (!cwd.empty()) {
1016 paths.push_back(cwd + "/");
1017 paths.push_back(cwd + "/resources/");
1018 }
1019 paths.push_back(cm_get_path());
1020 paths.push_back(cm_get_path() + "resources/");
1021 char *m = getenv("MIDASSYS");
1022 if (m) {
1023 paths.push_back(std::string(m) + "/resources/");
1024 }
1025
1026 return paths;
1027}
1028
1029/*------------------------------------------------------------------*/
1030
1031bool 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 if (strlen(filename) < 1) {
1038 cm_msg(MERROR, "open_resource_file", "Invalid resource file name \'%s\' is too short",
1039 filename);
1040 return false;
1041 }
1042
1043 if (filename[0] == DIR_SEPARATOR) {
1044 cm_msg(MERROR, "open_resource_file", "Invalid resource file name \'%s\' starting with \'%c\' which is not allowed",
1045 filename, DIR_SEPARATOR);
1046 return false;
1047 }
1048
1049 if (strstr(filename, "..") != NULL) {
1050 cm_msg(MERROR, "open_resource_file", "Invalid resource file name \'%s\' containing \'..\' which is not allowed",
1051 filename);
1052 return false;
1053 }
1054
1055 std::vector<std::string> paths = get_resource_paths();
1056
1057 std::vector<std::string> paths_not_found;
1058
1059 for (unsigned i=0; i<paths.size(); i++) {
1060 std::string path = paths[i];
1061 if (path.length() < 1)
1062 continue;
1063 if (path[0] == '#')
1064 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 std::string xpath = cm_expand_env(path.c_str());
1072
1073 if (xpath[xpath.length()-1] != DIR_SEPARATOR)
1074 xpath += DIR_SEPARATOR_STR;
1075 xpath += filename;
1076
1077 //printf("path [%s] [%s] [%s]\n", paths[i].c_str(), path.c_str(), xpath.c_str());
1078
1079 FILE* fp = fopen(xpath.c_str(), "r");
1080 if (fp) {
1081 struct stat statbuf;
1082 int status = fstat(fileno(fp), &statbuf);
1083 if (status != 0) {
1084 cm_msg(MERROR, "open_resource_file", "Cannot fstat() file \'%s\', error %d (%s)", xpath.c_str(), errno, strerror(errno));
1085 fclose(fp);
1086 fp = NULL;
1087 }
1088
1089 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 } else if (statbuf.st_mode & S_IFDIR) {
1096 cm_msg(MERROR, "open_resource_file", "File \'%s\' for resource \'%s\' is a directory", xpath.c_str(), filename);
1097 fclose(fp);
1098 fp = NULL;
1099 } else {
1100 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 fclose(fp);
1102 fp = NULL;
1103 }
1104
1105 if (fp) {
1106 if (ppath)
1107 *ppath = xpath;
1108 if (pfp) {
1109 *pfp = fp;
1110 } else {
1111 fclose(fp);
1112 fp = NULL;
1113 }
1114 //cm_msg(MINFO, "open_resource_file", "Resource file \'%s\' is \'%s\'", filename, xpath.c_str());
1115 return true;
1116 }
1117 }
1118
1119 paths_not_found.push_back(xpath);
1120 }
1121
1122 std::string s;
1123 for (unsigned i=0; i<paths_not_found.size(); i++) {
1124 if (i>0)
1125 s += ", ";
1126 s += paths_not_found[i];
1127 }
1128
1129 cm_msg(MERROR, "open_resource_file", "Cannot find resource file \'%s\', tried %s", filename, s.c_str());
1130 return false;
1131}
1132
1133/*------------------------------------------------------------------*/
1134
1135std::string get_content_type(const char* filename)
1136{
1137 std::string ext_upper;
1138 const char* p = filename;
1139 const char* last_dot = NULL;
1140 for (; *p; p++) {
1141 if (*p == '.')
1142 last_dot = p;
1143 if (*p == DIR_SEPARATOR)
1144 last_dot = NULL;
1145 }
1146
1147 if (last_dot) {
1148 p = last_dot;
1149 for (; *p; p++)
1150 ext_upper += toupper(*p);
1151 }
1152
1153 //printf("filename: [%s], ext [%s]\n", filename, ext_upper.c_str());
1154
1155 std::string type = GetMimetype(ext_upper);
1156 if (type.length() > 0)
1157 return type;
1158
1159 cm_msg(MERROR, "get_content_type", "Unknown HTTP Content-Type for resource file \'%s\', file extension \'%s\'", filename, ext_upper.c_str());
1160
1161 return "text/plain";
1162}
1163
1164/*------------------------------------------------------------------*/
1165
1166bool send_fp(Return* r, const std::string& path, FILE* fp)
1167{
1168 assert(fp != NULL);
1169
1170 // send HTTP headers
1171
1172 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1173 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1174 r->rsprintf("Accept-Ranges: bytes\r\n");
1175
1176 // send HTTP cache control headers
1177
1178 time_t now = time(NULL);
1179 now += (int) (3600 * 24);
1180 struct tm gmt_tms;
1181 gmtime_r(&now, &gmt_tms);
1182 const char* format = "%A, %d-%b-%y %H:%M:%S GMT";
1183
1184 char str[256];
1185 strftime(str, sizeof(str), format, &gmt_tms);
1186 r->rsprintf("Expires: %s\r\n", str);
1187
1188 // send Content-Type header
1189
1190 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 fstat(fileno(fp), &stat_buf);
1196 int length = stat_buf.st_size;
1197 r->rsprintf("Content-Length: %d\r\n", length);
1198
1199 // send end of headers
1200
1201 r->rsprintf("\r\n");
1202
1203 // send file data
1204
1205 r->rread(path.c_str(), fileno(fp), length);
1206
1207 fclose(fp);
1208
1209 return true;
1210}
1211
1212bool send_file(Return* r, const std::string& path, bool generate_404 = true)
1213{
1214 FILE *fp = fopen(path.c_str(), "rb");
1215
1216 if (!fp) {
1217 if (generate_404) {
1218 /* header */
1219 r->rsprintf("HTTP/1.1 404 Not Found\r\n");
1220 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1221 r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
1222 r->rsprintf("\r\n");
1223 r->rsprintf("Error: Cannot read \"%s\", fopen() errno %d (%s)\n", path.c_str(), errno, strerror(errno));
1224 }
1225 return false;
1226 }
1227
1228 return send_fp(r, path, fp);
1229}
1230
1231bool send_resource(Return* r, const std::string& name, bool generate_404 = true)
1232{
1233 std::string path;
1234 FILE *fp = NULL;
1235
1236 bool found = open_resource_file(name.c_str(), &path, &fp);
1237
1238 if (!found) {
1239 if (generate_404) {
1240 /* header */
1241 r->rsprintf("HTTP/1.1 404 Not Found\r\n");
1242 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1243 r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
1244 r->rsprintf("\r\n");
1245 r->rsprintf("Error: resource file \"%s\" not found, see messages\n", name.c_str());
1246 }
1247 return false;
1248 }
1249
1250 return send_fp(r, path, fp);
1251}
1252
1253/*------------------------------------------------------------------*/
1254
1255INT 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 if (verbose)
1263 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 s = socket(AF_INET, SOCK_STREAM, 0);
1267 if (s == -1)
1268 return -1;
1269
1270 /* connect to remote node port 25 */
1271 memset(&bind_addr, 0, sizeof(bind_addr));
1272 bind_addr.sin_family = AF_INET;
1273 bind_addr.sin_port = htons((short) 25);
1274
1275 phe = gethostbyname(smtp_host);
1276 if (phe == NULL)
1277 return -1;
1278 memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
1279
1280 if (connect(s, (const sockaddr*)&bind_addr, sizeof(bind_addr)) < 0) {
1281 closesocket(s);
1282 return -1;
1283 }
1284
1285 strsize = TEXT_SIZE + 1000;
1286 str = (char*)malloc(strsize);
1287
1288 assert(str != NULL);
1289
1290 recv_string(s, str, strsize, 3000);
1291 if (verbose)
1292 puts(str);
1293
1294 /* drain server messages */
1295 do {
1296 str[0] = 0;
1297 recv_string(s, str, strsize, 300);
1298 if (verbose)
1299 puts(str);
1300 } while (str[0]);
1301
1302 sprintf(str, "HELO %s\r\n", from_host);
1303 send(s, str, strlen(str), 0);
1304 if (verbose)
1305 puts(str);
1306 recv_string(s, str, strsize, 3000);
1307 if (verbose)
1308 puts(str);
1309
1310 if (strchr(from, '<')) {
1311 mstrlcpy(buf, strchr(from, '<') + 1, sizeof(buf));
1312 if (strchr(buf, '>'))
1313 *strchr(buf, '>') = 0;
1314 } else
1315 mstrlcpy(buf, from, sizeof(buf));
1316
1317 sprintf(str, "MAIL FROM: %s\n", buf);
1318 send(s, str, strlen(str), 0);
1319 if (verbose)
1320 puts(str);
1321 recv_string(s, str, strsize, 3000);
1322 if (verbose)
1323 puts(str);
1324
1325 sprintf(str, "RCPT TO: <%s>\r\n", to);
1326 send(s, str, strlen(str), 0);
1327 if (verbose)
1328 puts(str);
1329 recv_string(s, str, strsize, 3000);
1330 if (verbose)
1331 puts(str);
1332
1333 sprintf(str, "DATA\r\n");
1334 send(s, str, strlen(str), 0);
1335 if (verbose)
1336 puts(str);
1337 recv_string(s, str, strsize, 3000);
1338 if (verbose)
1339 puts(str);
1340
1341 sprintf(str, "To: %s\r\nFrom: %s\r\nSubject: %s\r\n", to, from, subject);
1342 send(s, str, strlen(str), 0);
1343 if (verbose)
1344 puts(str);
1345
1346 sprintf(str, "X-Mailer: mhttpd revision %s\r\n", mhttpd_revision());
1347 send(s, str, strlen(str), 0);
1348 if (verbose)
1349 puts(str);
1350
1351 ss_tzset(); // required for localtime_r()
1352 time_t now;
1353 time(&now);
1354 struct tm tms;
1355 localtime_r(&now, &tms);
1356 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tms);
1357 offset = (-(int) timezone);
1358 if (tms.tm_isdst)
1359 offset += 3600;
1360 sprintf(str, "Date: %s %+03d%02d\r\n", buf, (int) (offset / 3600),
1361 (int) ((abs((int) offset) / 60) % 60));
1362 send(s, str, strlen(str), 0);
1363 if (verbose)
1364 puts(str);
1365
1366 sprintf(str, "Content-Type: TEXT/PLAIN; charset=US-ASCII\r\n\r\n");
1367 send(s, str, strlen(str), 0);
1368 if (verbose)
1369 puts(str);
1370
1371 /* analyze text for "." at beginning of line */
1372 const char* p = text;
1373 str[0] = 0;
1374 while (strstr(p, "\r\n.\r\n")) {
1375 i = (POINTER_T) strstr(p, "\r\n.\r\n") - (POINTER_T) p + 1;
1376 mstrlcat(str, p, i);
1377 p += i + 4;
1378 mstrlcat(str, "\r\n..\r\n", strsize);
1379 }
1380 mstrlcat(str, p, strsize);
1381 mstrlcat(str, "\r\n", strsize);
1382 send(s, str, strlen(str), 0);
1383 if (verbose)
1384 puts(str);
1385
1386 /* send ".<CR>" to signal end of message */
1387 sprintf(str, ".\r\n");
1388 send(s, str, strlen(str), 0);
1389 if (verbose)
1390 puts(str);
1391 recv_string(s, str, strsize, 3000);
1392 if (verbose)
1393 puts(str);
1394
1395 sprintf(str, "QUIT\n");
1396 send(s, str, strlen(str), 0);
1397 if (verbose)
1398 puts(str);
1399 recv_string(s, str, strsize, 3000);
1400 if (verbose)
1401 puts(str);
1402
1403 closesocket(s);
1404 free(str);
1405
1406 return 1;
1407}
1408
1409/*------------------------------------------------------------------*/
1410
1411void redirect(Return *r, const char *path)
1412{
1413 char str[256];
1414
1415 //printf("redirect to [%s]\n", path);
1416
1417 mstrlcpy(str, path, sizeof(str));
1418 if (str[0] == 0)
1419 strcpy(str, "./");
1420
1421 /* redirect */
1422 r->rsprintf("HTTP/1.1 302 Found\r\n");
1423 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1424 r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
1425
1426 if (strncmp(path, "http:", 5) == 0)
1427 r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
1428 else if (strncmp(path, "https:", 6) == 0)
1429 r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
1430 else {
1431 r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
1432 }
1433}
1434
1435void redirect_307(Return *r, const char *path)
1436{
1437 //printf("redirect_307 to [%s]\n", path);
1438
1439 /* redirect */
1440 r->rsprintf("HTTP/1.1 307 Temporary Redirect\r\n");
1441 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1442 r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
1443 r->rsprintf("Location: %s\r\n", path);
1444 r->rsprintf("\r\n");
1445 r->rsprintf("<html>redirect to %s</html>\r\n", path);
1446}
1447
1448void redirect2(Return* r, const char *path)
1449{
1450 redirect(r, path);
1451}
1452
1453/*------------------------------------------------------------------*/
1454
1456{
1458 const char* search_name;
1459};
1460
1462{
1463 search_data* sinfo = (search_data*)info;
1464 int i;
1465 INT size, status;
1466
1467 Return* r = sinfo->r;
1468 const char* search_name = sinfo->search_name;
1469
1470 /* convert strings to uppercase */
1471
1472 char xstr1[MAX_ODB_PATH];
1473 for (i = 0; key->name[i]; i++)
1474 xstr1[i] = toupper(key->name[i]);
1475 xstr1[i] = 0;
1476
1477 char str2[MAX_ODB_PATH];
1478 for (i = 0; search_name[i] ; i++)
1479 str2[i] = toupper(search_name[i]);
1480 str2[i] = 0;
1481
1482 if (strstr(xstr1, str2) != NULL) {
1483 char data[10000];
1484 std::string path = db_get_path(hDB, hKey).substr(1);
1485 std::string path_encoded = urlEncode(path.c_str());
1486
1487 if (key->type == TID_KEY || key->type == TID_LINK) {
1488 /* for keys, don't display data value */
1489 r->rsprintf("<tr><td class=\"ODBkey\"><a href=\"?cmd=odb&odb_path=/%s\">/%s</a></tr>\n", path_encoded.c_str(), path.c_str());
1490 } else {
1491 /* strip variable name from path */
1492 char* p = const_cast<char *>(path.data() + path.length() - 1);
1493 while (*p && *p != '/')
1494 *p-- = 0;
1495 if (*p == '/')
1496 *p = 0;
1497
1498 /* display single value */
1499 if (key->num_values == 1) {
1500 size = sizeof(data);
1501 status = db_get_data(hDB, hKey, data, &size, key->type);
1502 std::string data_str;
1503 if (status == DB_NO_ACCESS)
1504 data_str = "<no read access>";
1505 else
1506 data_str = db_sprintf(data, key->item_size, 0, key->type);
1507
1508 r->rsprintf("<tr><td class=\"ODBkey\">");
1509 r->rsprintf("<a href=\"?cmd=odb&odb_path=/%s\">/%s/%s</a></td>", path_encoded.c_str(), path.c_str(), key->name);
1510 r->rsprintf("<td class=\"ODBvalue\">%s</td></tr>\n", data_str.c_str());
1511 } else {
1512 /* display first value */
1513 i = key->num_values;
1514 if (i > 10)
1515 i = 11;
1516 r->rsprintf("<tr><td rowspan=%d class=\"ODBkey\">", i);
1517 r->rsprintf("<a href=\"?cmd=odb&odb_path=/%s\">/%s/%s\n", path_encoded.c_str(), path.c_str(), key->name);
1518
1519 for (int i = 0; i < key->num_values; i++) {
1520 size = sizeof(data);
1521 db_get_data_index(hDB, hKey, data, &size, i, key->type);
1522
1523 std::string data_str = db_sprintf(data, key->item_size, 0, key->type);
1524
1525 if (i > 0)
1526 r->rsprintf("<tr>");
1527
1528 r->rsprintf("<td class=\"ODBvalue\">[%d] %s</td></tr>\n", i, data_str.c_str());
1529
1530 if (i > 8) {
1531 r->rsprintf("<tr><td class=\"ODBvalue\">... [%d] values ...</td></tr>\n", key->num_values - i - 1);
1532 break;
1533 }
1534 }
1535 }
1536 }
1537 }
1538
1539 return SUCCESS;
1540}
1541
1542/*------------------------------------------------------------------*/
1543
1544void show_help_page(Return* r, const char* dec_path)
1545{
1546 const char *s;
1547 char str[256];
1548 int status;
1549
1550 show_header(r, "Help", "", "./", 0);
1551 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
1552 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
1553 show_navigation_bar(r, "Help");
1554
1555 r->rsprintf("<table class=\"mtable\" style=\"width: 95%%\">\n");
1556 r->rsprintf(" <tr>\n");
1557 r->rsprintf(" <td class=\"mtableheader\">MIDAS Help Page</td>\n");
1558 r->rsprintf(" </tr>\n");
1559 r->rsprintf(" <tr>\n");
1560 r->rsprintf(" <td>\n");
1561 r->rsprintf(" <table>\n");
1562
1563 r->rsprintf(" <tr>\n");
1564 r->rsprintf(" <td style=\"text-align:right;\">Documentation:</td>\n");
1565 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://midas.triumf.ca\">https://midas.triumf.ca</a></td>\n");
1566 r->rsprintf(" </tr>\n");
1567 r->rsprintf(" <tr>\n");
1568 r->rsprintf(" <td style=\"text-align:right;\">Discussion Forum:</td>\n");
1569 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://midas.triumf.ca/forum/\">https://midas.triumf.ca/forum/</a></td>\n");
1570 r->rsprintf(" </tr>\n");
1571 r->rsprintf(" <tr>\n");
1572 r->rsprintf(" <td style=\"text-align:right;\">Code:</td>\n");
1573 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://bitbucket.org/tmidas/midas/\">https://bitbucket.org/tmidas/midas/</a></td>\n");
1574 r->rsprintf(" </tr>\n");
1575 r->rsprintf(" <tr>\n");
1576 r->rsprintf(" <td style=\"text-align:right;\">Report a bug:</td>\n");
1577 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://bitbucket.org/tmidas/midas/issues/\">https://bitbucket.org/tmidas/midas/issues/</a></td>\n");
1578 r->rsprintf(" </tr>\n");
1579
1580 r->rsprintf(" <tr>\n");
1581 r->rsprintf(" <td style=\"text-align:right;\">Version:</td>\n");
1582 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_version());
1583 r->rsprintf(" </tr>\n");
1584 r->rsprintf(" <tr>\n");
1585 r->rsprintf(" <td style=\"text-align:right;\">Revision:</td>\n");
1586 std::string rev = cm_get_revision();
1587 std::string url = "https://bitbucket.org/tmidas/midas/commits/";
1588 // rev format looks like this:
1589 // Fri Nov 24 10:15:54 2017 -0800 - midas-2017-07-c-171-gb8928d5c-dirty on branch develop
1590 // -gXXX is the commit hash
1591 // -dirty should be removed from the hash url, if present
1592 // " " before "on branch" should be removed from the hash url
1593 std::string::size_type pos = rev.find("-g");
1594 if (pos != std::string::npos) {
1595 std::string hash = rev.substr(pos+2);
1596 pos = hash.find("-dirty");
1597 if (pos != std::string::npos) {
1598 hash = hash.substr(0, pos);
1599 }
1600 pos = hash.find(" ");
1601 if (pos != std::string::npos) {
1602 hash = hash.substr(0, pos);
1603 }
1604 url += hash;
1605 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"%s\">%s</a></td>\n", url.c_str(), rev.c_str());
1606 } else {
1607 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", rev.c_str());
1608 }
1609 r->rsprintf(" </tr>\n");
1610
1611 r->rsprintf(" <tr>\n");
1612 r->rsprintf(" <td style=\"text-align:right;\">MIDASSYS:</td>\n");
1613 s = getenv("MIDASSYS");
1614 if (!s) s = "(unset)";
1615 mstrlcpy(str, s, sizeof(str));
1616 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", str);
1617 r->rsprintf(" </tr>\n");
1618
1619 r->rsprintf(" <tr>\n");
1620 r->rsprintf(" <td style=\"text-align:right;\">mhttpd current directory:</td>\n");
1621 std::string cwd = ss_getcwd();
1622 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cwd.c_str());
1623 r->rsprintf(" </tr>\n");
1624
1625 r->rsprintf(" <tr>\n");
1626 r->rsprintf(" <td style=\"text-align:right;\">Exptab file:</td>\n");
1627 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_exptab_filename().c_str());
1628 r->rsprintf(" </tr>\n");
1629
1630 r->rsprintf(" <tr>\n");
1631 r->rsprintf(" <td style=\"text-align:right;\">Experiment:</td>\n");
1632 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_experiment_name().c_str());
1633 r->rsprintf(" </tr>\n");
1634
1635 r->rsprintf(" <tr>\n");
1636 r->rsprintf(" <td style=\"text-align:right;\">Experiment directory:</td>\n");
1637 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_path().c_str());
1638 r->rsprintf(" </tr>\n");
1639
1642
1643 if (status == CM_SUCCESS) {
1644 if (list.size() == 1) {
1645 r->rsprintf(" <tr>\n");
1646 r->rsprintf(" <td style=\"text-align:right;\">System logfile:</td>\n");
1647 std::string s;
1648 cm_msg_get_logfile("midas", 0, &s, NULL, NULL);
1649 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", s.c_str());
1650 r->rsprintf(" </tr>\n");
1651 } else {
1652 r->rsprintf(" <tr>\n");
1653 r->rsprintf(" <td style=\"text-align:right;\">Logfiles:</td>\n");
1654 r->rsprintf(" <td style=\"text-align:left;\">\n");
1655 for (unsigned i=0 ; i<list.size() ; i++) {
1656 if (i>0)
1657 r->rsputs("<br />\n");
1658 std::string s;
1659 cm_msg_get_logfile(list[i].c_str(), 0, &s, NULL, NULL);
1660 r->rsputs(s.c_str());
1661 }
1662 r->rsprintf("\n </td>\n");
1663 r->rsprintf(" </tr>\n");
1664 }
1665 }
1666
1667 r->rsprintf(" <tr>\n");
1668 r->rsprintf(" <td style=\"text-align:right;\">Image history:</td>\n");
1669 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_history_path("IMAGE").c_str());
1670 r->rsprintf(" </tr>\n");
1671
1672 r->rsprintf(" <tr>\n");
1673 r->rsprintf(" <td style=\"text-align:right;\">Resource paths:</td>\n");
1674 r->rsprintf(" <td style=\"text-align:left;\">");
1675 std::vector<std::string> resource_paths = get_resource_paths();
1676 for (unsigned i=0; i<resource_paths.size(); i++) {
1677 if (i>0)
1678 r->rsputs("<br>");
1679 r->rsputs(resource_paths[i].c_str());
1680 std::string exp = cm_expand_env(resource_paths[i].c_str());
1681 //printf("%d %d [%s] [%s]\n", resource_paths[i].length(), exp.length(), resource_paths[i].c_str(), exp.c_str());
1682 if (exp != resource_paths[i]) {
1683 r->rsputs(" (");
1684 r->rsputs(exp.c_str());
1685 r->rsputs(")");
1686 }
1687 }
1688 r->rsprintf(" </td>\n");
1689 r->rsprintf(" </tr>\n");
1690
1691 std::string path;
1692
1693 r->rsprintf(" <tr>\n");
1694 r->rsprintf(" <td style=\"text-align:right;\">midas.css:</td>\n");
1695 if (open_resource_file("midas.css", &path, NULL))
1696 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1697 else
1698 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1699 r->rsprintf(" </tr>\n");
1700
1701 r->rsprintf(" <tr>\n");
1702 r->rsprintf(" <td style=\"text-align:right;\">midas.js:</td>\n");
1703 if (open_resource_file("midas.js", &path, NULL))
1704 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1705 else
1706 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1707 r->rsprintf(" </tr>\n");
1708
1709 r->rsprintf(" <tr>\n");
1710 r->rsprintf(" <td style=\"text-align:right;\">controls.js:</td>\n");
1711 if (open_resource_file("controls.js", &path, NULL))
1712 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1713 else
1714 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1715 r->rsprintf(" </tr>\n");
1716
1717 r->rsprintf(" <tr>\n");
1718 r->rsprintf(" <td style=\"text-align:right;\">mhttpd.js:</td>\n");
1719 if (open_resource_file("mhttpd.js", &path, NULL))
1720 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1721 else
1722 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1723 r->rsprintf(" </tr>\n");
1724
1725 r->rsprintf(" <tr>\n");
1726 r->rsprintf(" <td style=\"text-align:right;\">obsolete.js:</td>\n");
1727 if (open_resource_file("obsolete.js", &path, NULL))
1728 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1729 else
1730 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1731 r->rsprintf(" </tr>\n");
1732
1733 r->rsprintf(" <tr>\n");
1734 r->rsprintf(" <td style=\"text-align:right;\">Obsolete mhttpd.css:</td>\n");
1735 if (open_resource_file("mhttpd.css", &path, NULL))
1736 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1737 else
1738 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1739 r->rsprintf(" </tr>\n");
1740
1741 r->rsprintf(" <tr>\n");
1742 r->rsprintf(" <td style=\"text-align:right;\">JSON-RPC schema:</td>\n");
1743 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");
1744 r->rsprintf(" </tr>\n");
1745
1746 r->rsprintf(" <tr>\n");
1747 r->rsprintf(" <td style=\"text-align:right;\">JavaScript examples:</td>\n");
1748 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?cmd=example\">example.html</a></td>\n");
1749 r->rsprintf(" </tr>\n");
1750
1751 r->rsprintf(" <tr>\n");
1752 r->rsprintf(" <td style=\"text-align:right;\">Custom page example:</td>\n");
1753 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?cmd=custom_example\">custom_example.html</a></td>\n");
1754 r->rsprintf(" </tr>\n");
1755
1756 r->rsprintf(" <tr>\n");
1757 r->rsprintf(" <td style=\"text-align:right;\">MPlot custom plot examples:</td>\n");
1758 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?cmd=plot_example\">plot_example.html</a></td>\n");
1759 r->rsprintf(" </tr>\n");
1760
1761 r->rsprintf(" </table>\n");
1762 r->rsprintf(" </td>\n");
1763 r->rsprintf(" </tr>\n");
1764 r->rsprintf("</table>\n");
1765
1766 r->rsprintf("<table class=\"mtable\" style=\"width: 95%%\">\n");
1767 r->rsprintf(" <tr>\n");
1768 r->rsprintf(" <td class=\"mtableheader\">Contributions</td>\n");
1769 r->rsprintf(" </tr>\n");
1770 r->rsprintf(" <tr>\n");
1771 r->rsprintf(" <td>\n");
1772 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");
1773 r->rsprintf(" </td>\n");
1774 r->rsprintf(" </tr>\n");
1775 r->rsprintf("</table>\n");
1776
1777 r->rsprintf("</div></form>\n");
1778 r->rsprintf("</body></html>\r\n");
1779}
1780
1781/*------------------------------------------------------------------*/
1782
1783void show_header(Return* r, const char *title, const char *method, const char *path, int refresh)
1784{
1785 HNDLE hDB;
1786 time_t now;
1787 char str[256];
1788
1790
1791 /* header */
1792 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1793 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1794 r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
1795 r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
1796 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
1797
1798 r->rsprintf("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n");
1799 r->rsprintf("<html><head>\n");
1800
1801 /* style sheet */
1802 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
1803 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
1804 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
1805
1806 /* auto refresh */
1807 if (refresh > 0)
1808 r->rsprintf("<meta http-equiv=\"Refresh\" content=\"%02d\">\n", refresh);
1809
1810 r->rsprintf("<title>%s</title></head>\n", title);
1811
1812 mstrlcpy(str, path, sizeof(str));
1813 urlEncode(str, sizeof(str));
1814
1815 if (equal_ustring(method, "POST"))
1816 r->rsprintf
1817 ("<body><form name=\"form1\" method=\"POST\" action=\"%s\" enctype=\"multipart/form-data\">\n\n",
1818 str);
1819 else if (equal_ustring(method, "GET"))
1820 r->rsprintf("<body><form name=\"form1\" method=\"GET\" action=\"%s\">\n\n", str);
1821
1822 /* title row */
1823
1824 std::string exptname;
1825 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &exptname, TRUE);
1826 time(&now);
1827}
1828
1829/*------------------------------------------------------------------*/
1830
1832{
1833 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1834 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1835 r->rsprintf("Access-Control-Allow-Origin: *\r\n");
1836 r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
1837 r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
1838 r->rsprintf("Content-Type: text/plain; charset=%s\r\n\r\n", HTTP_ENCODING);
1839}
1840
1841/*------------------------------------------------------------------*/
1842
1843void show_error(Return* r, const char *error)
1844{
1845 /* header */
1846 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1847 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1848 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
1849
1850 r->rsprintf("<html><head>\n");
1851 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
1852 r->rsprintf("<title>MIDAS error</title></head>\n");
1853 r->rsprintf("<body><H1>%s</H1></body></html>\n", error);
1854}
1855
1856/*------------------------------------------------------------------*/
1857
1858void show_error_404(Return* r, const char *error)
1859{
1860 /* header */
1861 r->rsprintf("HTTP/1.1 404 Not Found\r\n");
1862 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1863 r->rsprintf("Content-Type: text/plain\r\n");
1864 r->rsprintf("\r\n");
1865
1866 r->rsprintf("MIDAS error: %s\n", error);
1867}
1868
1869/*------------------------------------------------------------------*/
1870
1871void show_navigation_bar(Return* r, const char *cur_page)
1872{
1873 r->rsprintf("<script>\n");
1874 r->rsprintf("window.addEventListener(\"load\", function(e) { mhttpd_init('%s', 1000); });\n", cur_page);
1875 r->rsprintf("</script>\n");
1876
1877 r->rsprintf("<!-- header and side navigation will be filled in mhttpd_init -->\n");
1878 r->rsprintf("<div id=\"mheader\"></div>\n");
1879 r->rsprintf("<div id=\"msidenav\"></div>\n");
1880 r->rsprintf("<div id=\"mmain\">\n");
1881}
1882
1883/*------------------------------------------------------------------*/
1884
1885void check_obsolete_odb(HNDLE hDB, const char* odb_path)
1886{
1887 HNDLE hKey;
1888 int status = db_find_key(hDB, 0, odb_path, &hKey);
1889 if (status == DB_SUCCESS) {
1890 cm_msg(MERROR, "check_obsolete_odb", "ODB \"%s\" is obsolete, please delete it.", odb_path);
1891 }
1892}
1893
1894void init_menu_buttons(MVOdb* odb)
1895{
1896 HNDLE hDB;
1897 BOOL true_value = TRUE;
1898 BOOL false_value = FALSE;
1899 int size = sizeof(true_value);
1901 db_get_value(hDB, 0, "/Experiment/Menu/Status", &true_value, &size, TID_BOOL, TRUE);
1902 db_get_value(hDB, 0, "/Experiment/Menu/Start", &false_value, &size, TID_BOOL, TRUE);
1903 db_get_value(hDB, 0, "/Experiment/Menu/Transition", &true_value, &size, TID_BOOL, TRUE);
1904 db_get_value(hDB, 0, "/Experiment/Menu/ODB", &true_value, &size, TID_BOOL, TRUE);
1905 db_get_value(hDB, 0, "/Experiment/Menu/OldODB", &true_value, &size, TID_BOOL, TRUE);
1906 db_get_value(hDB, 0, "/Experiment/Menu/Messages", &true_value, &size, TID_BOOL, TRUE);
1907 db_get_value(hDB, 0, "/Experiment/Menu/Chat", &true_value, &size, TID_BOOL, TRUE);
1908 db_get_value(hDB, 0, "/Experiment/Menu/Elog", &true_value, &size, TID_BOOL, TRUE);
1909 db_get_value(hDB, 0, "/Experiment/Menu/Alarms", &true_value, &size, TID_BOOL, TRUE);
1910 db_get_value(hDB, 0, "/Experiment/Menu/Programs", &true_value, &size, TID_BOOL, TRUE);
1911 db_get_value(hDB, 0, "/Experiment/Menu/Buffers", &true_value, &size, TID_BOOL, TRUE);
1912 db_get_value(hDB, 0, "/Experiment/Menu/History", &true_value, &size, TID_BOOL, TRUE);
1913 db_get_value(hDB, 0, "/Experiment/Menu/OldHistory", &true_value, &size, TID_BOOL, TRUE);
1914 db_get_value(hDB, 0, "/Experiment/Menu/MSCB", &true_value, &size, TID_BOOL, TRUE);
1915 db_get_value(hDB, 0, "/Experiment/Menu/Sequencer", &true_value, &size, TID_BOOL, TRUE);
1916 db_get_value(hDB, 0, "/Experiment/Menu/PySequencer",&true_value, &size, TID_BOOL, TRUE);
1917 db_get_value(hDB, 0, "/Experiment/Menu/Event Dump", &true_value, &size, TID_BOOL, TRUE);
1918 db_get_value(hDB, 0, "/Experiment/Menu/Config", &true_value, &size, TID_BOOL, TRUE);
1919 db_get_value(hDB, 0, "/Experiment/Menu/Example", &true_value, &size, TID_BOOL, TRUE);
1920 db_get_value(hDB, 0, "/Experiment/Menu/Help", &true_value, &size, TID_BOOL, TRUE);
1921
1922 //std::string buf;
1923 //status = db_get_value_string(hDB, 0, "/Experiment/Menu buttons", 0, &buf, FALSE);
1924 //if (status == DB_SUCCESS) {
1925 // cm_msg(MERROR, "init_menu_buttons", "ODB \"/Experiment/Menu buttons\" is obsolete, please delete it.");
1926 //}
1927
1928 check_obsolete_odb(hDB, "/Experiment/Menu buttons");
1929 check_obsolete_odb(hDB, "/Experiment/Menu/OldSequencer");
1930 check_obsolete_odb(hDB, "/Experiment/Menu/NewSequencer");
1931}
1932
1933/*------------------------------------------------------------------*/
1934
1935void init_mhttpd_odb(MVOdb* odb)
1936{
1937 HNDLE hDB;
1938 HNDLE hKey;
1939 int status;
1940 std::string s;
1942
1943 status = db_find_key(hDB, 0, "/Experiment/Base URL", &hKey);
1944 if (status == DB_SUCCESS) {
1945 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/Base URL\" is obsolete, please delete it.");
1946 }
1947
1948 status = db_find_key(hDB, 0, "/Experiment/CSS File", &hKey);
1949 if (status == DB_SUCCESS) {
1950 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/CSS File\" is obsolete, please delete it.");
1951 }
1952
1953 status = db_find_key(hDB, 0, "/Experiment/JS File", &hKey);
1954 if (status == DB_SUCCESS) {
1955 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/JS File\" is obsolete, please delete it.");
1956 }
1957
1958 status = db_find_key(hDB, 0, "/Experiment/Start-Stop Buttons", &hKey);
1959 if (status == DB_SUCCESS) {
1960 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/Start-Stop Buttons\" is obsolete, please delete it.");
1961 }
1962
1963 bool xdefault = true;
1964 odb->RB("Experiment/Pause-Resume Buttons", &xdefault, true);
1965
1966#ifdef HAVE_MONGOOSE616
1967 check_obsolete_odb(hDB, "/Experiment/midas http port");
1968 check_obsolete_odb(hDB, "/Experiment/midas https port");
1969 check_obsolete_odb(hDB, "/Experiment/http redirect to https");
1970 check_obsolete_odb(hDB, "/Experiment/Security/mhttpd hosts");
1971#endif
1972
1973 status = db_find_key(hDB, 0, "/Logger/Message file", &hKey);
1974 if (status == DB_SUCCESS) {
1975 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.");
1976 }
1977
1978 check_obsolete_odb(hDB, "/Logger/Watchdog timeout");
1979}
1980
1981/*------------------------------------------------------------------*/
1982
1984{
1985 HNDLE hDB;
1986 int size;
1987 HNDLE hkey;
1989
1990 BOOL external_elog = FALSE;
1991 std::string external_elog_url;
1992
1993 size = sizeof(external_elog);
1994 db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, TRUE);
1995 db_get_value_string(hDB, 0, "/Elog/URL", 0, &external_elog_url, TRUE);
1996
1997 BOOL allow_delete = FALSE;
1998 BOOL allow_edit = FALSE;
1999 size = sizeof(BOOL);
2000 db_get_value(hDB, 0, "/Elog/Allow delete", &allow_delete, &size, TID_BOOL, TRUE);
2001 db_get_value(hDB, 0, "/Elog/Allow edit", &allow_edit, &size, TID_BOOL, TRUE);
2002 //db_get_value(hDB, 0, "/Elog/Display run number", &display_run_number, &size, TID_BOOL, TRUE);
2003
2004 if (db_find_key(hDB, 0, "/Elog/Buttons", &hkey) != DB_SUCCESS) {
2005 const char def_button[][NAME_LENGTH] = { "8h", "24h", "7d" };
2006 db_set_value(hDB, 0, "/Elog/Buttons", def_button, NAME_LENGTH*3, 3, TID_STRING);
2007 }
2008
2009
2010 /* get type list from ODB */
2011 size = 20 * NAME_LENGTH;
2012 if (db_find_key(hDB, 0, "/Elog/Types", &hkey) != DB_SUCCESS) {
2013 db_set_value(hDB, 0, "/Elog/Types", default_type_list, NAME_LENGTH * 20, 20, TID_STRING);
2014 }
2015
2016 /* get system list from ODB */
2017 size = 20 * NAME_LENGTH;
2018 if (db_find_key(hDB, 0, "/Elog/Systems", &hkey) != DB_SUCCESS)
2019 db_set_value(hDB, 0, "/Elog/Systems", default_system_list, NAME_LENGTH * 20, 20, TID_STRING);
2020}
2021
2022/*------------------------------------------------------------------*/
2023
2024void strencode(Return* r, const char *text)
2025{
2026 size_t len = strlen(text);
2027 for (size_t i = 0; i < len; i++) {
2028 switch (text[i]) {
2029 case '\n':
2030 r->rsprintf("<br>\n");
2031 break;
2032 case '<':
2033 r->rsprintf("&lt;");
2034 break;
2035 case '>':
2036 r->rsprintf("&gt;");
2037 break;
2038 case '&':
2039 r->rsprintf("&amp;");
2040 break;
2041 case '\"':
2042 r->rsprintf("&quot;");
2043 break;
2044 default:
2045 r->rsprintf("%c", text[i]);
2046 }
2047 }
2048}
2049
2050/*------------------------------------------------------------------*/
2051
2052std::string strencode2(const char *text)
2053{
2054 std::string b;
2055 size_t len = strlen(text);
2056 for (size_t i = 0; i < len; i++) {
2057 switch (text[i]) {
2058 case '\n':
2059 b += "<br>\n";
2060 break;
2061 case '<':
2062 b += "&lt;";
2063 break;
2064 case '>':
2065 b += "&gt;";
2066 break;
2067 case '&':
2068 b += "&amp;";
2069 break;
2070 case '\"':
2071 b += "&quot;";
2072 break;
2073 default:
2074 b += text[i];
2075 break;
2076 }
2077 }
2078 return b;
2079}
2080
2081/*------------------------------------------------------------------*/
2082
2083void strencode3(Return* r, const char *text)
2084{
2085 size_t len = strlen(text);
2086 for (size_t i = 0; i < len; i++) {
2087 switch (text[i]) {
2088 case '<':
2089 r->rsprintf("&lt;");
2090 break;
2091 case '>':
2092 r->rsprintf("&gt;");
2093 break;
2094 case '&':
2095 r->rsprintf("&amp;");
2096 break;
2097 case '\"':
2098 r->rsprintf("&quot;");
2099 break;
2100 default:
2101 r->rsprintf("%c", text[i]);
2102 }
2103 }
2104}
2105
2106/*------------------------------------------------------------------*/
2107
2108void strencode4(Return* r, const char *text)
2109{
2110 size_t len = strlen(text);
2111 for (size_t i = 0; i < len; i++) {
2112 switch (text[i]) {
2113 case '\n':
2114 r->rsprintf("<br>\n");
2115 break;
2116 case '<':
2117 r->rsprintf("&lt;");
2118 break;
2119 case '>':
2120 r->rsprintf("&gt;");
2121 break;
2122 case '&':
2123 r->rsprintf("&amp;");
2124 break;
2125 case '\"':
2126 r->rsprintf("&quot;");
2127 break;
2128 case ' ':
2129 r->rsprintf("&nbsp;");
2130 break;
2131 default:
2132 r->rsprintf("%c", text[i]);
2133 }
2134 }
2135}
2136
2137/*------------------------------------------------------------------*/
2138
2139void gen_odb_attachment(Return* r, const char *path, std::string& bout)
2140{
2141 HNDLE hDB, hkeyroot, hkey;
2142 KEY key;
2143 INT i, j, size;
2144 char data[1024];
2145 time_t now;
2146
2148 db_find_key(hDB, 0, path, &hkeyroot);
2149 assert(hkeyroot);
2150
2151 /* title row */
2152 //size = sizeof(str);
2153 //str[0] = 0;
2154 //db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
2155 time(&now);
2156
2157 bout += "<table border=3 cellpadding=1 class=\"dialogTable\">\n";
2158 char ctimebuf[32];
2159 ctime_r(&now, ctimebuf);
2160 bout += msprintf("<tr><th colspan=2>%s</tr>\n", ctimebuf);
2161 bout += msprintf("<tr><th colspan=2>%s</tr>\n", path);
2162
2163 /* enumerate subkeys */
2164 for (i = 0;; i++) {
2165 db_enum_link(hDB, hkeyroot, i, &hkey);
2166 if (!hkey)
2167 break;
2168 db_get_key(hDB, hkey, &key);
2169
2170 /* resolve links */
2171 if (key.type == TID_LINK) {
2172 db_enum_key(hDB, hkeyroot, i, &hkey);
2173 db_get_key(hDB, hkey, &key);
2174 }
2175
2176 if (key.type == TID_KEY) {
2177 /* for keys, don't display data value */
2178 bout += msprintf("<tr><td colspan=2>%s</td></tr>\n", key.name);
2179 } else {
2180 /* display single value */
2181 if (key.num_values == 1) {
2182 size = sizeof(data);
2183 db_get_data(hDB, hkey, data, &size, key.type);
2184 //printf("data size %d [%s]\n", size, data);
2185 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2186 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2187
2188 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2189 data_str = "(empty)";
2190 hex_str = "";
2191 }
2192
2193 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
2194 //sprintf(b, "<tr><td>%s</td><td>%s (%s)</td></tr>\n", key.name, data_str, hex_str);
2195 bout += "<tr><td>";
2196 bout += key.name;
2197 bout += "</td><td>";
2198 bout += data_str;
2199 bout += " (";
2200 bout += hex_str;
2201 bout += ")</td></tr>\n";
2202 } else {
2203 bout += msprintf("<tr><td>%s</td><td>", key.name);
2204 bout += strencode2(data_str.c_str());
2205 bout += "</td></tr>\n";
2206 }
2207 } else {
2208 /* display first value */
2209 bout += msprintf("<tr><td rowspan=%d>%s</td>\n", key.num_values, key.name);
2210
2211 for (j = 0; j < key.num_values; j++) {
2212 size = sizeof(data);
2213 db_get_data_index(hDB, hkey, data, &size, j, key.type);
2214 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2215 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2216
2217 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2218 data_str = "(empty)";
2219 hex_str = "";
2220 }
2221
2222 if (j > 0) {
2223 bout += "<tr>";
2224 }
2225
2226 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
2227 //sprintf(b, "<td>[%d] %s (%s)<br></td></tr>\n", j, data_str, hex_str);
2228 bout += "<td>[";
2229 bout += toString(j);
2230 bout += "] ";
2231 bout += data_str;
2232 bout += " (";
2233 bout += hex_str;
2234 bout += ")<br></td></tr>\n";
2235 } else {
2236 //sprintf(b, "<td>[%d] %s<br></td></tr>\n", j, data_str);
2237 bout += "<td>[";
2238 bout += toString(j);
2239 bout += "] ";
2240 bout += data_str;
2241 bout += "<br></td></tr>\n";
2242 }
2243 }
2244 }
2245 }
2246 }
2247
2248 bout += "</table>\n";
2249}
2250
2251/*------------------------------------------------------------------*/
2252
2253void submit_elog(MVOdb* odb, Param* pp, Return* r, Attachment* a)
2254{
2255 char path[256], path1[256];
2256 char mail_to[256], mail_from[256], mail_list[256],
2257 smtp_host[256], tag[80], mail_param[1000];
2258 char *p, *pitem;
2259 HNDLE hDB, hkey;
2260 char att_file[3][256];
2261 int fh, size, n_mail;
2262 char mhttpd_full_url[256];
2263
2265 mstrlcpy(att_file[0], pp->getparam("attachment0"), sizeof(att_file[0]));
2266 mstrlcpy(att_file[1], pp->getparam("attachment1"), sizeof(att_file[1]));
2267 mstrlcpy(att_file[2], pp->getparam("attachment2"), sizeof(att_file[2]));
2268
2269 /* check for valid attachment files */
2270 for (int i = 0; i < 3; i++) {
2271 char str[256];
2272 sprintf(str, "attachment%d", i);
2273 //printf("submit_elog: att %d, [%s] param [%s], size %d\n", i, str, pp->getparam(str), a->_attachment_size[i]);
2274 if (pp->getparam(str) && *pp->getparam(str) && a->attachment_size[i] == 0) {
2275 /* replace '\' by '/' */
2276 mstrlcpy(path, pp->getparam(str), sizeof(path));
2277 mstrlcpy(path1, path, sizeof(path1));
2278 while (strchr(path, '\\'))
2279 *strchr(path, '\\') = '/';
2280
2281 /* check if valid ODB tree */
2282 if (db_find_key(hDB, 0, path, &hkey) == DB_SUCCESS) {
2283 std::string bout;
2284 gen_odb_attachment(r, path, bout);
2285 int bufsize = bout.length()+1;
2286 char* buf = (char*)M_MALLOC(bufsize);
2287 memcpy(buf, bout.c_str(), bufsize);
2288 mstrlcpy(att_file[i], path, sizeof(att_file[0]));
2289 mstrlcat(att_file[i], ".html", sizeof(att_file[0]));
2290 a->attachment_buffer[i] = buf;
2291 a->attachment_size[i] = bufsize;
2292 }
2293 /* check if local file */
2294 else if ((fh = open(path1, O_RDONLY | O_BINARY)) >= 0) {
2295 size = lseek(fh, 0, SEEK_END);
2296 char* buf = (char*)M_MALLOC(size);
2297 lseek(fh, 0, SEEK_SET);
2298 int rd = read(fh, buf, size);
2299 if (rd < 0)
2300 rd = 0;
2301 close(fh);
2302 mstrlcpy(att_file[i], path, sizeof(att_file[0]));
2303 a->attachment_buffer[i] = buf;
2304 a->attachment_size[i] = rd;
2305 } else if (strncmp(path, "/HS/", 4) == 0) {
2306 char* buf = (char*)M_MALLOC(100000);
2307 size = 100000;
2308 mstrlcpy(str, path + 4, sizeof(str));
2309 if (strchr(str, '?')) {
2310 p = strchr(str, '?') + 1;
2311 p = strtok(p, "&");
2312 while (p != NULL) {
2313 pitem = p;
2314 p = strchr(p, '=');
2315 if (p != NULL) {
2316 *p++ = 0;
2317 urlDecode(pitem); // parameter name
2318 urlDecode(p); // parameter value
2319
2320 pp->setparam(pitem, p);
2321
2322 p = strtok(NULL, "&");
2323 }
2324 }
2325 *strchr(str, '?') = 0;
2326 }
2327 show_hist_page(odb, pp, r, "image.gif", buf, &size, 0);
2328 mstrlcpy(att_file[i], str, sizeof(att_file[0]));
2329 a->attachment_buffer[i] = buf;
2330 a->attachment_size[i] = size;
2331 pp->unsetparam("scale");
2332 pp->unsetparam("offset");
2333 pp->unsetparam("width");
2334 pp->unsetparam("index");
2335 } else {
2336 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
2337 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
2338 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
2339
2340 r->rsprintf("<html><head>\n");
2341 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
2342 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
2343 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
2344 r->rsprintf("<title>ELog Error</title></head>\n");
2345 r->rsprintf("<i>Error: Attachment file <i>%s</i> not valid.</i><p>\n", pp->getparam(str));
2346 r->rsprintf("Please go back and enter a proper filename (use the <b>Browse</b> button).\n");
2347 r->rsprintf("<body></body></html>\n");
2348 return;
2349 }
2350 }
2351 }
2352
2353 int edit = atoi(pp->getparam("edit"));
2354 //printf("submit_elog: edit [%s] %d, orig [%s]\n", pp->getparam("edit"), edit, pp->getparam("orig"));
2355
2356 tag[0] = 0;
2357 if (edit) {
2358 mstrlcpy(tag, pp->getparam("orig"), sizeof(tag));
2359 }
2360
2361 int status = el_submit(atoi(pp->getparam("run")),
2362 pp->getparam("author"),
2363 pp->getparam("type"),
2364 pp->getparam("system"),
2365 pp->getparam("subject"),
2366 pp->getparam("text"),
2367 pp->getparam("orig"),
2368 *pp->getparam("html") ? "HTML" : "plain",
2369 att_file[0], a->attachment_buffer[0], a->attachment_size[0],
2370 att_file[1], a->attachment_buffer[1], a->attachment_size[1],
2371 att_file[2], a->attachment_buffer[2], a->attachment_size[2],
2372 tag, sizeof(tag));
2373
2374 //printf("el_submit status %d, tag [%s]\n", status, tag);
2375
2376 if (status != EL_SUCCESS) {
2377 cm_msg(MERROR, "submit_elog", "el_submit() returned status %d", status);
2378 }
2379
2380 /* supersede host name with "/Elog/Host name" */
2381 std::string elog_host_name;
2382 db_get_value_string(hDB, 0, "/Elog/Host name", 0, &elog_host_name, TRUE);
2383
2384 // K.O. FIXME: we cannot guess the Elog URL like this because
2385 // we do not know if access is through a proxy or redirect
2386 // we do not know if it's http: or https:, etc. Better
2387 // to read the whole "mhttpd_full_url" string from ODB.
2388 sprintf(mhttpd_full_url, "http://%s/", elog_host_name.c_str());
2389
2390 /* check for mail submissions */
2391 mail_param[0] = 0;
2392 n_mail = 0;
2393
2394 for (int index = 0; index <= 1; index++) {
2395 std::string str;
2396 str += "/Elog/Email ";
2397 if (index == 0)
2398 str += pp->getparam("type");
2399 else
2400 str += pp->getparam("system");
2401
2402 if (db_find_key(hDB, 0, str.c_str(), &hkey) == DB_SUCCESS) {
2403 size = sizeof(mail_list);
2404 db_get_data(hDB, hkey, mail_list, &size, TID_STRING);
2405
2406 if (db_find_key(hDB, 0, "/Elog/SMTP host", &hkey) != DB_SUCCESS) {
2407 show_error(r, "No SMTP host defined under /Elog/SMTP host");
2408 return;
2409 }
2410 size = sizeof(smtp_host);
2411 db_get_data(hDB, hkey, smtp_host, &size, TID_STRING);
2412
2413 p = strtok(mail_list, ",");
2414 while (1) {
2415 mstrlcpy(mail_to, p, sizeof(mail_to));
2416
2417 std::string exptname;
2418 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &exptname, TRUE);
2419
2420 sprintf(mail_from, "MIDAS %s <MIDAS@%s>", exptname.c_str(), elog_host_name.c_str());
2421
2422 std::string mail_text;
2423 mail_text += "A new entry has been submitted by ";
2424 mail_text += pp->getparam("author");
2425 mail_text += "\n";
2426 mail_text += "\n";
2427
2428 mail_text += "Experiment : ";
2429 mail_text += exptname.c_str();
2430 mail_text += "\n";
2431
2432 mail_text += "Type : ";
2433 mail_text += pp->getparam("type");
2434 mail_text += "\n";
2435
2436 mail_text += "System : ";
2437 mail_text += pp->getparam("system");
2438 mail_text += "\n";
2439
2440 mail_text += "Subject : ";
2441 mail_text += pp->getparam("subject");
2442 mail_text += "\n";
2443
2444 mail_text += "Link : ";
2445 mail_text += mhttpd_full_url;
2446 mail_text += "/EL/";
2447 mail_text += tag;
2448 mail_text += "\n";
2449
2450 mail_text += "\n";
2451
2452 mail_text += pp->getparam("text");
2453 mail_text += "\n";
2454
2455 sendmail(elog_host_name.c_str(), smtp_host, mail_from, mail_to, pp->getparam("type"), mail_text.c_str());
2456
2457 if (mail_param[0] == 0)
2458 mstrlcpy(mail_param, "?", sizeof(mail_param));
2459 else
2460 mstrlcat(mail_param, "&", sizeof(mail_param));
2461 sprintf(mail_param + strlen(mail_param), "mail%d=%s", n_mail++, mail_to);
2462
2463 p = strtok(NULL, ",");
2464 if (!p)
2465 break;
2466 while (*p == ' ')
2467 p++;
2468 }
2469 }
2470 }
2471
2472 r->rsprintf("HTTP/1.1 302 Found\r\n");
2473 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
2474
2475 //if (mail_param[0])
2476 // r->rsprintf("Location: ../EL/%s?%s\n\n<html>redir</html>\r\n", tag, mail_param + 1);
2477 //else
2478 // r->rsprintf("Location: ../EL/%s\n\n<html>redir</html>\r\n", tag);
2479
2480 if (mail_param[0])
2481 r->rsprintf("Location: ?cmd=Show+elog&tag=%s&%s\n\n<html>redir</html>\r\n", tag, mail_param + 1);
2482 else
2483 r->rsprintf("Location: ?cmd=Show+elog&tag=%s\n\n<html>redir</html>\r\n", tag);
2484}
2485
2486/*------------------------------------------------------------------*/
2487
2488void show_elog_attachment(Param* p, Return* r, const char* path)
2489{
2490 HNDLE hDB;
2491 int size;
2492 int status;
2493 char file_name[256];
2494
2496 file_name[0] = 0;
2497 if (hDB > 0) {
2498 size = sizeof(file_name);
2499 memset(file_name, 0, size);
2500
2501 status = db_get_value(hDB, 0, "/Logger/Elog dir", file_name, &size, TID_STRING, FALSE);
2502 if (status != DB_SUCCESS)
2503 db_get_value(hDB, 0, "/Logger/Data dir", file_name, &size, TID_STRING, TRUE);
2504
2505 if (file_name[0] != 0)
2506 if (file_name[strlen(file_name) - 1] != DIR_SEPARATOR)
2507 mstrlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
2508 }
2509 mstrlcat(file_name, path, sizeof(file_name));
2510
2511 int fh = open(file_name, O_RDONLY | O_BINARY);
2512 if (fh > 0) {
2513 lseek(fh, 0, SEEK_END);
2514 int length = TELL(fh);
2515 lseek(fh, 0, SEEK_SET);
2516
2517 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
2518 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
2519 r->rsprintf("Accept-Ranges: bytes\r\n");
2520 //r->rsprintf("Content-disposition: attachment; filename=%s\r\n", path);
2521
2522 r->rsprintf("Content-Type: %s\r\n", get_content_type(file_name).c_str());
2523
2524 r->rsprintf("Content-Length: %d\r\n\r\n", length);
2525
2526 r->rread(file_name, fh, length);
2527
2528 close(fh);
2529 }
2530
2531 return;
2532}
2533
2534/*------------------------------------------------------------------*/
2535
2536BOOL is_editable(char *eq_name, char *var_name)
2537{
2538 HNDLE hDB, hkey;
2539 KEY key;
2540 char str[256];
2541 int i, size;
2542
2544 sprintf(str, "/Equipment/%s/Settings/Editable", eq_name);
2545 db_find_key(hDB, 0, str, &hkey);
2546
2547 /* if no editable entry found, use default */
2548 if (!hkey) {
2549 return (equal_ustring(var_name, "Demand") ||
2550 equal_ustring(var_name, "Output") || strncmp(var_name, "D_", 2) == 0);
2551 }
2552
2553 db_get_key(hDB, hkey, &key);
2554 for (i = 0; i < key.num_values; i++) {
2555 size = sizeof(str);
2556 db_get_data_index(hDB, hkey, str, &size, i, TID_STRING);
2558 return TRUE;
2559 }
2560 return FALSE;
2561}
2562
2563#ifdef OBSOLETE
2564void show_eqtable_page(Param* pp, Return* r, int refresh)
2565{
2566 int i, j, k, colspan, size, n_var, i_edit, i_set, line;
2567 char eq_name[32], group[32];
2568 char group_name[MAX_GROUPS][32], data[256], style[80];
2569 HNDLE hDB;
2570 char odb_path[256];
2571
2573
2574 /* check if variable to edit */
2575 i_edit = -1;
2576 if (equal_ustring(pp->getparam("cmd"), "Edit"))
2577 i_edit = atoi(pp->getparam("index"));
2578
2579 /* check if variable to set */
2580 i_set = -1;
2581 if (equal_ustring(pp->getparam("cmd"), "Set"))
2582 i_set = atoi(pp->getparam("index"));
2583
2584 /* get equipment and group */
2585 if (pp->getparam("eq"))
2586 mstrlcpy(eq_name, pp->getparam("eq"), sizeof(eq_name));
2587 mstrlcpy(group, "All", sizeof(group));
2588 if (pp->getparam("group") && *pp->getparam("group"))
2589 mstrlcpy(group, pp->getparam("group"), sizeof(group));
2590
2591#if 0
2592 /* check for "names" in settings */
2593 if (eq_name[0]) {
2594 sprintf(str, "/Equipment/%s/Settings", eq_name);
2595 HNDLE hkeyset;
2596 db_find_key(hDB, 0, str, &hkeyset);
2597 HNDLE hkeynames = 0;
2598 if (hkeyset) {
2599 for (i = 0;; i++) {
2600 db_enum_link(hDB, hkeyset, i, &hkeynames);
2601
2602 if (!hkeynames)
2603 break;
2604
2605 KEY key;
2606 db_get_key(hDB, hkeynames, &key);
2607
2608 if (strncmp(key.name, "Names", 5) == 0)
2609 break;
2610 }
2611 }
2612
2613 /* redirect if no names found */
2614 if (!hkeyset || !hkeynames) {
2615 /* redirect */
2616 sprintf(str, "?cmd=odb&odb_path=/Equipment/%s/Variables", eq_name);
2617 redirect(r, str);
2618 return;
2619 }
2620 }
2621#endif
2622
2623 show_header(r, "MIDAS slow control", "", group, i_edit == -1 ? refresh : 0);
2624 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
2625 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
2626 r->rsprintf("<script type=\"text/javascript\" src=\"obsolete.js\"></script>\n");
2627 show_navigation_bar(r, "SC");
2628
2629 /*---- menu buttons ----*/
2630
2631 r->rsprintf("<tr><td colspan=15>\n");
2632
2633 if (equal_ustring(pp->getparam("cmd"), "Edit"))
2634 r->rsprintf("<input type=submit name=cmd value=Set>\n");
2635
2636 r->rsprintf("</tr>\n\n");
2637 r->rsprintf("</table>"); //end header table
2638
2639 r->rsprintf("<table class=\"ODBtable\" style=\"max-width:700px;\">"); //body table
2640
2641 /*---- enumerate SC equipment ----*/
2642
2643 r->rsprintf("<tr><td class=\"subStatusTitle\" colspan=15><i>Equipment:</i> &nbsp;&nbsp;\n");
2644
2645 HNDLE hkeyeqroot;
2646 db_find_key(hDB, 0, "/Equipment", &hkeyeqroot);
2647 if (hkeyeqroot)
2648 for (i = 0;; i++) {
2649 HNDLE hkeyeq;
2650 db_enum_link(hDB, hkeyeqroot, i, &hkeyeq);
2651
2652 if (!hkeyeq)
2653 break;
2654
2655 KEY eqkey;
2656 db_get_key(hDB, hkeyeq, &eqkey);
2657
2658 HNDLE hkeyset;
2659 db_find_key(hDB, hkeyeq, "Settings", &hkeyset);
2660 if (hkeyset) {
2661 for (j = 0;; j++) {
2662 HNDLE hkeynames;
2663 db_enum_link(hDB, hkeyset, j, &hkeynames);
2664
2665 if (!hkeynames)
2666 break;
2667
2668 KEY key;
2669 db_get_key(hDB, hkeynames, &key);
2670
2671 if (strncmp(key.name, "Names", 5) == 0) {
2672 if (equal_ustring(eq_name, eqkey.name))
2673 r->rsprintf("<b>%s</b> &nbsp;&nbsp;", eqkey.name);
2674 else {
2675 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eqkey.name).c_str(), eqkey.name);
2676 }
2677 break;
2678 }
2679 }
2680 }
2681 }
2682 r->rsprintf("</tr>\n");
2683
2684 if (!eq_name[0]) {
2685 r->rsprintf("</table>");
2686 return;
2687 }
2688
2689 /*---- display SC ----*/
2690
2691 n_var = 0;
2692 std::string names_path = msprintf("/Equipment/%s/Settings/Names", eq_name);
2693 HNDLE hkeyeqnames;
2694 db_find_key(hDB, 0, names_path.c_str(), &hkeyeqnames);
2695
2696 if (hkeyeqnames) {
2697
2698 /*---- single name array ----*/
2699 r->rsprintf("<tr><td colspan=15><i>Groups:</i> &nbsp;&nbsp;");
2700
2701 /* "all" group */
2702 if (equal_ustring(group, "All"))
2703 r->rsprintf("<b>All</b> &nbsp;&nbsp;");
2704 else
2705 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">All</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2706
2707 /* collect groups */
2708
2709 memset(group_name, 0, sizeof(group_name));
2710 KEY key;
2711 db_get_key(hDB, hkeyeqnames, &key);
2712
2713 for (int level = 0; ; level++) {
2714 bool next_level = false;
2715 for (i = 0; i < key.num_values; i++) {
2716 char name_str[256];
2717 size = sizeof(name_str);
2718 db_get_data_index(hDB, hkeyeqnames, name_str, &size, i, TID_STRING);
2719
2720 char *s = strchr(name_str, '%');
2721 for (int k=0; s && k<level; k++)
2722 s = strchr(s+1, '%');
2723
2724 if (s) {
2725 *s = 0;
2726 if (strchr(s+1, '%'))
2727 next_level = true;
2728
2729 //printf("try group [%s] name [%s], level %d, %d\n", name_str, s+1, level, next_level);
2730
2731 for (j = 0; j < MAX_GROUPS; j++) {
2732 if (equal_ustring(group_name[j], name_str) || group_name[j][0] == 0)
2733 break;
2734 }
2735 if ((j < MAX_GROUPS) && (group_name[j][0] == 0))
2736 mstrlcpy(group_name[j], name_str, sizeof(group_name[0]));
2737 }
2738 }
2739
2740 if (!next_level)
2741 break;
2742 }
2743
2744 for (i = 0; i < MAX_GROUPS && group_name[i][0]; i++) {
2745 if (equal_ustring(group_name[i], group))
2746 r->rsprintf("<b>%s</b> &nbsp;&nbsp;", group_name[i]);
2747 else {
2748 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]);
2749 }
2750 }
2751
2752 r->rsprintf("<i>ODB:</i> &nbsp;&nbsp;");
2753 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Common\">Common</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2754 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Settings\">Settings</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2755 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Variables\">Variables</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2756 r->rsprintf("</tr>\n");
2757
2758 /* count variables */
2759 std::string vars_path = msprintf("/Equipment/%s/Variables", eq_name);
2760 HNDLE hkeyvar;
2761 db_find_key(hDB, 0, vars_path.c_str(), &hkeyvar);
2762 if (!hkeyvar) {
2763 r->rsprintf("</table>");
2764 return;
2765 }
2766 for (i = 0;; i++) {
2767 HNDLE hkey;
2768 db_enum_link(hDB, hkeyvar, i, &hkey);
2769 if (!hkey)
2770 break;
2771 }
2772
2773 if (i == 0 || i > 15) {
2774 r->rsprintf("</table>");
2775 return;
2776 }
2777
2778 /* title row */
2779 colspan = 15 - i;
2780 r->rsprintf("<tr class=\"subStatusTitle\"><th colspan=%d>Names", colspan);
2781
2782 /* display entries for this group */
2783 for (int i = 0;; i++) {
2784 HNDLE hkey;
2785 db_enum_link(hDB, hkeyvar, i, &hkey);
2786
2787 if (!hkey)
2788 break;
2789
2790 KEY key;
2791 db_get_key(hDB, hkey, &key);
2792 r->rsprintf("<th>%s", key.name);
2793 }
2794
2795 r->rsprintf("</tr>\n");
2796
2797 /* data for current group */
2798 std::string names_path = msprintf("/Equipment/%s/Settings/Names", eq_name);
2799 int num_values = 0;
2800 HNDLE hnames_key;
2801 db_find_key(hDB, 0, names_path.c_str(), &hnames_key);
2802 if (hnames_key) {
2803 KEY names_key;
2804 db_get_key(hDB, hnames_key, &names_key);
2805 num_values = names_key.num_values;
2806 }
2807 for (int i = 0; i < num_values; i++) {
2808 char names_str[256];
2809 size = sizeof(names_str);
2810 db_get_data_index(hDB, hnames_key, names_str, &size, i, TID_STRING);
2811
2812 char name[NAME_LENGTH+32];
2813 mstrlcpy(name, names_str, sizeof(name));
2814
2815 //printf("group [%s], name [%s], str [%s]\n", group, name, names_str);
2816
2817 if (!equal_ustring(group, "All")) {
2818 // check if name starts with the name of the group we want to display
2819 char *s = strstr(name, group);
2820 if (s != name)
2821 continue;
2822 if (name[strlen(group)] != '%')
2823 continue;
2824 }
2825
2826 if (strlen(name) < 1)
2827 sprintf(name, "[%d]", i);
2828
2829 if (i % 2 == 0)
2830 r->rsprintf("<tr class=\"ODBtableEven\"><td colspan=%d><nobr>%s</nobr>", colspan, name);
2831 else
2832 r->rsprintf("<tr class=\"ODBtableOdd\"><td colspan=%d><nobr>%s</nobr>", colspan, name);
2833
2834 for (int j = 0;; j++) {
2835 HNDLE hkey;
2836 db_enum_link(hDB, hkeyvar, j, &hkey);
2837 if (!hkey)
2838 break;
2839
2840 KEY varkey;
2841 db_get_key(hDB, hkey, &varkey);
2842
2843 /* check if "variables" array is shorter than the "names" array */
2844 if (i >= varkey.num_values)
2845 continue;
2846
2847 size = sizeof(data);
2848 db_get_data_index(hDB, hkey, data, &size, i, varkey.type);
2849 std::string data_str = db_sprintf(data, varkey.item_size, 0, varkey.type);
2850
2851 if (is_editable(eq_name, varkey.name)) {
2852 if (n_var == i_set) {
2853 /* set value */
2854 char str[256];
2855 mstrlcpy(str, pp->getparam("value"), sizeof(str));
2856 db_sscanf(str, data, &size, 0, varkey.type);
2857 db_set_data_index(hDB, hkey, data, size, i, varkey.type);
2858
2859 /* redirect (so that 'reload' does not reset value) */
2860 r->reset();
2861 redirect(r, group);
2862 return;
2863 }
2864 if (n_var == i_edit) {
2865 r->rsprintf("<td align=center>");
2866 r->rsprintf("<input type=text size=10 maxlenth=80 name=value value=\"%s\">\n", data_str.c_str());
2867 r->rsprintf("<input type=submit size=20 name=cmd value=Set>\n");
2868 r->rsprintf("<input type=hidden name=index value=%d>\n", i_edit);
2869 n_var++;
2870 } else {
2871 sprintf(odb_path, "Equipment/%s/Variables/%s[%d]", eq_name, varkey.name, i);
2872 r->rsprintf("<td align=center>");
2873 r->rsprintf("<a href=\"#\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\', 0);return false;\" >%s</a>", odb_path, data_str.c_str());
2874 n_var++;
2875 }
2876 } else
2877 r->rsprintf("<td align=center>%s", data_str.c_str());
2878 }
2879
2880 r->rsprintf("</tr>\n");
2881 }
2882 } else {
2883 /*---- multiple name arrays ----*/
2884 r->rsprintf("<tr><td colspan=15><i>Groups:</i> ");
2885
2886 /* "all" group */
2887 if (equal_ustring(group, "All"))
2888 r->rsprintf("<b>All</b> &nbsp;&nbsp;");
2889 else
2890 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">All</a> &nbsp;&nbsp;", eq_name);
2891
2892 /* groups from Variables tree */
2893
2894 std::string vars_path = msprintf("/Equipment/%s/Variables", eq_name);
2895 HNDLE hkeyvar;
2896 db_find_key(hDB, 0, vars_path.c_str(), &hkeyvar);
2897
2898 if (hkeyvar) {
2899 for (int i = 0;; i++) {
2900 HNDLE hkey;
2901 db_enum_link(hDB, hkeyvar, i, &hkey);
2902
2903 if (!hkey)
2904 break;
2905
2906 KEY key;
2907 db_get_key(hDB, hkey, &key);
2908
2909 if (equal_ustring(key.name, group)) {
2910 r->rsprintf("<b>%s</b> &nbsp;&nbsp;", key.name);
2911 } else {
2912 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);
2913 }
2914 }
2915 }
2916
2917 r->rsprintf("<i>ODB:</i> &nbsp;&nbsp;");
2918 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Common\">Common</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2919 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Settings\">Settings</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2920 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Variables\">Variables</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2921 r->rsprintf("</tr>\n");
2922
2923 /* enumerate variable arrays */
2924 line = 0;
2925 for (i = 0;; i++) {
2926 HNDLE hkey;
2927 db_enum_link(hDB, hkeyvar, i, &hkey);
2928
2929 if (line % 2 == 0)
2930 mstrlcpy(style, "ODBtableEven", sizeof(style));
2931 else
2932 mstrlcpy(style, "ODBtableOdd", sizeof(style));
2933
2934 if (!hkey)
2935 break;
2936
2937 KEY varkey;
2938 db_get_key(hDB, hkey, &varkey);
2939
2940 if (!equal_ustring(group, "All") && !equal_ustring(varkey.name, group))
2941 continue;
2942
2943 /* title row */
2944 r->rsprintf("<tr class=\"subStatusTitle\"><th colspan=9>Names<th>%s</tr>\n", varkey.name);
2945
2946 if (varkey.type == TID_KEY) {
2947 HNDLE hkeyroot = hkey;
2948
2949 /* enumerate subkeys */
2950 for (j = 0;; j++) {
2951 db_enum_key(hDB, hkeyroot, j, &hkey);
2952 if (!hkey)
2953 break;
2954
2955 KEY key;
2956 db_get_key(hDB, hkey, &key);
2957
2958 if (key.type == TID_KEY) {
2959 /* for keys, don't display data value */
2960 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<br></tr>\n", style, key.name);
2961 } else {
2962 /* display single value */
2963 if (key.num_values == 1) {
2964 size = sizeof(data);
2965 db_get_data(hDB, hkey, data, &size, key.type);
2966
2967 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2968 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2969
2970 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2971 data_str = "(empty)";
2972 hex_str = "";
2973 }
2974
2975 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
2976 r->rsprintf
2977 ("<tr class=\"%s\" ><td colspan=9>%s<td align=center>%s (%s)<br></tr>\n",
2978 style, key.name, data_str.c_str(), hex_str.c_str());
2979 else
2980 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<td align=center>%s<br></tr>\n",
2981 style, key.name, data_str.c_str());
2982 line++;
2983 } else {
2984 /* display first value */
2985 r->rsprintf("<tr class=\"%s\"><td colspan=9 rowspan=%d>%s\n", style, key.num_values,
2986 key.name);
2987
2988 for (k = 0; k < key.num_values; k++) {
2989 size = sizeof(data);
2990 db_get_data_index(hDB, hkey, data, &size, k, key.type);
2991 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2992 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2993
2994 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2995 data_str = "(empty)";
2996 hex_str = "";
2997 }
2998
2999 if (k > 0)
3000 r->rsprintf("<tr>");
3001
3002 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
3003 r->rsprintf("<td>[%d] %s (%s)<br></tr>\n", k, data_str.c_str(), hex_str.c_str());
3004 else
3005 r->rsprintf("<td>[%d] %s<br></tr>\n", k, data_str.c_str());
3006 line++;
3007 }
3008 }
3009 }
3010 }
3011 } else {
3012 /* data for current group */
3013 std::string names_path = msprintf("/Equipment/%s/Settings/Names %s", eq_name, varkey.name);
3014 HNDLE hkeyset;
3015 db_find_key(hDB, 0, names_path.c_str(), &hkeyset);
3016 KEY key;
3017 if (hkeyset)
3018 db_get_key(hDB, hkeyset, &key);
3019
3020 if (varkey.num_values > 1000)
3021 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<td align=center><i>... %d values ...</i>",
3022 style, varkey.name, varkey.num_values);
3023 else {
3024 for (j = 0; j < varkey.num_values; j++) {
3025
3026 if (line % 2 == 0)
3027 mstrlcpy(style, "ODBtableEven", sizeof(style));
3028 else
3029 mstrlcpy(style, "ODBtableOdd", sizeof(style));
3030
3031 char name[NAME_LENGTH+32];
3032 if (hkeyset && j<key.num_values) {
3033 size = sizeof(name);
3034 db_get_data_index(hDB, hkeyset, name, &size, j, TID_STRING);
3035 } else {
3036 sprintf(name, "%s[%d]", varkey.name, j);
3037 }
3038
3039 if (strlen(name) < 1) {
3040 sprintf(name, "%s[%d]", varkey.name, j);
3041 }
3042
3043 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s", style, name);
3044
3045 size = sizeof(data);
3046 db_get_data_index(hDB, hkey, data, &size, j, varkey.type);
3047 std::string data_str = db_sprintf(data, varkey.item_size, 0, varkey.type);
3048
3049 if (is_editable(eq_name, varkey.name)) {
3050 if (n_var == i_set) {
3051 /* set value */
3052 char str[256];
3053 mstrlcpy(str, pp->getparam("value"), sizeof(str));
3054 db_sscanf(str, data, &size, 0, varkey.type);
3055 db_set_data_index(hDB, hkey, data, size, j, varkey.type);
3056
3057 /* redirect (so that 'reload' does not reset value) */
3058 r->reset();
3059 sprintf(str, "%s", group);
3060 redirect(r, str);
3061 return;
3062 }
3063 if (n_var == i_edit) {
3064 r->rsprintf("<td align=center><input type=text size=10 maxlenth=80 name=value value=\"%s\">\n", data_str.c_str());
3065 r->rsprintf("<input type=submit size=20 name=cmd value=Set></tr>\n");
3066 r->rsprintf("<input type=hidden name=index value=%d>\n", i_edit);
3067 r->rsprintf("<input type=hidden name=cmd value=Set>\n");
3068 n_var++;
3069 } else {
3070 sprintf(odb_path, "Equipment/%s/Variables/%s[%d]", eq_name, varkey.name, j);
3071
3072 r->rsprintf("<td align=cernter>");
3073 r->rsprintf("<a href=\"#\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\', 0);return false;\" >%s</a>", odb_path, data_str.c_str());
3074 n_var++;
3075 }
3076
3077 } else
3078 r->rsprintf("<td align=center>%s\n", data_str.c_str());
3079 r->rsprintf("</tr>\n");
3080 line++;
3081 }
3082 }
3083
3084 r->rsprintf("</tr>\n");
3085 }
3086 }
3087 }
3088
3089 r->rsprintf("</table>\n");
3090 r->rsprintf("</div>\n"); // closing for <div id="mmain">
3091 r->rsprintf("</form>\n");
3092 r->rsprintf("</body></html>\r\n");
3093}
3094#endif
3095
3096/*------------------------------------------------------------------*/
3097
3098char *find_odb_tag(char *p, char *path, char *format, int *edit, char *type, char *pwd, char *tail)
3099{
3100 char str[256], *ps, *pt;
3101 BOOL in_script;
3102
3103 *edit = 0;
3104 *tail = 0;
3105 *format = 0;
3106 pwd[0] = 0;
3107 in_script = FALSE;
3108 strcpy(type, "text");
3109 do {
3110 while (*p && *p != '<')
3111 p++;
3112
3113 /* return if end of string reached */
3114 if (!*p)
3115 return NULL;
3116
3117 p++;
3118 while (*p && ((*p == ' ') || iscntrl(*p)))
3119 p++;
3120
3121 strncpy(str, p, 6);
3122 str[6] = 0;
3123 if (equal_ustring(str, "script"))
3124 in_script = TRUE;
3125
3126 strncpy(str, p, 7);
3127 str[7] = 0;
3128 if (equal_ustring(str, "/script"))
3129 in_script = FALSE;
3130
3131 strncpy(str, p, 4);
3132 str[4] = 0;
3133 if (equal_ustring(str, "odb ")) {
3134 ps = p - 1;
3135 p += 4;
3136 while (*p && ((*p == ' ') || iscntrl(*p)))
3137 p++;
3138
3139 do {
3140 strncpy(str, p, 7);
3141 str[7] = 0;
3142 if (equal_ustring(str, "format=")) {
3143 p += 7;
3144 if (*p == '\"') {
3145 p++;
3146 while (*p && *p != '\"')
3147 *format++ = *p++;
3148 *format = 0;
3149 if (*p == '\"')
3150 p++;
3151 } else {
3152 while (*p && *p != ' ' && *p != '>')
3153 *format++ = *p++;
3154 *format = 0;
3155 }
3156
3157 } else {
3158
3159 strncpy(str, p, 4);
3160 str[4] = 0;
3161 if (equal_ustring(str, "src=")) {
3162 p += 4;
3163 if (*p == '\"') {
3164 p++;
3165 while (*p && *p != '\"')
3166 *path++ = *p++;
3167 *path = 0;
3168 if (*p == '\"')
3169 p++;
3170 } else {
3171 while (*p && *p != ' ' && *p != '>')
3172 *path++ = *p++;
3173 *path = 0;
3174 }
3175 } else {
3176
3177 if (in_script)
3178 break;
3179
3180 strncpy(str, p, 5);
3181 str[5] = 0;
3182 if (equal_ustring(str, "edit=")) {
3183 p += 5;
3184
3185 if (*p == '\"') {
3186 p++;
3187 *edit = atoi(p);
3188 if (*p == '\"')
3189 p++;
3190 } else {
3191 *edit = atoi(p);
3192 while (*p && *p != ' ' && *p != '>')
3193 p++;
3194 }
3195
3196 } else {
3197
3198 strncpy(str, p, 5);
3199 str[5] = 0;
3200 if (equal_ustring(str, "type=")) {
3201 p += 5;
3202 if (*p == '\"') {
3203 p++;
3204 while (*p && *p != '\"')
3205 *type++ = *p++;
3206 *type = 0;
3207 if (*p == '\"')
3208 p++;
3209 } else {
3210 while (*p && *p != ' ' && *p != '>')
3211 *type++ = *p++;
3212 *type = 0;
3213 }
3214 } else {
3215 strncpy(str, p, 4);
3216 str[4] = 0;
3217 if (equal_ustring(str, "pwd=")) {
3218 p += 4;
3219 if (*p == '\"') {
3220 p++;
3221 while (*p && *p != '\"')
3222 *pwd++ = *p++;
3223 *pwd = 0;
3224 if (*p == '\"')
3225 p++;
3226 } else {
3227 while (*p && *p != ' ' && *p != '>')
3228 *pwd++ = *p++;
3229 *pwd = 0;
3230 }
3231 } else {
3232 if (strchr(p, '=')) {
3233 mstrlcpy(str, p, sizeof(str));
3234 pt = strchr(str, '=')+1;
3235 if (*pt == '\"') {
3236 pt++;
3237 while (*pt && *pt != '\"')
3238 pt++;
3239 if (*pt == '\"')
3240 pt++;
3241 *pt = 0;
3242 } else {
3243 while (*pt && *pt != ' ' && *pt != '>')
3244 pt++;
3245 *pt = 0;
3246 }
3247 if (tail[0]) {
3248 mstrlcat(tail, " ", 256);
3249 mstrlcat(tail, str, 256);
3250 } else {
3251 mstrlcat(tail, str, 256);
3252 }
3253 p += strlen(str);
3254 }
3255 }
3256 }
3257 }
3258 }
3259 }
3260
3261 while (*p && ((*p == ' ') || iscntrl(*p)))
3262 p++;
3263
3264 if (*p == '<') {
3265 cm_msg(MERROR, "find_odb_tag", "Invalid odb tag '%s'", ps);
3266 return NULL;
3267 }
3268 } while (*p != '>');
3269
3270 return ps;
3271 }
3272
3273 while (*p && *p != '>')
3274 p++;
3275
3276 } while (1);
3277
3278}
3279
3280/*------------------------------------------------------------------*/
3281
3282void 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)
3283{
3284 int size, index, i_edit, i_set;
3285 char data[TEXT_SIZE], full_keypath[256], keypath[256], *p;
3286 HNDLE hDB, hkey;
3287 KEY key;
3288
3289 /* check if variable to edit */
3290 i_edit = -1;
3291 if (equal_ustring(pp->getparam("cmd"), "Edit"))
3292 i_edit = atoi(pp->getparam("index"));
3293
3294 /* check if variable to set */
3295 i_set = -1;
3296 if (equal_ustring(pp->getparam("cmd"), "Set"))
3297 i_set = atoi(pp->getparam("index"));
3298
3299 /* check if path contains index */
3300 mstrlcpy(full_keypath, keypath1, sizeof(full_keypath));
3301 mstrlcpy(keypath, keypath1, sizeof(keypath));
3302 index = 0;
3303
3304 if (strchr(keypath, '[') && strchr(keypath, ']')) {
3305 for (p = strchr(keypath, '[') + 1; *p && *p != ']'; p++)
3306 if (!isdigit(*p))
3307 break;
3308
3309 if (*p && *p == ']') {
3310 index = atoi(strchr(keypath, '[') + 1);
3311 *strchr(keypath, '[') = 0;
3312 }
3313 }
3314
3316 db_find_key(hDB, 0, keypath, &hkey);
3317 if (!hkey)
3318 r->rsprintf("<b>Key \"%s\" not found in ODB</b>\n", keypath);
3319 else {
3320 db_get_key(hDB, hkey, &key);
3321 size = sizeof(data);
3322 db_get_data_index(hDB, hkey, data, &size, index, key.type);
3323
3324 std::string data_str;
3325 if (format && strlen(format)>0)
3326 data_str = db_sprintff(format, data, key.item_size, 0, key.type);
3327 else
3328 data_str= db_sprintf(data, key.item_size, 0, key.type);
3329
3330 if (equal_ustring(type, "checkbox")) {
3331
3332 if (pp->isparam("cbi"))
3333 i_set = atoi(pp->getparam("cbi"));
3334 if (n_var == i_set) {
3335 /* toggle state */
3336 if (key.type == TID_BOOL) {
3337 if (data_str[0] == 'y')
3338 data_str = "n";
3339 else
3340 data_str = "y";
3341 } else {
3342 if (atoi(data_str.c_str()) > 0)
3343 data_str = "0";
3344 else
3345 data_str = "1";
3346 }
3347
3348 db_sscanf(data_str.c_str(), data, &size, 0, key.type);
3349 db_set_data_index(hDB, hkey, data, size, index, key.type);
3350 }
3351
3352 std::string options;
3353 if (data_str[0] == 'y' || atoi(data_str.c_str()) > 0)
3354 options += "checked ";
3355 if (!edit)
3356 options += "disabled ";
3357 else {
3358 if (edit == 1) {
3359 options += "onClick=\"o=document.createElement('input');o.type='hidden';o.name='cbi';o.value='";
3360 options += msprintf("%d", n_var);
3361 options += "';document.form1.appendChild(o);";
3362 options += "document.form1.submit();\" ";
3363 }
3364 }
3365
3366 if (tail[0])
3367 options += tail;
3368
3369 r->rsprintf("<input type=\"checkbox\" %s>\n", options.c_str());
3370
3371 } else { // checkbox
3372
3373 if (edit == 1) {
3374 if (n_var == i_set) {
3375 /* set value */
3376 char str[256];
3377 mstrlcpy(str, pp->getparam("value"), sizeof(str));
3378 db_sscanf(str, data, &size, 0, key.type);
3379 db_set_data_index(hDB, hkey, data, size, index, key.type);
3380
3381 /* read back value */
3382 size = sizeof(data);
3383 db_get_data_index(hDB, hkey, data, &size, index, key.type);
3384 data_str = db_sprintf(data, key.item_size, 0, key.type);
3385 }
3386
3387 if (n_var == i_edit) {
3388 r->rsprintf("<input type=text size=10 maxlength=80 name=value value=\"%s\">\n", data_str.c_str());
3389 r->rsprintf("<input type=submit size=20 name=cmd value=Set>\n");
3390 r->rsprintf("<input type=hidden name=index value=%d>\n", n_var);
3391 r->rsprintf("<input type=hidden name=cmd value=Set>\n");
3392 } else {
3393 if (edit == 2) {
3394 /* edit handling through user supplied JavaScript */
3395 r->rsprintf("<a href=\"#\" %s>", tail);
3396 } else {
3397 /* edit handling through form submission */
3398 if (pwd[0]) {
3399 r->rsprintf("<a onClick=\"promptpwd('%s?cmd=Edit&index=%d&pnam=%s')\" href=\"#\">", path, n_var, pwd);
3400 } else {
3401 r->rsprintf("<a href=\"%s?cmd=Edit&index=%d\" %s>", path, n_var, tail);
3402 }
3403 }
3404
3405 r->rsputs(data_str.c_str());
3406 r->rsprintf("</a>");
3407 }
3408 } else if (edit == 2) {
3409 r->rsprintf("<a href=\"#\" onclick=\"ODBEdit('%s')\">\n", full_keypath);
3410 r->rsputs(data_str.c_str());
3411 r->rsprintf("</a>");
3412 }
3413 else
3414 r->rsputs(data_str.c_str());
3415 }
3416 }
3417}
3418
3419/*------------------------------------------------------------------*/
3420
3421/* add labels using following syntax under /Custom/Images/<name.gif>/Labels/<name>:
3422
3423 [Name] [Description] [Example]
3424
3425 Src ODB path for vairable to display /Equipment/Environment/Variables/Input[0]
3426 Format Formt for float/double %1.2f Deg. C
3427 Font Font to use small | medium | giant
3428 X X-position in pixel 90
3429 Y Y-position from top 67
3430 Align horizontal align left/center/right left
3431 FGColor Foreground color RRGGBB 000000
3432 BGColor Background color RRGGBB FFFFFF
3433*/
3434
3435static const char *cgif_label_str[] = {
3436 "Src = STRING : [256] ",
3437 "Format = STRING : [32] %1.1f",
3438 "Font = STRING : [32] Medium",
3439 "X = INT : 0",
3440 "Y = INT : 0",
3441 "Align = INT : 0",
3442 "FGColor = STRING : [8] 000000",
3443 "BGColor = STRING : [8] FFFFFF",
3444 NULL
3445};
3446
3447typedef struct {
3448 char src[256];
3449 char format[32];
3450 char font[32];
3451 int x, y, align;
3452 char fgcolor[8];
3453 char bgcolor[8];
3454} CGIF_LABEL;
3455
3456/* add labels using following syntax under /Custom/Images/<name.gif>/Bars/<name>:
3457
3458 [Name] [Description] [Example]
3459
3460 Src ODB path for vairable to display /Equipment/Environment/Variables/Input[0]
3461 X X-position in pixel 90
3462 Y Y-position from top 67
3463 Width Width in pixel 20
3464 Height Height in pixel 100
3465 Direction 0(vertical)/1(horiz.) 0
3466 Axis Draw axis 0(none)/1(left)/2(right) 1
3467 Logscale Draw logarithmic axis n
3468 Min Min value for axis 0
3469 Max Max value for axis 10
3470 FGColor Foreground color RRGGBB 000000
3471 BGColor Background color RRGGBB FFFFFF
3472 BDColor Border color RRGGBB 808080
3473*/
3474
3475static const char *cgif_bar_str[] = {
3476 "Src = STRING : [256] ",
3477 "X = INT : 0",
3478 "Y = INT : 0",
3479 "Width = INT : 10",
3480 "Height = INT : 100",
3481 "Direction = INT : 0",
3482 "Axis = INT : 1",
3483 "Logscale = BOOL : n",
3484 "Min = DOUBLE : 0",
3485 "Max = DOUBLE : 10",
3486 "FGColor = STRING : [8] 000000",
3487 "BGColor = STRING : [8] FFFFFF",
3488 "BDColor = STRING : [8] 808080",
3489 NULL
3490};
3491
3492typedef struct {
3493 char src[256];
3494 int x, y, width, height, direction, axis;
3496 double min, max;
3497 char fgcolor[8];
3498 char bgcolor[8];
3499 char bdcolor[8];
3500} CGIF_BAR;
3501
3502/*------------------------------------------------------------------*/
3503
3504int evaluate_src(char *key, char *src, double *fvalue)
3505{
3506 HNDLE hDB, hkeyval;
3507 KEY vkey;
3508 int i, n, size, ivalue;
3509 char str[256], data[256];
3510
3512
3513 /* separate source from operators */
3514 for (i=0 ; i<(int)strlen(src) ; i++)
3515 if (src[i] == '>' || src[i] == '&')
3516 break;
3517 strncpy(str, src, i);
3518 str[i] = 0;
3519
3520 /* strip trailing blanks */
3521 while (strlen(str) > 0 && str[strlen(str)-1] == ' ')
3522 str[strlen(str)-1] = 0;
3523
3524 db_find_key(hDB, 0, str, &hkeyval);
3525 if (!hkeyval) {
3526 cm_msg(MERROR, "evaluate_src", "Invalid Src key \"%s\" for Fill \"%s\"",
3527 src, key);
3528 return 0;
3529 }
3530
3531 db_get_key(hDB, hkeyval, &vkey);
3532 size = sizeof(data);
3533 db_get_value(hDB, 0, src, data, &size, vkey.type, FALSE);
3534 std::string value = db_sprintf(data, size, 0, vkey.type);
3535 if (equal_ustring(value.c_str(), "NAN"))
3536 return 0;
3537
3538 if (vkey.type == TID_BOOL) {
3539 *fvalue = (value[0] == 'y');
3540 } else
3541 *fvalue = atof(value.c_str());
3542
3543 /* evaluate possible operators */
3544 do {
3545 if (src[i] == '>' && src[i+1] == '>') {
3546 i+=2;
3547 n = atoi(src+i);
3548 while (src[i] == ' ' || isdigit(src[i]))
3549 i++;
3550 ivalue = (int)*fvalue;
3551 ivalue >>= n;
3552 *fvalue = ivalue;
3553 }
3554
3555 if (src[i] == '&') {
3556 i+=1;
3557 while (src[i] == ' ')
3558 i++;
3559 if (src[i] == '0' && src[i+1] == 'x')
3560 sscanf(src+2+i, "%x", &n);
3561 else
3562 n = atoi(src+i);
3563 while (src[i] == ' ' || isxdigit(src[i]) || src[i] == 'x')
3564 i++;
3565 ivalue = (int)*fvalue;
3566 ivalue &= n;
3567 *fvalue = ivalue;
3568 }
3569
3570 } while (src[i]);
3571
3572 return 1;
3573}
3574
3575/*------------------------------------------------------------------*/
3576
3577std::string add_custom_path(const std::string& filename)
3578{
3579 // do not append custom path to absolute filenames
3580
3581 if (filename[0] == '/')
3582 return filename;
3583 if (filename[0] == DIR_SEPARATOR)
3584 return filename;
3585
3586 HNDLE hDB;
3588
3589 std::string custom_path = "";
3590
3591 int status = db_get_value_string(hDB, 0, "/Custom/Path", 0, &custom_path, TRUE);
3592
3593 if (status != DB_SUCCESS)
3594 return filename;
3595
3596 if (custom_path.length() < 1)
3597 return filename;
3598
3599 if ((custom_path == DIR_SEPARATOR_STR) || !strchr(custom_path.c_str(), DIR_SEPARATOR)) {
3600 cm_msg(MERROR, "add_custom_path", "ODB /Custom/Path has a forbidden value \"%s\", please change it", custom_path.c_str());
3601 return filename;
3602 }
3603
3604 custom_path = ss_replace_env_variables(custom_path);
3605
3606 std::string full_filename = custom_path;
3607 if (full_filename[full_filename.length()-1] != DIR_SEPARATOR)
3608 full_filename += DIR_SEPARATOR_STR;
3609 full_filename += filename;
3610
3611 return full_filename;
3612}
3613
3614/*------------------------------------------------------------------*/
3615
3616void show_custom_file(Return* r, const char *name)
3617{
3618 char str[256];
3619 std::string filename;
3620 HNDLE hDB;
3621
3623
3624 HNDLE hkey;
3625 sprintf(str, "/Custom/%s", name);
3626 db_find_key(hDB, 0, str, &hkey);
3627
3628 if (!hkey) {
3629 sprintf(str, "/Custom/%s&", name);
3630 db_find_key(hDB, 0, str, &hkey);
3631 if (!hkey) {
3632 sprintf(str, "/Custom/%s!", name);
3633 db_find_key(hDB, 0, str, &hkey);
3634 }
3635 }
3636
3637 if(!hkey){
3638 sprintf(str,"show_custom_file: Invalid custom page: \"/Custom/%s\" not found in ODB", name);
3639 show_error_404(r, str);
3640 return;
3641 }
3642
3643 int status;
3644 KEY key;
3645
3646 status = db_get_key(hDB, hkey, &key);
3647
3648 if (status != DB_SUCCESS) {
3649 char errtext[512];
3650 sprintf(errtext, "show_custom_file: Error: db_get_key() for \"%s\" status %d", str, status);
3651 show_error_404(r, errtext);
3652 return;
3653 }
3654
3655 int size = key.total_size;
3656 char* ctext = (char*)malloc(size);
3657
3658 status = db_get_data(hDB, hkey, ctext, &size, TID_STRING);
3659
3660 if (status != DB_SUCCESS) {
3661 char errtext[512];
3662 sprintf(errtext, "show_custom_file: Error: db_get_data() for \"%s\" status %d", str, status);
3663 show_error_404(r, errtext);
3664 free(ctext);
3665 return;
3666 }
3667
3668 filename = add_custom_path(ctext);
3669
3670 free(ctext);
3671
3672 send_file(r, filename, true);
3673
3674 return;
3675}
3676
3677/*------------------------------------------------------------------*/
3678
3679void show_custom_gif(Return* rr, const char *name)
3680{
3681 char str[256], data[256], src[256];
3682 int i, index, length, status, size, width, height, bgcol, fgcol, bdcol, r, g, b, x, y;
3683 HNDLE hDB, hkeygif, hkeyroot, hkey, hkeyval;
3684 double fvalue, ratio;
3685 KEY key, vkey;
3686 gdImagePtr im;
3687 gdGifBuffer gb;
3688 gdFontPtr pfont;
3689 FILE *f;
3690 CGIF_LABEL label;
3691 CGIF_BAR bar;
3692
3694
3695 /* find image description in ODB */
3696 sprintf(str, "/Custom/Images/%s", name);
3697 db_find_key(hDB, 0, str, &hkeygif);
3698 if (!hkeygif) {
3699
3700 // If we don't have Images directory,
3701 // then just treat this like any other custom file.
3703 return;
3704 }
3705
3706 /* load background image */
3707 std::string filename;
3708 db_get_value_string(hDB, hkeygif, "Background", 0, &filename, FALSE);
3709
3710 std::string full_filename = add_custom_path(filename);
3711
3712 f = fopen(full_filename.c_str(), "rb");
3713 if (f == NULL) {
3714 sprintf(str, "show_custom_gif: Cannot open file \"%s\"", full_filename.c_str());
3715 show_error_404(rr, str);
3716 return;
3717 }
3718
3719 im = gdImageCreateFromGif(f);
3720 fclose(f);
3721
3722 if (im == NULL) {
3723 sprintf(str, "show_custom_gif: File \"%s\" is not a GIF image", filename.c_str());
3724 show_error_404(rr, str);
3725 return;
3726 }
3727
3729
3730 /*---- draw labels ----------------------------------------------*/
3731
3732 db_find_key(hDB, hkeygif, "Labels", &hkeyroot);
3733 if (hkeyroot) {
3734 for (index = 0;; index++) {
3735 db_enum_key(hDB, hkeyroot, index, &hkey);
3736 if (!hkey)
3737 break;
3738 db_get_key(hDB, hkey, &key);
3739
3740 size = sizeof(label);
3741 status = db_get_record1(hDB, hkey, &label, &size, 0, strcomb1(cgif_label_str).c_str());
3742 if (status != DB_SUCCESS) {
3743 cm_msg(MERROR, "show_custom_gif", "Cannot open data record for label \"%s\"",
3744 key.name);
3745 continue;
3746 }
3747
3748 if (label.src[0] == 0) {
3749 cm_msg(MERROR, "show_custom_gif", "Empty Src key for label \"%s\"", key.name);
3750 continue;
3751 }
3752
3753 db_find_key(hDB, 0, label.src, &hkeyval);
3754 if (!hkeyval) {
3755 cm_msg(MERROR, "show_custom_gif", "Invalid Src key \"%s\" for label \"%s\"",
3756 label.src, key.name);
3757 continue;
3758 }
3759
3760 db_get_key(hDB, hkeyval, &vkey);
3761 size = sizeof(data);
3762 status = db_get_value(hDB, 0, label.src, data, &size, vkey.type, FALSE);
3763
3764 std::string value;
3765
3766 if (label.format[0]) {
3767 if (vkey.type == TID_FLOAT)
3768 value = msprintf(label.format, *(((float *) data)));
3769 else if (vkey.type == TID_DOUBLE)
3770 value = msprintf(label.format, *(((double *) data)));
3771 else if (vkey.type == TID_INT)
3772 value = msprintf(label.format, *(((INT *) data)));
3773 else if (vkey.type == TID_BOOL) {
3774 if (strstr(label.format, "%c"))
3775 value = msprintf(label.format, *(((INT *) data)) ? 'y' : 'n');
3776 else
3777 value = msprintf(label.format, *(((INT *) data)));
3778 } else
3779 value = db_sprintf(data, size, 0, vkey.type);
3780 } else
3781 value = db_sprintf(data, size, 0, vkey.type);
3782
3783 sscanf(label.fgcolor, "%02x%02x%02x", &r, &g, &b);
3784 fgcol = gdImageColorAllocate(im, r, g, b);
3785 if (fgcol == -1)
3786 fgcol = gdImageColorClosest(im, r, g, b);
3787
3788 sscanf(label.bgcolor, "%02x%02x%02x", &r, &g, &b);
3789 bgcol = gdImageColorAllocate(im, r, g, b);
3790 if (bgcol == -1)
3791 bgcol = gdImageColorClosest(im, r, g, b);
3792
3793 /* select font */
3794 if (equal_ustring(label.font, "Small"))
3795 pfont = gdFontSmall;
3796 else if (equal_ustring(label.font, "Medium"))
3797 pfont = gdFontMediumBold;
3798 else if (equal_ustring(label.font, "Giant"))
3799 pfont = gdFontGiant;
3800 else
3801 pfont = gdFontMediumBold;
3802
3803 width = value.length() * pfont->w + 5 + 5;
3804 height = pfont->h + 2 + 2;
3805
3806 if (label.align == 0) {
3807 /* left */
3808 gdImageFilledRectangle(im, label.x, label.y, label.x + width,
3809 label.y + height, bgcol);
3810 gdImageRectangle(im, label.x, label.y, label.x + width, label.y + height,
3811 fgcol);
3812 gdImageString(im, pfont, label.x + 5, label.y + 2, value.c_str(), fgcol);
3813 } else if (label.align == 1) {
3814 /* center */
3815 gdImageFilledRectangle(im, label.x - width / 2, label.y, label.x + width / 2,
3816 label.y + height, bgcol);
3817 gdImageRectangle(im, label.x - width / 2, label.y, label.x + width / 2,
3818 label.y + height, fgcol);
3819 gdImageString(im, pfont, label.x + 5 - width / 2, label.y + 2, value.c_str(), fgcol);
3820 } else {
3821 /* right */
3822 gdImageFilledRectangle(im, label.x - width, label.y, label.x,
3823 label.y + height, bgcol);
3824 gdImageRectangle(im, label.x - width, label.y, label.x, label.y + height,
3825 fgcol);
3826 gdImageString(im, pfont, label.x - width + 5, label.y + 2, value.c_str(), fgcol);
3827 }
3828 }
3829 }
3830
3831 /*---- draw bars ------------------------------------------------*/
3832
3833 db_find_key(hDB, hkeygif, "Bars", &hkeyroot);
3834 if (hkeyroot) {
3835 for (index = 0;; index++) {
3836 db_enum_key(hDB, hkeyroot, index, &hkey);
3837 if (!hkey)
3838 break;
3839 db_get_key(hDB, hkey, &key);
3840
3841 size = sizeof(bar);
3842 status = db_get_record1(hDB, hkey, &bar, &size, 0, strcomb1(cgif_bar_str).c_str());
3843 if (status != DB_SUCCESS) {
3844 cm_msg(MERROR, "show_custom_gif", "Cannot open data record for bar \"%s\"",
3845 key.name);
3846 continue;
3847 }
3848
3849 if (bar.src[0] == 0) {
3850 cm_msg(MERROR, "show_custom_gif", "Empty Src key for bar \"%s\"", key.name);
3851 continue;
3852 }
3853
3854 db_find_key(hDB, 0, bar.src, &hkeyval);
3855 if (!hkeyval) {
3856 cm_msg(MERROR, "show_custom_gif", "Invalid Src key \"%s\" for bar \"%s\"",
3857 bar.src, key.name);
3858 continue;
3859 }
3860
3861 db_get_key(hDB, hkeyval, &vkey);
3862 size = sizeof(data);
3863 status = db_get_value(hDB, 0, bar.src, data, &size, vkey.type, FALSE);
3864 std::string value = db_sprintf(data, size, 0, vkey.type);
3865 if (equal_ustring(value.c_str(), "NAN"))
3866 continue;
3867
3868 fvalue = atof(value.c_str());
3869
3870 sscanf(bar.fgcolor, "%02x%02x%02x", &r, &g, &b);
3871 fgcol = gdImageColorAllocate(im, r, g, b);
3872 if (fgcol == -1)
3873 fgcol = gdImageColorClosest(im, r, g, b);
3874
3875 sscanf(bar.bgcolor, "%02x%02x%02x", &r, &g, &b);
3876 bgcol = gdImageColorAllocate(im, r, g, b);
3877 if (bgcol == -1)
3878 bgcol = gdImageColorClosest(im, r, g, b);
3879
3880 sscanf(bar.bdcolor, "%02x%02x%02x", &r, &g, &b);
3881 bdcol = gdImageColorAllocate(im, r, g, b);
3882 if (bdcol == -1)
3883 bdcol = gdImageColorClosest(im, r, g, b);
3884
3885 if (bar.min == bar.max)
3886 bar.max += 1;
3887
3888 if (bar.logscale) {
3889 if (fvalue < 1E-20)
3890 fvalue = 1E-20;
3891 ratio = (log(fvalue) - log(bar.min)) / (log(bar.max) - log(bar.min));
3892 } else
3893 ratio = (fvalue - bar.min) / (bar.max - bar.min);
3894 if (ratio < 0)
3895 ratio = 0;
3896 if (ratio > 1)
3897 ratio = 1;
3898
3899 if (bar.direction == 0) {
3900 /* vertical */
3901 ratio = (bar.height - 2) - ratio * (bar.height - 2);
3902 r = (int) (ratio + 0.5);
3903
3904 gdImageFilledRectangle(im, bar.x, bar.y, bar.x + bar.width,
3905 bar.y + bar.height, bgcol);
3906 gdImageRectangle(im, bar.x, bar.y, bar.x + bar.width, bar.y + bar.height,
3907 bdcol);
3908 gdImageFilledRectangle(im, bar.x + 1, bar.y + r + 1, bar.x + bar.width - 1,
3909 bar.y + bar.height - 1, fgcol);
3910
3911 if (bar.axis == 1)
3912 vaxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y + bar.height, bar.height, -3,
3913 -5, -7, -8, 0, bar.min, bar.max, bar.logscale);
3914 else if (bar.axis == 2)
3915 vaxis(im, gdFontSmall, bdcol, 0, bar.x + bar.width, bar.y + bar.height,
3916 bar.height, 3, 5, 7, 10, 0, bar.min, bar.max, bar.logscale);
3917
3918 } else {
3919 /* horizontal */
3920 ratio = ratio * (bar.height - 2);
3921 r = (int) (ratio + 0.5);
3922
3923 gdImageFilledRectangle(im, bar.x, bar.y, bar.x + bar.height,
3924 bar.y + bar.width, bgcol);
3925 gdImageRectangle(im, bar.x, bar.y, bar.x + bar.height, bar.y + bar.width,
3926 bdcol);
3927 gdImageFilledRectangle(im, bar.x + 1, bar.y + 1, bar.x + r,
3928 bar.y + bar.width - 1, fgcol);
3929
3930 if (bar.axis == 1)
3931 haxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y, bar.height, -3, -5, -7, -18,
3932 0, bar.min, bar.max);
3933 else if (bar.axis == 2)
3934 haxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y + bar.width, bar.height, 3,
3935 5, 7, 8, 0, bar.min, bar.max);
3936 }
3937 }
3938 }
3939
3940 /*---- draw fills -----------------------------------------------*/
3941
3942 db_find_key(hDB, hkeygif, "Fills", &hkeyroot);
3943 if (hkeyroot) {
3944 for (index = 0;; index++) {
3945 db_enum_key(hDB, hkeyroot, index, &hkey);
3946 if (!hkey)
3947 break;
3948 db_get_key(hDB, hkey, &key);
3949
3950 size = sizeof(src);
3951 src[0] = 0;
3952 db_get_value(hDB, hkey, "Src", src, &size, TID_STRING, TRUE);
3953
3954 if (src[0] == 0) {
3955 cm_msg(MERROR, "show_custom_gif", "Empty Src key for Fill \"%s\"", key.name);
3956 continue;
3957 }
3958
3959 if (!evaluate_src(key.name, src, &fvalue))
3960 continue;
3961
3962 x = y = 0;
3963 size = sizeof(x);
3964 db_get_value(hDB, hkey, "X", &x, &size, TID_INT, TRUE);
3965 db_get_value(hDB, hkey, "Y", &y, &size, TID_INT, TRUE);
3966
3967 size = sizeof(data);
3968 status = db_get_value(hDB, hkey, "Limits", data, &size, TID_DOUBLE, FALSE);
3969 if (status != DB_SUCCESS) {
3970 cm_msg(MERROR, "show_custom_gif", "No \"Limits\" entry for Fill \"%s\"",
3971 key.name);
3972 continue;
3973 }
3974 for (i = 0; i < size / (int) sizeof(double); i++)
3975 if (*((double *) data + i) > fvalue)
3976 break;
3977 if (i > 0)
3978 i--;
3979
3980 db_find_key(hDB, hkey, "Fillcolors", &hkeyval);
3981 if (!hkeyval) {
3982 cm_msg(MERROR, "show_custom_gif", "No \"Fillcolors\" entry for Fill \"%s\"",
3983 key.name);
3984 continue;
3985 }
3986
3987 size = sizeof(data);
3988 strcpy(data, "FFFFFF");
3989 status = db_get_data_index(hDB, hkeyval, data, &size, i, TID_STRING);
3990 if (status == DB_SUCCESS) {
3991 sscanf(data, "%02x%02x%02x", &r, &g, &b);
3992 fgcol = gdImageColorAllocate(im, r, g, b);
3993 if (fgcol == -1)
3994 fgcol = gdImageColorClosest(im, r, g, b);
3995 gdImageFill(im, x, y, fgcol);
3996 }
3997 }
3998 }
3999
4000 /* generate GIF */
4001 gdImageInterlace(im, 1);
4002 gdImageGif(im, &gb);
4003 gdImageDestroy(im);
4004 length = gb.size;
4005
4006 rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
4007 rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
4008
4009 rr->rsprintf("Content-Type: image/gif\r\n");
4010 rr->rsprintf("Content-Length: %d\r\n", length);
4011 rr->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
4012 rr->rsprintf("Expires: Fri, 01-Jan-1983 00:00:00 GMT\r\n\r\n");
4013
4014 rr->rmemcpy(gb.data, length);
4015}
4016
4017
4018
4019/*------------------------------------------------------------------*/
4020
4022{
4023 static RPC_LIST rpc_list[] = {
4024 { 9999, "mhttpd_jrpc_rev0", {
4025 {TID_STRING, RPC_IN}, // arg0
4026 {TID_STRING, RPC_IN}, // arg1
4027 {TID_STRING, RPC_IN}, // arg2
4028 {TID_STRING, RPC_IN}, // arg3
4029 {TID_STRING, RPC_IN}, // arg4
4030 {TID_STRING, RPC_IN}, // arg5
4031 {TID_STRING, RPC_IN}, // arg6
4032 {TID_STRING, RPC_IN}, // arg7
4033 {TID_STRING, RPC_IN}, // arg8
4034 {TID_STRING, RPC_IN}, // arg9
4035 {0}} },
4036 { 0 }
4037 };
4038
4039 int count = 0, substring = 0, rpc;
4040
4041 const char *xname = p->getparam("name");
4042 const char *srpc = p->getparam("rpc");
4043
4044 if (!srpc || !xname) {
4046 r->rsprintf("<INVALID_ARGUMENTS>");
4047 return;
4048 }
4049
4050 char sname[256];
4051 mstrlcpy(sname, xname, sizeof(sname));
4052
4053 if (sname[strlen(sname)-1]=='*') {
4054 sname[strlen(sname)-1] = 0;
4055 substring = 1;
4056 }
4057
4058 rpc = atoi(srpc);
4059
4060 if (rpc<RPC_MIN_ID || rpc>RPC_MAX_ID) {
4062 r->rsprintf("<INVALID_RPC_ID>");
4063 return;
4064 }
4065
4066 rpc_list[0].id = rpc;
4068
4070 r->rsprintf("calling rpc %d | ", rpc);
4071
4072 if (1) {
4073 int status, i;
4074 char str[256];
4075 HNDLE hDB, hrootkey, hsubkey, hkey;
4076
4078
4079 /* find client which exports FCNA function */
4080 status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
4081 if (status == DB_SUCCESS) {
4082 for (i=0; ; i++) {
4083 status = db_enum_key(hDB, hrootkey, i, &hsubkey);
4085 break;
4086
4087 sprintf(str, "RPC/%d", rpc);
4088 status = db_find_key(hDB, hsubkey, str, &hkey);
4089 if (status == DB_SUCCESS) {
4090 char client_name[NAME_LENGTH];
4091 HNDLE hconn;
4092 int size;
4093
4094 size = sizeof(client_name);
4095 status = db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, FALSE);
4096 if (status != DB_SUCCESS)
4097 continue;
4098
4099 if (strlen(sname) > 0) {
4100 if (substring) {
4101 if (strstr(client_name, sname) != client_name)
4102 continue;
4103 } else {
4104 if (strcmp(sname, client_name) != 0)
4105 continue;
4106 }
4107 }
4108
4109 count++;
4110
4111 r->rsprintf("client %s", client_name);
4112
4113 status = cm_connect_client(client_name, &hconn);
4114 r->rsprintf(" %d", status);
4115
4116 if (status == RPC_SUCCESS) {
4117 status = rpc_client_call(hconn, rpc,
4118 p->getparam("arg0"),
4119 p->getparam("arg1"),
4120 p->getparam("arg2"),
4121 p->getparam("arg3"),
4122 p->getparam("arg4"),
4123 p->getparam("arg5"),
4124 p->getparam("arg6"),
4125 p->getparam("arg7"),
4126 p->getparam("arg8"),
4127 p->getparam("arg9")
4128 );
4129 r->rsprintf(" %d", status);
4130
4131 //status = cm_disconnect_client(hconn, FALSE);
4132 r->rsprintf(" %d", status);
4133 }
4134
4135 r->rsprintf(" | ");
4136 }
4137 }
4138 }
4139 }
4140
4141 r->rsprintf("rpc %d, called %d clients\n", rpc, count);
4142}
4143
4144/*------------------------------------------------------------------*/
4145
4147{
4148 static RPC_LIST rpc_list[] = {
4149 { 9998, "mhttpd_jrpc_rev1", {
4150 {TID_STRING, RPC_OUT}, // return string
4151 {TID_INT, RPC_IN}, // return string max length
4152 {TID_STRING, RPC_IN}, // arg0
4153 {TID_STRING, RPC_IN}, // arg1
4154 {TID_STRING, RPC_IN}, // arg2
4155 {TID_STRING, RPC_IN}, // arg3
4156 {TID_STRING, RPC_IN}, // arg4
4157 {TID_STRING, RPC_IN}, // arg5
4158 {TID_STRING, RPC_IN}, // arg6
4159 {TID_STRING, RPC_IN}, // arg7
4160 {TID_STRING, RPC_IN}, // arg8
4161 {TID_STRING, RPC_IN}, // arg9
4162 {0}} },
4163 { 0 }
4164 };
4165
4166 int status, substring = 0, rpc;
4167
4168 const char *xname = p->getparam("name");
4169 const char *srpc = p->getparam("rpc");
4170
4171 if (!srpc || !xname) {
4173 r->rsprintf("<INVALID_ARGUMENTS>");
4174 return;
4175 }
4176
4177 char sname[256];
4178 mstrlcpy(sname, xname, sizeof(sname));
4179
4180 if (sname[strlen(sname)-1]=='*') {
4181 sname[strlen(sname)-1] = 0;
4182 substring = 1;
4183 }
4184
4185 rpc = atoi(srpc);
4186
4187 if (rpc<RPC_MIN_ID || rpc>RPC_MAX_ID) {
4189 r->rsprintf("<INVALID_RPC_ID>");
4190 return;
4191 }
4192
4193 rpc_list[0].id = rpc;
4195
4196 //printf("cm_register_functions() for format \'%s\' status %d\n", sformat, status);
4197
4199
4200 std::string reply_header;
4201 std::string reply_body;
4202
4203 //r->rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", HTTP_ENCODING);
4204 //r->rsprintf("<!-- created by MHTTPD on (timestamp) -->\n");
4205 //r->rsprintf("<jrpc_rev1>\n");
4206 //r->rsprintf(" <rpc>%d</rpc>\n", rpc);
4207
4208 if (1) {
4209 HNDLE hDB, hrootkey, hsubkey, hkey;
4210
4212
4213 int buf_length = 1024;
4214
4215 int max_reply_length = atoi(p->getparam("max_reply_length"));
4216 if (max_reply_length > buf_length)
4217 buf_length = max_reply_length;
4218
4219 char* buf = (char*)malloc(buf_length);
4220
4221 assert(buf != NULL);
4222
4223 /* find client which exports our RPC function */
4224 status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
4225 if (status == DB_SUCCESS) {
4226 for (int i=0; ; i++) {
4227 status = db_enum_key(hDB, hrootkey, i, &hsubkey);
4229 break;
4230
4231 char str[256];
4232 sprintf(str, "RPC/%d", rpc);
4233 status = db_find_key(hDB, hsubkey, str, &hkey);
4234 if (status == DB_SUCCESS) {
4235 char client_name[NAME_LENGTH];
4236 HNDLE hconn;
4237 int size;
4238
4239 size = sizeof(client_name);
4240 status = db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, FALSE);
4241 if (status != DB_SUCCESS)
4242 continue;
4243
4244 if (strlen(sname) > 0) {
4245 if (substring) {
4246 if (strstr(client_name, sname) != client_name)
4247 continue;
4248 } else {
4249 if (strcmp(sname, client_name) != 0)
4250 continue;
4251 }
4252 }
4253
4254 //r->rsprintf(" <client>\n");
4255 //r->rsprintf(" <name>%s</name>\n", client_name);
4256
4257 int connect_status = -1;
4258 int call_status = -1;
4259 int call_length = 0;
4260 int disconnect_status = -1;
4261
4262 connect_status = cm_connect_client(client_name, &hconn);
4263
4264 //r->rsprintf(" <connect_status>%d</connect_status>\n", status);
4265
4266 if (connect_status == RPC_SUCCESS) {
4267 buf[0] = 0;
4268
4269 call_status = rpc_client_call(hconn, rpc,
4270 buf,
4271 buf_length,
4272 p->getparam("arg0"),
4273 p->getparam("arg1"),
4274 p->getparam("arg2"),
4275 p->getparam("arg3"),
4276 p->getparam("arg4"),
4277 p->getparam("arg5"),
4278 p->getparam("arg6"),
4279 p->getparam("arg7"),
4280 p->getparam("arg8"),
4281 p->getparam("arg9")
4282 );
4283
4284 //r->rsprintf(" <rpc_status>%d</rpc_status>\n", status);
4286 //r->rsputs("<data>");
4287 //r->rsputs(buf);
4288 //r->rsputs("</data>\n");
4289
4290 if (call_status == RPC_SUCCESS) {
4291 call_length = strlen(buf);
4292 reply_body += buf;
4293 }
4294
4295 //disconnect_status = cm_disconnect_client(hconn, FALSE);
4296 //r->rsprintf(" <disconnect_status>%d</disconnect_status>\n", status);
4297 }
4298
4299 //r->rsprintf(" </client>\n");
4300
4301 if (reply_header.length() > 0)
4302 reply_header += " | ";
4303
4304 char tmp[256];
4305 sprintf(tmp, "%s %d %d %d %d", client_name, connect_status, call_status, disconnect_status, call_length);
4306 reply_header += tmp;
4307 }
4308 }
4309 }
4310
4311 free(buf);
4312 }
4313
4314 //r->rsprintf(" <called_clients>%d</called_clients>\n", count);
4315 //r->rsprintf("</jrpc_rev1>\n");
4316
4317 if (reply_header.length() > 0) {
4318 r->rsputs(reply_header.c_str());
4319 r->rsputs(" || ");
4320 r->rsputs(reply_body.c_str());
4321 r->rsputs("\n");
4322 }
4323}
4324
4325/*------------------------------------------------------------------*/
4326
4328{
4329 int status;
4330
4331 const char *name = p->getparam("name");
4332 const char *cmd = p->getparam("rcmd");
4333 const char *args = p->getparam("rarg");
4334
4335 if (!name || !cmd || !args) {
4337 r->rsprintf("<INVALID_ARGUMENTS>");
4338 return;
4339 }
4340
4342
4343 int buf_length = 1024;
4344
4345 int max_reply_length = atoi(p->getparam("max_reply_length"));
4346 if (max_reply_length > buf_length)
4347 buf_length = max_reply_length;
4348
4349 char* buf = (char*)malloc(buf_length);
4350 assert(buf != NULL);
4351
4352 buf[0] = 0;
4353
4354 HNDLE hconn;
4355
4356 status = cm_connect_client(name, &hconn);
4357
4358 if (status != RPC_SUCCESS) {
4359 r->rsprintf("<RPC_CONNECT_ERROR>%d</RPC_CONNECT_ERROR>", status);
4360 free(buf);
4361 return;
4362 }
4363
4364 status = rpc_client_call(hconn, RPC_JRPC, cmd, args, buf, buf_length);
4365
4366 if (status != RPC_SUCCESS) {
4367 r->rsprintf("<RPC_CALL_ERROR>%d</RPC_CALL_ERROR>", status);
4368 free(buf);
4369 return;
4370 }
4371
4372 r->rsprintf("%s", buf);
4373
4374 //status = cm_disconnect_client(hconn, FALSE);
4375
4376 free(buf);
4377}
4378
4379/*------------------------------------------------------------------*/
4380
4381void output_key(Param* p, Return* r, HNDLE hkey, int index, const char *format)
4382{
4383 int size, i;
4384 HNDLE hDB, hsubkey;
4385 KEY key;
4386 char data[TEXT_SIZE];
4387
4389
4390 db_get_key(hDB, hkey, &key);
4391 if (key.type == TID_KEY) {
4392 for (i=0 ; ; i++) {
4393 db_enum_key(hDB, hkey, i, &hsubkey);
4394 if (!hsubkey)
4395 break;
4396 output_key(p, r, hsubkey, -1, format);
4397 }
4398 } else {
4399 if (key.item_size <= (int)sizeof(data)) {
4400 size = sizeof(data);
4401 db_get_data(hDB, hkey, data, &size, key.type);
4402 if (index == -1) {
4403 for (i=0 ; i<key.num_values ; i++) {
4404 if (p->isparam("name") && atoi(p->getparam("name")) == 1) {
4405 if (key.num_values == 1)
4406 r->rsprintf("%s:", key.name);
4407 else
4408 r->rsprintf("%s[%d]:", key.name, i);
4409 }
4410 std::string data_str;
4411 if (format && format[0])
4412 data_str = db_sprintff(format, data, key.item_size, i, key.type);
4413 else
4414 data_str = db_sprintf(data, key.item_size, i, key.type);
4415 r->rsputs(data_str.c_str());
4416 if (i<key.num_values-1)
4417 r->rsputs("\n");
4418 }
4419 } else {
4420 if (p->isparam("name") && atoi(p->getparam("name")) == 1)
4421 r->rsprintf("%s[%d]:", key.name, index);
4422 if (index >= key.num_values)
4423 r->rsputs("<DB_OUT_OF_RANGE>");
4424 else {
4425 std::string data_str;
4426 if (p->isparam("format"))
4427 data_str = db_sprintff(p->getparam("format"), data, key.item_size, index, key.type);
4428 else
4429 data_str = db_sprintf(data, key.item_size, index, key.type);
4430 r->rsputs(data_str.c_str());
4431 }
4432 }
4433 r->rsputs("\n");
4434 }
4435 }
4436}
4437
4438/*------------------------------------------------------------------*/
4439
4440bool starts_with(const std::string& s1, const char* s2)
4441{
4442 if (s1.length() < strlen(s2))
4443 return false;
4444 return (strncasecmp(s1.c_str(), s2, strlen(s2)) == 0);
4445}
4446
4447//static bool ends_with_char(const std::string& s, char c)
4448//{
4449// if (s.length() < 1)
4450// return false;
4451// return s[s.length()-1] == c;
4452//}
4453
4454/*------------------------------------------------------------------*/
4455
4456void javascript_commands(Param* p, Return* r, const char *cookie_cpwd)
4457{
4458 int status;
4459 int size, i, n, index, type;
4460 unsigned int t;
4461 char str[TEXT_SIZE], format[256], facility[256], user[256];
4462 HNDLE hDB, hkey;
4463 KEY key;
4464 char data[TEXT_SIZE];
4465
4467
4468 // process common parameters
4469
4470 const int ENCODING_NONE = 0;
4471 const int ENCODING_ODB = 1;
4472 const int ENCODING_XML = 2;
4473 const int ENCODING_JSON = 3;
4474
4475 std::string cmd_parameter;
4476 std::string encoding_parameter;
4477 int encoding = ENCODING_NONE; // default encoding
4478 bool jsonp = false; // default is no JSONP wrapper
4479 std::string jsonp_callback; // default is no JSONP
4480 bool single = false; // single encoding
4481 bool multiple = false; // multiple encoding
4482 std::vector<std::string> odb; // multiple odb parameters
4483 //HNDLE hodb; // ODB handle for single odb parameter
4484 //std::vector<HNDLE> hodbm; // ODB handle for multiple odb parameter
4485
4486 if (p->isparam("cmd")) {
4487 cmd_parameter = p->getparam("cmd");
4488 }
4489
4490 if (p->isparam("encoding")) {
4491 encoding_parameter = p->getparam("encoding");
4492 }
4493
4494 if (encoding_parameter.length() > 0) {
4495 if (starts_with(encoding_parameter, "odb"))
4496 encoding = ENCODING_ODB;
4497 else if (starts_with(encoding_parameter, "xml"))
4498 encoding = ENCODING_XML;
4499 else if (starts_with(encoding_parameter, "json"))
4500 encoding = ENCODING_JSON;
4501 }
4502
4503 if (encoding == ENCODING_JSON) {
4504 if (p->isparam("callback")) {
4505 jsonp = true;
4506 jsonp_callback = p->getparam("callback");
4507 }
4508 }
4509
4510 if (p->isparam("odb")) {
4511 single = true;
4512 odb.push_back(p->getparam("odb"));
4513 }
4514
4515 if (p->isparam("odb0")) {
4516 multiple = true;
4517 for (int i=0 ; ; i++) {
4518 char ppath[256];
4519 sprintf(ppath, "odb%d", i);
4520 if (!p->isparam(ppath))
4521 break;
4522 odb.push_back(p->getparam(ppath));
4523 }
4524 }
4525
4526 if (/* DISABLES CODE */ (0)) {
4527 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());
4528 }
4529
4530 /* process "jset" command */
4531 if (equal_ustring(p->getparam("cmd"), "jset")) {
4532
4533 if (*p->getparam("pnam")) {
4534 std::string ppath;
4535 ppath += "/Custom/Pwd/";
4536 ppath += p->getparam("pnam");
4537 str[0] = 0;
4538 db_get_value(hDB, 0, ppath.c_str(), str, &size, TID_STRING, TRUE);
4539 if (!equal_ustring(cookie_cpwd, str)) {
4541 r->rsprintf("Invalid password!");
4542 return;
4543 }
4544 }
4545 mstrlcpy(str, p->getparam("odb"), sizeof(str));
4546 if (strchr(str, '[')) {
4547 if (*(strchr(str, '[')+1) == '*')
4548 index = -1;
4549 else
4550 index = atoi(strchr(str, '[')+1);
4551 *strchr(str, '[') = 0;
4552 } else
4553 index = 0;
4554
4555 if (db_find_key(hDB, 0, str, &hkey) == DB_SUCCESS && p->isparam("value")) {
4556 db_get_key(hDB, hkey, &key);
4557 memset(data, 0, sizeof(data));
4558 if (key.item_size <= (int)sizeof(data)) {
4559 if (index == -1) {
4560 const char* ptr = p->getparam("value");
4561 for (i=0 ; ptr != NULL ; i++) {
4562 size = sizeof(data);
4563 db_sscanf(ptr, data, &size, 0, key.type);
4564 if (strchr(data, ','))
4565 *strchr(data, ',') = 0;
4567 ptr = strchr(ptr, ',');
4568 if (ptr != NULL)
4569 ptr++;
4570 }
4571 } else {
4572 size = sizeof(data);
4573 db_sscanf(p->getparam("value"), data, &size, 0, key.type);
4574
4575 /* extend data size for single string if necessary */
4576 if ((key.type == TID_STRING || key.type == TID_LINK)
4577 && (int) strlen(data) + 1 > key.item_size && key.num_values == 1) {
4578 key.item_size = strlen(data) + 1;
4579 db_set_data(hDB, hkey, data, key.item_size, 1, key.type);
4580 } else
4582 }
4583 }
4584 } else {
4585 if (p->isparam("value") && p->isparam("type") && p->isparam("len")) {
4586 int type = atoi(p->getparam("type"));
4587 if (type == 0) {
4589 r->rsprintf("Invalid type %d!", type);
4590 return;
4591 }
4592 db_create_key(hDB, 0, str, type);
4593 db_find_key(hDB, 0, str, &hkey);
4594 if (!hkey) {
4596 r->rsprintf("Cannot create \'%s\' type %d", str, type);
4597 return;
4598 }
4599 db_get_key(hDB, hkey, &key);
4600 memset(data, 0, sizeof(data));
4601 size = sizeof(data);
4602 db_sscanf(p->getparam("value"), data, &size, 0, key.type);
4603 if (key.type == TID_STRING)
4604 db_set_data(hDB, hkey, data, atoi(p->getparam("len")), 1, TID_STRING);
4605 else {
4606 for (i=0 ; i<atoi(p->getparam("len")) ; i++)
4608 }
4609 }
4610 }
4611
4613 r->rsprintf("OK");
4614 return;
4615 }
4616
4617 /* process "jget" command */
4618 if (equal_ustring(p->getparam("cmd"), "jget")) {
4619
4620 if (p->isparam("odb")) {
4621 mstrlcpy(str, p->getparam("odb"), sizeof(str));
4622 if (strchr(str, '[')) {
4623 if (*(strchr(str, '[')+1) == '*')
4624 index = -1;
4625 else
4626 index = atoi(strchr(str, '[')+1);
4627 *strchr(str, '[') = 0;
4628 } else
4629 index = 0;
4630
4632
4633 status = db_find_key(hDB, 0, str, &hkey);
4634
4635 if (status == DB_SUCCESS)
4636 output_key(p, r, hkey, index, p->getparam("format"));
4637 else
4638 r->rsputs("<DB_NO_KEY>");
4639 }
4640
4641 if (p->isparam("odb0")) {
4643 for (i=0 ; ; i++) {
4644 char ppath[256];
4645 sprintf(ppath, "odb%d", i);
4646 sprintf(format, "format%d", i);
4647 if (p->isparam(ppath)) {
4648 mstrlcpy(str, p->getparam(ppath), sizeof(str));
4649 if (strchr(str, '[')) {
4650 if (*(strchr(str, '[')+1) == '*')
4651 index = -1;
4652 else
4653 index = atoi(strchr(str, '[')+1);
4654 *strchr(str, '[') = 0;
4655 } else
4656 index = 0;
4657 if (i > 0)
4658 r->rsputs("$#----#$\n");
4659 if (db_find_key(hDB, 0, str, &hkey) == DB_SUCCESS)
4660 output_key(p, r, hkey, index, p->getparam(format));
4661 else
4662 r->rsputs("<DB_NO_KEY>");
4663
4664 } else
4665 break;
4666 }
4667 }
4668
4669 return;
4670 }
4671
4672 /* process "jcopy" command */
4673 if (equal_ustring(p->getparam("cmd"), "jcopy")) {
4674
4675 bool fmt_odb = false;
4676 bool fmt_xml = false;
4677 bool fmt_json = true;
4678 bool fmt_jsonp = false;
4679 int follow_links = 1;
4680 int save_keys = 1;
4681 int recurse = 1;
4682 const char* fmt = NULL;
4683 const char* jsonp_callback = "callback";
4684
4685 if (p->isparam("encoding")) {
4686 fmt = p->getparam("encoding");
4687 } else if (p->isparam("format")) {
4688 fmt = p->getparam("format");
4689 }
4690
4691 if (fmt) {
4692 fmt_odb = (equal_ustring(fmt, "odb") > 0);
4693 fmt_xml = (equal_ustring(fmt, "xml") > 0);
4694 fmt_json = (strstr(fmt, "json") != NULL);
4695
4696 if (fmt_odb)
4697 fmt_xml = fmt_json = false;
4698 if (fmt_xml)
4699 fmt_odb = fmt_json = false;
4700 if (fmt_json)
4701 fmt_odb = fmt_xml = false;
4702
4703 if (fmt_json)
4704 fmt_jsonp = (strstr(fmt, "-p") != NULL);
4705 if (fmt_jsonp && p->isparam("callback"))
4706 jsonp_callback = p->getparam("callback");
4707 if (fmt_json && strstr(fmt, "-nofollowlinks"))
4708 follow_links = 0;
4709 if (fmt_json && strstr(fmt, "-nokeys"))
4710 save_keys = 2;
4711 if (fmt_json && strstr(fmt, "-nolastwritten"))
4712 save_keys = 0;
4713 if (fmt_json && strstr(fmt, "-norecurse"))
4714 recurse = 0;
4715 }
4716
4717 if (p->isparam("odb")) {
4718 mstrlcpy(str, p->getparam("odb"), sizeof(str));
4719
4721
4722 if (fmt_json)
4723 status = db_find_link(hDB, 0, str, &hkey);
4724 else
4725 status = db_find_key(hDB, 0, str, &hkey);
4726 if (status == DB_SUCCESS) {
4727
4728 if (fmt_jsonp) {
4729 r->rsputs(jsonp_callback);
4730 r->rsputs("(");
4731 }
4732
4733 int end = 0;
4734 int bufsize = WEB_BUFFER_SIZE;
4735 char* buf = (char *)malloc(bufsize);
4736
4737 if (fmt_xml)
4738 db_copy_xml(hDB, hkey, buf, &bufsize, true);
4739 else if (fmt_json)
4740 db_copy_json_obsolete(hDB, hkey, &buf, &bufsize, &end, save_keys, follow_links, recurse);
4741 else
4742 db_copy(hDB, hkey, buf, &bufsize, (char *)"");
4743
4744 r->rsputs(buf);
4745 free(buf);
4746
4747 if (fmt_jsonp) {
4748 r->rsputs(");\n");
4749 }
4750 } else
4751 r->rsputs("<DB_NO_KEY>");
4752 }
4753
4754 if (p->isparam("odb0")) {
4756 if (fmt_jsonp) {
4757 r->rsputs(jsonp_callback);
4758 r->rsputs("(");
4759 }
4760 if (fmt_xml) {
4761 r->rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", HTTP_ENCODING);
4762 r->rsputs("<jcopy>\n");
4763 r->rsputs("<data>\n");
4764 } else if (fmt_json)
4765 r->rsputs("[\n");
4766 else
4767 r->rsputs("");
4768 for (int i=0 ; ; i++) {
4769 char ppath[256];
4770 sprintf(ppath, "odb%d", i);
4771 if (!p->isparam(ppath))
4772 break;
4773 mstrlcpy(str, p->getparam(ppath), sizeof(str));
4774
4775 if (i > 0) {
4776 if (fmt_xml)
4777 r->rsputs("</data>\n<data>\n");
4778 else if (fmt_json)
4779 r->rsputs(",\n");
4780 else
4781 r->rsputs("$#----#$\n");
4782 }
4783
4784 if (fmt_json)
4785 status = db_find_link(hDB, 0, str, &hkey);
4786 else
4787 status = db_find_key(hDB, 0, str, &hkey);
4788 if (status != DB_SUCCESS) {
4789 if (fmt_xml)
4790 r->rsputs("<DB_NO_KEY/>\n");
4791 else if (fmt_json) {
4792 char tmp[256];
4793 sprintf(tmp, "{ \"/error\" : %d }\n", status);
4794 r->rsputs(tmp);
4795 } else
4796 r->rsputs("<DB_NO_KEY>\n");
4797 continue;
4798 }
4799
4800 int end = 0;
4801 int bufsize = WEB_BUFFER_SIZE;
4802 char* buf = (char *)malloc(bufsize);
4803
4804 if (fmt_xml) {
4805 db_copy_xml(hDB, hkey, buf, &bufsize, true);
4806 const char* s = strstr(buf, "-->");
4807 if (s)
4808 s+=4;
4809 else
4810 s = buf;
4811 r->rsputs(s);
4812 } else if (fmt_json) {
4813 db_copy_json_obsolete(hDB, hkey, &buf, &bufsize, &end, save_keys, follow_links, recurse);
4814 r->rsputs(buf);
4815 } else {
4816 db_copy(hDB, hkey, buf, &bufsize, (char *)"");
4817 r->rsputs(buf);
4818 }
4819
4820 free(buf);
4821 }
4822
4823 if (fmt_xml)
4824 r->rsputs("</data>\n</jcopy>\n");
4825 else if (fmt_json)
4826 r->rsputs("]\n");
4827 else
4828 r->rsputs("");
4829
4830 if (fmt_jsonp) {
4831 r->rsputs(");\n");
4832 }
4833 }
4834 return;
4835 }
4836
4837 /* process "jkey" command */
4838 if (equal_ustring(p->getparam("cmd"), "jkey")) {
4839
4840 // test:
4841 // curl "http://localhost:8080?cmd=jkey&odb0=/runinfo/run+number&odb1=/nonexistant&odb2=/&encoding=json&callback=aaa"
4842
4844
4845 if (jsonp) {
4846 r->rsputs(jsonp_callback.c_str());
4847 r->rsputs("(");
4848 }
4849
4850 if (multiple) {
4851 switch (encoding) {
4852 default:
4853 break;
4854 case ENCODING_JSON:
4855 r->rsprintf("[ ");
4856 break;
4857 }
4858 }
4859
4860 for (unsigned i=0; i<odb.size(); i++) {
4861 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
4862 if (status == DB_SUCCESS)
4863 status = db_get_key(hDB, hkey, &key);
4864 switch (encoding) {
4865 default:
4866 if (multiple && i>0)
4867 r->rsputs("$#----#$\n");
4868 if (status == DB_SUCCESS) {
4869 r->rsprintf("%s\n", key.name);
4870 r->rsprintf("TID_%s\n", rpc_tid_name(key.type));
4871 r->rsprintf("%d\n", key.num_values);
4872 r->rsprintf("%d\n", key.item_size);
4873 r->rsprintf("%d\n", key.last_written);
4874 } else {
4875 r->rsputs("<DB_NO_KEY>\n");
4876 }
4877 break;
4878 case ENCODING_JSON:
4879 if (multiple && i>0)
4880 r->rsprintf(", ");
4881 if (status == DB_SUCCESS) {
4882 r->rsprintf("{ ");
4883 r->rsprintf("\"name\":\"%s\",", key.name);
4884 r->rsprintf("\"type\":%d,", key.type);
4885 r->rsprintf("\"type_name\":\"TID_%s\",", rpc_tid_name(key.type));
4886 r->rsprintf("\"num_values\":%d,", key.num_values);
4887 r->rsprintf("\"item_size\":%d,", key.item_size);
4888 r->rsprintf("\"last_written\":%d", key.last_written);
4889 r->rsprintf(" }");
4890 } else {
4891 r->rsprintf("{ \"/error\":%d }", status);
4892 }
4893 break;
4894 }
4895 }
4896
4897 if (multiple) {
4898 switch (encoding) {
4899 default:
4900 break;
4901 case ENCODING_JSON:
4902 r->rsprintf(" ]");
4903 break;
4904 }
4905 }
4906
4907 if (jsonp) {
4908 r->rsputs(");\n");
4909 }
4910
4911 return;
4912 }
4913
4914 /* process "jcreate" command */
4915 if (equal_ustring(p->getparam("cmd"), "jcreate")) {
4916
4917 // test:
4918 // curl "http://localhost:8080?cmd=jcreate&odb0=/test/foo&type0=7&odb1=/nonexistant&type1=100&odb2=/test/bar&type2=12&encoding=json&callback=aaa"
4919 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo&type=7"
4920 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo70&type=7&arraylen=10"
4921 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo12s&type=12&strlen=32"
4922 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo12s5&type=12&strlen=32&arraylen=5"
4923 // curl "http://localhost:8080?cmd=jcreate&odb0=/test/foo12s5x&type0=12&strlen0=32&arraylen0=5"
4924
4925
4927
4928 if (jsonp) {
4929 r->rsputs(jsonp_callback.c_str());
4930 r->rsputs("(");
4931 }
4932
4933 if (multiple) {
4934 switch (encoding) {
4935 default:
4936 case ENCODING_JSON:
4937 r->rsprintf("[ ");
4938 break;
4939 }
4940 }
4941
4942 for (unsigned i=0; i<odb.size(); i++) {
4943 HNDLE hkey = 0;
4944 int type = 0;
4945 int arraylength = 0;
4946 int strlength = 0;
4947
4948 if (single) {
4949 type = atoi(p->getparam("type"));
4950 arraylength = atoi(p->getparam("arraylen"));
4951 strlength = atoi(p->getparam("strlen"));
4952 }
4953 else if (multiple) {
4954 char buf[256];
4955 sprintf(buf, "type%d", i);
4956 type = atoi(p->getparam(buf));
4957 sprintf(buf, "arraylen%d", i);
4958 arraylength = atoi(p->getparam(buf));
4959 sprintf(buf, "strlen%d", i);
4960 strlength = atoi(p->getparam(buf));
4961 }
4962
4963 status = db_create_key(hDB, 0, odb[i].c_str(), type);
4964
4965 if (status == DB_SUCCESS) {
4966 status = db_find_link(hDB, 0, odb[i].c_str(), &hkey);
4967 }
4968
4969 if (status == DB_SUCCESS && hkey && type == TID_STRING && strlength > 0) {
4970 char* s = (char*)calloc(strlength, 1); // initialized to zero
4971 status = db_set_data(hDB, hkey, s, strlength, 1, TID_STRING);
4972 free(s);
4973 }
4974
4975 if (status == DB_SUCCESS && hkey && arraylength > 1) {
4976 status = db_set_num_values(hDB, hkey, arraylength);
4977 }
4978
4979 switch (encoding) {
4980 default:
4981 case ENCODING_JSON:
4982 if (multiple && i>0)
4983 r->rsprintf(", ");
4984 r->rsprintf("%d", status);
4985 break;
4986 }
4987 }
4988
4989 if (multiple) {
4990 switch (encoding) {
4991 default:
4992 case ENCODING_JSON:
4993 r->rsprintf(" ]");
4994 break;
4995 }
4996 }
4997
4998 if (jsonp) {
4999 r->rsputs(");\n");
5000 }
5001
5002 return;
5003 }
5004
5005 /* process "jresize" command */
5006 if (equal_ustring(p->getparam("cmd"), "jresize")) {
5007
5008 // test:
5009
5010 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo70&arraylen=5"
5011 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&arraylen=5"
5012 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&strlen=16"
5013 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&strlen=30&arraylen=10"
5014
5016
5017 if (jsonp) {
5018 r->rsputs(jsonp_callback.c_str());
5019 r->rsputs("(");
5020 }
5021
5022 if (multiple) {
5023 switch (encoding) {
5024 default:
5025 case ENCODING_JSON:
5026 r->rsprintf("[ ");
5027 break;
5028 }
5029 }
5030
5031 for (unsigned i=0; i<odb.size(); i++) {
5032 HNDLE hkey;
5033 KEY key;
5034 int arraylength = 0;
5035 int strlength = 0;
5036
5037 if (single) {
5038 arraylength = atoi(p->getparam("arraylen"));
5039 strlength = atoi(p->getparam("strlen"));
5040 }
5041 else if (multiple) {
5042 char buf[256];
5043 sprintf(buf, "arraylen%d", i);
5044 arraylength = atoi(p->getparam(buf));
5045 sprintf(buf, "strlen%d", i);
5046 strlength = atoi(p->getparam(buf));
5047 }
5048
5049 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
5050
5051 if (status == DB_SUCCESS && hkey) {
5052 status = db_get_key(hDB, hkey, &key);
5053 }
5054
5055 if (status == DB_SUCCESS && hkey && key.type == TID_STRING && strlength > 0) {
5056 int oldsize = key.item_size * key.num_values;
5057 char* olddata = (char*)malloc(oldsize);
5058 int size = oldsize;
5059 status = db_get_data(hDB, hkey, olddata, &size, TID_STRING);
5060
5061 if (status == DB_SUCCESS) {
5062 int newsize = strlength * key.num_values;
5063 char* s = (char*)calloc(newsize, 1); // initialized to zero
5064 for (int k=0; k<key.num_values; k++) {
5065 mstrlcpy(s + strlength*k, olddata + key.item_size*k, strlength);
5066 }
5067
5068 status = db_set_data(hDB, hkey, s, newsize, key.num_values, TID_STRING);
5069 free(s);
5070 }
5071
5072 free(olddata);
5073 }
5074
5075 if (status == DB_SUCCESS && hkey && arraylength > 0) {
5076 status = db_set_num_values(hDB, hkey, arraylength);
5077 }
5078
5079 switch (encoding) {
5080 default:
5081 case ENCODING_JSON:
5082 if (multiple && i>0)
5083 r->rsprintf(", ");
5084 r->rsprintf("%d", status);
5085 break;
5086 }
5087 }
5088
5089 if (multiple) {
5090 switch (encoding) {
5091 default:
5092 case ENCODING_JSON:
5093 r->rsprintf(" ]");
5094 break;
5095 }
5096 }
5097
5098 if (jsonp) {
5099 r->rsputs(");\n");
5100 }
5101
5102 return;
5103 }
5104
5105 /* process "jrename" command */
5106 if (equal_ustring(p->getparam("cmd"), "jrename")) {
5107
5108 // test:
5109 // curl "http://localhost:8080?cmd=jrename&odb0=/test/foo&type0=7&odb1=/nonexistant&type1=100&odb2=/test/bar&type2=12&encoding=json&callback=aaa"
5110 // curl "http://localhost:8080?cmd=jrename&odb=/test/foo&name=foofoo"
5111
5113
5114 if (jsonp) {
5115 r->rsputs(jsonp_callback.c_str());
5116 r->rsputs("(");
5117 }
5118
5119 if (multiple) {
5120 switch (encoding) {
5121 default:
5122 case ENCODING_JSON:
5123 r->rsprintf("[ ");
5124 break;
5125 }
5126 }
5127
5128 for (unsigned i=0; i<odb.size(); i++) {
5129 const char* name = NULL;
5130 if (single)
5131 name = p->getparam("name");
5132 else if (multiple) {
5133 char buf[256];
5134 sprintf(buf, "name%d", i);
5135 name = p->getparam(buf);
5136 }
5137 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
5138 if (status == DB_SUCCESS) {
5139 status = db_rename_key(hDB, hkey, name);
5140 }
5141 switch (encoding) {
5142 default:
5143 case ENCODING_JSON:
5144 if (multiple && i>0)
5145 r->rsprintf(", ");
5146 r->rsprintf("%d", status);
5147 break;
5148 }
5149 }
5150
5151 if (multiple) {
5152 switch (encoding) {
5153 default:
5154 case ENCODING_JSON:
5155 r->rsprintf(" ]");
5156 break;
5157 }
5158 }
5159
5160 if (jsonp) {
5161 r->rsputs(");\n");
5162 }
5163
5164 return;
5165 }
5166
5167 /* process "jlink" command */
5168 if (equal_ustring(p->getparam("cmd"), "jlink")) {
5169
5170 // test:
5171 // curl "http://localhost:8080?cmd=jlink&odb=/test/link&dest=/test/foo"
5172 // curl "http://localhost:8080?cmd=jlink&odb0=/test/link0&dest0=/test/foo&odb1=/test/link1&dest1=/test/foo"
5173
5175
5176 if (jsonp) {
5177 r->rsputs(jsonp_callback.c_str());
5178 r->rsputs("(");
5179 }
5180
5181 if (multiple) {
5182 switch (encoding) {
5183 default:
5184 case ENCODING_JSON:
5185 r->rsprintf("[ ");
5186 break;
5187 }
5188 }
5189
5190 for (unsigned i=0; i<odb.size(); i++) {
5191 const char* dest = NULL;
5192 if (single)
5193 dest = p->getparam("dest");
5194 else if (multiple) {
5195 char buf[256];
5196 sprintf(buf, "dest%d", i);
5197 dest = p->getparam(buf);
5198 }
5199
5200 status = db_create_link(hDB, 0, odb[i].c_str(), dest);
5201
5202 switch (encoding) {
5203 default:
5204 case ENCODING_JSON:
5205 if (multiple && i>0)
5206 r->rsprintf(", ");
5207 r->rsprintf("%d", status);
5208 break;
5209 }
5210 }
5211
5212 if (multiple) {
5213 switch (encoding) {
5214 default:
5215 case ENCODING_JSON:
5216 r->rsprintf(" ]");
5217 break;
5218 }
5219 }
5220
5221 if (jsonp) {
5222 r->rsputs(");\n");
5223 }
5224
5225 return;
5226 }
5227
5228 /* process "jreorder" command */
5229 if (equal_ustring(p->getparam("cmd"), "jreorder")) {
5230
5231 // test:
5232 // curl "http://localhost:8080?cmd=jreorder&odb0=/test/foo&index0=0&odb1=/test/bar&index1=1"
5233 // curl "http://localhost:8080?cmd=jreorder&odb=/test/bar&index=0"
5234
5236
5237 if (jsonp) {
5238 r->rsputs(jsonp_callback.c_str());
5239 r->rsputs("(");
5240 }
5241
5242 if (multiple) {
5243 switch (encoding) {
5244 default:
5245 case ENCODING_JSON:
5246 r->rsprintf("[ ");
5247 break;
5248 }
5249 }
5250
5251 for (unsigned i=0; i<odb.size(); i++) {
5252 int index = 0;
5253 if (single)
5254 index = atoi(p->getparam("index"));
5255 else if (multiple) {
5256 char buf[256];
5257 sprintf(buf, "index%d", i);
5258 index = atoi(p->getparam(buf));
5259 }
5260
5261 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
5262 if (status == DB_SUCCESS) {
5263 status = db_reorder_key(hDB, hkey, index);
5264 }
5265
5266 switch (encoding) {
5267 default:
5268 case ENCODING_JSON:
5269 if (multiple && i>0)
5270 r->rsprintf(", ");
5271 r->rsprintf("%d", status);
5272 break;
5273 }
5274 }
5275
5276 if (multiple) {
5277 switch (encoding) {
5278 default:
5279 case ENCODING_JSON:
5280 r->rsprintf(" ]");
5281 break;
5282 }
5283 }
5284
5285 if (jsonp) {
5286 r->rsputs(");\n");
5287 }
5288
5289 return;
5290 }
5291
5292 /* process "jdelete" command */
5293 if (equal_ustring(p->getparam("cmd"), "jdelete")) {
5294
5295 // test:
5296 // curl "http://localhost:8080?cmd=jdelete&odb0=/test/foo&odb1=/nonexistant&odb2=/test/bar&encoding=json&callback=aaa"
5297 // curl "http://localhost:8080?cmd=jdelete&odb=/test/foo"
5298
5300
5301 if (jsonp) {
5302 r->rsputs(jsonp_callback.c_str());
5303 r->rsputs("(");
5304 }
5305
5306 if (multiple) {
5307 switch (encoding) {
5308 default:
5309 case ENCODING_JSON:
5310 r->rsprintf("[ ");
5311 break;
5312 }
5313 }
5314
5315 for (unsigned i=0; i<odb.size(); i++) {
5316 status = db_delete(hDB, 0, odb[i].c_str());
5317 switch (encoding) {
5318 default:
5319 case ENCODING_JSON:
5320 if (multiple && i>0)
5321 r->rsprintf(", ");
5322 r->rsprintf("%d", status);
5323 break;
5324 }
5325 }
5326
5327 if (multiple) {
5328 switch (encoding) {
5329 default:
5330 case ENCODING_JSON:
5331 r->rsprintf(" ]");
5332 break;
5333 }
5334 }
5335
5336 if (jsonp) {
5337 r->rsputs(");\n");
5338 }
5339
5340 return;
5341 }
5342
5343 /* process "jmsg" command */
5344 if (equal_ustring(p->getparam("cmd"), "jmsg")) {
5345
5346 if (p->getparam("f") && *p->getparam("f"))
5347 mstrlcpy(facility, p->getparam("f"), sizeof(facility));
5348 else
5349 mstrlcpy(facility, "midas", sizeof(facility));
5350
5351 n = 1;
5352 if (p->getparam("n") && *p->getparam("n"))
5353 n = atoi(p->getparam("n"));
5354
5355 t = 0;
5356 if (p->getparam("t") && p->getparam("t"))
5357 t = atoi(p->getparam("t"));
5358
5360 char* messages = NULL;
5361 int num_messages = 0;
5362 cm_msg_retrieve2(facility, t, n, &messages, &num_messages);
5363 if (messages) {
5364 r->rsputs(messages);
5365 free(messages);
5366 }
5367 return;
5368 }
5369
5370 /* process "jgenmsg" command */
5371 if (equal_ustring(p->getparam("cmd"), "jgenmsg")) {
5372
5373 if (p->getparam("facility") && *p->getparam("facility"))
5374 mstrlcpy(facility, p->getparam("facility"), sizeof(facility));
5375 else
5376 mstrlcpy(facility, "midas", sizeof(facility));
5377
5378 if (p->getparam("user") && *p->getparam("user"))
5379 mstrlcpy(user, p->getparam("user"), sizeof(user));
5380 else
5381 mstrlcpy(user, "javascript_commands", sizeof(user));
5382
5383 if (p->getparam("type") && *p->getparam("type"))
5384 type = atoi(p->getparam("type"));
5385 else
5386 type = MT_INFO;
5387
5388 if (p->getparam("msg") && *p->getparam("msg")) {
5389 cm_msg1(type, __FILE__, __LINE__, facility, user, "%s", p->getparam("msg"));
5390 }
5391
5393 r->rsputs("Message successfully created\n");
5394 return;
5395 }
5396
5397 /* process "jalm" command */
5398 if (equal_ustring(p->getparam("cmd"), "jalm")) {
5399
5401 std::string alarms;
5402 al_get_alarms(&alarms);
5403 r->rsputs(alarms.c_str());
5404 return;
5405 }
5406
5407 /* process "jrpc" command */
5408 if (equal_ustring(p->getparam("cmd"), "jrpc_rev0")) {
5409 do_jrpc_rev0(p, r);
5410 return;
5411 }
5412
5413 /* process "jrpc" command */
5414 if (equal_ustring(p->getparam("cmd"), "jrpc_rev1")) {
5415 do_jrpc_rev1(p, r);
5416 return;
5417 }
5418
5419 /* process "jrpc" command */
5420 if (equal_ustring(p->getparam("cmd"), "jrpc")) {
5421 do_jrpc(p, r);
5422 return;
5423 }
5424}
5425
5426/*------------------------------------------------------------------*/
5427
5428void show_custom_page(Param* pp, Return* r, const char *cookie_cpwd)
5429{
5430 int size, n_var, index, edit;
5431 char keypath[256], type[32], *p, *ps;
5432 char pwd[256], tail[256];
5433 HNDLE hDB, hkey;
5434 KEY key;
5435 char data[TEXT_SIZE];
5436
5437 std::string path = pp->getparam("page");
5438
5439 if (path[0] == 0) {
5440 show_error_404(r, "show_custom_page: Invalid custom page: \"page\" parameter is empty");
5441 return;
5442 }
5443
5444 if (strstr(path.c_str(), "..")) {
5445 std::string str;
5446 str += "Invalid custom page name \'";
5447 str += path;
5448 str += "\' contains \'..\'";
5449 show_error_404(r, str.c_str());
5450 return;
5451 }
5452
5453 if (strstr(path.c_str(), ".gif")) {
5454 show_custom_gif(r, path.c_str());
5455 return;
5456 }
5457
5458 if (strchr(path.c_str(), '.')) {
5459 show_custom_file(r, path.c_str());
5460 return;
5461 }
5462
5464
5465 std::string xpath = std::string("/Custom/") + path;
5466 db_find_key(hDB, 0, xpath.c_str(), &hkey);
5467 if (!hkey) {
5468 xpath = std::string("/Custom/") + path + "&";
5469 db_find_key(hDB, 0, xpath.c_str(), &hkey);
5470 if (!hkey) {
5471 xpath = std::string("/Custom/") + path + "!";
5472 db_find_key(hDB, 0, xpath.c_str(), &hkey);
5473 }
5474 }
5475
5476 if (hkey) {
5477 char* ctext;
5478 int status;
5479
5480 status = db_get_key(hDB, hkey, &key);
5481 assert(status == DB_SUCCESS);
5482 size = key.total_size;
5483 ctext = (char*)malloc(size);
5484 status = db_get_data(hDB, hkey, ctext, &size, TID_STRING);
5485 if (status != DB_SUCCESS) {
5486 std::string errtext = msprintf("show_custom_page: Error: db_get_data() for \"%s\" status %d", xpath.c_str(), status);
5487 show_error_404(r, errtext.c_str());
5488 free(ctext);
5489 return;
5490 }
5491
5492 std::string content_type = "text/html";
5493
5494 /* check for link */
5495 if (std::string(ctext).substr(0, 5) == "?cmd=") {
5496 redirect(r, ctext);
5497 free(ctext);
5498 return;
5499 }
5500
5501 /* check if filename */
5502 if (strchr(ctext, '\n') == 0) {
5503 std::string full_filename = add_custom_path(ctext);
5504 int fh = open(full_filename.c_str(), O_RDONLY | O_BINARY);
5505 if (fh < 0) {
5506 std::string str = msprintf("show_custom_page: Cannot open file \"%s\", open() errno %d (%s)", full_filename.c_str(), errno, strerror(errno));
5507 show_error_404(r, str.c_str());
5508 free(ctext);
5509 return;
5510 }
5511 free(ctext);
5512 ctext = NULL;
5513 off_t off = lseek(fh, 0, SEEK_END);
5514 if (off < 0) {
5515 std::string str = msprintf("show_custom_page: Cannot open file \"%s\", lseek(SEEK_END) errno %d (%s)", full_filename.c_str(), errno, strerror(errno));
5516 show_error_404(r, str.c_str());
5517 free(ctext);
5518 close(fh);
5519 return;
5520 }
5521 size_t size = off;
5522 lseek(fh, 0, SEEK_SET);
5523 ctext = (char*)malloc(size+1);
5524 ssize_t rd = read(fh, ctext, size);
5525 if (rd > 0) {
5526 ctext[rd] = 0; // make sure string is zero-terminated
5527 size = rd;
5528 } else {
5529 ctext[0] = 0;
5530 size = 0;
5531 }
5532 close(fh);
5533
5534 content_type = get_content_type(full_filename.c_str());
5535 }
5536
5537 /* check for valid password */
5538 if (equal_ustring(pp->getparam("cmd"), "Edit")) {
5539 p = ps = ctext;
5540 n_var = 0;
5541 do {
5542 char format[256];
5543
5544 p = find_odb_tag(ps, keypath, format, &edit, type, pwd, tail);
5545 if (p == NULL)
5546 break;
5547 ps = strchr(p, '>') + 1;
5548
5549 if (pwd[0] && n_var == atoi(pp->getparam("index"))) {
5550 char str[256];
5551 size = NAME_LENGTH;
5552 mstrlcpy(str, path.c_str(), sizeof(str)); // FIXME: overflows "str"
5553 if (strlen(str)>0 && str[strlen(str)-1] == '&')
5554 str[strlen(str)-1] = 0;
5555 std::string ppath;
5556 ppath += "/Custom/Pwd/";
5557 if (pp->getparam("pnam") && *pp->getparam("pnam")) {
5558 ppath += pp->getparam("pnam");
5559 } else {
5560 ppath += str;
5561 }
5562 str[0] = 0;
5563 db_get_value(hDB, 0, ppath.c_str(), str, &size, TID_STRING, TRUE);
5564 if (!equal_ustring(cookie_cpwd, str)) {
5565 show_error_404(r, "show_custom_page: Invalid password!");
5566 free(ctext);
5567 return;
5568 } else
5569 break;
5570 }
5571
5572 n_var++;
5573 } while (p != NULL);
5574 }
5575
5576 /* process toggle command */
5577 if (equal_ustring(pp->getparam("cmd"), "Toggle")) {
5578
5579 if (pp->getparam("pnam") && *pp->getparam("pnam")) {
5580 std::string ppath;
5581 ppath += "/Custom/Pwd/";
5582 ppath += pp->getparam("pnam");
5583 std::string str;
5584 db_get_value_string(hDB, 0, ppath.c_str(), 0, &str, TRUE, 256);
5585 if (!equal_ustring(cookie_cpwd, str.c_str())) {
5586 show_error_404(r, "show_custom_page: Invalid password!");
5587 free(ctext);
5588 return;
5589 }
5590 }
5591 std::string podb = pp->getparam("odb");
5592 std::string::size_type pos = podb.find('[');
5593 if (pos != std::string::npos) {
5594 index = atoi(podb.substr(pos+1).c_str());
5595 podb.resize(pos);
5596 //printf("found index %d in [%s] [%s]\n", index, pp->getparam("odb"), podb.c_str());
5597 } else
5598 index = 0;
5599
5600 if (db_find_key(hDB, 0, podb.c_str(), &hkey)) {
5601 db_get_key(hDB, hkey, &key);
5602 memset(data, 0, sizeof(data));
5603 if (key.item_size <= (int)sizeof(data)) {
5604 size = sizeof(data);
5605 db_get_data_index(hDB, hkey, data, &size, index, key.type);
5606 std::string data_str = db_sprintf(data, size, 0, key.type);
5607 if (atoi(data_str.c_str()) == 0)
5608 db_sscanf("1", data, &size, 0, key.type);
5609 else
5610 db_sscanf("0", data, &size, 0, key.type);
5612 }
5613 }
5614
5615 /* redirect (so that 'reload' does not toggle again) */
5616 redirect(r, path.c_str());
5617 free(ctext);
5618 return;
5619 }
5620
5621 /* HTTP header */
5622 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
5623 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
5624 r->rsprintf("Content-Type: %s; charset=%s\r\n\r\n", content_type.c_str(), HTTP_ENCODING);
5625
5626 /* interprete text, replace <odb> tags with ODB values */
5627 p = ps = ctext;
5628 n_var = 0;
5629 do {
5630 char format[256];
5631 p = find_odb_tag(ps, keypath, format, &edit, type, pwd, tail);
5632 if (p != NULL)
5633 *p = 0;
5634 r->rsputs(ps);
5635
5636 if (p == NULL)
5637 break;
5638 ps = strchr(p + 1, '>') + 1;
5639
5640 show_odb_tag(pp, r, path.c_str(), keypath, format, n_var, edit, type, pwd, tail);
5641 n_var++;
5642
5643 } while (p != NULL);
5644
5645 if (equal_ustring(pp->getparam("cmd"), "Set") || pp->isparam("cbi")) {
5646 /* redirect (so that 'reload' does not change value) */
5647 r->reset();
5648 redirect(r, path.c_str());
5649 }
5650
5651 free(ctext);
5652 ctext = NULL;
5653 } else {
5654 std::string str = msprintf("Invalid custom page: Page \"%s\" not found in ODB", path.c_str());
5655 show_error_404(r, str.c_str());
5656 return;
5657 }
5658}
5659
5660/*------------------------------------------------------------------*/
5661
5662static void show_cnaf_page(Param* p, Return* rr)
5663{
5664 char str[256];
5665 int c, n, a, f, d, q, x, r, ia, id, w;
5666 int i, size, status;
5667 HNDLE hDB, hrootkey, hsubkey, hkey;
5668
5669 static char client_name[NAME_LENGTH];
5670 static HNDLE hconn = 0;
5671
5673
5674 /* find FCNA server if not specified */
5675 if (hconn == 0) {
5676 /* find client which exports FCNA function */
5677 status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
5678 if (status == DB_SUCCESS) {
5679 for (i = 0;; i++) {
5680 status = db_enum_key(hDB, hrootkey, i, &hsubkey);
5682 break;
5683
5684 sprintf(str, "RPC/%d", RPC_CNAF16);
5685 status = db_find_key(hDB, hsubkey, str, &hkey);
5686 if (status == DB_SUCCESS) {
5687 size = sizeof(client_name);
5688 db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, TRUE);
5689 break;
5690 }
5691 }
5692 }
5693
5694 if (client_name[0]) {
5695 status = cm_connect_client(client_name, &hconn);
5696 if (status != RPC_SUCCESS)
5697 hconn = 0;
5698 }
5699 }
5700
5701 /* header */
5702 rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
5703 rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
5704 rr->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
5705
5706 rr->rsprintf("<html><head>\n");
5707 rr->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
5708 rr->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
5709 rr->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
5710 rr->rsprintf("<title>MIDAS CAMAC interface</title></head>\n");
5711 rr->rsprintf("<body><form method=\"GET\" action=\"CNAF\">\n\n");
5712
5713 /* title row */
5714
5715 size = sizeof(str);
5716 str[0] = 0;
5717 db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
5718
5719 rr->rsprintf("<table border=3 cellpadding=1>\n");
5720 rr->rsprintf("<tr><th colspan=3>MIDAS experiment \"%s\"", str);
5721
5722 if (client_name[0] == 0)
5723 rr->rsprintf("<th colspan=3 class=\"redLight\">No CAMAC server running</tr>\n");
5724 else if (hconn == 0)
5725 rr->rsprintf("<th colspan=3 class=\"redLight\">Cannot connect to %s</tr>\n", client_name);
5726 else
5727 rr->rsprintf("<th colspan=3>CAMAC server: %s</tr>\n", client_name);
5728
5729 /* default values */
5730 c = n = 1;
5731 a = f = d = q = x = 0;
5732 r = 1;
5733 ia = id = w = 0;
5734
5735 /*---- menu buttons ----*/
5736
5737 rr->rsprintf("<tr><td colspan=3>\n");
5738 rr->rsprintf("<input type=submit name=cmd value=Execute>\n");
5739
5740 rr->rsprintf("<td colspan=3>\n");
5741 rr->rsprintf("<input type=submit name=cmd value=ODB>\n");
5742 rr->rsprintf("<input type=submit name=cmd value=Status>\n");
5743 rr->rsprintf("<input type=submit name=cmd value=Help>\n");
5744 rr->rsprintf("</tr>\n\n");
5745
5746 /* header */
5747 rr->rsprintf("<tr><th>N");
5748 rr->rsprintf("<th>A");
5749 rr->rsprintf("<th>F");
5750 rr->rsprintf("<th colspan=3>Data");
5751
5752 /* execute commands */
5753 size = sizeof(d);
5754
5755 const char* cmd = p->getparam("cmd");
5756 if (equal_ustring(cmd, "C cycle")) {
5757 rpc_client_call(hconn, RPC_CNAF16, CNAF_CRATE_CLEAR, 0, 0, 0, 0, 0, &d, &size, &x,
5758 &q);
5759
5760 rr->rsprintf("<tr><td colspan=6 class=\"greenLight\">C cycle executed sucessfully</tr>\n");
5761 } else if (equal_ustring(cmd, "Z cycle")) {
5762 rpc_client_call(hconn, RPC_CNAF16, CNAF_CRATE_ZINIT, 0, 0, 0, 0, 0, &d, &size, &x,
5763 &q);
5764
5765 rr->rsprintf("<tr><td colspan=6 class=\"greenLight\">Z cycle executed sucessfully</tr>\n");
5766 } else if (equal_ustring(cmd, "Clear inhibit")) {
5767 rpc_client_call(hconn, RPC_CNAF16, CNAF_INHIBIT_CLEAR, 0, 0, 0, 0, 0, &d, &size, &x,
5768 &q);
5769
5770 rr->rsprintf
5771 ("<tr><td colspan=6 class=\"greenLight\">Clear inhibit executed sucessfully</tr>\n");
5772 } else if (equal_ustring(cmd, "Set inhibit")) {
5773 rpc_client_call(hconn, RPC_CNAF16, CNAF_INHIBIT_SET, 0, 0, 0, 0, 0, &d, &size, &x,
5774 &q);
5775
5776 rr->rsprintf
5777 ("<tr><td colspan=6 class=\"greenLight\">Set inhibit executed sucessfully</tr>\n");
5778 } else if (equal_ustring(cmd, "Execute")) {
5779 c = atoi(p->getparam("C"));
5780 n = atoi(p->getparam("N"));
5781 a = atoi(p->getparam("A"));
5782 f = atoi(p->getparam("F"));
5783 r = atoi(p->getparam("R"));
5784 w = atoi(p->getparam("W"));
5785 id = atoi(p->getparam("ID"));
5786 ia = atoi(p->getparam("IA"));
5787
5788 const char* pd = p->getparam("D");
5789 if (strncmp(pd, "0x", 2) == 0)
5790 sscanf(pd + 2, "%x", &d);
5791 else
5792 d = atoi(p->getparam("D"));
5793
5794 /* limit repeat range */
5795 if (r == 0)
5796 r = 1;
5797 if (r > 100)
5798 r = 100;
5799 if (w > 1000)
5800 w = 1000;
5801
5802 for (i = 0; i < r; i++) {
5803 status = SUCCESS;
5804
5805 if (hconn) {
5806 size = sizeof(d);
5807 status =
5808 rpc_client_call(hconn, RPC_CNAF24, CNAF, 0, c, n, a, f, &d, &size, &x,
5809 &q);
5810
5811 if (status == RPC_NET_ERROR) {
5812 /* try to reconnect */
5813 //cm_disconnect_client(hconn, FALSE);
5814 status = cm_connect_client(client_name, &hconn);
5815 if (status != RPC_SUCCESS) {
5816 hconn = 0;
5817 client_name[0] = 0;
5818 }
5819
5820 if (hconn) {
5821 status = rpc_client_call(hconn, RPC_CNAF24, CNAF, 0, c, n, a, f, &d, &size, &x, &q);
5822 }
5823 }
5824 }
5825
5826 if (status != SUCCESS) {
5827 rr->rsprintf
5828 ("<tr><td colspan=6 class=\"redLight\">Error executing function, code = %d</tr>",
5829 status);
5830 } else {
5831 rr->rsprintf("<tr align=center><td>%d", n);
5832 rr->rsprintf("<td>%d", a);
5833 rr->rsprintf("<td>%d", f);
5834 rr->rsprintf("<td colspan=3>%d / 0x%04X Q%d X%d", d, d, q, x);
5835 }
5836
5837 d += id;
5838 a += ia;
5839
5840 if (w > 0)
5841 ss_sleep(w);
5842 }
5843 }
5844
5845 /* input fields */
5846 rr->rsprintf
5847 ("<tr align=center><td><input type=text size=3 name=N value=%d>\n",
5848 n);
5849 rr->rsprintf("<td><input type=text size=3 name=A value=%d>\n", a);
5850 rr->rsprintf("<td><input type=text size=3 name=F value=%d>\n", f);
5851 rr->rsprintf
5852 ("<td colspan=3><input type=text size=8 name=D value=%d></tr>\n",
5853 d);
5854
5855 /* control fields */
5856 rr->rsprintf("<tr><td colspan=2>Repeat");
5857 rr->rsprintf("<td><input type=text size=3 name=R value=%d>\n", r);
5858
5859 rr->rsprintf
5860 ("<td align=center colspan=3><input type=submit name=cmd value=\"C cycle\">\n");
5861 rr->rsprintf("<input type=submit name=cmd value=\"Z cycle\">\n");
5862
5863 rr->rsprintf("<tr><td colspan=2>Repeat delay [ms]");
5864 rr->rsprintf("<td><input type=text size=3 name=W value=%d>\n", w);
5865
5866 rr->rsprintf
5867 ("<td align=center colspan=3><input type=submit name=cmd value=\"Set inhibit\">\n");
5868 rr->rsprintf("<input type=submit name=cmd value=\"Clear inhibit\">\n");
5869
5870 rr->rsprintf("<tr><td colspan=2>Data increment");
5871 rr->rsprintf("<td><input type=text size=3 name=ID value=%d>\n", id);
5872
5873 rr->rsprintf
5874 ("<td colspan=3 align=center>Branch <input type=text size=3 name=B value=0>\n");
5875
5876 rr->rsprintf("<tr><td colspan=2>A increment");
5877 rr->rsprintf("<td><input type=text size=3 name=IA value=%d>\n", ia);
5878
5879 rr->rsprintf
5880 ("<td colspan=3 align=center>Crate <input type=text size=3 name=C value=%d>\n",
5881 c);
5882
5883 rr->rsprintf("</table></body>\r\n");
5884}
5885
5886/*------------------------------------------------------------------*/
5887
5888#ifdef HAVE_MSCB
5889
5890typedef struct {
5891 signed char id;
5892 char name[32];
5893} NAME_TABLE;
5894
5895static const NAME_TABLE prefix_table[] = {
5896 {PRFX_PICO, "pico",},
5897 {PRFX_NANO, "nano",},
5898 {PRFX_MICRO, "micro",},
5899 {PRFX_MILLI, "milli",},
5900 {PRFX_NONE, "",},
5901 {PRFX_KILO, "kilo",},
5902 {PRFX_MEGA, "mega",},
5903 {PRFX_GIGA, "giga",},
5904 {PRFX_TERA, "tera",},
5905 {99}
5906};
5907
5908static const NAME_TABLE unit_table[] = {
5909
5910 {UNIT_METER, "meter",},
5911 {UNIT_GRAM, "gram",},
5912 {UNIT_SECOND, "second",},
5913 {UNIT_MINUTE, "minute",},
5914 {UNIT_HOUR, "hour",},
5915 {UNIT_AMPERE, "ampere",},
5916 {UNIT_KELVIN, "kelvin",},
5917 {UNIT_CELSIUS, "deg. celsius",},
5918 {UNIT_FARENHEIT, "deg. farenheit",},
5919
5920 {UNIT_HERTZ, "hertz",},
5921 {UNIT_PASCAL, "pascal",},
5922 {UNIT_BAR, "bar",},
5923 {UNIT_WATT, "watt",},
5924 {UNIT_VOLT, "volt",},
5925 {UNIT_OHM, "ohm",},
5926 {UNIT_TESLA, "tesls",},
5927 {UNIT_LITERPERSEC, "liter/sec",},
5928 {UNIT_RPM, "RPM",},
5929 {UNIT_FARAD, "farad",},
5930
5931 {UNIT_BOOLEAN, "boolean",},
5932 {UNIT_BYTE, "byte",},
5933 {UNIT_WORD, "word",},
5934 {UNIT_DWORD, "dword",},
5935 {UNIT_ASCII, "ascii",},
5936 {UNIT_STRING, "string",},
5937 {UNIT_BAUD, "baud",},
5938
5939 {UNIT_PERCENT, "percent",},
5940 {UNIT_PPM, "RPM",},
5941 {UNIT_COUNT, "counts",},
5942 {UNIT_FACTOR, "factor",},
5943 {0}
5944};
5945
5946/*------------------------------------------------------------------*/
5947
5948void print_mscb_var(char *value, char *evalue, char *unit, MSCB_INFO_VAR *info_chn, void *pdata)
5949{
5950 char str[80];
5951 signed short sdata;
5952 unsigned short usdata;
5953 signed int idata;
5954 unsigned int uidata;
5955 float fdata;
5956 int i;
5957
5958 value[0] = 0;
5959 evalue[0] = 0;
5960
5961 if (info_chn->unit == UNIT_STRING) {
5962 memset(str, 0, sizeof(str));
5963 strncpy(str, (char *)pdata, info_chn->width);
5964 for (i = 0; i < (int) strlen(str); i++)
5965 switch (str[i]) {
5966 case 1:
5967 strcat(value, "\\001");
5968 break;
5969 case 2:
5970 strcat(value, "\\002");
5971 break;
5972 case 9:
5973 strcat(value, "\\t");
5974 break;
5975 case 10:
5976 strcat(value, "\\n");
5977 break;
5978 case 13:
5979 strcat(value, "\\r");
5980 break;
5981 default:
5982 value[strlen(value) + 1] = 0;
5983 value[strlen(value)] = str[i];
5984 break;
5985 }
5986 mstrlcpy(evalue, value, 256);
5987 } else {
5988 switch (info_chn->width) {
5989 case 0:
5990 strcpy(value, "0");
5991 strcpy(evalue, "0");
5992 break;
5993
5994 case 1:
5995 if (info_chn->flags & MSCBF_SIGNED) {
5996 sprintf(value, "%d (0x%02X/", *((signed char *)pdata), *((signed char *)pdata));
5997 sprintf(evalue, "%d", *((signed char *)pdata));
5998 } else {
5999 sprintf(value, "%u (0x%02X/", *((unsigned char *)pdata), *((unsigned char *)pdata));
6000 sprintf(evalue, "%u", *((unsigned char *)pdata));
6001 }
6002
6003 for (i = 0; i < 8; i++)
6004 if (*((unsigned char *)pdata) & (0x80 >> i))
6005 sprintf(value + strlen(value), "1");
6006 else
6007 sprintf(value + strlen(value), "0");
6008 sprintf(value + strlen(value), ")");
6009 break;
6010
6011 case 2:
6012 if (info_chn->flags & MSCBF_SIGNED) {
6013 sdata = *((signed short *)pdata);
6014 WORD_SWAP(&sdata);
6015 sprintf(value, "%d (0x%04X)", sdata, sdata);
6016 sprintf(evalue, "%d", sdata);
6017 } else {
6018 usdata = *((unsigned short *)pdata);
6019 WORD_SWAP(&usdata);
6020 sprintf(value, "%u (0x%04X)", usdata, usdata);
6021 sprintf(evalue, "%u", usdata);
6022 }
6023 break;
6024
6025 case 4:
6026 if (info_chn->flags & MSCBF_FLOAT) {
6027 fdata = *((float *)pdata);
6028 DWORD_SWAP(&fdata);
6029 sprintf(value, "%1.6lg", fdata);
6030 sprintf(evalue, "%1.6lg", fdata);
6031 } else {
6032 if (info_chn->flags & MSCBF_SIGNED) {
6033 idata = *((signed int *)pdata);
6034 DWORD_SWAP(&idata);
6035 sprintf(value, "%d (0x%08X)", idata, idata);
6036 sprintf(evalue, "%d", idata);
6037 } else {
6038 uidata = *((unsigned int *)pdata);
6039 DWORD_SWAP(&uidata);
6040 sprintf(value, "%u (0x%08X)", uidata, uidata);
6041 sprintf(evalue, "%u", uidata);
6042 }
6043 }
6044 break;
6045 }
6046 }
6047
6048 /* evaluate prefix */
6049 unit[0] = 0;
6050 if (info_chn->prefix) {
6051 for (i = 0; prefix_table[i].id != 99; i++)
6052 if ((unsigned char)prefix_table[i].id == info_chn->prefix)
6053 break;
6054 if (prefix_table[i].id)
6055 strcpy(unit, prefix_table[i].name);
6056 }
6057
6058 /* evaluate unit */
6059 if (info_chn->unit && info_chn->unit != UNIT_STRING) {
6060 for (i = 0; unit_table[i].id; i++)
6061 if ((unsigned char)unit_table[i].id == info_chn->unit)
6062 break;
6063 if (unit_table[i].id)
6064 strcat(unit, unit_table[i].name);
6065 }
6066}
6067
6068static int cmp_int(const void *a, const void *b)
6069{
6070 return *((int *)a) > *((int *)b);
6071}
6072
6073/*------------------------------------------------------------------*/
6074
6075void create_mscb_tree()
6076{
6077 HNDLE hDB, hKeySubm, hKeyEq, hKeyAdr, hKey, hKeyDev;
6078 KEY key;
6079 int i, j, k, l, size, address[1000], dev_badr[1000], dev_adr[1000], dev_chn[1000],
6080 n_address, n_dev_adr;
6081 char mscb_dev[256], mscb_pwd[32], eq_name[32];
6082
6084
6085 db_create_key(hDB, 0, "MSCB/Submaster", TID_KEY);
6086 db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
6087 assert(hKeySubm);
6088
6089 /*---- go through equipment list ----*/
6090 db_find_key(hDB, 0, "Equipment", &hKeyEq);
6091 if (hKeyEq) {
6092 for (i=0 ; ; i++) {
6093 db_enum_key(hDB, hKeyEq, i, &hKey);
6094 if (!hKey)
6095 break;
6096 db_get_key(hDB, hKey, &key);
6097 strcpy(eq_name, key.name);
6098 db_find_key(hDB, hKey, "Settings/Devices", &hKeyDev);
6099 if (hKeyDev) {
6100 for (j=0 ;; j++) {
6101 db_enum_key(hDB, hKeyDev, j, &hKey);
6102 if (!hKey)
6103 break;
6104
6105 if (db_find_key(hDB, hKey, "MSCB Address", &hKeyAdr) == DB_SUCCESS) {
6106 /* mscbdev type of device */
6107 size = sizeof(mscb_dev);
6108 if (db_get_value(hDB, hKey, "Device", mscb_dev, &size, TID_STRING, FALSE) != DB_SUCCESS)
6109 continue;
6110 size = sizeof(mscb_pwd);
6111 if (db_get_value(hDB, hKey, "Pwd", mscb_pwd, &size, TID_STRING, FALSE) != DB_SUCCESS)
6112 continue;
6113
6114 size = sizeof(dev_adr);
6115 db_get_data(hDB, hKeyAdr, dev_adr, &size, TID_INT);
6116 n_dev_adr = size / sizeof(int);
6117 } else if (db_find_key(hDB, hKey, "Block Address", &hKeyAdr) == DB_SUCCESS) {
6118 /* mscbhvr type of device */
6119 size = sizeof(mscb_dev);
6120 if (db_get_value(hDB, hKey, "MSCB Device", mscb_dev, &size, TID_STRING, FALSE) != DB_SUCCESS)
6121 continue;
6122 size = sizeof(mscb_pwd);
6123 if (db_get_value(hDB, hKey, "MSCB Pwd", mscb_pwd, &size, TID_STRING, FALSE) != DB_SUCCESS)
6124 continue;
6125
6126 n_dev_adr = 0;
6127 size = sizeof(dev_badr);
6128 db_get_data(hDB, hKeyAdr, dev_badr, &size, TID_INT);
6129 size = sizeof(dev_chn);
6130 if (db_get_value(hDB, hKey, "Block Channels", dev_chn, &size, TID_INT, FALSE) == DB_SUCCESS) {
6131 for (k=0 ; k<size/(int)sizeof(int) && n_dev_adr < (int)(sizeof(dev_adr)/sizeof(int)) ; k++) {
6132 for (l=0 ; l<dev_chn[k] ; l++)
6133 dev_adr[n_dev_adr++] = dev_badr[k]+l;
6134 }
6135 }
6136 } else
6137 continue;
6138
6139 /* create or open submaster entry */
6140 db_find_key(hDB, hKeySubm, mscb_dev, &hKey);
6141 if (!hKey) {
6142 db_create_key(hDB, hKeySubm, mscb_dev, TID_KEY);
6143 db_find_key(hDB, hKeySubm, mscb_dev, &hKey);
6144 assert(hKey);
6145 }
6146
6147 /* get old address list */
6148 size = sizeof(address);
6149 if (db_get_value(hDB, hKey, "Address", address, &size, TID_INT, FALSE) == DB_SUCCESS)
6150 n_address = size / sizeof(int);
6151 else
6152 n_address = 0;
6153
6154 /* merge with new address list */
6155 for (k=0 ; k<n_dev_adr ; k++) {
6156 for (l=0 ; l<n_address ; l++)
6157 if (address[l] == dev_adr[k])
6158 break;
6159
6160 if (l == n_address)
6161 address[n_address++] = dev_adr[k];
6162 }
6163
6164 /* sort address list */
6165 qsort(address, n_address, sizeof(int), cmp_int);
6166
6167 /* store new address list */
6168 db_set_value(hDB, hKey, "Pwd", mscb_pwd, 32, 1, TID_STRING);
6169 db_set_value(hDB, hKey, "Comment", eq_name, 32, 1, TID_STRING);
6170 db_set_value(hDB, hKey, "Address", address, n_address*sizeof(int), n_address, TID_INT);
6171 }
6172 }
6173 }
6174 }
6175}
6176
6177/*------------------------------------------------------------------*/
6178
6179void show_mscb_page(Param* p, Return* r, int refresh)
6180{
6181 int i, j, n, ind, fi, fd, status, size, n_addr, cur_node, adr, show_hidden;
6182 unsigned int uptime;
6183 BOOL comment_created;
6184 float fvalue;
6185 char *pd;
6186 char dbuf[256], evalue[256], unit[256], cur_subm_name[256];
6187 HNDLE hDB, hKeySubm, hKeyCurSubm, hKey, hKeyAddr, hKeyComm;
6188 KEY key;
6189 MSCB_INFO info;
6190 MSCB_INFO_VAR info_var;
6191 int ping_addr[0x10000];
6192
6194
6195 status = db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
6196 if (!hKeySubm)
6197 create_mscb_tree();
6198
6199 mstrlcpy(cur_subm_name, p->getparam("subm"), sizeof(cur_subm_name));
6200 if (cur_subm_name[0] == 0) {
6201 db_enum_key(hDB, hKeySubm, 0, &hKeyCurSubm);
6202 if (!hKeyCurSubm) {
6203 char errorstr[256];
6204 sprintf(errorstr, "No submaster defined under /MSCB/Submaster");
6205 show_error(r, errorstr);
6206 return;
6207 }
6208 db_get_key(hDB, hKeyCurSubm, &key);
6209 strcpy(cur_subm_name, key.name);
6210 } else
6211 db_find_key(hDB, hKeySubm, cur_subm_name, &hKeyCurSubm);
6212
6213 if (p->isparam("node"))
6214 cur_node = atoi(p->getparam("node"));
6215 else
6216 cur_node = -1;
6217
6218 /* perform MSCB rescan */
6219 if (p->isparam("mcmd") && equal_ustring(p->getparam("mcmd"), "Rescan") && p->isparam("subm")) {
6220 /* create Pwd and Comment if not there */
6221 char tmp[32];
6222 size = 32;
6223 tmp[0] = 0;
6224 db_get_value(hDB, hKeyCurSubm, "Pwd", (void *)tmp, &size, TID_STRING, true);
6225 tmp[0] = 0;
6226 db_get_value(hDB, hKeyCurSubm, "Comment", (void *)tmp, &size, TID_STRING, true);
6227
6228 db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
6229 std::vector<int> addr;
6230 std::vector<char> node_comment;
6231 if (hKeyAddr) {
6232 /* get current address array */
6233 db_get_key(hDB, hKeyAddr, &key);
6234 n_addr = key.num_values;
6235 addr.resize(n_addr);
6236 size = sizeof(int)*n_addr;
6237 db_get_data(hDB, hKeyAddr, addr.data(), &size, TID_INT);
6238 } else {
6239 /* create new address array */
6240 db_create_key(hDB, hKeyCurSubm, "Address", TID_INT);
6241 db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
6242 n_addr = 0;
6243 addr.resize(1);
6244 }
6245
6246 comment_created = FALSE;
6247 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6248 if (hKeyComm) {
6249 /* get current node comments */
6250 db_get_key(hDB, hKeyComm, &key);
6251 node_comment.resize(32*key.num_values);
6252 size = 32*key.num_values;
6253 db_get_data(hDB, hKeyComm, node_comment.data(), &size, TID_STRING);
6254 } else {
6255 /* create new comment array */
6256 db_create_key(hDB, hKeyCurSubm, "Node comment", TID_STRING);
6257 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6258 node_comment.resize(32);
6259 comment_created = TRUE;
6260 }
6261
6262 fd = mscb_init(cur_subm_name, 0, "", FALSE);
6263 if (fd >= 0) {
6264 /* fill table of possible addresses */
6265 for (i=0 ; i<0x10000 ; i++)
6266 ping_addr[i] = 0;
6267 for (i=0 ; i<1000 ; i++) // 0..999
6268 ping_addr[i] = 1;
6269 for (i=0 ; i<0x10000 ; i+=100) // 100, 200, ...
6270 ping_addr[i] = 1;
6271 for (i=0 ; i<0x10000 ; i+= 0x100)
6272 ping_addr[i] = 1; // 256, 512, ...
6273 for (i=0xFF00 ; i<0x10000 ; i++)
6274 ping_addr[i] = 1; // 0xFF00-0xFFFF
6275
6276 for (ind = n = 0; ind < 0x10000; ind++) {
6277 if (!ping_addr[ind])
6278 continue;
6279
6280 status = mscb_ping(fd, (unsigned short) ind, 1, 0);
6281 if (status == MSCB_SUCCESS) {
6282
6283 /* node found, search next 100 as well */
6284 for (j=ind; j<ind+100 && j<0x10000 ; j++)
6285 if (j >= 0)
6286 ping_addr[j] = 1;
6287
6288 status = mscb_info(fd, (unsigned short) ind, &info);
6289
6290 if (status == MSCB_SUCCESS) {
6291 /* check if node already in list */
6292 for (j=0 ; j<n_addr ; j++)
6293 if (addr[j] == ind)
6294 break;
6295 if (j == n_addr) {
6296 addr.resize(n_addr+1);
6297 addr[n_addr] = ind;
6298 node_comment.resize(32*(n_addr+1));
6299 /* use node name as default comment */
6300 strncpy(node_comment.data()+n_addr*32, info.node_name, 32);
6301 n_addr ++;
6302 } else if (comment_created) {
6303 node_comment.resize(32*n_addr);
6304 /* use node name as default comment */
6305 strncpy(node_comment.data()+j*32, info.node_name, 32);
6306 }
6307 }
6308 }
6309 }
6310
6311 db_set_data(hDB, hKeyAddr, addr.data(), n_addr*sizeof(int), n_addr, TID_INT);
6312 db_set_data(hDB, hKeyComm, node_comment.data(), n_addr*32, n_addr, TID_STRING);
6313
6314 char redirstr[512];
6315 sprintf(redirstr, "?cmd=mscb&subm=%s", cur_subm_name);
6316 redirect(r, redirstr);
6317 return;
6318
6319 } else {
6320 char errorstr[512];
6321 sprintf(errorstr, "Cannot talk to submaster \"%s\"", cur_subm_name);
6322 show_error(r, errorstr);
6323 return;
6324 }
6325 }
6326
6327 /* write data to node */
6328 if (p->isparam("subm") && p->isparam("node") &&
6329 p->isparam("idx") && p->isparam("value")) {
6330 i = atoi(p->getparam("idx"));
6331 char value[256];
6332 mstrlcpy(value, p->getparam("value"), sizeof(value));
6333
6334 fd = mscb_init(cur_subm_name, 0, "", FALSE);
6335 if (fd >= 0) {
6336 status = mscb_info_variable(fd,
6337 (unsigned short) cur_node, (unsigned char) i, &info_var);
6338 if (status == MSCB_SUCCESS) {
6339 if (info_var.unit == UNIT_STRING) {
6340 char valstr[256];
6341 mstrlcpy(valstr, value, sizeof(valstr));
6342 if (strlen(valstr) > 0 && valstr[strlen(valstr) - 1] == '\n')
6343 valstr[strlen(valstr) - 1] = 0;
6344
6345 status = mscb_write(fd, (unsigned short) cur_node,
6346 (unsigned char) i, valstr, strlen(valstr) + 1);
6347 } else {
6348 if (info_var.flags & MSCBF_FLOAT) {
6349 fvalue = (float) atof(value);
6350 memcpy(&dbuf, &fvalue, sizeof(float));
6351 } else {
6352 if (value[1] == 'x')
6353 sscanf(value + 2, "%x", (int *)&dbuf);
6354 else
6355 *((int *)dbuf) = atoi(value);
6356 }
6357
6358 status = mscb_write(fd, (unsigned short) cur_node,
6359 (unsigned char) i, dbuf, info_var.width);
6360 }
6361 }
6362 }
6363 char redirstr[512];
6364 sprintf(redirstr, "?cmd=mscb&subm=%s&node=%d", cur_subm_name, cur_node);
6365 redirect(r, redirstr);
6366 return;
6367 }
6368
6369 if (p->isparam("hidden"))
6370 show_hidden = atoi(p->getparam("hidden"));
6371 else
6372 show_hidden = FALSE;
6373
6374 show_header(r, "MSCB", "GET", "./", refresh);
6375 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
6376 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
6377 show_navigation_bar(r, "MSCB");
6378
6379 /* style sheet */
6380 r->rsprintf("<style type=\"text/css\">\r\n");
6381 r->rsprintf("select { width:150px; background-color:#FFFFE0; font-size:12px; }\r\n");
6382 r->rsprintf(".subm {\r\n");
6383 r->rsprintf(" background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
6384 r->rsprintf(" padding:5px;\r\n");
6385 r->rsprintf(" vertical-align:top;\r\n");
6386 r->rsprintf(" font-size:16px;\r\n");
6387 r->rsprintf(" border-right:1px solid #808080;\r\n");
6388 r->rsprintf("}\r\n");
6389 r->rsprintf(".node {\r\n");
6390 r->rsprintf(" background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
6391 r->rsprintf(" padding:5px;\r\n");
6392 r->rsprintf(" vertical-align:top;\r\n");
6393 r->rsprintf(" font-size:16px;\r\n");
6394 r->rsprintf(" border-right:1px solid #808080;\r\n");
6395 r->rsprintf("}\r\n");
6396 r->rsprintf(".vars {\r\n");
6397 r->rsprintf(" background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
6398 r->rsprintf(" padding:5px;\r\n");
6399 r->rsprintf(" vertical-align:top;\r\n");
6400 r->rsprintf(" font-size:10px;\r\n");
6401 r->rsprintf("}\r\n");
6402 r->rsprintf(".v1 {\r\n");
6403 r->rsprintf(" padding:3px;\r\n");
6404 r->rsprintf(" font-weight:bold;\r\n");
6405 r->rsprintf(" font-size:12px;\r\n");
6406 r->rsprintf("}\r\n");
6407 r->rsprintf(".v2 {\r\n");
6408 r->rsprintf(" background-color:#F0F0F0;\r\n");
6409 r->rsprintf(" padding:3px;\r\n");
6410 r->rsprintf(" font-size:12px;\r\n");
6411 r->rsprintf(" border:1px solid #808080;\r\n");
6412 r->rsprintf(" border-right:1px solid #FFFFFF;\r\n");
6413 r->rsprintf(" border-bottom:1px solid #FFFFFF;\r\n");
6414 r->rsprintf("}\r\n");
6415 r->rsprintf(".v3 {\r\n");
6416 r->rsprintf(" padding:3px;\r\n");
6417 r->rsprintf(" font-size:12px;\r\n");
6418 r->rsprintf("}\r\n");
6419 r->rsprintf("</style>\r\n\r\n");
6420
6421 /* javascript */
6422 r->rsprintf("<script type=\"text/javascript\">\r\n");
6423 r->rsprintf("function mscb_edit(index, value)\r\n");
6424 r->rsprintf("{\r\n");
6425 r->rsprintf(" var new_value = prompt('Please enter new value', value);\r\n");
6426 r->rsprintf(" if (new_value != undefined) {\r\n");
6427 r->rsprintf(" window.location.search = '?cmd=mscb&subm=%s&node=%d&idx='+index+'&value='+new_value;\n", cur_subm_name, cur_node);
6428 r->rsprintf(" }\n");
6429 r->rsprintf("}\r\n");
6430 r->rsprintf("</script>\r\n\r\n");
6431
6432 /*---- main content ----*/
6433
6434 r->rsprintf("<table class=\"mtable\">"); //main table
6435 r->rsprintf("<tr><th class=\"mtableheader\" colspan=2>MSCB</th><tr>");
6436
6437 /*---- menu buttons ----*/
6438
6439 r->rsprintf("<tr><td colspan=2>\n");
6440 r->rsprintf("<table width=100%%><tr>\n");
6441 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());
6442
6443 r->rsprintf("<tr><td colspan=\"2\" cellpadding=\"0\" cellspacing=\"0\">\r\n");
6444
6445 status = db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
6446 if (status != DB_SUCCESS) {
6447 r->rsprintf("<h1>No MSCB Submasters defined in ODB</h1>\r\n");
6448 r->rsprintf("</td></tr>\r\n");
6449 r->rsprintf("</table>\r\n"); //submaster table
6450 r->rsprintf("</td></tr>\r\n");
6451 r->rsprintf("</table>\r\n"); //main table
6452 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6453 r->rsprintf("</form>\n");
6454 r->rsprintf("</body></html>\r\n");
6455 return;
6456 }
6457
6458 r->rsprintf("<table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\">");
6459
6460 /*---- submaster list ----*/
6461 r->rsprintf("<tr><td class=\"subm\">\r\n");
6462 r->rsprintf("Submaster<hr>\r\n");
6463
6464 /* count submasters */
6465 for (i = 0;;i++) {
6466 db_enum_key(hDB, hKeySubm, i, &hKey);
6467 if (!hKey)
6468 break;
6469 }
6470 if (i<2)
6471 i = 2;
6472
6473 r->rsprintf("<select name=\"subm\" id=\"subm\" size=%d ", i);
6474 r->rsprintf("onChange=\"window.location.search='?cmd=mscb&subm='+document.getElementById('subm').value;\">\r\n");
6475 hKeyCurSubm = 0;
6476 for (i = 0;;i++) {
6477 db_enum_key(hDB, hKeySubm, i, &hKey);
6478 if (!hKey)
6479 break;
6480 db_get_key(hDB, hKey, &key);
6481 char str[NAME_LENGTH+10+256];
6482 mstrlcpy(str, key.name, sizeof(str));
6483 char comment[256];
6484 size = sizeof(comment);
6485 if (db_get_value(hDB, hKey, "Comment", comment, &size, TID_STRING, FALSE) == DB_SUCCESS) {
6486 mstrlcat(str, ": ", sizeof(str));
6487 mstrlcat(str, comment, sizeof(str));
6488 }
6489
6490 if ((cur_subm_name[0] && equal_ustring(cur_subm_name, key.name)) ||
6491 (cur_subm_name[0] == 0 && i == 0)) {
6492 r->rsprintf("<option value=\"%s\" selected>%s</option>\r\n", key.name, str);
6493 hKeyCurSubm = hKey;
6494 } else
6495 r->rsprintf("<option value=\"%s\">%s</option>\r\n", key.name, str);
6496 }
6497 r->rsprintf("</select>\r\n");
6498
6499 /*---- node list ----*/
6500 r->rsprintf("<td class=\"node\">\r\n");
6501 r->rsprintf("Node ");
6502
6503 r->rsprintf("<script type=\"text/javascript\">\n");
6504 r->rsprintf("<!--\n");
6505 r->rsprintf("function rescan()\n");
6506 r->rsprintf("{\n");
6507 r->rsprintf(" flag = confirm('Rescan can take up to one minute.');\n");
6508 r->rsprintf(" if (flag == true)\n");
6509 r->rsprintf(" window.location.href = '?cmd=mscb&mcmd=Rescan&subm=%s';\n", cur_subm_name);
6510 r->rsprintf("}\n");
6511 r->rsprintf("//-->\n");
6512 r->rsprintf("</script>\n");
6513
6514 r->rsprintf("<input type=button name=cmd value=\"Rescan\" onClick=\"rescan();\">");
6515 r->rsprintf("<hr>\r\n");
6516
6517 if (!hKeyCurSubm) {
6518 r->rsprintf("No submaster found in ODB\r\n");
6519 r->rsprintf("</td></tr>\r\n");
6520 r->rsprintf("</table>\r\n"); //inner submaster table
6521 r->rsprintf("</td></tr>\r\n");
6522 r->rsprintf("</table>\r\n"); //submaster table
6523 r->rsprintf("</td></tr>\r\n");
6524 r->rsprintf("</table>\r\n"); //main table
6525 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6526 r->rsprintf("</form>\n");
6527 r->rsprintf("</body></html>\r\n");
6528 return;
6529 }
6530
6531 db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
6532 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6533
6534 i = 10;
6535 if (hKeyAddr) {
6536 db_get_key(hDB, hKeyAddr, &key);
6537 i = key.num_values;
6538
6539 if (hKeyComm == 0) {
6540 db_create_key(hDB, hKeyCurSubm, "Node comment", TID_STRING);
6541 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6542 }
6543 db_get_key(hDB, hKeyComm, &key);
6544 if (key.num_values < i) {
6545 char str[32] = "";
6546 for (int j=key.num_values ; j<i ; j++)
6547 db_set_data_index(hDB, hKeyComm, str, 32, j, TID_STRING);
6548 }
6549 }
6550 if (i < 2)
6551 i = 2;
6552
6553 r->rsprintf("<select name=\"node\" id=\"node\" size=%d ", i);
6554 r->rsprintf("onChange=\"window.location.search='?cmd=mscb&subm=%s&node='+document.getElementById('node').value;\">\r\n", cur_subm_name);
6555
6556 if (hKeyAddr) {
6557 db_get_key(hDB, hKeyAddr, &key);
6558 size = sizeof(adr);
6559
6560 /* check if current node is in list */
6561 for (i = 0; i<key.num_values ;i++) {
6562 size = sizeof(adr);
6563 db_get_data_index(hDB, hKeyAddr, &adr, &size, i, TID_INT);
6564 if (adr == cur_node)
6565 break;
6566 }
6567 if (i == key.num_values) // if not found, use first one in list
6568 db_get_data_index(hDB, hKeyAddr, &cur_node, &size, 0, TID_INT);
6569
6570 for (i = 0; i<key.num_values ;i++) {
6571 char str[100+256];
6572 size = sizeof(adr);
6573 db_get_data_index(hDB, hKeyAddr, &adr, &size, i, TID_INT);
6574 if (hKeyComm) {
6575 char comment[256];
6576 size = sizeof(comment);
6577 db_get_data_index(hDB, hKeyComm, comment, &size, i, TID_STRING);
6578 sprintf(str, "%d: %s", adr, comment);
6579 } else {
6580 sprintf(str, "%d", adr);
6581 }
6582 if (cur_node == 0 && i == 0)
6583 cur_node = adr;
6584 if (adr == cur_node)
6585 r->rsprintf("<option selected>%s</option>\r\n", str);
6586 else
6587 r->rsprintf("<option>%s</option>\r\n", str);
6588 }
6589 }
6590 r->rsprintf("</select>\r\n");
6591
6592 /*---- node contents ----*/
6593 r->rsprintf("<td class=\"vars\">\r\n");
6594 r->rsprintf("<table>\r\n");
6595 db_get_key(hDB, hKeyCurSubm, &key);
6596 if (cur_node != -1)
6597 r->rsprintf("<tr><td colspan=3 align=center><b>%s:%d</b>", key.name, cur_node);
6598 else
6599 r->rsprintf("<tr><td colspan=3 align=center><b>%s</b>", key.name);
6600 r->rsprintf("<hr></td></tr>\r\n");
6601
6602 char passwd[32];
6603 passwd[0] = 0;
6604 size = 32;
6605 db_get_value(hDB, hKeyCurSubm, "Pwd", passwd, &size, TID_STRING, TRUE);
6606
6607 fd = mscb_init(key.name, 0, passwd, FALSE);
6608 if (fd < 0) {
6609 if (fd == EMSCB_WRONG_PASSWORD)
6610 r->rsprintf("<tr><td colspan=3><b>Invalid password</b></td>");
6611 else
6612 r->rsprintf("<tr><td colspan=3><b>Submaster does not respond</b></td>");
6613 goto mscb_error;
6614 }
6615 mscb_set_eth_max_retry(fd, 3);
6616 mscb_set_max_retry(1);
6617
6618 status = mscb_ping(fd, cur_node, 0, 1);
6619 if (status != MSCB_SUCCESS) {
6620 r->rsprintf("<tr><td colspan=3><b>No response from node</b></td>");
6621 goto mscb_error;
6622 }
6623 status = mscb_info(fd, (unsigned short) cur_node, &info);
6624 if (status != MSCB_SUCCESS) {
6625 r->rsprintf("<tr><td colspan=3><b>No response from node</b></td>");
6626 goto mscb_error;
6627 }
6628 char tr16[17];
6629 mstrlcpy(tr16, info.node_name, sizeof(tr16));
6630 r->rsprintf("<tr><td class=\"v1\">Node name<td colspan=2 class=\"v2\">%s</tr>\n", tr16);
6631 r->rsprintf("<tr><td class=\"v1\">GIT revision<td colspan=2 class=\"v2\">%d</tr>\n", info.revision);
6632
6633 if (info.rtc[0] && info.rtc[0] != 0xFF) {
6634 for (i=0 ; i<6 ; i++)
6635 info.rtc[i] = (info.rtc[i] / 0x10) * 10 + info.rtc[i] % 0x10;
6636 r->rsprintf("<tr><td class=\"v1\">Real Time Clock<td colspan=2 class=\"v2\">%02d-%02d-%02d %02d:%02d:%02d</td>\n",
6637 info.rtc[0], info.rtc[1], info.rtc[2],
6638 info.rtc[3], info.rtc[4], info.rtc[5]);
6639 }
6640
6641 status = mscb_uptime(fd, (unsigned short) cur_node, &uptime);
6642 if (status == MSCB_SUCCESS)
6643 r->rsprintf("<tr><td class=\"v1\">Uptime<td colspan=2 class=\"v2\">%dd %02dh %02dm %02ds</tr>\n",
6644 uptime / (3600 * 24),
6645 (uptime % (3600 * 24)) / 3600, (uptime % 3600) / 60,
6646 (uptime % 60));
6647
6648 r->rsprintf("<tr><td colspan=3><hr></td></tr>\r\n");
6649
6650 /* check for hidden variables */
6651 for (i=0 ; i < info.n_variables ; i++) {
6652 mscb_info_variable(fd, cur_node, i, &info_var);
6653 if (info_var.flags & MSCBF_HIDDEN)
6654 break;
6655 }
6656 if (i < info.n_variables) {
6657 char str[32];
6658 strcpy(str, show_hidden ? " checked" : "");
6659 r->rsprintf("<tr><td colspan=3><input type=checkbox%s name=\"hidden\" value=\"1\"", str);
6660 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);
6661 }
6662
6663 /* read variables in blocks of 100 bytes */
6664 for (fi=0 ; fi < info.n_variables ; ) {
6665 for (i=fi,size=0 ; i < info.n_variables && size < 100; i++) {
6666 mscb_info_variable(fd, cur_node, i, &info_var);
6667 size += info_var.width;
6668 }
6669
6670 size = sizeof(dbuf);
6671 status = mscb_read_range(fd, cur_node, fi, i-1, dbuf, &size);
6672 if (status != MSCB_SUCCESS) {
6673 r->rsprintf("<tr><td colspan=3><b>Error reading data from node</b></td>");
6674 goto mscb_error;
6675 }
6676 pd = dbuf;
6677
6678 for (j=fi ; j<i ; j++) {
6679 status = mscb_info_variable(fd, cur_node, j, &info_var);
6680 if ((info_var.flags & MSCBF_HIDDEN) == 0 || show_hidden) {
6681 char tr8[9];
6682 mstrlcpy(tr8, info_var.name, sizeof(tr8));
6683 r->rsprintf("<tr><td class=\"v1\">%s</td>\r\n", tr8);
6684 r->rsprintf("<td class=\"v2\">\r\n");
6685 char value[256];
6686 print_mscb_var(value, evalue, unit, &info_var, pd);
6687 r->rsprintf("<a href=\"#\" onClick=\"mscb_edit(%d,'%s')\">%s</a>",
6688 j, evalue, value);
6689 r->rsprintf("</td><td class=\"v3\">%s</td>", unit);
6690 r->rsprintf("</tr>\r\n");
6691 }
6692 pd += info_var.width;
6693 }
6694
6695 fi = i;
6696 }
6697
6698mscb_error:
6699 r->rsprintf("</tr></table>\r\n");
6700 r->rsprintf("</td></tr></table>\r\n");
6701 r->rsprintf("</td></tr></table>\r\n");
6702 r->rsprintf("</td></tr></table>\r\n");
6703 r->rsprintf("</div></body></html>\r\n");
6704}
6705
6706#endif // HAVE_MSCB
6707
6708/*------------------------------------------------------------------*/
6709
6710void show_password_page(Return* r, const char* dec_path, const char *password)
6711{
6712 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
6713 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
6714 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
6715
6716 r->rsprintf("<html><head>\n");
6717 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
6718 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
6719 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
6720 r->rsprintf("<title>Enter password</title></head><body>\n\n");
6721
6722 r->rsprintf("<form method=\"GET\" action=\".\">\n\n");
6723
6724 /*---- page header ----*/
6725 r->rsprintf("<table class=\"headerTable\"><tr><td></td><tr></table>\n");
6726
6727 r->rsprintf("<table class=\"dialogTable\">\n"); //main table
6728 if (password[0])
6729 r->rsprintf("<tr><th class=\"redLight\">Wrong password!</tr>\n");
6730
6731 r->rsprintf("<tr><th>Please enter password</tr>\n");
6732 r->rsprintf("<tr><td align=center><input type=password name=pwd></tr>\n");
6733 r->rsprintf("<tr><td align=center><input type=submit value=Submit></tr>");
6734
6735 r->rsprintf("</table>\n");
6736
6737 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6738 r->rsprintf("</form>\n");
6739 r->rsprintf("</body></html>\r\n");
6740}
6741
6742/*------------------------------------------------------------------*/
6743
6744BOOL check_web_password(Return* r, HNDLE hDB, const char* dec_path, const char *password, const char *redir)
6745{
6746 HNDLE hkey;
6747 INT size;
6748 char str[256];
6749
6750 /* check for password */
6751 db_find_key(hDB, 0, "/Experiment/Security/Web Password", &hkey);
6752 if (hkey) {
6753 size = sizeof(str);
6754 db_get_data(hDB, hkey, str, &size, TID_STRING);
6755 if (strcmp(password, str) == 0)
6756 return TRUE;
6757
6758 /* show web password page */
6759 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
6760 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
6761 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
6762
6763 r->rsprintf("<html><head>\n");
6764 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
6765 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
6766 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
6767 r->rsprintf("<title>Enter password</title></head><body>\n\n");
6768
6769 r->rsprintf("<form method=\"GET\" action=\".\">\n\n");
6770
6771 /* define hidden fields for current experiment and destination */
6772 if (redir[0])
6773 r->rsprintf("<input type=hidden name=redir value=\"%s\">\n", redir);
6774
6775 /*---- page header ----*/
6776 r->rsprintf("<table class=\"headerTable\"><tr><td></td><tr></table>\n");
6777
6778 r->rsprintf("<table class=\"dialogTable\">\n"); //main table
6779
6780 if (password[0])
6781 r->rsprintf("<tr><th class=\"redLight\">Wrong password!</tr>\n");
6782
6783 r->rsprintf
6784 ("<tr><th>Please enter password to obtain write access</tr>\n");
6785 r->rsprintf("<tr><td align=center><input type=password name=wpwd></tr>\n");
6786 r->rsprintf("<tr><td align=center><input type=submit value=Submit></tr>");
6787
6788 r->rsprintf("</table>\n");
6789
6790 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6791 r->rsprintf("</form>\n");
6792 r->rsprintf("</body></html>\r\n");
6793
6794 return FALSE;
6795 } else
6796 return TRUE;
6797}
6798
6799/*------------------------------------------------------------------*/
6800
6801void show_odb_page(Param* pp, Return* r, const char* dec_path, int write_access)
6802{
6803 int keyPresent, size, status, line, link_index;
6804 char colspan;
6805 char style[32];
6806 HNDLE hDB, hkey, hkeyroot;
6807 KEY key;
6808 DWORD delta;
6809
6811
6812 //printf("path [%s]\n", dec_path);
6813
6814 if (strcmp(dec_path, "root") == 0) {
6815 dec_path = "";
6816 }
6817
6818 char xdecpath[256];
6819 mstrlcpy(xdecpath, dec_path, sizeof(xdecpath));
6820 if (strrchr(xdecpath, '/'))
6821 mstrlcpy(xdecpath, strrchr(xdecpath, '/')+1, sizeof(xdecpath));
6822 if (xdecpath[0] == 0)
6823 mstrlcpy(xdecpath, "root", sizeof(xdecpath));
6824 show_header(r, "MIDAS online database", "", xdecpath, 0);
6825
6826 /* use javascript file */
6827 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
6828 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
6829 r->rsprintf("<script type=\"text/javascript\" src=\"obsolete.js\"></script>\n");
6830 r->rsprintf("<script type=\"text/javascript\" src=\"controls.js\"></script>\n");
6831
6832 /* find key via path */
6833 status = db_find_key(hDB, 0, dec_path, &hkeyroot);
6834 if (status != DB_SUCCESS) {
6835 r->rsprintf("Error: cannot find key %s<P>\n", dec_path);
6836 r->rsprintf("</body></html>\r\n");
6837 return;
6838 }
6839
6840 char xdec_path[MAX_ODB_PATH];
6841
6842 /* if key is not of type TID_KEY, cut off key name */
6843 db_get_key(hDB, hkeyroot, &key);
6844 if (key.type != TID_KEY) {
6845 mstrlcpy(xdec_path, dec_path, sizeof(xdec_path));
6846
6847 /* strip variable name from path */
6848 char* p = xdec_path + strlen(xdec_path) - 1;
6849 while (*p && *p != '/')
6850 *p-- = 0;
6851 if (*p == '/')
6852 *p = 0;
6853
6854 status = db_find_key(hDB, 0, xdec_path, &hkeyroot);
6855 if (status != DB_SUCCESS) {
6856 r->rsprintf("Error: cannot find key %s<P>\n", xdec_path);
6857 r->rsprintf("</body></html>\r\n");
6858 return;
6859 }
6860
6861 dec_path = xdec_path;
6862 }
6863
6864 //mstrlcpy(enc_path, dec_path, enc_path_size);
6865 //urlEncode(enc_path, enc_path_size);
6866
6867 std::string odbpath = db_get_path(hDB, hkeyroot);
6868
6869 /*---- navigation bar ----*/
6870
6871 colspan = 7;
6872
6873 if (elog_mode) {
6874 r->rsprintf("<table class=\"mtableheader\">\n");
6875 r->rsprintf("<tr><td colspan=%d>\n", colspan);
6876 r->rsprintf("<input type=button value=ELog onclick=\"self.location=\'?cmd=Alarms\';\">\n");
6877 r->rsprintf("</td></tr></table>\n\n");
6878 } else
6879 show_navigation_bar(r, "ODB");
6880
6881 /*---- begin ODB directory table ----*/
6882
6883 r->rsprintf("<table class=\"mtable\" style=\"border-spacing:0px;\">\n");
6884 r->rsprintf("<tr><th colspan=%d class=\"mtableheader\">Online Database Browser</tr>\n", colspan);
6885 //buttons:
6886 if(!elog_mode){
6887 r->rsprintf("<tr><td colspan=%d>\n", colspan);
6888 r->rsprintf("<input type=button value=Find onclick=\"self.location=\'?cmd=Find\';\">\n");
6889 r->rsprintf("<input type=button value=Create onclick=\"dlgShow('dlgCreate')\">\n");
6890 r->rsprintf("<input type=button value=Link onclick=\"dlgShow('dlgLink')\">\n");
6891 r->rsprintf("<input type=button value=Delete onclick=\"dlgShow('dlgDelete')\">\n");
6892 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());
6893 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());
6894 r->rsprintf("<input type=button value=\"Show ODB clients\" onclick=\"self.location=\'?cmd=odb_scl\';\">\n");
6895 r->rsprintf("</td></tr>\n");
6896 }
6897
6898 /*---- Build the Delete dialog------------------------------------*/
6899
6900 std::string dd = "";
6901
6902 dd += "<!-- Demo dialog -->\n";
6903 dd += "<div id=\"dlgDelete\" class=\"dlgFrame\">\n";
6904 dd += "<div class=\"dlgTitlebar\">Delete ODB entry</div>\n";
6905 dd += "<div class=\"dlgPanel\">\n";
6906 dd += "<div id=odbpath>";
6907 dd += "\"";
6908 dd += MJsonNode::Encode(odbpath.c_str());
6909 dd += "\"";
6910 dd += "</div>\n";
6911 dd += "<div><br></div>\n";
6912
6913 dd += "<table class=\"dialogTable\">\n";
6914 dd += "<th colspan=2>Delete ODB entries:</th>\n";
6915
6916 std::vector<std::string> delete_list;
6917
6918 int count_delete = 0;
6919
6920 /*---- ODB display -----------------------------------------------*/
6921
6922 /* display root key */
6923 r->rsprintf("<tr><td colspan=%d class='ODBpath'><b>", colspan);
6924 r->rsprintf("<a href=\"?cmd=oldodb\">/</a> \n");
6925
6926 std::string enc_root_path;
6927
6928 /*---- display path ----*/
6929 {
6930 const char* p = dec_path;
6931 while (*p) {
6932 std::string pd;
6933 while (*p && *p != '/')
6934 pd += *p++;
6935
6936 enc_root_path += urlEncode(pd.c_str());
6937
6938 if (pd.length() > 0)
6939 r->rsprintf("<a href=\"?cmd=oldodb&odb_path=%s\">%s</a>\n / ", enc_root_path.c_str(), pd.c_str());
6940
6941 enc_root_path += "/";
6942 if (*p == '/')
6943 p++;
6944 }
6945 }
6946
6947 r->rsprintf("</b></tr>\n");
6948
6949 /* enumerate subkeys */
6950 keyPresent = 0;
6951 for(int scan=0; scan<2; scan++){
6952 if(scan==1 && keyPresent==1) {
6953 r->rsprintf("<tr class=\"titleRow\">\n");
6954 r->rsprintf("<th class=\"ODBkey\">Key</th>\n");
6955 r->rsprintf("<th class=\"ODBvalue\">Value&nbsp;");
6956 r->rsprintf("<script type=\"text/javascript\">\n");
6957 r->rsprintf("function expand()\n");
6958 r->rsprintf("{\n");
6959 r->rsprintf(" var n = document.getElementsByName('ext');\n");
6960 r->rsprintf(" for (i=0 ; i<n.length ; i++) {\n");
6961 r->rsprintf(" if (n[i].style.display == 'none')\n");
6962 r->rsprintf(" n[i].style.display = 'table-cell';\n");
6963 r->rsprintf(" else\n");
6964 r->rsprintf(" n[i].style.display = 'none';\n");
6965 r->rsprintf(" }\n");
6966 r->rsprintf(" if (document.getElementById('expp').expflag === true) {\n");
6967 r->rsprintf(" document.getElementById('expp').expflag = false;\n");
6968 r->rsprintf(" document.getElementById('expp').innerHTML = '&#x21E5;';\n");
6969 r->rsprintf(" } else {\n");
6970 r->rsprintf(" document.getElementById('expp').expflag = true;\n");
6971 r->rsprintf(" document.getElementById('expp').innerHTML = '&#x21E4;';\n");
6972 r->rsprintf(" }\n");
6973 r->rsprintf("}\n");
6974 r->rsprintf("</script>");
6975 r->rsprintf("<div style=\"display:inline;float:right\"><a id=\"expp\"href=\"#\" onClick=\"expand();return false;\">&#x21E5;</div>");
6976 r->rsprintf("</th>\n");
6977 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Type</th>\n");
6978 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">#Val</th>\n");
6979 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Size</th>\n");
6980 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Written</th>\n");
6981 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Mode</th>\n");
6982 r->rsprintf("</tr>\n");
6983 }
6984 line = 0;
6985 for (int i = 0;; i++) {
6986 db_enum_link(hDB, hkeyroot, i, &hkey);
6987 if (!hkey)
6988 break;
6989 db_get_link(hDB, hkey, &key);
6990
6991 if (scan == 0) {
6992 delete_list.push_back(key.name);
6993 }
6994
6995 if (line % 2 == 0)
6996 mstrlcpy(style, "ODBtableEven", sizeof(style));
6997 else
6998 mstrlcpy(style, "ODBtableOdd", sizeof(style));
6999
7000 std::string keyname = key.name;
7001 std::string enc_keyname = urlEncode(key.name);
7002
7003 std::string enc_full_path = enc_root_path + enc_keyname;
7004
7005 std::string odb_path = dec_path;
7006 if (odb_path.length() > 0 && odb_path[odb_path.length() - 1] != '/')
7007 odb_path += "/";
7008 odb_path += key.name;
7009
7010 /* resolve links */
7011 std::string enc_link_ref;
7012 char link_name[MAX_ODB_PATH];
7013 link_name[0] = 0;
7015 if (key.type == TID_LINK) {
7016 size = sizeof(link_name);
7017 db_get_link_data(hDB, hkey, link_name, &size, TID_LINK);
7018
7019 status = db_find_key(hDB, 0, link_name, &hkey);
7020
7021 if (status == DB_SUCCESS)
7022 db_get_key(hDB, hkey, &key);
7023
7024 //sprintf(link_ref, "?cmd=Set&odb_path=%s", full_path);
7025 enc_link_ref = "?cmd=Set&odb_path=";
7026 enc_link_ref += enc_full_path;
7027
7028 if (status == DB_SUCCESS && link_name[0] == 0) {
7029 // fake the case when an empty link somehow resolves
7030 sprintf(link_name, "%s", "(empty)");
7031 }
7032 }
7033
7034 std::string enc_ref;
7035
7036 if (link_name[0]) {
7037 if (enc_root_path.back() == '/' && link_name[0] == '/') {
7038 //sprintf(ref, "?cmd=Set&odb_path=%s%s", root_path, link_name+1);
7039 enc_ref = "";
7040 enc_ref += "?cmd=Set&odb_path=";
7041 enc_ref += enc_root_path;
7042 enc_ref += urlEncode(link_name + 1);
7043 } else {
7044 //sprintf(ref, "?cmd=Set&odb_path=%s%s", root_path, link_name);
7045 enc_ref = "";
7046 enc_ref += "?cmd=Set&odb_path=";
7047 enc_ref += enc_root_path;
7048 enc_ref += urlEncode(link_name);
7049 }
7050 } else {
7051 //sprintf(ref, "?cmd=Set&odb_path=%s", full_path);
7052 enc_ref = "";
7053 enc_ref += "?cmd=Set&odb_path=";
7054 enc_ref += enc_full_path;
7055 }
7056
7057 if (status != DB_SUCCESS) {
7058 if (scan == 1) {
7059 r->rsprintf("<tr><td class=\"yellowLight\">");
7060 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)");
7061 }
7062 } else {
7063
7064 if (key.type == TID_KEY && scan == 0) {
7065 /* for keys, don't display data value */
7066 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());
7067 if (link_name[0])
7068 r->rsprintf("<i>&rarr; <a href=\"%s\">%s</a></i>", enc_link_ref.c_str(), link_name);
7069 r->rsprintf("</tr>\n");
7070 } else if(key.type != TID_KEY && scan == 1) {
7071
7072 if (strchr(link_name, '['))
7073 link_index = atoi(strchr(link_name, '[')+1);
7074 else
7075 link_index = -1;
7076
7077 /* display single value */
7078 if (key.num_values == 1 || link_index != -1) {
7079 char data[TEXT_SIZE];
7080 size = sizeof(data);
7081 db_get_data(hDB, hkey, data, &size, key.type);
7082
7083 std::string data_str;
7084
7085 if (link_index != -1)
7086 data_str = db_sprintf(data, key.item_size, link_index, key.type);
7087 else
7088 data_str = db_sprintf(data, key.item_size, 0, key.type);
7089
7090 if (key.type == TID_STRING) {
7091 if (size == sizeof(data)) {
7092 data_str += "...(truncated)";
7093 }
7094 }
7095
7096 std::string hex_str;
7097
7098 if (key.type != TID_STRING) {
7099 if (link_index != -1)
7100 hex_str = db_sprintfh(data, key.item_size, link_index, key.type);
7101 else
7102 hex_str = db_sprintfh(data, key.item_size, 0, key.type);
7103 }
7104
7105 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
7106 data_str = "(empty)";
7107 hex_str = "";
7108 }
7109
7110 r->rsprintf("<tr>\n");
7111 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
7112 if (link_name[0]) {
7113 r->rsprintf("<td class=\"ODBkey\">\n");
7114 r->rsprintf("%s <i>&rarr; ", keyname.c_str());
7115 r->rsprintf("<a href=\"%s\">%s</a></i>\n", enc_link_ref.c_str(), link_name);
7116 r->rsprintf("<td class=\"%s\">\n", style);
7117 if (!write_access)
7118 r->rsprintf("%s (%s)", data_str.c_str(), hex_str.c_str());
7119 else {
7120 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7121 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">%s (%s)</a>\n", odb_path.c_str(), data_str.c_str(), hex_str.c_str());
7122 }
7123 } else {
7124 r->rsprintf("<td class=\"ODBkey\">\n");
7125 r->rsprintf("%s<td class=\"%s\">", keyname.c_str(), style);
7126 if (!write_access)
7127 r->rsprintf("%s (%s)", data_str.c_str(), hex_str.c_str());
7128 else {
7129 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7130 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">%s (%s)</a>\n", odb_path.c_str(), data_str.c_str(), hex_str.c_str());
7131 }
7132 }
7133 } else {
7134 if (strchr(data_str.c_str(), '\n')) {
7135 if (link_name[0]) {
7136 r->rsprintf("<td class=\"ODBkey\">");
7137 r->rsprintf("%s <i>&rarr; <a href=\"%s\">%s</a></i><td class=\"ODBvalue\">", keyname.c_str(), enc_link_ref.c_str(), link_name);
7138 } else
7139 r->rsprintf("<td class=\"ODBkey\">%s<td class=\"%s\">", keyname.c_str(), style);
7140 r->rsprintf("\n<pre>");
7141 strencode3(r, data_str.c_str());
7142 r->rsprintf("</pre>");
7143 if (strlen(data) > data_str.length())
7144 r->rsprintf("<i>... (%d bytes total)<p>\n", (int)strlen(data));
7145
7146 r->rsprintf("<a href=\"%s\">Edit</a>\n", enc_ref.c_str());
7147 } else {
7148 if (link_name[0]) {
7149 r->rsprintf("<td class=\"ODBkey\">\n");
7150 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);
7151 if (!write_access)
7152 strencode(r, data_str.c_str());
7153 else {
7154 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7155 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", odb_path.c_str());
7156 strencode(r, data_str.c_str());
7157 r->rsprintf("</a>\n");
7158 }
7159 } else {
7160 r->rsprintf("<td class=\"ODBkey\">%s<td class=\"%s\">", keyname.c_str(), style);
7161 if (!write_access) {
7162 strencode(r, data_str.c_str());
7163 } else {
7164 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7165 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", odb_path.c_str());
7166 strencode(r, data_str.c_str());
7167 r->rsprintf("</a>\n");
7168 }
7169 }
7170 }
7171 }
7172
7173 /* extended key information */
7174 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7175 r->rsprintf("%s", rpc_tid_name(key.type));
7176 r->rsprintf("</td>\n");
7177
7178 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7179 r->rsprintf("%d", key.num_values);
7180 r->rsprintf("</td>\n");
7181
7182 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7183 r->rsprintf("%d", key.item_size);
7184 r->rsprintf("</td>\n");
7185
7186 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7187 db_get_key_time(hDB, hkey, &delta);
7188 if (delta < 60)
7189 r->rsprintf("%ds", delta);
7190 else if (delta < 3600)
7191 r->rsprintf("%1.0lfm", delta / 60.0);
7192 else if (delta < 86400)
7193 r->rsprintf("%1.0lfh", delta / 3600.0);
7194 else if (delta < 86400 * 99)
7195 r->rsprintf("%1.0lfd", delta / 86400.0);
7196 else
7197 r->rsprintf(">99d");
7198 r->rsprintf("</td>\n");
7199
7200 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7202 r->rsprintf("R");
7204 r->rsprintf("W");
7206 r->rsprintf("D");
7208 r->rsprintf("E");
7209 r->rsprintf("</td>\n");
7210
7211 line++;
7212 r->rsprintf("</tr>\n");
7213 } else { /* display array value */
7214 /* check for exceeding length */
7215 if (key.num_values > 1000 && !pp->isparam("all"))
7216 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);
7217 else {
7218 /* display first value */
7219 if (link_name[0])
7220 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);
7221 else
7222 r->rsprintf("<tr><td class=\"ODBkey\" rowspan=%d>%s\n", key.num_values, keyname.c_str());
7223
7224 for (int j = 0; j < key.num_values; j++) {
7225 if (line % 2 == 0)
7226 mstrlcpy(style, "ODBtableEven", sizeof(style));
7227 else
7228 mstrlcpy(style, "ODBtableOdd", sizeof(style));
7229
7230 char data[TEXT_SIZE];
7231 size = sizeof(data);
7232 db_get_data_index(hDB, hkey, data, &size, j, key.type);
7233 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
7234
7235 std::string hex_str;
7236 if (key.type == TID_STRING || key.type == TID_LINK) {
7237 hex_str = "";
7238 } else {
7239 hex_str = db_sprintfh(data, key.item_size, 0, key.type);
7240 }
7241
7242 if (key.type == TID_STRING) {
7243 if (size == sizeof(data)) {
7244 data_str += "...(truncated)";
7245 }
7246 }
7247
7248 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
7249 data_str = "(empty)";
7250 hex_str = "";
7251 }
7252
7253 //sprintf(ref, "?cmd=Set&odb_path=%s&index=%d", full_path, j);
7254 enc_ref = "";
7255 enc_ref += "?cmd=Set&odb_path=";
7256 enc_ref += enc_full_path;
7257 enc_ref += "&index=";
7258 enc_ref += toString(j);
7259
7260 std::string tmpstr;
7261 //sprintf(str, "%s[%d]", odb_path, j);
7262 tmpstr += odb_path;
7263 tmpstr += "[";
7264 tmpstr += toString(j);
7265 tmpstr += "]";
7266
7267 if (j > 0)
7268 r->rsprintf("<tr>");
7269
7270 r->rsprintf("<td class=\"%s\">[%d]&nbsp;", style, j);
7271 if (!write_access)
7272 r->rsprintf("<a href=\"%s\">", enc_ref.c_str());
7273 else {
7274 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), tmpstr.c_str());
7275 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", tmpstr.c_str());
7276 }
7277 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
7278 r->rsprintf("%s (%s)</a>\n", data_str.c_str(), hex_str.c_str());
7279 else
7280 r->rsprintf("%s</a>\n", data_str.c_str());
7281
7282 if (j == 0) {
7283 /* extended key information */
7284 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7285 r->rsprintf("%s", rpc_tid_name(key.type));
7286 r->rsprintf("</td>\n");
7287
7288 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7289 r->rsprintf("%d", key.num_values);
7290 r->rsprintf("</td>\n");
7291
7292 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7293 r->rsprintf("%d", key.item_size);
7294 r->rsprintf("</td>\n");
7295
7296 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7297 db_get_key_time(hDB, hkey, &delta);
7298 if (delta < 60)
7299 r->rsprintf("%ds", delta);
7300 else if (delta < 3600)
7301 r->rsprintf("%1.0lfm", delta / 60.0);
7302 else if (delta < 86400)
7303 r->rsprintf("%1.0lfh", delta / 3600.0);
7304 else if (delta < 86400 * 99)
7305 r->rsprintf("%1.0lfh", delta / 86400.0);
7306 else
7307 r->rsprintf(">99d");
7308 r->rsprintf("</td>\n");
7309
7310 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7312 r->rsprintf("R");
7314 r->rsprintf("W");
7316 r->rsprintf("D");
7318 r->rsprintf("E");
7319 r->rsprintf("</td>\n");
7320 }
7321 line++;
7322 }
7323
7324 r->rsprintf("</tr>\n");
7325 }
7326 }
7327 } else if(key.type != TID_KEY){
7328 keyPresent = 1; //flag that we've seen a key on the first pass, and should therefore write the Key / Value headline
7329 }
7330 }
7331 }
7332 }
7333 r->rsprintf("</table>\n");
7334 r->rsprintf("</div>\n"); // <div id="mmain">
7335
7336 /*---- Build the Delete dialog------------------------------------*/
7337
7338 std::sort(delete_list.begin(), delete_list.end());
7339
7340 for (unsigned i=0; i<delete_list.size(); i++) {
7341 std::string name = delete_list[i];
7342
7343 dd += "<tr><td style=\"text-align:left;\" align=left><input align=left type=checkbox id=delete";
7344 dd += toString(count_delete++);
7345 dd += " value=\'";
7346 dd += "\"";
7347 dd += MJsonNode::Encode(name.c_str());
7348 dd += "\"";
7349 dd += "\'>";
7350 dd += name;
7351 dd += "</input></td></tr>\n";
7352 }
7353
7354 dd += "</table>\n";
7355 dd += "<input type=button value=Delete onClick='mhttpd_delete_page_handle_delete(event);'>\n";
7356 dd += "<input type=button value=Cancel onClick='mhttpd_delete_page_handle_cancel(event);'>\n";
7357 dd += "</div>\n";
7358 dd += "</div>\n";
7359
7360 r->rsputs(dd.c_str());
7361
7362 /*---- Build the Create dialog------------------------------------*/
7363
7364 std::string cd = "";
7365
7366 cd += "<!-- Demo dialog -->\n";
7367 cd += "<div id=\"dlgCreate\" class=\"dlgFrame\">\n";
7368 cd += "<div class=\"dlgTitlebar\">Create ODB entry</div>\n";
7369 cd += "<div class=\"dlgPanel\">\n";
7370 cd += "<br />\n";
7371 cd += "<div id=odbpath>";
7372 cd += "\"";
7373 cd += MJsonNode::Encode(odbpath.c_str());
7374 cd += "\"";
7375 cd += "</div>\n";
7376 cd += "<div><br></div>\n";
7377
7378 cd += "<table class=\"dialogTable\">\n";
7379 cd += "<th colspan=2>Create ODB entry:</th>\n";
7380 cd += "<tr>";
7381 cd += "<td>Type";
7382 cd += "<td>";
7383 cd += "<select type=text size=1 id=create_tid name=type>";
7384 cd += "<option value=7>Integer (32-bit)";
7385 cd += "<option value=9>Float (4 Bytes)";
7386 cd += "<option value=12>String";
7387 cd += "<option selected value=15>Subdirectory";
7388 cd += "<option value=1>Byte";
7389 cd += "<option value=2>Signed byte";
7390 cd += "<option value=3>Character (8-bit)";
7391 cd += "<option value=4>Word (16-bit)";
7392 cd += "<option value=5>Short integer (16-bit)";
7393 cd += "<option value=6>Double Word (32-bit)";
7394 cd += "<option value=8>Boolean";
7395 cd += "<option value=10>Double float (8 Bytes)";
7396 //cd += "<option value=16>Symbolic link";
7397 cd += "</select>";
7398 cd += "</tr>\n";
7399 cd += "<tr><td>Name<td><input type=text size=31 maxlength=31 id=create_name name=value></tr>\n";
7400 cd += "<tr><td>Array size<td><input type=text size=31 maxlength=31 id=create_array_length name=index value=1></tr>\n";
7401 cd += "<tr><td>String length<td><input type=text size=31 maxlength=31 id=create_strlen name=strlen value=32></tr>\n";
7402 cd += "</table>\n";
7403 cd += "<input type=button value=Create onClick='mhttpd_create_page_handle_create(event);'>\n";
7404 cd += "<input type=button value=Cancel onClick='mhttpd_create_page_handle_cancel(event);'>\n";
7405 cd += "</div>\n";
7406 cd += "</div>\n";
7407
7408 r->rsputs(cd.c_str());
7409
7410 /*---- Build the Link dialog------------------------------------*/
7411
7412 std::string ld = "";
7413
7414 ld += "<!-- Demo dialog -->\n";
7415 ld += "<div id=\"dlgLink\" class=\"dlgFrame\">\n";
7416 ld += "<div class=\"dlgTitlebar\">Create a link to an ODB entry</div>\n";
7417 ld += "<div class=\"dlgPanel\">\n";
7418 ld += "<br />\n";
7419 ld += "<div id=link_odbpath>";
7420 ld += "\"";
7421 ld += MJsonNode::Encode(odbpath.c_str());
7422 ld += "\"";
7423 ld += "</div>\n";
7424 ld += "<div><br></div>\n";
7425
7426 ld += "<table class=\"dialogTable\">\n";
7427 ld += "<th colspan=2>Create a link to an ODB entry:</th>\n";
7428 ld += "<tr><td>Name<td><input type=text size=31 maxlength=31 id=link_name name=value></tr>\n";
7429 ld += "<tr><td>Link target<td><input type=text size=31 maxlength=256 id=link_target name=target></tr>\n";
7430 ld += "</table>\n";
7431 ld += "<input type=button value=Link onClick='mhttpd_link_page_handle_link(event);'>\n";
7432 ld += "<input type=button value=Cancel onClick='mhttpd_link_page_handle_cancel(event);'>\n";
7433 ld += "</div>\n";
7434 ld += "</div>\n";
7435
7436 r->rsputs(ld.c_str());
7437}
7438
7439/*------------------------------------------------------------------*/
7440
7442 const char *group,
7443 int index, const char *value)
7444{
7445 int status, size;
7446 HNDLE hDB, hkey;
7447 KEY key;
7448 char data[TEXT_SIZE];
7449
7450 std::string odb_path = pp->getparam("odb_path");
7451
7452 //printf("show_set_page: odb_path [%s] group [%s] index %d value [%s]\n", odb_path.c_str(), group, index, value);
7453
7455
7456 /* show set page if no value is given */
7457 if (!pp->isparam("value") && !*pp->getparam("text")) {
7458 status = db_find_link(hDB, 0, odb_path.c_str(), &hkey);
7459 if (status != DB_SUCCESS) {
7460 r->rsprintf("Error: cannot find key %s<P>\n", odb_path.c_str());
7461 return;
7462 }
7463 db_get_link(hDB, hkey, &key);
7464
7465 show_header(r, "Set value", "POST", "", 0);
7466 //close header:
7467 r->rsprintf("</table>");
7468
7469 //main table:
7470 r->rsprintf("<table class=\"dialogTable\">");
7471
7472 if (index > 0)
7473 r->rsprintf("<input type=hidden name=index value=\"%d\">\n", index);
7474 else
7475 index = 0;
7476
7477 if (group[0])
7478 r->rsprintf("<input type=hidden name=group value=\"%s\">\n", group);
7479
7480 r->rsprintf("<input type=hidden name=odb_path value=\"%s\">\n", odb_path.c_str());
7481
7482 std::string data_str1 = rpc_tid_name(key.type);
7483 std::string str1;
7484 if (key.num_values > 1) {
7485 data_str1 += msprintf("[%d]", key.num_values);
7486 str1 = msprintf("%s[%d]", odb_path.c_str(), index);
7487 } else
7488 str1 = odb_path.c_str();
7489
7490 r->rsprintf("<tr><th colspan=2>Set new value - type = %s</tr>\n", data_str1.c_str());
7491 r->rsprintf("<tr><td>%s<td>\n", str1.c_str());
7492
7493 /* set current value as default */
7494 size = sizeof(data);
7495 db_get_link_data(hDB, hkey, data, &size, key.type);
7496 std::string data_str = db_sprintf(data, key.item_size, index, key.type);
7497
7498 if (equal_ustring(data_str.c_str(), "<NULL>"))
7499 data_str = "";
7500
7501 if (strchr(data_str.c_str(), '\n') != NULL) {
7502 r->rsprintf("<textarea rows=20 cols=80 name=\"text\">\n");
7503 strencode3(r, data);
7504 r->rsprintf("</textarea>\n");
7505 } else {
7506 size = 20;
7507 if ((int) data_str.length() > size)
7508 size = data_str.length() + 3;
7509 if (size > 80)
7510 size = 80;
7511
7512 r->rsprintf("<input type=\"text\" size=%d maxlength=256 name=\"value\" value=\"", size);
7513 strencode(r, data_str.c_str());
7514 r->rsprintf("\">\n");
7515 }
7516
7517 r->rsprintf("</tr>\n");
7518
7519 r->rsprintf("<tr><td align=center colspan=2>");
7520 r->rsprintf("<input type=submit name=cmd value=Set>");
7521 r->rsprintf("<input type=submit name=cmd value=Cancel>");
7522 r->rsprintf("</tr>");
7523 r->rsprintf("</table>");
7524
7525 r->rsprintf("<input type=hidden name=cmd value=Set>\n");
7526
7527 r->rsprintf("</div>\n"); // closing for <div id="mmain">
7528 r->rsprintf("</form>\n");
7529 r->rsprintf("</body></html>\r\n");
7530 return;
7531 } else {
7532 /* set value */
7533
7534 status = db_find_link(hDB, 0, odb_path.c_str(), &hkey);
7535 if (status != DB_SUCCESS) {
7536 r->rsprintf("Error: cannot find key %s<P>\n", odb_path.c_str());
7537 return;
7538 }
7539 db_get_link(hDB, hkey, &key);
7540
7541 memset(data, 0, sizeof(data));
7542
7543 if (pp->getparam("text") && *pp->getparam("text"))
7544 mstrlcpy(data, pp->getparam("text"), sizeof(data));
7545 else
7546 db_sscanf(value, data, &size, 0, key.type);
7547
7548 if (index < 0)
7549 index = 0;
7550
7551 /* extend data size for single string if necessary */
7552 if ((key.type == TID_STRING || key.type == TID_LINK)
7553 && (int) strlen(data) + 1 > key.item_size && key.num_values == 1)
7554 key.item_size = strlen(data) + 1;
7555
7556 if (key.item_size == 0)
7558
7559 if (key.num_values > 1)
7561 else
7563
7564 if (status == DB_NO_ACCESS)
7565 r->rsprintf("<h2>Write access not allowed</h2>\n");
7566
7567 redirect(r, "");
7568
7569 return;
7570 }
7571}
7572
7573/*------------------------------------------------------------------*/
7574
7575void show_find_page(Return* r, const char *value)
7576{
7577 HNDLE hDB, hkey;
7578
7580
7581 if (value[0] == 0) {
7582 /* without value, show find dialog */
7583 show_header(r, "Find value", "GET", "", 0);
7584
7585 //end header:
7586 r->rsprintf("</table>");
7587
7588 //find dialog:
7589 r->rsprintf("<table class=\"dialogTable\">");
7590
7591 r->rsprintf("<tr><th colspan=2>Find string in Online Database</tr>\n");
7592 r->rsprintf("<tr><td>Enter substring (case insensitive)\n");
7593
7594 r->rsprintf("<td><input type=\"text\" size=\"20\" maxlength=\"80\" name=\"value\">\n");
7595 r->rsprintf("</tr>");
7596
7597 r->rsprintf("<tr><td align=center colspan=2>");
7598 r->rsprintf("<input type=submit name=cmd value=Find>");
7599 r->rsprintf("<input type=submit name=cmd value=Cancel>");
7600 r->rsprintf("</tr>");
7601 r->rsprintf("</table>");
7602
7603 r->rsprintf("<input type=hidden name=cmd value=Find>");
7604
7605 r->rsprintf("</div>\n"); // closing for <div id="mmain">
7606 r->rsprintf("</form>\n");
7607 r->rsprintf("</body></html>\r\n");
7608 } else {
7609 show_header(r, "Search results", "GET", "", 0);
7610
7611 r->rsprintf("<table class=\"mtable\">\n");
7612 r->rsprintf("<tr><th colspan=2 class=\"mtableheader\">");
7613 r->rsprintf("Results of search for substring \"%s\"</tr>\n", value);
7614 r->rsprintf("<tr><th class=\"titlerow\">Key<th>Value</tr>\n");
7615
7616 /* start from root */
7617 db_find_key(hDB, 0, "", &hkey);
7618 assert(hkey);
7619
7620 /* scan tree, call "search_callback" for each key */
7622 data.r = r;
7623 data.search_name = value;
7624
7625 db_scan_tree(hDB, hkey, 0, search_callback, (void *)&data);
7626
7627 r->rsprintf("</table>");
7628 r->rsprintf("</div>\n"); // closing for <div id="mmain">
7629 r->rsprintf("</form>\n");
7630 r->rsprintf("</body></html>\r\n");
7631 }
7632}
7633
7634/*------------------------------------------------------------------*/
7635
7636#define LN10 2.302585094
7637#define LOG2 0.301029996
7638#define LOG5 0.698970005
7639
7640void haxis(gdImagePtr im, gdFont * font, int col, int gcol,
7641 int x1, int y1, int width,
7642 int minor, int major, int text, int label, int grid, double xmin, double xmax)
7643{
7644 double dx, int_dx, frac_dx, x_act, label_dx, major_dx, x_screen, maxwidth;
7645 int tick_base, major_base, label_base, n_sig1, n_sig2, xs;
7646 char str[80];
7647 double base[] = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
7648
7649 if (xmax <= xmin || width <= 0)
7650 return;
7651
7652 /* use 5 as min tick distance */
7653 dx = (xmax - xmin) / (double) (width / 5);
7654
7655 frac_dx = modf(log(dx) / LN10, &int_dx);
7656 if (frac_dx < 0) {
7657 frac_dx += 1;
7658 int_dx -= 1;
7659 }
7660
7661 tick_base = frac_dx < LOG2 ? 1 : frac_dx < LOG5 ? 2 : 3;
7662 major_base = label_base = tick_base + 1;
7663
7664 /* rounding up of dx, label_dx */
7665 dx = pow(10, int_dx) * base[tick_base];
7666 major_dx = pow(10, int_dx) * base[major_base];
7667 label_dx = major_dx;
7668
7669 /* number of significant digits */
7670 if (xmin == 0)
7671 n_sig1 = 0;
7672 else
7673 n_sig1 = (int) floor(log(fabs(xmin)) / LN10) - (int) floor(log(fabs(label_dx)) / LN10) + 1;
7674
7675 if (xmax == 0)
7676 n_sig2 = 0;
7677 else
7678 n_sig2 =
7679 (int) floor(log(fabs(xmax)) / LN10) - (int) floor(log(fabs(label_dx)) / LN10) + 1;
7680
7681 n_sig1 = MAX(n_sig1, n_sig2);
7682 n_sig1 = MAX(n_sig1, 4);
7683
7684 /* determination of maximal width of labels */
7685 sprintf(str, "%1.*lG", n_sig1, floor(xmin / dx) * dx);
7686 maxwidth = font->h / 2 * strlen(str);
7687 sprintf(str, "%1.*lG", n_sig1, floor(xmax / dx) * dx);
7688 maxwidth = MAX(maxwidth, font->h / 2 * strlen(str));
7689 sprintf(str, "%1.*lG", n_sig1, floor(xmax / dx) * dx + label_dx);
7690 maxwidth = MAX(maxwidth, font->h / 2 * strlen(str));
7691
7692 /* increasing label_dx, if labels would overlap */
7693 while (maxwidth > 0.7 * label_dx / (xmax - xmin) * width) {
7694 label_base++;
7695 label_dx = pow(10, int_dx) * base[label_base];
7696 if (label_base % 3 == 2 && major_base % 3 == 1) {
7697 major_base++;
7698 major_dx = pow(10, int_dx) * base[major_base];
7699 }
7700 }
7701
7702 x_act = floor(xmin / dx) * dx;
7703
7704 gdImageLine(im, x1, y1, x1 + width, y1, col);
7705
7706 do {
7707 x_screen = (x_act - xmin) / (xmax - xmin) * width + x1;
7708 xs = (int) (x_screen + 0.5);
7709
7710 if (x_screen > x1 + width + 0.001)
7711 break;
7712
7713 if (x_screen >= x1) {
7714 if (fabs(floor(x_act / major_dx + 0.5) - x_act / major_dx) <
7715 dx / major_dx / 10.0) {
7716
7717 if (fabs(floor(x_act / label_dx + 0.5) - x_act / label_dx) <
7718 dx / label_dx / 10.0) {
7719 /* label tick mark */
7720 gdImageLine(im, xs, y1, xs, y1 + text, col);
7721
7722 /* grid line */
7723 if (grid != 0 && xs > x1 && xs < x1 + width)
7724 gdImageLine(im, xs, y1, xs, y1 + grid, col);
7725
7726 /* label */
7727 if (label != 0) {
7728 sprintf(str, "%1.*lG", n_sig1, x_act);
7729 gdImageString(im, font, (int) xs - font->w * strlen(str) / 2,
7730 y1 + label, str, col);
7731 }
7732 } else {
7733 /* major tick mark */
7734 gdImageLine(im, xs, y1, xs, y1 + major, col);
7735
7736 /* grid line */
7737 if (grid != 0 && xs > x1 && xs < x1 + width)
7738 gdImageLine(im, xs, y1 - 1, xs, y1 + grid, gcol);
7739 }
7740
7741 } else
7742 /* minor tick mark */
7743 gdImageLine(im, xs, y1, xs, y1 + minor, col);
7744
7745 }
7746
7747 x_act += dx;
7748
7749 /* supress 1.23E-17 ... */
7750 if (fabs(x_act) < dx / 100)
7751 x_act = 0;
7752
7753 } while (1);
7754}
7755
7756/*------------------------------------------------------------------*/
7757
7758void sec_to_label(char *result, int sec, int base, int force_date)
7759{
7760 char mon[80];
7761 time_t t_sec;
7762
7763 t_sec = (time_t) sec;
7764
7765 struct tm tms;
7766 localtime_r(&t_sec, &tms);
7767 strcpy(mon, mname[tms.tm_mon]);
7768 mon[3] = 0;
7769
7770 if (force_date) {
7771 if (base < 600)
7772 sprintf(result, "%02d %s %02d %02d:%02d:%02d",
7773 tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min,
7774 tms.tm_sec);
7775 else if (base < 3600 * 24)
7776 sprintf(result, "%02d %s %02d %02d:%02d",
7777 tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min);
7778 else
7779 sprintf(result, "%02d %s %02d", tms.tm_mday, mon, tms.tm_year % 100);
7780 } else {
7781 if (base < 600)
7782 sprintf(result, "%02d:%02d:%02d", tms.tm_hour, tms.tm_min, tms.tm_sec);
7783 else if (base < 3600 * 3)
7784 sprintf(result, "%02d:%02d", tms.tm_hour, tms.tm_min);
7785 else if (base < 3600 * 24)
7786 sprintf(result, "%02d %s %02d %02d:%02d",
7787 tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min);
7788 else
7789 sprintf(result, "%02d %s %02d", tms.tm_mday, mon, tms.tm_year % 100);
7790 }
7791}
7792
7793void taxis(gdImagePtr im, gdFont * font, int col, int gcol,
7794 int x1, int y1, int width, int xr,
7795 int minor, int major, int text, int label, int grid, double xmin, double xmax)
7796{
7797 int dx, x_act, label_dx, major_dx, x_screen, maxwidth;
7798 int tick_base, major_base, label_base, xs, xl;
7799 char str[80];
7800 const int base[] = { 1, 5, 10, 60, 300, 600, 1800, 3600, 3600 * 6, 3600 * 12, 3600 * 24, 0 };
7801 time_t ltime;
7802 int force_date, d1, d2;
7803 struct tm tms;
7804
7805 if (xmax <= xmin || width <= 0)
7806 return;
7807
7808 /* force date display if xmax not today */
7809 ltime = ss_time();
7810 localtime_r(&ltime, &tms);
7811 d1 = tms.tm_mday;
7812 ltime = (time_t) xmax;
7813 localtime_r(&ltime, &tms);
7814 d2 = tms.tm_mday;
7815 force_date = (d1 != d2);
7816
7817 /* use 5 pixel as min tick distance */
7818 dx = (int) ((xmax - xmin) / (double) (width / 5) + 0.5);
7819
7820 for (tick_base = 0; base[tick_base]; tick_base++) {
7821 if (base[tick_base] > dx)
7822 break;
7823 }
7824 if (!base[tick_base])
7825 tick_base--;
7826 dx = base[tick_base];
7827
7828 if (base[tick_base + 1])
7829 major_base = tick_base + 1;
7830 else
7831 major_base = tick_base;
7832 major_dx = base[major_base];
7833
7834 if (base[major_base + 1])
7835 label_base = major_base + 1;
7836 else
7837 label_base = major_base;
7838 label_dx = base[label_base];
7839
7840 do {
7841 sec_to_label(str, (int) (xmin + 0.5), label_dx, force_date);
7842 maxwidth = font->h / 2 * strlen(str);
7843
7844 /* increasing label_dx, if labels would overlap */
7845 if (maxwidth > 0.7 * label_dx / (xmax - xmin) * width) {
7846 if (base[label_base + 1])
7847 label_dx = base[++label_base];
7848 else
7849 label_dx += 3600 * 24;
7850 } else
7851 break;
7852 } while (1);
7853
7854 x_act =
7855 (int) floor((double) (xmin - ss_timezone()) / label_dx) * label_dx + ss_timezone();
7856
7857 gdImageLine(im, x1, y1, x1 + width, y1, col);
7858
7859 do {
7860 x_screen = (int) ((x_act - xmin) / (xmax - xmin) * width + x1 + 0.5);
7861 xs = (int) (x_screen + 0.5);
7862
7863 if (x_screen > x1 + width + 0.001)
7864 break;
7865
7866 if (x_screen >= x1) {
7867 if ((x_act - ss_timezone()) % major_dx == 0) {
7868 if ((x_act - ss_timezone()) % label_dx == 0) {
7869 /* label tick mark */
7870 gdImageLine(im, xs, y1, xs, y1 + text, col);
7871
7872 /* grid line */
7873 if (grid != 0 && xs > x1 && xs < x1 + width)
7874 gdImageLine(im, xs, y1, xs, y1 + grid, col);
7875
7876 /* label */
7877 if (label != 0) {
7878 sec_to_label(str, x_act, label_dx, force_date);
7879
7880 /* if labels at edge, shift them in */
7881 xl = (int) xs - font->w * strlen(str) / 2;
7882 if (xl < 0)
7883 xl = 0;
7884 if (xl + font->w * (int) strlen(str) > xr)
7885 xl = xr - font->w * strlen(str);
7886 gdImageString(im, font, xl, y1 + label, str, col);
7887 }
7888 } else {
7889 /* major tick mark */
7890 gdImageLine(im, xs, y1, xs, y1 + major, col);
7891
7892 /* grid line */
7893 if (grid != 0 && xs > x1 && xs < x1 + width)
7894 gdImageLine(im, xs, y1 - 1, xs, y1 + grid, gcol);
7895 }
7896
7897 } else
7898 /* minor tick mark */
7899 gdImageLine(im, xs, y1, xs, y1 + minor, col);
7900
7901 }
7902
7903 x_act += dx;
7904
7905 /* supress 1.23E-17 ... */
7906 if (fabs((double)x_act) < dx / 100)
7907 x_act = 0;
7908
7909 } while (1);
7910}
7911
7912/*------------------------------------------------------------------*/
7913
7914int vaxis(gdImagePtr im, gdFont * font, int col, int gcol,
7915 int x1, int y1, int width,
7916 int minor, int major, int text, int label, int grid, double ymin, double ymax,
7917 BOOL logaxis)
7918{
7919 double dy, int_dy, frac_dy, y_act, label_dy, major_dy, y_screen, y_next;
7920 int tick_base, major_base, label_base, n_sig1, n_sig2, ys, max_width;
7921 int last_label_y;
7922 char str[80];
7923 const double base[] = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
7924
7925 if (ymax <= ymin || width <= 0)
7926 return 0;
7927
7928 // 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
7929 if (fabs(ymax - ymin) <= 1e-10)
7930 return 0;
7931
7932 if (logaxis) {
7933 dy = pow(10, floor(log(ymin) / LN10));
7934 label_dy = dy;
7935 major_dy = dy * 10;
7936 n_sig1 = 4;
7937 } else {
7938 dy = (ymax - ymin) / (double) (width / 5);
7939
7940 frac_dy = modf(log(dy) / LN10, &int_dy);
7941 if (frac_dy < 0) {
7942 frac_dy += 1;
7943 int_dy -= 1;
7944 }
7945
7946 tick_base = frac_dy < LOG2 ? 1 : frac_dy < LOG5 ? 2 : 3;
7947 major_base = label_base = tick_base + 1;
7948
7949 /* rounding up of dy, label_dy */
7950 dy = pow(10, int_dy) * base[tick_base];
7951 major_dy = pow(10, int_dy) * base[major_base];
7952 label_dy = major_dy;
7953
7954 /* number of significant digits */
7955 if (ymin == 0)
7956 n_sig1 = 0;
7957 else
7958 n_sig1 =
7959 (int) floor(log(fabs(ymin)) / LN10) -
7960 (int) floor(log(fabs(label_dy)) / LN10) + 1;
7961
7962 if (ymax == 0)
7963 n_sig2 = 0;
7964 else
7965 n_sig2 =
7966 (int) floor(log(fabs(ymax)) / LN10) -
7967 (int) floor(log(fabs(label_dy)) / LN10) + 1;
7968
7969 n_sig1 = MAX(n_sig1, n_sig2);
7970 n_sig1 = MAX(n_sig1, 4);
7971
7972 /* increasing label_dy, if labels would overlap */
7973 while (label_dy / (ymax - ymin) * width < 1.5 * font->h) {
7974 label_base++;
7975 label_dy = pow(10, int_dy) * base[label_base];
7976 if (label_base % 3 == 2 && major_base % 3 == 1) {
7977 major_base++;
7978 major_dy = pow(10, int_dy) * base[major_base];
7979 }
7980 }
7981 }
7982
7983 max_width = 0;
7984 y_act = floor(ymin / dy) * dy;
7985
7986 if (x1 != 0 || y1 != 0)
7987 gdImageLine(im, x1, y1, x1, y1 - width, col);
7988
7989 last_label_y = y1 + 2 * font->h;
7990
7991 do {
7992 if (logaxis)
7993 y_screen = y1 - (log(y_act) - log(ymin)) / (log(ymax) - log(ymin)) * width;
7994 else
7995 y_screen = y1 - (y_act - ymin) / (ymax - ymin) * width;
7996 ys = (int) (y_screen + 0.5);
7997
7998 if (y_screen < y1 - width - 0.001)
7999 break;
8000
8001 if (y_screen <= y1 + 0.001) {
8002 if (fabs(floor(y_act / major_dy + 0.5) - y_act / major_dy) <
8003 dy / major_dy / 10.0) {
8004 if (fabs(floor(y_act / label_dy + 0.5) - y_act / label_dy) <
8005 dy / label_dy / 10.0) {
8006 if (x1 != 0 || y1 != 0) {
8007 /* label tick mark */
8008 gdImageLine(im, x1, ys, x1 + text, ys, col);
8009
8010 /* grid line */
8011 if (grid != 0 && y_screen < y1 && y_screen > y1 - width) {
8012 if (grid > 0)
8013 gdImageLine(im, x1 + 1, ys, x1 + grid, ys, gcol);
8014 else
8015 gdImageLine(im, x1 - 1, ys, x1 + grid, ys, gcol);
8016 }
8017
8018 /* label */
8019 if (label != 0) {
8020 sprintf(str, "%1.*lG", n_sig1, y_act);
8021 if (label < 0)
8022 gdImageString(im, font, x1 + label - font->w * strlen(str),
8023 ys - font->h / 2, str, col);
8024 else
8025 gdImageString(im, font, x1 + label, ys - font->h / 2, str, col);
8026
8027 last_label_y = ys - font->h / 2;
8028 }
8029 } else {
8030 sprintf(str, "%1.*lG", n_sig1, y_act);
8031 max_width = MAX(max_width, (int) (font->w * strlen(str)));
8032 }
8033 } else {
8034 if (x1 != 0 || y1 != 0) {
8035 /* major tick mark */
8036 gdImageLine(im, x1, ys, x1 + major, ys, col);
8037
8038 /* grid line */
8039 if (grid != 0 && y_screen < y1 && y_screen > y1 - width)
8040 gdImageLine(im, x1, ys, x1 + grid, ys, col);
8041 }
8042 }
8043 if (logaxis) {
8044 dy *= 10;
8045 major_dy *= 10;
8046 label_dy *= 10;
8047 }
8048
8049 } else {
8050 if (x1 != 0 || y1 != 0) {
8051 /* minor tick mark */
8052 gdImageLine(im, x1, ys, x1 + minor, ys, col);
8053 }
8054
8055 /* for logaxis, also put labes on minor tick marks */
8056 if (logaxis) {
8057 if (label != 0) {
8058 if (x1 != 0 || y1 != 0) {
8059 /* calculate position of next major label */
8060 y_next = pow(10, floor(log(y_act) / LN10) + 1);
8061 y_screen =
8062 (int) (y1 -
8063 (log(y_next) - log(ymin)) / (log(ymax) -
8064 log(ymin)) * width + 0.5);
8065
8066 if (ys + font->h / 2 < last_label_y
8067 && ys - font->h / 2 > y_screen + font->h / 2) {
8068 sprintf(str, "%1.*lG", n_sig1, y_act);
8069 if (label < 0)
8070 gdImageString(im, font, x1 + label - font->w * strlen(str),
8071 ys - font->h / 2, str, col);
8072 else
8073 gdImageString(im, font, x1 + label, ys - font->h / 2, str,
8074 col);
8075 }
8076
8077 last_label_y = ys - font->h / 2;
8078 } else {
8079 sprintf(str, "%1.*lG", n_sig1, y_act);
8080 max_width = MAX(max_width, (int) (font->w * strlen(str)));
8081 }
8082 }
8083 }
8084 }
8085 }
8086
8087 y_act += dy;
8088
8089 /* supress 1.23E-17 ... */
8090 if (fabs(y_act) < dy / 100)
8091 y_act = 0;
8092
8093 } while (1);
8094
8095 return max_width + abs(label);
8096}
8097
8098/*------------------------------------------------------------------*/
8099
8100int time_to_sec(const char *str)
8101{
8102 double s;
8103
8104 s = atof(str);
8105 switch (str[strlen(str) - 1]) {
8106 case 'm':
8107 case 'M':
8108 s *= 60;
8109 break;
8110 case 'h':
8111 case 'H':
8112 s *= 3600;
8113 break;
8114 case 'd':
8115 case 'D':
8116 s *= 3600 * 24;
8117 break;
8118 }
8119
8120 return (int) s;
8121}
8122
8123/*------------------------------------------------------------------*/
8124
8125time_t string_to_time(const char *str)
8126{
8127 time_t t = 0;
8128 for (; *str != 0; str++) {
8129 if (*str < '0')
8130 break;
8131 if (*str > '9')
8132 break;
8133 t *= 10;
8134 t += *str - '0';
8135 }
8136 return t;
8137}
8138
8139/*------------------------------------------------------------------*/
8140
8141std::string time_to_string(time_t t)
8142{
8143 char buf[256];
8144 sprintf(buf, "%.0f", (double)t);
8145 return buf;
8146}
8147
8148/*------------------------------------------------------------------*/
8149
8150static bool gDoSetupHistoryWatch = true;
8151static bool gDoReloadHistory = false;
8152
8154{
8155 //printf("history_watch_callback %d %d %d\n", hDB, hKey, index);
8156 gDoReloadHistory = true;
8157 cm_msg(MINFO, "history_watch_callback", "History configuration may have changed, will reconnect");
8158}
8159
8161static HNDLE gMhkey = 0;
8162
8163/*------------------------------------------------------------------*/
8164
8165static MidasHistoryInterface* get_history(bool reset = false)
8166{
8167 int status;
8168 HNDLE hDB;
8169
8170 // history reconnect requested by watch callback?
8171
8172 if (gDoReloadHistory) {
8173 gDoReloadHistory = false;
8174 reset = true;
8175 }
8176
8177 // disconnect from previous history
8178
8179 if (reset && gMh) {
8180 gMh->hs_disconnect();
8181 delete gMh;
8182 gMh = NULL;
8183 gMhkey = 0;
8184 }
8185
8187 assert(status == CM_SUCCESS);
8188
8189 // setup a watch on history configuration
8190
8192 HNDLE hKey;
8193 gDoSetupHistoryWatch = false;
8194
8195 status = db_find_key(hDB, 0, "/Logger/History", &hKey);
8196 if (status == DB_SUCCESS)
8198
8199 status = db_find_key(hDB, 0, "/History/LoggerHistoryChannel", &hKey);
8200 if (status == DB_SUCCESS)
8202 }
8203
8204 // find out if ODB settings have changed and we need to connect to a different history channel
8205
8206 HNDLE hKey = 0;
8208 if (status != HS_SUCCESS)
8209 return gMh;
8210
8211 //printf("mh %p, hKey %d, mhkey %d\n", mh, hKey, mhkey);
8212
8213 if (gMh && hKey == gMhkey) // same channel as before
8214 return gMh;
8215
8216 if (gMh) {
8217 delete gMh;
8218 gMh = NULL;
8219 gMhkey = 0;
8220 }
8221
8223 if (status != HS_SUCCESS || gMh==NULL) {
8224 cm_msg(MERROR, "get_history", "Cannot configure history, hs_get_history() status %d", status);
8225 gMh = NULL;
8226 return NULL;
8227 }
8228
8229 gMhkey = hKey;
8230
8231 // cm_msg(MINFO, "get_history", "Reading history from channel \'%s\' type \'%s\'", mh->name, mh->type);
8232
8233 return gMh;
8234}
8235
8236/*------------------------------------------------------------------*/
8237
8238#ifdef OS_WINNT
8239#undef DELETE
8240#endif
8241
8242#define ALLOC(t,n) (t*)calloc(sizeof(t),(n))
8243#define DELETE(x) if (x) { free(x); (x)=NULL; }
8244#define DELETEA(x, n) if (x) { for (int i=0; i<(n); i++) { free((x)[i]); (x)[i]=NULL; }; DELETE(x); }
8245#define STRDUP(x) strdup(x)
8246
8248{
8257 time_t** t;
8258 double** v;
8259
8262
8263 time_t tstart;
8264 time_t tend;
8265 time_t scale;
8266
8267 void Allocate(int xnvars) {
8268 if (alloc_nvars > 0)
8269 Free();
8270 nvars = 0;
8271 alloc_nvars = xnvars;
8272 event_names = ALLOC(char*, alloc_nvars);
8273 var_names = ALLOC(char*, alloc_nvars);
8274 var_index = ALLOC(int, alloc_nvars);
8275 odb_index = ALLOC(int, alloc_nvars);
8276 status = ALLOC(int, alloc_nvars);
8278 t = ALLOC(time_t*, alloc_nvars);
8279 v = ALLOC(double*, alloc_nvars);
8280
8281 have_last_written = false;
8282 last_written = ALLOC(time_t, alloc_nvars);
8283 }
8284
8285 void Free() {
8290 DELETE(status);
8295 nvars = 0;
8296 alloc_nvars = 0;
8297 have_last_written = false;
8298 }
8299
8300 void Print() const {
8301 printf("this %p, nvars %d. tstart %d, tend %d, scale %d\n", this, nvars, (int)tstart, (int)tend, (int)scale);
8302 for (int i=0; i<nvars; i++) {
8303 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]);
8304 if (status[i]==HS_SUCCESS && num_entries[i]>0 && t[i] && v[i])
8305 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]);
8306 printf(" last_written %d", (int)last_written[i]);
8307 printf("\n");
8308 }
8309 }
8310
8311 HistoryData() // ctor
8312 {
8313 nvars = 0;
8314 alloc_nvars = 0;
8315 have_last_written = false;
8316 tstart = 0;
8317 tend = 0;
8318 scale = 0;
8319 }
8320
8322 {
8323 if (alloc_nvars > 0)
8324 Free();
8325 }
8326};
8327
8328#define READ_HISTORY_DATA 0x1
8329#define READ_HISTORY_RUNMARKER 0x2
8330#define READ_HISTORY_LAST_WRITTEN 0x4
8331
8333{
8334 std::string event_name;
8335 std::string tag_name;
8336 std::string formula;
8337 std::string colour;
8338 std::string label;
8339 bool show_raw_value = false;
8340 int order = -1;
8341 double factor = 1.0;
8342 double offset = 0;
8343 double voffset = 0;
8344};
8345
8347{
8348 std::string timescale = "1h";
8349 double minimum = 0;
8350 double maximum = 0;
8351 bool zero_ylow = false;
8352 bool log_axis = false;
8353 bool show_run_markers = true;
8354 bool show_values = true;
8355 bool show_fill = true;
8356 bool show_factor = false;
8357 bool enable_factor = true;
8358
8359 std::vector<HistVar> vars;
8360};
8361
8362static void LoadHistPlotFromOdb(MVOdb* odb, HistPlot* hp, const char* group, const char* panel);
8363
8364int 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)
8365{
8366 //HNDLE hkeypanel, hkeydvar, hkey;
8367 //KEY key;
8368 //char path[256];
8369 //int n_vars;
8370 int status;
8371 int debug = 1;
8372
8373 //mstrlcpy(path, group, sizeof(path));
8374 //mstrlcat(path, "/", sizeof(path));
8375 //mstrlcat(path, panel, sizeof(path));
8376
8377 //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);
8378
8379 /* connect to history */
8381 if (mh == NULL) {
8382 //r->rsprintf(str, "History is not configured\n");
8383 return HS_FILE_ERROR;
8384 }
8385
8386#if 0
8387 /* check panel name in ODB */
8388 status = db_find_key(hDB, 0, "/History/Display", &hkey);
8389 if (!hkey) {
8390 cm_msg(MERROR, "read_history", "Cannot find \'/History/Display\' in ODB, status %d", status);
8391 return HS_FILE_ERROR;
8392 }
8393
8394 /* check panel name in ODB */
8395 status = db_find_key(hDB, hkey, path, &hkeypanel);
8396 if (!hkeypanel) {
8397 cm_msg(MERROR, "read_history", "Cannot find \'%s\' in ODB, status %d", path, status);
8398 return HS_FILE_ERROR;
8399 }
8400
8401 status = db_find_key(hDB, hkeypanel, "Variables", &hkeydvar);
8402 if (!hkeydvar) {
8403 cm_msg(MERROR, "read_history", "Cannot find \'%s/Variables\' in ODB, status %d", path, status);
8404 return HS_FILE_ERROR;
8405 }
8406
8407 db_get_key(hDB, hkeydvar, &key);
8408 n_vars = key.num_values;
8409#endif
8410
8411 data->Allocate(hp.vars.size()+2);
8412
8413 data->tstart = tstart;
8414 data->tend = tend;
8415 data->scale = scale;
8416
8417 for (size_t i=0; i<hp.vars.size(); i++) {
8418 if (index != -1 && (size_t)index != i)
8419 continue;
8420
8421 //char str[256];
8422 //int size = sizeof(str);
8423 //status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
8424 //if (status != DB_SUCCESS) {
8425 // cm_msg(MERROR, "read_history", "Cannot read tag %d in panel %s, status %d", i, path, status);
8426 // continue;
8427 //}
8428
8429 /* split varname in event, variable and index: "event/tag[index]" */
8430
8431 //char *p = strchr(str, ':');
8432 //if (!p)
8433 // p = strchr(str, '/');
8434 //
8435 //if (!p) {
8436 // cm_msg(MERROR, "read_history", "Tag \"%s\" has wrong format in panel \"%s\"", str, path);
8437 // continue;
8438 //}
8439
8440 //*p = 0;
8441
8442 data->odb_index[data->nvars] = i;
8443 data->event_names[data->nvars] = STRDUP(hp.vars[i].event_name.c_str());
8444 data->var_names[data->nvars] = STRDUP(hp.vars[i].tag_name.c_str());
8445 data->var_index[data->nvars] = 0;
8446
8447 char *q = strchr(data->var_names[data->nvars], '[');
8448 if (q) {
8449 data->var_index[data->nvars] = atoi(q+1);
8450 *q = 0;
8451 }
8452
8453 data->nvars++;
8454 } // loop over variables
8455
8456 /* write run markes if selected */
8457 if (flags & READ_HISTORY_RUNMARKER) {
8458
8459 data->event_names[data->nvars+0] = STRDUP("Run transitions");
8460 data->event_names[data->nvars+1] = STRDUP("Run transitions");
8461
8462 data->var_names[data->nvars+0] = STRDUP("State");
8463 data->var_names[data->nvars+1] = STRDUP("Run number");
8464
8465 data->var_index[data->nvars+0] = 0;
8466 data->var_index[data->nvars+1] = 0;
8467
8468 data->odb_index[data->nvars+0] = -1;
8469 data->odb_index[data->nvars+1] = -2;
8470
8471 data->nvars += 2;
8472 }
8473
8474 bool get_last_written = false;
8475
8476 if (flags & READ_HISTORY_DATA) {
8477 status = mh->hs_read(tstart, tend, scale,
8478 data->nvars,
8479 data->event_names,
8480 data->var_names,
8481 data->var_index,
8482 data->num_entries,
8483 data->t,
8484 data->v,
8485 data->status);
8486
8487 if (debug) {
8488 printf("read_history: nvars %d, hs_read() status %d\n", data->nvars, status);
8489 for (int i=0; i<data->nvars; i++) {
8490 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]);
8491 }
8492 }
8493
8494 if (status != HS_SUCCESS) {
8495 cm_msg(MERROR, "read_history", "Complete history failure, hs_read() status %d, see messages", status);
8496 return HS_FILE_ERROR;
8497 }
8498
8499 for (int i=0; i<data->nvars; i++) {
8500 if (data->status[i] != HS_SUCCESS || data->num_entries[i] < 1) {
8501 get_last_written = true;
8502 break;
8503 }
8504 }
8505 }
8506
8507 if (flags & READ_HISTORY_LAST_WRITTEN)
8508 get_last_written = true;
8509
8510 if (get_last_written) {
8511 data->have_last_written = true;
8512
8514 tstart,
8515 data->nvars,
8516 data->event_names,
8517 data->var_names,
8518 data->var_index,
8519 data->last_written);
8520
8521 if (status != HS_SUCCESS) {
8522 data->have_last_written = false;
8523 }
8524 }
8525
8526 return SUCCESS;
8527}
8528
8529int get_hist_last_written(MVOdb* odb, const char *group, const char *panel, time_t endtime, int index, int want_all, time_t *plastwritten)
8530{
8531 //HNDLE hDB;
8532 int status;
8533
8534 time_t now = ss_time();
8535
8536 if (endtime == 0)
8537 endtime = now;
8538
8539 HistoryData hsxxx;
8540 HistoryData* hsdata = &hsxxx;
8541
8542 //cm_get_experiment_database(&hDB, NULL);
8543
8544 HistPlot hp;
8545 LoadHistPlotFromOdb(odb, &hp, group, panel);
8546
8547 double tstart = ss_millitime();
8548
8549 int flags = READ_HISTORY_LAST_WRITTEN;
8550
8551 status = read_history(hp, /*hDB, group, panel,*/ index, flags, endtime, endtime, 0, hsdata);
8552
8553 if (status != HS_SUCCESS) {
8554 //sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
8555 return status;
8556 }
8557
8558 if (!hsdata->have_last_written) {
8559 //sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
8560 return HS_FILE_ERROR;
8561 }
8562
8563 int count = 0;
8564 time_t tmin = endtime;
8565 time_t tmax = 0;
8566
8567 for (int k=0; k<hsdata->nvars; k++) {
8568 int i = hsdata->odb_index[k];
8569
8570 if (i<0)
8571 continue;
8572 if (index != -1 && index != i)
8573 continue;
8574
8575 time_t lw = hsdata->last_written[k];
8576
8577 if (lw==0) // no last_written for this variable, skip it.
8578 continue;
8579
8580 if (lw > endtime)
8581 lw = endtime; // just in case hs_get_last_written() returns dates in the "future" for this plot
8582
8583 if (lw > tmax)
8584 tmax = lw;
8585
8586 if (lw < tmin)
8587 tmin = lw;
8588
8589 count++;
8590
8591 //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);
8592 }
8593
8594 if (count == 0) // all variables have no last_written
8595 return HS_FILE_ERROR;
8596
8597 if (want_all)
8598 *plastwritten = tmin; // all variables have data
8599 else
8600 *plastwritten = tmax; // at least one variable has data
8601
8602 //printf("tmin %.0f, tmax %.0f, endtime %.0f, last written %.0f\n", (double)tmin, (double)tmax, (double)endtime, (double)*plastwritten);
8603
8604 double tend = ss_millitime();
8605
8606 if (/* DISABLES CODE */ (0))
8607 printf("get_hist_last_written: elapsed time %f ms\n", tend-tstart);
8608
8609 return HS_SUCCESS;
8610}
8611
8612void generate_hist_graph(MVOdb* odb, Return* rr, const char *hgroup, const char *hpanel, char *buffer, int *buffer_size,
8613 int width, int height,
8614 time_t xendtime,
8615 int scale,
8616 int index,
8617 int labels, const char *bgcolor, const char *fgcolor, const char *gridcolor)
8618{
8619 HNDLE hDB;
8620 //KEY key;
8621 gdImagePtr im;
8622 gdGifBuffer gb;
8623 int i, j, k, l;
8624 //int n_vars;
8625 int size, status, r, g, b;
8626 //int x_marker;
8627 int length;
8628 int white, grey, red;
8629 //int black, ltgrey, green, blue;
8630 int fgcol, bgcol, gridcol;
8631 int curve_col[MAX_VARS];
8632 int state_col[3];
8633 char str[256], *p;
8634 //INT var_index[MAX_VARS];
8635 //char event_name[MAX_VARS][NAME_LENGTH];
8636 //char tag_name[MAX_VARS][64];
8637 //char var_name[MAX_VARS][NAME_LENGTH];
8638 //char varname[64];
8639 //char key_name[256];
8640 //float factor[MAX_VARS], offset[MAX_VARS];
8641 //BOOL logaxis, runmarker;
8642 //double xmin, xrange;
8643 double ymin, ymax;
8644 double upper_limit[MAX_VARS], lower_limit[MAX_VARS];
8645 //float minvalue = (float) -HUGE_VAL;
8646 //float maxvalue = (float) +HUGE_VAL;
8647 //int show_values = 0;
8648 //int sort_vars = 0;
8649 //int old_vars = 0;
8650 time_t starttime, endtime;
8651 int flags;
8652
8653 time_t now = ss_time();
8654
8655 if (xendtime == 0)
8656 xendtime = now;
8657
8658 HistPlot hp;
8659 LoadHistPlotFromOdb(odb, &hp, hgroup, hpanel);
8660
8661 std::vector<int> var_index; var_index.resize(hp.vars.size());
8662
8663 for (size_t i=0; i<hp.vars.size(); i++) {
8664 var_index[i] = 0;
8665 const char *vp = strchr(hp.vars[i].tag_name.c_str(), '[');
8666 if (vp) {
8667 var_index[i] = atoi(vp + 1);
8668 }
8669 }
8670
8671 int logaxis = hp.log_axis;
8672 double minvalue = hp.minimum;
8673 double maxvalue = hp.maximum;
8674
8675 if ((minvalue == 0) && (maxvalue == 0)) {
8676 minvalue = -HUGE_VAL;
8677 maxvalue = +HUGE_VAL;
8678 }
8679
8680 std::vector<int> x[MAX_VARS];
8681 std::vector<double> y[MAX_VARS];
8682
8683 HistoryData hsxxx;
8684 HistoryData* hsdata = &hsxxx;
8685
8687
8688 /* generate image */
8689 im = gdImageCreate(width, height);
8690
8691 /* allocate standard colors */
8692 sscanf(bgcolor, "%02x%02x%02x", &r, &g, &b);
8693 bgcol = gdImageColorAllocate(im, r, g, b);
8694 sscanf(fgcolor, "%02x%02x%02x", &r, &g, &b);
8695 fgcol = gdImageColorAllocate(im, r, g, b);
8696 sscanf(gridcolor, "%02x%02x%02x", &r, &g, &b);
8697 gridcol = gdImageColorAllocate(im, r, g, b);
8698
8699 grey = gdImageColorAllocate(im, 192, 192, 192);
8700 //ltgrey = gdImageColorAllocate(im, 208, 208, 208);
8701 white = gdImageColorAllocate(im, 255, 255, 255);
8702 //black = gdImageColorAllocate(im, 0, 0, 0);
8703 red = gdImageColorAllocate(im, 255, 0, 0);
8704 //green = gdImageColorAllocate(im, 0, 255, 0);
8705 //blue = gdImageColorAllocate(im, 0, 0, 255);
8706
8707 curve_col[0] = gdImageColorAllocate(im, 0, 0, 255);
8708 curve_col[1] = gdImageColorAllocate(im, 0, 192, 0);
8709 curve_col[2] = gdImageColorAllocate(im, 255, 0, 0);
8710 curve_col[3] = gdImageColorAllocate(im, 0, 192, 192);
8711 curve_col[4] = gdImageColorAllocate(im, 255, 0, 255);
8712 curve_col[5] = gdImageColorAllocate(im, 192, 192, 0);
8713 curve_col[6] = gdImageColorAllocate(im, 128, 128, 128);
8714 curve_col[7] = gdImageColorAllocate(im, 128, 255, 128);
8715 curve_col[8] = gdImageColorAllocate(im, 255, 128, 128);
8716 curve_col[9] = gdImageColorAllocate(im, 128, 128, 255);
8717 for (i=10; i<MAX_VARS; i++)
8718 curve_col[i] = gdImageColorAllocate(im, 128, 128, 128);
8719
8720 state_col[0] = gdImageColorAllocate(im, 255, 0, 0);
8721 state_col[1] = gdImageColorAllocate(im, 255, 255, 0);
8722 state_col[2] = gdImageColorAllocate(im, 0, 255, 0);
8723
8724 /* Set transparent color. */
8725 gdImageColorTransparent(im, grey);
8726
8727 /* Title */
8728 gdImageString(im, gdFontGiant, width / 2 - (strlen(hpanel) * gdFontGiant->w) / 2, 2, (char*)hpanel, fgcol);
8729
8730 /* connect to history */
8732 if (mh == NULL) {
8733 sprintf(str, "History is not configured, see messages");
8734 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8735 goto error;
8736 }
8737
8739 //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
8740 //db_find_key(hDB, 0, str, &hkeypanel);
8741 //if (!hkeypanel) {
8742 // sprintf(str, "Cannot find /History/Display/%s/%s in ODB", hgroup, hpanel);
8743 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8744 // goto error;
8745 //}
8746
8747 //db_find_key(hDB, hkeypanel, "Variables", &hkeydvar);
8748 //if (!hkeydvar) {
8749 // sprintf(str, "Cannot find /History/Display/%s/%s/Variables in ODB", hgroup, hpanel);
8750 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8751 // goto error;
8752 //}
8753
8754 //db_get_key(hDB, hkeydvar, &key);
8755 //n_vars = key.num_values;
8756
8757 if (hp.vars.empty()) {
8758 sprintf(str, "No variables in panel %s/%s", hgroup, hpanel);
8759 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8760 goto error;
8761 }
8762
8763 if (hp.vars.size() > MAX_VARS) {
8764 sprintf(str, "Too many variables in panel %s/%s", hgroup, hpanel);
8765 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8766 goto error;
8767 }
8768
8769 ymin = ymax = 0;
8770 //logaxis = runmarker = 0;
8771
8772 for (i = 0; i < (int)hp.vars.size(); i++) {
8773 if (index != -1 && index != i)
8774 continue;
8775
8776 //size = sizeof(str);
8777 //status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
8778 //if (status != DB_SUCCESS) {
8779 // sprintf(str, "Cannot read tag %d in panel %s/%s, status %d", i, hgroup, hpanel, status);
8780 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8781 // goto error;
8782 //}
8783 //
8784 //mstrlcpy(tag_name[i], str, sizeof(tag_name[0]));
8785
8787 //char *tp = strchr(tag_name[i], ':');
8788 //if (tp) {
8789 // mstrlcpy(event_name[i], tag_name[i], sizeof(event_name[0]));
8790 // char *ep = strchr(event_name[i], ':');
8791 // if (ep)
8792 // *ep = 0;
8793 // mstrlcpy(var_name[i], tp+1, sizeof(var_name[0]));
8794 // var_index[i] = 0;
8795 // char *vp = strchr(var_name[i], '[');
8796 // if (vp) {
8797 // var_index[i] = atoi(vp + 1);
8798 // *vp = 0;
8799 // }
8800 //} else {
8801 // sprintf(str, "Tag \"%s\" has wrong format in panel \"%s/%s\"", tag_name[i], hgroup, hpanel);
8802 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8803 // goto error;
8804 //}
8805 //
8806 //db_find_key(hDB, hkeypanel, "Colour", &hkey);
8807 //if (hkey) {
8808 // size = sizeof(str);
8809 // status = db_get_data_index(hDB, hkey, str, &size, i, TID_STRING);
8810 // if (status == DB_SUCCESS) {
8811 // if (str[0] == '#') {
8812 // char sss[3];
8813 // int r, g, b;
8814 //
8815 // sss[0] = str[1];
8816 // sss[1] = str[2];
8817 // sss[2] = 0;
8818 // r = strtoul(sss, NULL, 16);
8819 // sss[0] = str[3];
8820 // sss[1] = str[4];
8821 // sss[2] = 0;
8822 // g = strtoul(sss, NULL, 16);
8823 // sss[0] = str[5];
8824 // sss[1] = str[6];
8825 // sss[2] = 0;
8826 // b = strtoul(sss, NULL, 16);
8827 //
8828 // curve_col[i] = gdImageColorAllocate(im, r, g, b);
8829 // }
8830 // }
8831 //}
8832
8833 if (hp.vars[i].colour[0] == '#') {
8834 const char* str = hp.vars[i].colour.c_str();
8835 char sss[3];
8836 int r, g, b;
8837
8838 sss[0] = str[1];
8839 sss[1] = str[2];
8840 sss[2] = 0;
8841 r = strtoul(sss, NULL, 16);
8842 sss[0] = str[3];
8843 sss[1] = str[4];
8844 sss[2] = 0;
8845 g = strtoul(sss, NULL, 16);
8846 sss[0] = str[5];
8847 sss[1] = str[6];
8848 sss[2] = 0;
8849 b = strtoul(sss, NULL, 16);
8850
8851 curve_col[i] = gdImageColorAllocate(im, r, g, b);
8852 }
8853
8854 /* get timescale */
8855 if (scale == 0) {
8856 //std::string ts = "1h";
8857 //status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
8858 //if (status != DB_SUCCESS) {
8859 // /* delete old integer key */
8860 // db_find_key(hDB, hkeypanel, "Timescale", &hkey);
8861 // if (hkey)
8862 // db_delete_key(hDB, hkey, FALSE);
8863 //
8864 // ts = "1h";
8865 // status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
8866 //}
8867
8868 scale = time_to_sec(hp.timescale.c_str());
8869 }
8870
8871 //for (j = 0; j < MAX_VARS; j++) {
8872 // factor[j] = 1;
8873 // offset[j] = 0;
8874 //}
8875
8877 //size = sizeof(float) * n_vars;
8878 //db_get_value(hDB, hkeypanel, "Factor", factor, &size, TID_FLOAT, TRUE);
8879
8881 //size = sizeof(float) * n_vars;
8882 //db_get_value(hDB, hkeypanel, "Offset", offset, &size, TID_FLOAT, TRUE);
8883
8885 //size = sizeof(logaxis);
8886 //logaxis = 0;
8887 //db_get_value(hDB, hkeypanel, "Log axis", &logaxis, &size, TID_BOOL, TRUE);
8888
8890 //size = sizeof(show_values);
8891 //show_values = 0;
8892 //db_get_value(hDB, hkeypanel, "Show values", &show_values, &size, TID_BOOL, TRUE);
8893
8895 //size = sizeof(sort_vars);
8896 //sort_vars = 0;
8897 //db_get_value(hDB, hkeypanel, "Sort vars", &sort_vars, &size, TID_BOOL, TRUE);
8898
8900 //size = sizeof(old_vars);
8901 //old_vars = 0;
8902 //db_get_value(hDB, hkeypanel, "Show old vars", &old_vars, &size, TID_BOOL, TRUE);
8903
8905 //size = sizeof(minvalue);
8906 //minvalue = (float) -HUGE_VAL;
8907 //db_get_value(hDB, hkeypanel, "Minimum", &minvalue, &size, TID_FLOAT, TRUE);
8908
8910 //size = sizeof(maxvalue);
8911 //maxvalue = (float) +HUGE_VAL;
8912 //db_get_value(hDB, hkeypanel, "Maximum", &maxvalue, &size, TID_FLOAT, TRUE);
8913
8914 //if ((minvalue == 0) && (maxvalue == 0)) {
8915 // minvalue = (float) -HUGE_VAL;
8916 // maxvalue = (float) +HUGE_VAL;
8917 //}
8918
8920 //size = sizeof(runmarker);
8921 //runmarker = 1;
8922 //db_get_value(hDB, hkeypanel, "Show run markers", &runmarker, &size, TID_BOOL, TRUE);
8923
8924 /* make ODB path from tag name */
8925 std::string odbpath;
8926 HNDLE hkeyeq = 0;
8927 HNDLE hkeyroot;
8928 db_find_key(hDB, 0, "/Equipment", &hkeyroot);
8929 if (hkeyroot) {
8930 for (j = 0;; j++) {
8931 HNDLE hkeyeq;
8932 db_enum_key(hDB, hkeyroot, j, &hkeyeq);
8933
8934 if (!hkeyeq)
8935 break;
8936
8937 KEY key;
8938 db_get_key(hDB, hkeyeq, &key);
8939 if (equal_ustring(key.name, hp.vars[i].event_name.c_str())) {
8940 /* check if variable is individual key under variables/ */
8941 sprintf(str, "Variables/%s", hp.vars[i].tag_name.c_str());
8942 HNDLE hkey;
8943 db_find_key(hDB, hkeyeq, str, &hkey);
8944 if (hkey) {
8945 //sprintf(odbpath, "/Equipment/%s/Variables/%s", event_name[i], var_name[i]);
8946 odbpath = "";
8947 odbpath += "/Equipment/";
8948 odbpath += hp.vars[i].event_name;
8949 odbpath += "/Variables/";
8950 odbpath += hp.vars[i].tag_name;
8951 break;
8952 }
8953
8954 /* check if variable is in setttins/names array */
8955 HNDLE hkeynames;
8956 db_find_key(hDB, hkeyeq, "Settings/Names", &hkeynames);
8957 if (hkeynames) {
8958 /* extract variable name and Variables/<key> */
8959 mstrlcpy(str, hp.vars[i].tag_name.c_str(), sizeof(str));
8960 p = str + strlen(str) - 1;
8961 while (p > str && *p != ' ')
8962 p--;
8963 std::string key_name = p + 1;
8964 *p = 0;
8965
8966 std::string varname = str;
8967
8968 /* find key in single name array */
8969 db_get_key(hDB, hkeynames, &key);
8970 for (k = 0; k < key.num_values; k++) {
8971 size = sizeof(str);
8972 db_get_data_index(hDB, hkeynames, str, &size, k, TID_STRING);
8973 if (equal_ustring(str, varname.c_str())) {
8974 //sprintf(odbpath, "/Equipment/%s/Variables/%s[%d]", event_name[i], key_name, k);
8975 odbpath = "";
8976 odbpath += "/Equipment/";
8977 odbpath += hp.vars[i].event_name;
8978 odbpath += "/Variables/";
8979 odbpath += key_name;
8980 odbpath += "[";
8981 odbpath += toString(k);
8982 odbpath += "]";
8983 break;
8984 }
8985 }
8986 } else {
8987 /* go through /variables/<name> entries */
8988 HNDLE hkeyvars;
8989 db_find_key(hDB, hkeyeq, "Variables", &hkeyvars);
8990 if (hkeyvars) {
8991 for (k = 0;; k++) {
8992 db_enum_key(hDB, hkeyvars, k, &hkey);
8993
8994 if (!hkey)
8995 break;
8996
8997 /* find "settins/names <key>" for this key */
8998 db_get_key(hDB, hkey, &key);
8999
9000 /* find key in key_name array */
9001 std::string key_name = key.name;
9002
9003 std::string path;
9004 //sprintf(str, "Settings/Names %s", key_name);
9005 path += "Settings/Names ";
9006 path += key_name;
9007
9008 HNDLE hkeynames;
9009 db_find_key(hDB, hkeyeq, path.c_str(), &hkeynames);
9010 if (hkeynames) {
9011 db_get_key(hDB, hkeynames, &key);
9012 for (l = 0; l < key.num_values; l++) {
9013 size = sizeof(str);
9014 db_get_data_index(hDB, hkeynames, str, &size, l, TID_STRING);
9015 if (equal_ustring(str, hp.vars[i].tag_name.c_str())) {
9016 //sprintf(odbpath, "/Equipment/%s/Variables/%s[%d]", event_name[i], key_name, l);
9017 odbpath = "";
9018 odbpath += "/Equipment/";
9019 odbpath += hp.vars[i].event_name;
9020 odbpath += "/Variables/";
9021 odbpath += key_name;
9022 odbpath += "[";
9023 odbpath += toString(l);
9024 odbpath += "]";
9025 break;
9026 }
9027 }
9028 }
9029 }
9030 }
9031 }
9032
9033 break;
9034 }
9035 }
9036
9037 if (!hkeyeq) {
9038 db_find_key(hDB, 0, "/History/Links", &hkeyroot);
9039 if (hkeyroot) {
9040 for (j = 0;; j++) {
9041 HNDLE hkey;
9042 db_enum_link(hDB, hkeyroot, j, &hkey);
9043
9044 if (!hkey)
9045 break;
9046
9047 KEY key;
9048 db_get_key(hDB, hkey, &key);
9049 if (equal_ustring(key.name, hp.vars[i].event_name.c_str())) {
9050 db_enum_key(hDB, hkeyroot, j, &hkey);
9051 db_find_key(hDB, hkey, hp.vars[i].tag_name.c_str(), &hkey);
9052 if (hkey) {
9053 db_get_key(hDB, hkey, &key);
9054 odbpath = db_get_path(hDB, hkey);
9055 if (key.num_values > 1) {
9056 odbpath += "[";
9057 odbpath += toString(var_index[i]);
9058 odbpath += "]";
9059 }
9060 break;
9061 }
9062 }
9063 }
9064 }
9065 }
9066 }
9067
9068 /* search alarm limits */
9069 upper_limit[i] = lower_limit[i] = -12345;
9070 db_find_key(hDB, 0, "Alarms/Alarms", &hkeyroot);
9071 if (odbpath.length() > 0 && hkeyroot) {
9072 for (j = 0;; j++) {
9073 HNDLE hkey;
9074 db_enum_key(hDB, hkeyroot, j, &hkey);
9075
9076 if (!hkey)
9077 break;
9078
9079 size = sizeof(str);
9080 db_get_value(hDB, hkey, "Condition", str, &size, TID_STRING, TRUE);
9081
9082 if (strstr(str, odbpath.c_str())) {
9083 if (strchr(str, '<')) {
9084 p = strchr(str, '<') + 1;
9085 if (*p == '=')
9086 p++;
9087 if (hp.enable_factor) {
9088 lower_limit[i] = (hp.vars[i].factor * (atof(p) - hp.vars[i].voffset) + hp.vars[i].offset);
9089 } else {
9090 lower_limit[i] = atof(p);
9091 }
9092 }
9093 if (strchr(str, '>')) {
9094 p = strchr(str, '>') + 1;
9095 if (*p == '=')
9096 p++;
9097 if (hp.enable_factor) {
9098 upper_limit[i] = (hp.vars[i].factor * (atof(p) - hp.vars[i].voffset) + hp.vars[i].offset);
9099 } else {
9100 upper_limit[i] = atof(p);
9101 }
9102 }
9103 }
9104 }
9105 }
9106 } // loop over variables
9107
9108 //starttime = now - scale + toffset;
9109 //endtime = now + toffset;
9110
9111 starttime = xendtime - scale;
9112 endtime = xendtime;
9113
9114 //printf("now %d, scale %d, xendtime %d, starttime %d, endtime %d\n", now, scale, xendtime, starttime, endtime);
9115
9116 flags = READ_HISTORY_DATA;
9117 if (hp.show_run_markers)
9118 flags |= READ_HISTORY_RUNMARKER;
9119
9120 status = read_history(hp, /*hDB, hgroup, hpanel,*/ index, flags, starttime, endtime, scale/1000+1, hsdata);
9121
9122 if (status != HS_SUCCESS) {
9123 sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
9124 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
9125 goto error;
9126 }
9127
9128 DWORD n_point[MAX_VARS];
9129 char var_status[MAX_VARS][256];
9130
9131 for (int k=0; k<hsdata->nvars; k++) {
9132 int i = hsdata->odb_index[k];
9133
9134 if (i<0)
9135 continue;
9136
9137 if (index != -1 && index != i)
9138 continue;
9139
9140 n_point[i] = 0;
9141
9142 var_status[i][0] = 0;
9143 if (hsdata->status[k] == HS_UNDEFINED_VAR) {
9144 sprintf(var_status[i], "not found in history");
9145 continue;
9146 } else if (hsdata->status[k] != HS_SUCCESS) {
9147 sprintf(var_status[i], "hs_read() error %d, see messages", hsdata->status[k]);
9148 continue;
9149 }
9150
9151 int n_vp = 0;
9152 for (int j=0; j<hsdata->num_entries[k]; j++) {
9153 int xx = (int)(hsdata->t[k][j]);
9154 double yy = hsdata->v[k][j];
9155
9156 /* skip NaNs */
9157 if (ss_isnan(yy))
9158 continue;
9159
9160 /* skip INFs */
9161 if (!ss_isfin(yy))
9162 continue;
9163
9164 /* avoid overflow */
9165 if (yy > 1E30)
9166 yy = 1E30f;
9167
9168 /* apply factor and offset */
9169 if (hp.enable_factor) {
9170 yy = hp.vars[i].factor * (yy - hp.vars[i].voffset) + hp.vars[i].offset;
9171 }
9172
9173 /* calculate ymin and ymax */
9174 if ((i == 0 || index != -1) && n_vp == 0)
9175 ymin = ymax = yy;
9176 else {
9177 if (yy > ymax)
9178 ymax = yy;
9179 if (yy < ymin)
9180 ymin = yy;
9181 }
9182
9183 /* increment number of valid points */
9184
9185 x[i].push_back(xx);
9186 y[i].push_back(yy);
9187
9188 n_vp++;
9189
9190 } // loop over data
9191
9192 n_point[i] = n_vp;
9193
9194 assert(x[i].size() == y[i].size());
9195 }
9196
9197 //int flag;
9198 int xmaxm;
9199 int row;
9200 int xold;
9201 int yold;
9202 int aoffset;
9203 double yb1, yb2, yf1, yf2;
9204 int xs, ys;
9205 int x1, x2;
9206 int y1, y2;
9207 int xs_old;
9208 double ybase;
9209
9210 gdPoint poly[3];
9211
9212 if (ymin < minvalue)
9213 ymin = minvalue;
9214
9215 if (ymax > maxvalue)
9216 ymax = maxvalue;
9217
9218 /* check if ylow = 0 */
9219 if (index == -1) {
9220 //flag = 0;
9221 //size = sizeof(flag);
9222 //db_get_value(hDB, hkeypanel, "Zero ylow", &flag, &size, TID_BOOL, TRUE);
9223 if (hp.zero_ylow && ymin > 0)
9224 ymin = 0;
9225 }
9226
9227 /* if min and max too close together, switch to linear axis */
9228 if (logaxis && ymin > 0 && ymax > 0) {
9229 yb1 = pow(10, floor(log(ymin) / LN10));
9230 yf1 = floor(ymin / yb1);
9231 yb2 = pow(10, floor(log(ymax) / LN10));
9232 yf2 = floor(ymax / yb2);
9233
9234 if (yb1 == yb2 && yf1 == yf2)
9235 logaxis = 0;
9236 else {
9237 /* round down and up ymin and ymax */
9238 ybase = pow(10, floor(log(ymin) / LN10));
9239 ymin = (floor(ymin / ybase) * ybase);
9240 ybase = pow(10, floor(log(ymax) / LN10));
9241 ymax = ((floor(ymax / ybase) + 1) * ybase);
9242 }
9243 }
9244
9245 /* avoid negative limits for log axis */
9246 if (logaxis) {
9247 if (ymax <= 0)
9248 ymax = 1;
9249 if (ymin <= 0)
9250 ymin = 1E-12f;
9251 }
9252
9253 /* increase limits by 5% */
9254 if (ymin == 0 && ymax == 0) {
9255 ymin = -1;
9256 ymax = 1;
9257 } else {
9258 if (!logaxis) {
9259 ymax += (ymax - ymin) / 20.f;
9260
9261 if (ymin != 0)
9262 ymin -= (ymax - ymin) / 20.f;
9263 }
9264 }
9265
9266 /* avoid ymin == ymax */
9267 if (ymax == ymin) {
9268 if (logaxis) {
9269 ymax *= 2;
9270 ymin /= 2;
9271 } else {
9272 ymax += 10;
9273 ymin -= 10;
9274 }
9275 }
9276
9277 /* calculate X limits */
9278 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9279 //xmax = (double) (toffset / 3600.0);
9280 //xrange = xmax - xmin;
9281 //xrange = scale/3600.0;
9282
9283 /* caluclate required space for Y-axis */
9284 aoffset = vaxis(im, gdFontSmall, fgcol, gridcol, 0, 0, height, -3, -5, -7, -8, 0, ymin, ymax, logaxis);
9285 aoffset += 2;
9286
9287 x1 = aoffset;
9288 y1 = height - 20;
9289 x2 = width - 20;
9290 y2 = 20;
9291
9292 gdImageFilledRectangle(im, x1, y2, x2, y1, bgcol);
9293
9294 /* draw axis frame */
9295 taxis(im, gdFontSmall, fgcol, gridcol, x1, y1, x2 - x1, width, 3, 5, 9, 10, 0, (double)starttime, (double)endtime);
9296
9297 vaxis(im, gdFontSmall, fgcol, gridcol, x1, y1, y1 - y2, -3, -5, -7, -8, x2 - x1, ymin, ymax, logaxis);
9298 gdImageLine(im, x1, y2, x2, y2, fgcol);
9299 gdImageLine(im, x2, y2, x2, y1, fgcol);
9300
9301 xs = ys = xold = yold = 0;
9302
9303 /* old code for run markers, new code is below */
9304
9305 /* write run markes if selected */
9306 if (/* DISABLES CODE */ (0) && hp.show_run_markers) {
9307
9308 const char* event_names[] = {
9309 "Run transitions",
9310 "Run transitions",
9311 0 };
9312
9313 const char* tag_names[] = {
9314 "State",
9315 "Run number",
9316 0 };
9317
9318 const int tag_indexes[] = {
9319 0,
9320 0,
9321 0 };
9322
9323 int num_entries[3];
9324 time_t *tbuf[3];
9325 double *dbuf[3];
9326 int st[3];
9327
9328 num_entries[0] = 0;
9329 num_entries[1] = 0;
9330
9331 status = mh->hs_read(starttime - scale, endtime, 0,
9332 2, event_names, tag_names, tag_indexes,
9333 num_entries, tbuf, dbuf, st);
9334
9335 //printf("read run info: status %d, entries %d %d\n", status, num_entries[0], num_entries[1]);
9336
9337 int n_marker = num_entries[0];
9338
9339 if (status == HS_SUCCESS && n_marker > 0 && n_marker < 100) {
9340 xs_old = -1;
9341 xmaxm = x1;
9342 for (j = 0; j < (int) n_marker; j++) {
9343 int col;
9344
9345 // explicit algebra manipulation to clarify computations:
9346
9347 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9348 //xrange = scale/3600.0;
9349 //time_t starttime = now - scale + toffset;
9350
9351 //x_marker = (int)(tbuf[1][j] - now);
9352 //xs = (int) ((x_marker / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9353 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9354 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - (-scale / 3600.0 + toffset / 3600.0)) / (scale/3600.0) * (x2 - x1) + x1 + 0.5);
9355 //xs = (int) (((tbuf[1][j] - now) - (-scale + toffset)) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9356 xs = (int) ((tbuf[1][j] - starttime) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9357
9358 if (xs < x1)
9359 continue;
9360 if (xs >= x2)
9361 continue;
9362
9363 double run_number = dbuf[1][j];
9364
9365 if (xs <= xs_old)
9366 xs = xs_old + 1;
9367 xs_old = xs;
9368
9369 if (dbuf[0][j] == 1)
9370 col = state_col[0];
9371 else if (dbuf[0][j] == 2)
9372 col = state_col[1];
9373 else if (dbuf[0][j] == 3)
9374 col = state_col[2];
9375 else
9376 col = state_col[0];
9377
9378 gdImageDashedLine(im, xs, y1, xs, y2, col);
9379
9380 sprintf(str, "%.0f", run_number);
9381
9382 if (dbuf[0][j] == STATE_RUNNING) {
9383 if (xs > xmaxm) {
9384 gdImageStringUp(im, gdFontSmall, xs + 0, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9385 xmaxm = xs - 2 + gdFontSmall->h;
9386 }
9387 } else if (dbuf[0][j] == STATE_STOPPED) {
9388 if (xs + 2 - gdFontSmall->h > xmaxm) {
9389 gdImageStringUp(im, gdFontSmall, xs + 2 - gdFontSmall->h, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9390 xmaxm = xs - 1;
9391 }
9392 }
9393 }
9394 }
9395
9396 if (num_entries[0]) {
9397 free(tbuf[0]);
9398 free(dbuf[0]);
9399 tbuf[0] = NULL;
9400 dbuf[0] = NULL;
9401 }
9402
9403 if (num_entries[1]) {
9404 free(tbuf[1]);
9405 free(dbuf[1]);
9406 tbuf[1] = NULL;
9407 dbuf[1] = NULL;
9408 }
9409 }
9410
9411 /* write run markes if selected */
9412 if (hp.show_run_markers) {
9413
9414 int index_state = -1;
9415 int index_run_number = -1;
9416
9417 for (int k=0; k<hsdata->nvars; k++) {
9418 if (hsdata->odb_index[k] == -1)
9419 index_state = k;
9420
9421 if (hsdata->odb_index[k] == -2)
9422 index_run_number = k;
9423 }
9424
9425 bool ok = true;
9426
9427 if (ok)
9428 ok = (index_state >= 0) && (index_run_number >= 0);
9429
9430 if (ok)
9431 ok = (hsdata->status[index_state] == HS_SUCCESS);
9432
9433 if (ok)
9434 ok = (hsdata->status[index_run_number] == HS_SUCCESS);
9435
9436 if (/* DISABLES CODE */ (0) && ok)
9437 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]);
9438
9439 if (ok)
9440 ok = (hsdata->num_entries[index_state] == hsdata->num_entries[index_run_number]);
9441
9442 int n_marker = hsdata->num_entries[index_state];
9443
9444 if (ok && n_marker > 0 && n_marker < 100) {
9445 xs_old = -1;
9446 xmaxm = x1;
9447 for (j = 0; j < (int) n_marker; j++) {
9448 int col;
9449
9450 // explicit algebra manipulation to clarify computations:
9451
9452 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9453 //xrange = scale/3600.0;
9454 //time_t starttime = now - scale + toffset;
9455
9456 //x_marker = (int)(tbuf[1][j] - now);
9457 //xs = (int) ((x_marker / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9458 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9459 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - (-scale / 3600.0 + toffset / 3600.0)) / (scale/3600.0) * (x2 - x1) + x1 + 0.5);
9460 //xs = (int) (((tbuf[1][j] - now) - (-scale + toffset)) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9461 xs = (int) ((hsdata->t[index_state][j] - starttime) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9462
9463 if (xs < x1)
9464 continue;
9465 if (xs >= x2)
9466 continue;
9467
9468 double run_number = hsdata->v[index_run_number][j];
9469
9470 if (xs <= xs_old)
9471 xs = xs_old + 1;
9472 xs_old = xs;
9473
9474 int state = (int)hsdata->v[index_state][j];
9475
9476 if (state == 1)
9477 col = state_col[0];
9478 else if (state == 2)
9479 col = state_col[1];
9480 else if (state == 3)
9481 col = state_col[2];
9482 else
9483 col = state_col[0];
9484
9485 gdImageDashedLine(im, xs, y1, xs, y2, col);
9486
9487 sprintf(str, "%.0f", run_number);
9488
9489 if (state == STATE_RUNNING) {
9490 if (xs > xmaxm) {
9491 gdImageStringUp(im, gdFontSmall, xs + 0, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9492 xmaxm = xs - 2 + gdFontSmall->h;
9493 }
9494 } else if (state == STATE_STOPPED) {
9495 if (xs + 2 - gdFontSmall->h > xmaxm) {
9496 gdImageStringUp(im, gdFontSmall, xs + 2 - gdFontSmall->h, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9497 xmaxm = xs - 1;
9498 }
9499 }
9500 }
9501 }
9502 }
9503
9504 for (i = 0; i < (int)hp.vars.size(); i++) {
9505 if (index != -1 && index != i)
9506 continue;
9507
9508 /* draw alarm limits */
9509 if (lower_limit[i] != -12345) {
9510 if (logaxis) {
9511 if (lower_limit[i] <= 0)
9512 ys = y1;
9513 else
9514 ys = (int) (y1 - (log(lower_limit[i]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
9515 } else {
9516 ys = (int) (y1 - (lower_limit[i] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
9517 }
9518
9519 if (xs < 0)
9520 xs = 0;
9521 if (xs >= width)
9522 xs = width-1;
9523 if (ys < 0)
9524 ys = 0;
9525 if (ys >= height)
9526 ys = height-1;
9527
9528 if (ys > y2 && ys < y1) {
9529 gdImageDashedLine(im, x1, ys, x2, ys, curve_col[i]);
9530
9531 poly[0].x = x1;
9532 poly[0].y = ys;
9533 poly[1].x = x1 + 5;
9534 poly[1].y = ys;
9535 poly[2].x = x1;
9536 poly[2].y = ys - 5;
9537
9538 gdImageFilledPolygon(im, poly, 3, curve_col[i]);
9539 }
9540 }
9541 if (upper_limit[i] != -12345) {
9542 if (logaxis) {
9543 if (upper_limit[i] <= 0)
9544 ys = y1;
9545 else
9546 ys = (int) (y1 - (log(upper_limit[i]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
9547 } else {
9548 ys = (int) (y1 - (upper_limit[i] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
9549 }
9550
9551 if (xs < 0)
9552 xs = 0;
9553 if (xs >= width)
9554 xs = width-1;
9555 if (ys < 0)
9556 ys = 0;
9557 if (ys >= height)
9558 ys = height-1;
9559
9560 if (ys > y2 && ys < y1) {
9561 gdImageDashedLine(im, x1, ys, x2, ys, curve_col[i]);
9562
9563 poly[0].x = x1;
9564 poly[0].y = ys;
9565 poly[1].x = x1 + 5;
9566 poly[1].y = ys;
9567 poly[2].x = x1;
9568 poly[2].y = ys + 5;
9569
9570 gdImageFilledPolygon(im, poly, 3, curve_col[i]);
9571 }
9572 }
9573
9574 for (j = 0; j < (int) n_point[i]; j++) {
9575 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9576 //xrange = scale/3600.0;
9577 //xs = (int) (((x[i][j]-now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9578 //xs = (int) (((x[i][j] - now + scale - toffset) / 3600.0) / xrange * (x2 - x1) + x1 + 0.5);
9579 //xs = (int) (((x[i][j] - starttime) / 3600.0) / xrange * (x2 - x1) + x1 + 0.5);
9580 xs = (int) (((x[i][j] - starttime)/1.0) / (1.0*scale) * (x2 - x1) + x1 + 0.5);
9581
9582 if (logaxis) {
9583 if (y[i][j] <= 0)
9584 ys = y1;
9585 else
9586 ys = (int) (y1 - (log(y[i][j]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
9587 } else {
9588 ys = (int) (y1 - (y[i][j] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
9589 }
9590
9591 if (xs < 0)
9592 xs = 0;
9593 if (xs >= width)
9594 xs = width-1;
9595 if (ys < 0)
9596 ys = 0;
9597 if (ys >= height)
9598 ys = height-1;
9599
9600 if (j > 0)
9601 gdImageLine(im, xold, yold, xs, ys, curve_col[i]);
9602 xold = xs;
9603 yold = ys;
9604 }
9605
9606 if (n_point[i] > 0) {
9607 poly[0].x = xs;
9608 poly[0].y = ys;
9609 poly[1].x = xs + 12;
9610 poly[1].y = ys - 6;
9611 poly[2].x = xs + 12;
9612 poly[2].y = ys + 6;
9613
9614 gdImageFilledPolygon(im, poly, 3, curve_col[i]);
9615 }
9616 }
9617
9618 if (labels) {
9619 for (i = 0; i < (int)hp.vars.size(); i++) {
9620 if (index != -1 && index != i)
9621 continue;
9622
9623 //str[0] = 0;
9624 //status = db_find_key(hDB, hkeypanel, "Label", &hkeydvar);
9625 //if (status == DB_SUCCESS) {
9626 // size = sizeof(str);
9627 // status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
9628 //}
9629
9630 std::string str = hp.vars[i].label.c_str();
9631
9632 if (str.empty()) {
9633 if (hp.enable_factor) {
9634 str = hp.vars[i].tag_name;
9635
9636 if (hp.vars[i].voffset > 0)
9637 str += msprintf(" - %G", hp.vars[i].voffset);
9638 else if (hp.vars[i].voffset < 0)
9639 str += msprintf(" + %G", -hp.vars[i].voffset);
9640
9641 if (hp.vars[i].factor != 1) {
9642 if (hp.vars[i].voffset == 0)
9643 str += msprintf(" * %+G", hp.vars[i].factor);
9644 else {
9645 str = msprintf("(%s) * %+G", str.c_str(), hp.vars[i].factor);
9646 }
9647 }
9648
9649 if (hp.vars[i].offset > 0)
9650 str += msprintf(" + %G", hp.vars[i].offset);
9651 else if (hp.vars[i].offset < 0)
9652 str += msprintf(" - %G", -hp.vars[i].offset);
9653
9654 } else {
9655 str = hp.vars[i].tag_name;
9656 }
9657 }
9658
9659 int k=0;
9660 for (int j=0; j<hsdata->nvars; j++)
9661 if (hsdata->odb_index[j] == i) {
9662 k = j;
9663 break;
9664 }
9665
9666 if (/* DISABLES CODE */ (0)) {
9667 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]);
9668 }
9669
9670 if (hp.show_values) {
9671 char xstr[256];
9672 if (n_point[i] > 0) {
9673 sprintf(xstr," = %g", y[i][n_point[i]-1]);
9674 } else if (hsdata->num_entries[k] > 0) {
9675 sprintf(xstr," = all data is NaN or INF");
9676 } else if (hsdata->have_last_written) {
9677 if (hsdata->last_written[k]) {
9678 char ctimebuf[32];
9679 ctime_r(&hsdata->last_written[k], ctimebuf);
9680 sprintf(xstr," = last data %s", ctimebuf);
9681 // kill trailing '\n'
9682 char*s = strchr(xstr, '\n');
9683 if (s) *s=0;
9684 // clear the unnecessary error status report
9685 if (hsdata->status[k] == HS_UNDEFINED_VAR)
9686 var_status[i][0] = 0;
9687 } else {
9688 sprintf(xstr," = no data ever");
9689 }
9690 } else {
9691 sprintf(xstr," = no data");
9692 }
9693 str += xstr;
9694 }
9695
9696 if (strlen(var_status[i]) > 1) {
9697 str += msprintf(" (%s)", var_status[i]);
9698 }
9699
9700 row = index == -1 ? i : 0;
9701
9703 x1 + 10,
9704 y2 + 10 + row * (gdFontMediumBold->h + 10),
9705 x1 + 10 + str.length() * gdFontMediumBold->w + 10,
9706 y2 + 10 + row * (gdFontMediumBold->h + 10) +
9707 gdFontMediumBold->h + 2 + 2, white);
9708 gdImageRectangle(im, x1 + 10, y2 + 10 + row * (gdFontMediumBold->h + 10),
9709 x1 + 10 + str.length() * gdFontMediumBold->w + 10,
9710 y2 + 10 + row * (gdFontMediumBold->h + 10) +
9711 gdFontMediumBold->h + 2 + 2, curve_col[i]);
9712
9714 x1 + 10 + 5, y2 + 10 + 2 + row * (gdFontMediumBold->h + 10),
9715 (char*)str.c_str(),
9716 curve_col[i]);
9717 }
9718 }
9719
9720 gdImageRectangle(im, x1, y2, x2, y1, fgcol);
9721
9722 error:
9723
9724 /* generate GIF */
9725 gdImageInterlace(im, 1);
9726 gdImageGif(im, &gb);
9727 gdImageDestroy(im);
9728 length = gb.size;
9729
9730 if (buffer == NULL) {
9731 rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
9732 rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
9733
9734 rr->rsprintf("Content-Type: image/gif\r\n");
9735 rr->rsprintf("Content-Length: %d\r\n", length);
9736 rr->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
9737 rr->rsprintf("Expires: Fri, 01-Jan-1983 00:00:00 GMT\r\n\r\n");
9738
9739 rr->rmemcpy(gb.data, length);
9740 } else {
9741 if (length > *buffer_size) {
9742 printf("return buffer too small\n");
9743 return;
9744 }
9745
9746 memcpy(buffer, gb.data, length);
9747 *buffer_size = length;
9748 }
9749}
9750
9751/*------------------------------------------------------------------*/
9752
9753time_t mktime_with_dst(const struct tm* ptms)
9754{
9755 // this silly stuff is required to correctly handle daylight savings time (Summer time/Winter time)
9756 // when we fill "struct tm" from user input, we cannot know if daylight savings time is in effect
9757 // and we do not know how to initialize the value of tms.tm_isdst.
9758 // This can cause the output of mktime() to be off by one hour.
9759 // (Rules for daylight savings time are set by national and local govt and in some locations, changes yearly)
9760 // (There are no locations with 2 hour or half-hour daylight savings that I know of)
9761 // (Yes, "man mktime" talks about using "tms.tm_isdst = -1")
9762 //
9763 // We assume the user is using local time and we convert in two steps:
9764 //
9765 // first we convert "struct tm" to "time_t" using mktime() with unknown tm_isdst
9766 // second we convert "time_t" back to "struct tm" using localtime_r()
9767 // this fills "tm_isdst" with correct value from the system time zone database
9768 // then we reset all the time fields (except for sub-minute fields not affected by daylight savings)
9769 // and call mktime() again, now with the correct value of "tm_isdst".
9770 // K.O. 2013-09-14
9771
9772 struct tm tms = *ptms;
9773 struct tm tms2;
9774 time_t t1 = ss_mktime(&tms);
9775 localtime_r(&t1, &tms2);
9776 tms2.tm_year = ptms->tm_year;
9777 tms2.tm_mon = ptms->tm_mon;
9778 tms2.tm_mday = ptms->tm_mday;
9779 tms2.tm_hour = ptms->tm_hour;
9780 tms2.tm_min = ptms->tm_min;
9781 time_t t2 = ss_mktime(&tms2);
9782 //printf("t1 %.0f, t2 %.0f, diff %d\n", (double)t1, (double)t2, (int)(t1-t2));
9783 return t2;
9784}
9785
9786/*------------------------------------------------------------------*/
9787
9788static std::string add_param_to_url(const char* name, const char* value)
9789{
9790 std::string s;
9791 s += name; // FIXME: should be URI-encoded
9792 s += "=";
9793 s += value; // FIXME: should be URI-encoded
9794 return s;
9795}
9796
9797/*------------------------------------------------------------------*/
9798
9800{
9801 int i;
9802 HNDLE hDB;
9803
9804 if (p->getparam("m1") && *p->getparam("m1")) {
9805 struct tm tms;
9806 memset(&tms, 0, sizeof(struct tm));
9807
9808 tms.tm_year = atoi(p->getparam("y1")) % 100;
9809
9810 std::string m1 = p->getparam("m1");
9811 for (i = 0; i < 12; i++)
9812 if (equal_ustring(m1.c_str(), mname[i]))
9813 break;
9814 if (i == 12)
9815 i = 0;
9816
9817 tms.tm_mon = i;
9818 tms.tm_mday = atoi(p->getparam("d1"));
9819 tms.tm_hour = atoi(p->getparam("h1"));
9820
9821 if (tms.tm_year < 90)
9822 tms.tm_year += 100;
9823
9824 time_t ltime_start = mktime_with_dst(&tms);
9825
9826 memset(&tms, 0, sizeof(struct tm));
9827 tms.tm_year = atoi(p->getparam("y2")) % 100;
9828
9829 std::string m2 = p->getparam("m2");
9830 for (i = 0; i < 12; i++)
9831 if (equal_ustring(m2.c_str(), mname[i]))
9832 break;
9833 if (i == 12)
9834 i = 0;
9835
9836 tms.tm_mon = i;
9837 tms.tm_mday = atoi(p->getparam("d2"));
9838 tms.tm_hour = atoi(p->getparam("h2"));
9839
9840 if (tms.tm_year < 90)
9841 tms.tm_year += 100;
9842
9843 time_t ltime_end = mktime_with_dst(&tms);
9844
9845 if (ltime_end == ltime_start)
9846 ltime_end += 3600 * 24;
9847
9848 std::string redir;
9849 redir += "?cmd=oldhistory&";
9850 redir += add_param_to_url("group", p->getparam("group"));
9851 redir += "&";
9852 redir += add_param_to_url("panel", p->getparam("panel"));
9853 redir += "&";
9854 redir += add_param_to_url("scale", toString((int)(ltime_end - ltime_start)).c_str());
9855 redir += "&";
9856 redir += add_param_to_url("time", time_to_string(ltime_end).c_str());
9857 if (p->isparam("hindex")) {
9858 redir += "&";
9859 redir += add_param_to_url("index", p->getparam("hindex"));
9860 }
9861 redirect(r, redir.c_str());
9862 return;
9863 }
9864
9866 show_header(r, "History", "GET", "", 0);
9867
9868 /* set the times */
9869
9870 time_t now = time(NULL);
9871
9872 time_t starttime = now - 3600 * 24;
9873 time_t endtime = now;
9874 bool full_day = true;
9875
9876 if (p->isparam("htime")) {
9877 endtime = string_to_time(p->getparam("htime"));
9878
9879 if (p->isparam("hscale")) {
9880 starttime = endtime - atoi(p->getparam("hscale"));
9881 full_day = false;
9882 } else {
9883 starttime = endtime - 3600 * 24;
9884 full_day = false;
9885 }
9886 }
9887
9888 /* menu buttons */
9889 r->rsprintf("<tr><td colspan=2>\n");
9890 r->rsprintf("<input type=hidden name=cmd value=OldHistory>\n");
9891 r->rsprintf("<input type=submit name=hcmd value=Query>\n");
9892 r->rsprintf("<input type=submit name=hcmd value=Cancel>\n");
9893 if (p->isparam("group"))
9894 r->rsprintf("<input type=hidden name=group value=\"%s\">\n", p->getparam("group"));
9895 if (p->isparam("panel"))
9896 r->rsprintf("<input type=hidden name=panel value=\"%s\">\n", p->getparam("panel"));
9897 if (p->isparam("htime"))
9898 r->rsprintf("<input type=hidden name=htime value=\"%s\">\n", p->getparam("htime"));
9899 if (p->isparam("hscale"))
9900 r->rsprintf("<input type=hidden name=hscale value=\"%s\">\n", p->getparam("hscale"));
9901 if (p->isparam("hindex"))
9902 r->rsprintf("<input type=hidden name=hindex value=\"%s\">\n", p->getparam("hindex"));
9903 r->rsprintf("</tr>\n\n");
9904 r->rsprintf("</table>"); //end header
9905
9906 r->rsprintf("<table class=\"dialogTable\">"); //main table
9907
9908 struct tm tms;
9909 localtime_r(&starttime, &tms);
9910 tms.tm_year += 1900;
9911
9912 r->rsprintf("<tr><td nowrap>Start date:</td>");
9913
9914 r->rsprintf("<td>Month: <select name=\"m1\">\n");
9915 r->rsprintf("<option value=\"\">\n");
9916 for (i = 0; i < 12; i++)
9917 if (i == tms.tm_mon)
9918 r->rsprintf("<option selected value=\"%s\">%s\n", mname[i], mname[i]);
9919 else
9920 r->rsprintf("<option value=\"%s\">%s\n", mname[i], mname[i]);
9921 r->rsprintf("</select>\n");
9922
9923 r->rsprintf("&nbsp;Day: <select name=\"d1\">");
9924 r->rsprintf("<option selected value=\"\">\n");
9925 for (i = 0; i < 31; i++)
9926 if (i + 1 == tms.tm_mday)
9927 r->rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
9928 else
9929 r->rsprintf("<option value=%d>%d\n", i + 1, i + 1);
9930 r->rsprintf("</select>\n");
9931
9932 int start_hour = tms.tm_hour;
9933 if (full_day)
9934 start_hour = 0;
9935
9936 r->rsprintf("&nbsp;Hour: <input type=\"text\" size=5 maxlength=5 name=\"h1\" value=\"%d\">", start_hour);
9937
9938 r->rsprintf("&nbsp;Year: <input type=\"text\" size=5 maxlength=5 name=\"y1\" value=\"%d\">", tms.tm_year);
9939 r->rsprintf("</td></tr>\n");
9940
9941 r->rsprintf("<tr><td nowrap>End date:</td>");
9942
9943 localtime_r(&endtime, &tms);
9944 tms.tm_year += 1900;
9945
9946 r->rsprintf("<td>Month: <select name=\"m2\">\n");
9947 r->rsprintf("<option value=\"\">\n");
9948 for (i = 0; i < 12; i++)
9949 if (i == tms.tm_mon)
9950 r->rsprintf("<option selected value=\"%s\">%s\n", mname[i], mname[i]);
9951 else
9952 r->rsprintf("<option value=\"%s\">%s\n", mname[i], mname[i]);
9953 r->rsprintf("</select>\n");
9954
9955 r->rsprintf("&nbsp;Day: <select name=\"d2\">");
9956 r->rsprintf("<option selected value=\"\">\n");
9957 for (i = 0; i < 31; i++)
9958 if (i + 1 == tms.tm_mday)
9959 r->rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
9960 else
9961 r->rsprintf("<option value=%d>%d\n", i + 1, i + 1);
9962 r->rsprintf("</select>\n");
9963
9964 int end_hour = tms.tm_hour;
9965 if (full_day)
9966 end_hour = 24;
9967
9968 r->rsprintf("&nbsp;Hour: <input type=\"text\" size=5 maxlength=5 name=\"h2\" value=\"%d\">", end_hour);
9969
9970 r->rsprintf("&nbsp;Year: <input type=\"text\" size=5 maxlength=5 name=\"y2\" value=\"%d\">", tms.tm_year);
9971 r->rsprintf("</td></tr>\n");
9972
9973 r->rsprintf("</table>\n");
9974 r->rsprintf("</div>\n"); // closing for <div id="mmain">
9975 r->rsprintf("</form>\n");
9976 r->rsprintf("</body></html>\r\n");
9977}
9978
9979/*------------------------------------------------------------------*/
9980/* history plot code starts here */
9981/*------------------------------------------------------------------*/
9982
9983static int cmp_names(const void *a, const void *b)
9984{
9985 int i;
9986 const char*sa = (const char*)a;
9987 const char*sb = (const char*)b;
9988
9989 int debug = 0;
9990
9991 // Cannot use strcmp() because it does not know how to compare numerical values, e.g.
9992 // it thinks "111" is smaller than "9"
9993 //return strcmp(sa, sb);
9994
9995 if (debug)
9996 printf("compare [%s] and [%s]\n", sa, sb);
9997
9998 for (i=0; ; i++) {
9999 if (sa[i]==0 && sb[i]==0)
10000 return 0; // both strings have the same length and the same characters
10001
10002 //printf("index %d, char [%c] [%c], isdigit %d %d\n", i, sa[i], sb[i], isdigit(sa[i]), isdigit(sb[i]));
10003
10004 if (isdigit(sa[i]) && isdigit(sb[i])) {
10005 int va = atoi(sa+i);
10006 int vb = atoi(sb+i);
10007
10008 if (debug)
10009 printf("index %d, values %d %d\n", i, va, vb);
10010
10011 if (va < vb)
10012 return -1;
10013 else if (va > vb)
10014 return 1;
10015
10016 // values are equal, skip the the end of the digits, compare any trailing text
10017 continue;
10018 }
10019
10020 if (sa[i]==sb[i]) {
10021 continue;
10022 }
10023
10024 if (debug)
10025 printf("index %d, char [%c] [%c]\n", i, sa[i], sb[i]);
10026
10027 if (sa[i] == 0) // string sa is shorter
10028 return -1;
10029 else if (sb[i] == 0) // string sb is shorter
10030 return 1;
10031
10032 if (sa[i]<sb[i])
10033 return -1;
10034 else
10035 return 1;
10036 }
10037
10038 // NOT REACHED
10039}
10040
10041const bool cmp_events(const std::string& a, const std::string& b)
10042{
10043 return cmp_names(a.c_str(), b.c_str()) < 0;
10044}
10045
10046const bool cmp_events1(const std::string& a, const std::string& b)
10047{
10048 return a < b;
10049}
10050
10051const bool cmp_tags(const TAG& a, const TAG& b)
10052{
10053 return cmp_names(a.name, b.name) < 0;
10054}
10055
10056#if 0
10057static int cmp_tags(const void *a, const void *b)
10058{
10059 const TAG*sa = (const TAG*)a;
10060 const TAG*sb = (const TAG*)b;
10061 return cmp_names(sa->name, sb->name);
10062}
10063
10064static void sort_tags(int ntags, TAG* tags)
10065{
10066 qsort(tags, ntags, sizeof(TAG), cmp_tags);
10067}
10068#endif
10069
10070int xdb_get_data_index(HNDLE hDB, const char* str, void *value, int size, int index, int tid)
10071{
10072 HNDLE hKey;
10073 int status = db_find_key(hDB, 0, str, &hKey);
10074 if (status != DB_SUCCESS)
10075 return status;
10076
10077 KEY key;
10078 db_get_key(hDB, hKey, &key);
10079 if (index >= key.num_values)
10080 return DB_OUT_OF_RANGE;
10081
10082 status = db_get_data_index(hDB, hKey, value, &size, index, tid);
10083 return status;
10084}
10085
10086static int xdb_find_key(HNDLE hDB, HNDLE dir, const char* str, HNDLE* hKey, int tid, int size)
10087{
10088 int status = db_find_key(hDB, dir, str, hKey);
10089 if (status == DB_SUCCESS)
10090 return status;
10091
10092 db_create_key(hDB, dir, str, tid);
10093 status = db_find_key(hDB, dir, str, hKey);
10094 if (status != DB_SUCCESS || !*hKey) {
10095 cm_msg(MERROR, "xdb_find_key", "Invalid ODB path \"%s\"", str);
10096 str = "bad_xdb_find_key";
10097 db_create_key(hDB, dir, str, tid);
10098 db_find_key(hDB, dir, str, hKey);
10099 }
10100 assert(*hKey);
10101
10102 if (tid == TID_STRING) {
10103 db_set_data_index(hDB, *hKey, "", size, 0, TID_STRING);
10104 }
10105
10106 return status;
10107}
10108
10109static bool cmp_vars(const HistVar &a, const HistVar &b)
10110{
10111 return a.order < b.order;
10112}
10113
10114static void PrintHistPlot(const HistPlot& hp)
10115{
10116 printf("hist plot: %d variables\n", (int)hp.vars.size());
10117 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);
10118
10119 for (size_t i=0; i<hp.vars.size(); i++) {
10120 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);
10121 }
10122}
10123
10124static std::string NextHistPlotColour(const HistPlot& hp)
10125{
10126 const char* const colour[] =
10127 {
10128 "#00AAFF", "#FF9000", "#FF00A0", "#00C030",
10129 "#A0C0D0", "#D0A060", "#C04010", "#807060",
10130 "#F0C000", "#2090A0", "#D040D0", "#90B000",
10131 "#B0B040", "#B0B0FF", "#FFA0A0", "#A0FFA0",
10132 NULL };
10133
10134 for (int i=0; colour[i]; i++) {
10135 bool in_use = false;
10136
10137 for (size_t j=0; j<hp.vars.size(); j++)
10138 if (hp.vars[j].colour == colour[i]) {
10139 in_use = true;
10140 break;
10141 }
10142
10143 if (!in_use)
10144 return colour[i];
10145 }
10146
10147 return "#808080";
10148}
10149
10150static int NextHistPlotOrder(const HistPlot& hp)
10151{
10152 int order = 0;
10153 for (size_t i=0; i<hp.vars.size(); i++)
10154 if (hp.vars[i].order > order)
10155 order = hp.vars[i].order;
10156 return order + 10;
10157}
10158
10159static void SplitEventAndTagNames(std::string var_name, std::string& event_name, std::string& tag_name) {
10160 event_name = "";
10161 tag_name = "";
10162
10163 std::vector<size_t> colons;
10164
10165 for (size_t i = 0; i < var_name.size(); i++) {
10166 if (var_name[i] == ':') {
10167 colons.push_back(i);
10168 }
10169 }
10170
10171 if (colons.size() == 0) {
10172 // No colons - leave the tag name empty
10173 event_name = var_name;
10174 } else {
10175 size_t split_pos;
10176 size_t slash_pos = var_name.find("/");
10177 bool uses_per_variable_naming = (slash_pos != std::string::npos);
10178
10179 if (uses_per_variable_naming && colons.size() % 2 == 1) {
10180 size_t middle_colon_pos = colons[colons.size() / 2];
10181 std::string slash_to_mid = var_name.substr(slash_pos + 1, middle_colon_pos - slash_pos - 1);
10182 std::string mid_to_end = var_name.substr(middle_colon_pos + 1);
10183
10184 if (slash_to_mid == mid_to_end) {
10185 // Special case - we have a string of the form Beamlime/GS2:FC1:GS2:FC1.
10186 // Logger has already warned people that having colons in the equipment/event
10187 // names is a bad idea, so we only need to worry about them in the tag name.
10188 split_pos = middle_colon_pos;
10189 } else {
10190 // We have a string of the form Beamlime/Demand:GS2:FC1. Split at the first colon.
10191 split_pos = colons[0];
10192 }
10193 } else {
10194 // Normal case - split at the fist colon.
10195 split_pos = colons[0];
10196 }
10197
10198 event_name = var_name.substr(0, split_pos);
10199 tag_name = var_name.substr(split_pos + 1);
10200 }
10201}
10202
10203static void LoadHistPlotFromOdb(MVOdb* odb, HistPlot* hp, const char* group, const char* panel)
10204{
10205 std::string path = "History/Display/";
10206 path += group;
10207 path += "/";
10208 path += panel;
10209
10210 MVOdb* o = odb->Chdir(path.c_str());
10211 if (!o) {
10212 return;
10213 }
10214
10215 o->RS("Timescale", &hp->timescale);
10216 o->RD("Minimum", &hp->minimum);
10217 o->RD("Maximum", &hp->maximum);
10218 o->RB("Zero ylow", &hp->zero_ylow);
10219 o->RB("Log axis", &hp->log_axis);
10220 o->RB("Zero ylow", &hp->zero_ylow);
10221 o->RB("Show run markers", &hp->show_run_markers);
10222 o->RB("Show values", &hp->show_values);
10223 o->RB("Show fill", &hp->show_fill);
10224 o->RB("Show factor", &hp->show_factor);
10225 //o->RB("Enable factor and offset", &hp->enable_factor);
10226
10227 std::vector<std::string> hist_vars;
10228 std::vector<std::string> hist_formula;
10229 std::vector<std::string> hist_colour;
10230 std::vector<std::string> hist_label;
10231 std::vector<bool> hist_show_raw_value;
10232 std::vector<double> hist_factor;
10233 std::vector<double> hist_offset;
10234 std::vector<double> hist_voffset;
10235
10236 o->RSA("Variables", &hist_vars);
10237 o->RSA("Formula", &hist_formula);
10238 o->RSA("Colour", &hist_colour);
10239 o->RSA("Label", &hist_label);
10240 o->RBA("Show raw value", &hist_show_raw_value);
10241 o->RDA("Factor", &hist_factor);
10242 o->RDA("Offset", &hist_offset);
10243 o->RDA("VOffset", &hist_voffset);
10244
10245 // fix broken plots with "factor" all zero. for reasons
10246 // unknown the new history code has corrupted many
10247 // history plot definitions like this. K.O.
10248 {
10249 bool all_zero = true;
10250 for (size_t i=0; i<hist_factor.size(); i++) {
10251 if (hist_factor[i] != 0)
10252 all_zero = false;
10253 }
10254 if (all_zero) {
10255 for (size_t i=0; i<hist_factor.size(); i++) {
10256 hist_factor[i] = 1.0;
10257 }
10258 }
10259 }
10260
10261 size_t num = std::max(hist_vars.size(), hist_formula.size());
10262 num = std::max(num, hist_colour.size());
10263 num = std::max(num, hist_label.size());
10264 num = std::max(num, hist_show_raw_value.size());
10265 num = std::max(num, hist_factor.size());
10266 num = std::max(num, hist_offset.size());
10267 num = std::max(num, hist_voffset.size());
10268
10269 hist_vars.resize(num);
10270 hist_formula.resize(num);
10271 hist_colour.resize(num);
10272 hist_label.resize(num);
10273 hist_show_raw_value.resize(num);
10274 hist_factor.resize(num, 1.0);
10275 hist_offset.resize(num, 0.0);
10276 hist_voffset.resize(num, 0.0);
10277
10278 for (size_t i=0; i<num; i++) {
10279 HistVar v;
10280
10281 SplitEventAndTagNames(hist_vars[i], v.event_name, v.tag_name);
10282
10283 v.formula = hist_formula[i];
10284 v.colour = hist_colour[i];
10285 v.label = hist_label[i];
10286 v.show_raw_value = hist_show_raw_value[i];
10287 v.factor = hist_factor[i];
10288 v.offset = hist_offset[i];
10289 v.voffset = hist_voffset[i];
10290 v.order = NextHistPlotOrder(*hp);
10291
10292 // one-time migration of factor and offset to formula
10293 if (hp->enable_factor && v.formula.empty()) {
10294 if (v.factor!=1 || v.offset!=0 || v.voffset!=0) {
10295 v.formula = msprintf("%g%+g*(x%+g)", v.offset, v.factor, -v.voffset);
10296 }
10297 }
10298
10299 hp->vars.push_back(v);
10300 }
10301
10302// printf("Load from ODB %s: ", path.c_str());
10303// PrintHistPlot(*hp);
10304
10305 delete o;
10306}
10307
10309{
10310 hp->timescale = p->getparam("timescale");
10311 hp->minimum = strtod(p->getparam("minimum"), NULL);
10312 hp->maximum = strtod(p->getparam("maximum"), NULL);
10313 hp->zero_ylow = *p->getparam("zero_ylow");
10314 hp->log_axis = *p->getparam("log_axis");
10315 hp->show_run_markers = *p->getparam("run_markers");
10316 hp->show_values = *p->getparam("show_values");
10317 hp->show_fill = *p->getparam("show_fill");
10318 hp->show_factor = *p->getparam("show_factor");
10319 //hp->enable_factor = *p->getparam("enable_factor");
10320
10321 for (int index=0; ; index++) {
10322 char str[256];
10323 sprintf(str, "event%d", index);
10324
10325 //printf("param event %d: [%s] [%s] [%d]\n", index, str, p->getparam(str), *p->getparam(str));
10326
10327 if (!p->isparam(str))
10328 break;
10329
10330 if (*p->getparam(str) == '/') // "/empty"
10331 continue;
10332
10333 HistVar v;
10334
10335 v.event_name = p->xgetparam(str);
10336
10337 sprintf(str, "var%d", index);
10338 v.tag_name = p->xgetparam(str);
10339
10340 sprintf(str, "form%d", index);
10341 v.formula = p->xgetparam(str);
10342
10343 sprintf(str, "col%d", index);
10344 v.colour = p->xgetparam(str);
10345
10346 sprintf(str, "lab%d", index);
10347 v.label = p->xgetparam(str);
10348
10349 sprintf(str, "raw%d", index);
10350 v.show_raw_value = atoi(p->xgetparam(str).c_str());
10351
10352 sprintf(str, "factor%d", index);
10353 if (p->isparam(str)) {
10354 v.factor = atof(p->xgetparam(str).c_str());
10355 } else {
10356 v.factor = 1.0;
10357 }
10358
10359 sprintf(str, "offset%d", index);
10360 v.offset = atof(p->xgetparam(str).c_str());
10361
10362 sprintf(str, "voffset%d", index);
10363 v.voffset = atof(p->xgetparam(str).c_str());
10364
10365 sprintf(str, "ord%d", index);
10366 if (p->isparam(str)) {
10367 v.order = atoi(p->xgetparam(str).c_str());
10368 } else {
10369 v.order = NextHistPlotOrder(*hp);
10370 }
10371
10372 hp->vars.push_back(v);
10373 }
10374
10375 /* correctly number newly added variables */
10376 for (size_t index=0; index<hp->vars.size(); index++) {
10377 if (hp->vars[index].order < 0)
10378 hp->vars[index].order = NextHistPlotOrder(*hp);
10379 }
10380
10381// printf("Load from param:\n");
10382// PrintHistPlot(*hp);
10383}
10384
10386{
10387 int seln = atoi(p->getparam("seln"));
10388 for (int i=0; i<seln; i++) {
10389 char str[256];
10390 sprintf(str, "sel%d", i);
10391
10392 std::string par = p->getparam(str);
10393 if (par.length() < 1)
10394 continue;
10395
10396 std::string event_name, tag_name;
10397 SplitEventAndTagNames(par, event_name, tag_name);
10398
10399 if (tag_name == "")
10400 continue;
10401
10402 HistVar v;
10403
10404 v.event_name = event_name;
10405 v.tag_name = tag_name;
10406 v.colour = NextHistPlotColour(hp);
10407 v.order = NextHistPlotOrder(hp);
10408
10409 hp.vars.push_back(v);
10410 }
10411}
10412
10413static void SaveHistPlotToOdb(MVOdb* odb, const HistPlot& hp, const char* group, const char* panel)
10414{
10415 if (strlen(group) < 1) {
10416 cm_msg(MERROR, "SaveHistPlotToOdb", "Error: Cannot write history plot to ODB, group \"%s\", panel \"%s\", invalid group name", group, panel);
10417 return;
10418 }
10419
10420 if (strlen(panel) < 1) {
10421 cm_msg(MERROR, "SaveHistPlotToOdb", "Error: Cannot write history plot to ODB, group \"%s\", panel \"%s\", invalid panel name", group, panel);
10422 return;
10423 }
10424
10425 std::string path = "History/Display/";
10426 path += group;
10427 path += "/";
10428 path += panel;
10429
10430// printf("Save to ODB %s: ", path.c_str());
10431// PrintHistPlot(hp);
10432
10433 MVOdb* o = odb->Chdir(path.c_str(), true);
10434
10435 o->WS("Timescale", hp.timescale.c_str());
10436 o->WD("Minimum", hp.minimum);
10437 o->WD("Maximum", hp.maximum);
10438 o->WB("Zero ylow", hp.zero_ylow);
10439 o->WB("Log axis", hp.log_axis);
10440 o->WB("Show run markers", hp.show_run_markers);
10441 o->WB("Show values", hp.show_values);
10442 o->WB("Show fill", hp.show_fill);
10443 o->WB("Show factor and offset", hp.show_factor);
10444 //o->WB("Enable factor and offset", hp.enable_factor);
10445
10446 std::vector<std::string> hist_vars;
10447 std::vector<std::string> hist_formula;
10448 std::vector<std::string> hist_colour;
10449 std::vector<std::string> hist_label;
10450 std::vector<bool> hist_show_raw_value;
10451 std::vector<double> hist_factor;
10452 std::vector<double> hist_offset;
10453 std::vector<double> hist_voffset;
10454
10455 for (size_t i=0; i<hp.vars.size(); i++) {
10456 hist_vars.push_back(hp.vars[i].event_name + ":" + hp.vars[i].tag_name);
10457 hist_formula.push_back(hp.vars[i].formula);
10458 hist_colour.push_back(hp.vars[i].colour);
10459 hist_label.push_back(hp.vars[i].label);
10460 hist_show_raw_value.push_back(hp.vars[i].show_raw_value);
10461 hist_factor.push_back(hp.vars[i].factor);
10462 hist_offset.push_back(hp.vars[i].offset);
10463 hist_voffset.push_back(hp.vars[i].voffset);
10464 }
10465
10466 if (hp.vars.size() > 0) {
10467 o->WSA("Variables", hist_vars, 64);
10468 o->WSA("Formula", hist_formula, 64);
10469 o->WSA("Colour", hist_colour, NAME_LENGTH);
10470 o->WSA("Label", hist_label, NAME_LENGTH);
10471 o->WBA("Show raw value", hist_show_raw_value);
10472 o->WDA("Factor", hist_factor);
10473 o->WDA("Offset", hist_offset);
10474 o->WDA("VOffset", hist_voffset);
10475 } else {
10476 o->Delete("Variables");
10477 o->Delete("Formula");
10478 o->Delete("Colour");
10479 o->Delete("Label");
10480 o->Delete("Show raw value");
10481 o->Delete("Factor");
10482 o->Delete("Offset");
10483 o->Delete("VOffset");
10484 }
10485
10486 delete o;
10487}
10488
10490{
10491 /* delete variables according to "hist_order" */
10492
10493 while (1) {
10494 bool something_deleted = false;
10495 for (unsigned i=0; i<hp.vars.size(); i++) {
10496 if (hp.vars[i].order <= 0) {
10497 hp.vars.erase(hp.vars.begin() + i);
10498 something_deleted = true;
10499 }
10500 }
10501 if (!something_deleted)
10502 break;
10503 }
10504}
10505
10507{
10508 /* sort variables according to "hist_order" */
10509
10510 bool need_sort = false;
10511 for (size_t i=1; i<hp.vars.size(); i++) {
10512 if (hp.vars[i-1].order >= hp.vars[i].order) {
10513 need_sort = true;
10514 }
10515 }
10516
10517 if (need_sort) {
10518 /* sort variables by order */
10519 std::sort(hp.vars.begin(), hp.vars.end(), cmp_vars);
10520
10521 /* renumber the variables according to the new sorted order */
10522 for (size_t index=0; index<hp.vars.size(); index++)
10523 hp.vars[index].order = (index+1)*10;
10524 }
10525}
10526
10527void show_hist_config_page(MVOdb* odb, Param* p, Return* r, const char *hgroup, const char *hpanel)
10528{
10529 int status;
10530 int max_display_events = 20;
10531 int max_display_tags = 200;
10532 char str[256], hcmd[256];
10533
10534 odb->RI("History/MaxDisplayEvents", &max_display_events, true);
10535 odb->RI("History/MaxDisplayTags", &max_display_tags, true);
10536
10537 mstrlcpy(hcmd, p->getparam("hcmd"), sizeof(hcmd));
10538
10539 if (equal_ustring(hcmd, "Clear history cache")) {
10540 //printf("clear history cache!\n");
10541 strcpy(hcmd, "Refresh");
10543 if (mh)
10544 mh->hs_clear_cache();
10545 }
10546
10547 //printf("cmd [%s]\n", cmd);
10548 //printf("cmdx [%s]\n", p->getparam("cmdx"));
10549
10550 HistPlot hp;
10551
10552 if (equal_ustring(hcmd, "refresh") || equal_ustring(hcmd, "save")) {
10553 LoadHistPlotFromParam(&hp, p);
10555 } else {
10556 LoadHistPlotFromOdb(odb, &hp, hgroup, hpanel);
10557 }
10558
10559 SortHistPlotVars(hp);
10560
10561 if (strlen(p->getparam("seln")) > 0)
10563
10564 //hp->Print();
10565
10566 if (hcmd[0] && equal_ustring(hcmd, "save")) {
10567 SaveHistPlotToOdb(odb, hp, hgroup, hpanel);
10568
10569 if (p->getparam("redir") && *p->getparam("redir"))
10570 redirect(r, p->getparam("redir"));
10571 else {
10572 sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
10573 redirect(r, str);
10574 }
10575 return;
10576 }
10577
10578 show_header(r, "History Config", "GET", "", 0);
10579 r->rsprintf("</table>"); //close header table
10580
10581 r->rsprintf("<table class=\"mtable\">"); //open main table
10582
10583 r->rsprintf("<tr><th colspan=11 class=\"subStatusTitle\">History Panel \"%s\" / \"%s\"</th></tr>\n", hgroup, hpanel);
10584
10585 /* menu buttons */
10586 r->rsprintf("<tr><td colspan=11>\n");
10587
10588 r->rsprintf("<input type=button value=Refresh ");
10589 r->rsprintf("onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">\n");
10590
10591 r->rsprintf("<input type=button value=Save ");
10592 r->rsprintf("onclick=\"document.form1.hcmd.value='Save';document.form1.submit()\">\n");
10593
10594 {
10595 r->rsprintf("<input type=button value=Cancel ");
10596 std::string url = "?cmd=oldhistory&group=";
10597 url += hgroup;
10598 url += "&panel=";
10599 url += hpanel;
10600 url += "&hcmd=Cancel";
10601 if (p->getparam("redir")) {
10602 url += "&redir=";
10603 url += urlEncode(p->getparam("redir"));
10604 }
10605 r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
10606 }
10607 {
10608 r->rsprintf("<input type=button value=\"Edit in ODB\"");
10609 std::string url = "?cmd=odb&odb_path=";
10610 url += "/History/Display/";
10611 url += urlEncode(hgroup);
10612 url += "/";
10613 url += urlEncode(hpanel);
10614 r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
10615 }
10616 {
10617 r->rsprintf("<input type=button value=\"Edit in new editor\"");
10618 std::string url = "?cmd=hs_edit";
10619 url += "&group=";
10620 url += urlEncode(hgroup);
10621 url += "&panel=";
10622 url += urlEncode(hpanel);
10623 if (p->getparam("redir")) {
10624 url += "&redir=";
10625 url += urlEncode(p->getparam("redir"));
10626 }
10627 r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
10628 }
10629 r->rsprintf("<input type=button value=\"Clear history cache\"");
10630 r->rsprintf("onclick=\"document.form1.hcmd.value='Clear history cache';document.form1.submit()\">\n");
10631 r->rsprintf("<input type=button value=\"Delete panel\"");
10632 r->rsprintf("onclick=\"window.location.search='?cmd=oldhistory&group=%s&panel=%s&hcmd=Delete%%20panel'\">\n", hgroup, hpanel);
10633 r->rsprintf("</td></tr>\n");
10634
10635 r->rsprintf("<tr><td colspan=11>\n");
10636
10637 /* sort_vars */
10638 int sort_vars = *p->getparam("sort_vars");
10639 r->rsprintf("<input type=checkbox %s name=sort_vars value=1 onclick=\"this.form.submit();\">Sort variable names", sort_vars?"checked":"");
10640
10641 /* old_vars */
10642 int old_vars = *p->getparam("old_vars");
10643 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":"");
10644
10645 if (hp.show_factor)
10646 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_factor value=1 onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">");
10647 else
10648 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_factor value=1 onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">");
10649 r->rsprintf("Show&nbsp;factor&nbsp;and&nbsp;offset\n");
10650
10651 /* hidden command for refresh */
10652 r->rsprintf("<input type=hidden name=cmd value=Oldhistory>\n");
10653 r->rsprintf("<input type=hidden name=hcmd value=Refresh>\n");
10654 r->rsprintf("<input type=hidden name=panel value=\"%s\">\n", hpanel);
10655 r->rsprintf("<input type=hidden name=group value=\"%s\">\n", hgroup);
10656
10657 if (p->getparam("redir") && *p->getparam("redir"))
10658 r->rsprintf("<input type=hidden name=redir value=\"%s\">\n", p->getparam("redir"));
10659
10660 r->rsprintf("</td></tr>\n");
10661
10662 r->rsprintf("<tr><td colspan=4 style='text-align:right'>Time scale (in units 'm', 'h', 'd'):</td>\n");
10663 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());
10664
10665 r->rsprintf("<tr><td colspan=4 style='text-align:right'>Minimum (set to '-inf' for autoscale):</td>\n");
10666 r->rsprintf("<td colspan=3><input type=text size=12 name=minimum value=%f></td><td colspan=4></td></tr>\n", hp.minimum);
10667
10668 r->rsprintf("<tr><td colspan=4 style='text-align:right'>Maximum (set to 'inf' for autoscale):</td>\n");
10669 r->rsprintf("<td colspan=3><input type=text size=12 name=maximum value=%f></td><td colspan=4></td></tr>\n", hp.maximum);
10670
10671 r->rsprintf("<tr><td colspan=11>");
10672
10673 if (hp.zero_ylow)
10674 r->rsprintf("<input type=checkbox checked name=zero_ylow value=1>");
10675 else
10676 r->rsprintf("<input type=checkbox name=zero_ylow value=1>");
10677 r->rsprintf("Zero&nbsp;Y;&nbsp;axis\n");
10678
10679 if (hp.log_axis)
10680 r->rsprintf("<input type=checkbox checked name=log_axis value=1>");
10681 else
10682 r->rsprintf("<input type=checkbox name=log_axis value=1>");
10683 r->rsprintf("Logarithmic&nbsp;Y&nbsp;axis\n");
10684
10685 if (hp.show_run_markers)
10686 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=run_markers value=1>");
10687 else
10688 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=run_markers value=1>");
10689 r->rsprintf("Show&nbsp;run&nbsp;markers\n");
10690
10691 if (hp.show_values)
10692 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_values value=1>");
10693 else
10694 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_values value=1>");
10695 r->rsprintf("Show&nbsp;values&nbsp;of&nbsp;variables\n");
10696
10697 if (hp.show_fill)
10698 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_fill value=1>");
10699 else
10700 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_fill value=1>");
10701 r->rsprintf("Show&nbsp;graph&nbsp;fill\n");
10702
10703 r->rsprintf("</td></tr>\n");
10704
10705 /*---- events and variables ----*/
10706
10707 /* get display event name */
10708
10710 if (mh == NULL) {
10711 r->rsprintf(str, "History is not configured\n");
10712 return;
10713 }
10714
10715 time_t t = time(NULL);
10716
10717 if (old_vars)
10718 t = 0;
10719
10720 std::vector<std::string> events;
10721
10722 if (!old_vars)
10723 hs_read_event_list(&events);
10724
10725 if (events.size() == 0)
10726 mh->hs_get_events(t, &events);
10727
10728#if 0
10729 for (unsigned i=0; i<events.size(); i++)
10730 printf("event %d: \"%s\"\n", i, events[i].c_str());
10731#endif
10732
10733 // has to be sorted or equipment name code below would not work
10734 //std::sort(events.begin(), events.end(), cmp_events);
10735 std::sort(events.begin(), events.end(), cmp_events1);
10736
10737 if (strlen(p->getparam("cmdx")) > 0) {
10738 r->rsprintf("<tr><th colspan=8 class=\"subStatusTitle\">List of available history variables</th></tr>\n");
10739 r->rsprintf("<tr><th colspan=1>Sel<th colspan=1>Equipment<th colspan=1>Event<th colspan=1>Variable</tr>\n");
10740
10741 std::string cmdx = p->xgetparam("cmdx");
10742 std::string xeqname;
10743
10744 int i=0;
10745 for (unsigned e=0; e<events.size(); e++) {
10746 std::string eqname;
10747 eqname = events[e].substr(0, events[e].find("/"));
10748
10749 if (eqname.length() < 1)
10750 eqname = events[e];
10751
10752 bool once = false;
10753 if (eqname != xeqname)
10754 once = true;
10755
10756 std::string qcmd = "Expand " + eqname;
10757
10758 //printf("param [%s] is [%s]\n", qcmd.c_str(), p->getparam(qcmd.c_str()));
10759
10760 bool collapsed = true;
10761
10762 if (cmdx == qcmd)
10763 collapsed = false;
10764
10765 if (strlen(p->getparam(qcmd.c_str())) > 0)
10766 collapsed = false;
10767
10768 if (collapsed) {
10769 if (eqname == xeqname)
10770 continue;
10771
10772 r->rsprintf("<tr align=left>\n");
10773 r->rsprintf("<td></td>\n");
10774 r->rsprintf("<td>%s</td>\n", eqname.c_str());
10775 r->rsprintf("<td><input type=submit name=cmdx value=\"%s\"></td>\n", qcmd.c_str());
10776 r->rsprintf("<td>%s</td>\n", "");
10777 r->rsprintf("</tr>\n");
10778 xeqname = eqname;
10779 continue;
10780 }
10781
10782 if (once)
10783 r->rsprintf("<tr><input type=hidden name=\"%s\" value=%d></tr>\n", qcmd.c_str(), 1);
10784
10785 std::string rcmd = "Expand " + events[e];
10786
10787 //printf("param [%s] is [%s]\n", rcmd.c_str(), p->getparam(rcmd.c_str()));
10788
10789 collapsed = true;
10790
10791 if (cmdx == rcmd)
10792 collapsed = false;
10793
10794 if (strlen(p->getparam(rcmd.c_str())) > 0)
10795 collapsed = false;
10796
10797 if (collapsed) {
10798 r->rsprintf("<tr align=left>\n");
10799 r->rsprintf("<td></td>\n");
10800 r->rsprintf("<td>%s</td>\n", eqname.c_str());
10801 r->rsprintf("<td>%s</td>\n", events[e].c_str());
10802 r->rsprintf("<td><input type=submit name=cmdx value=\"%s\"></td>\n", rcmd.c_str());
10803 r->rsprintf("</tr>\n");
10804 continue;
10805 }
10806
10807 r->rsprintf("<tr><input type=hidden name=\"%s\" value=%d></tr>\n", rcmd.c_str(), 1);
10808
10809 xeqname = eqname;
10810
10811 std::vector<TAG> tags;
10812
10813 status = mh->hs_get_tags(events[e].c_str(), t, &tags);
10814
10815 if (status == HS_SUCCESS && tags.size() > 0) {
10816
10817 if (sort_vars)
10818 std::sort(tags.begin(), tags.end(), cmp_tags);
10819
10820 for (unsigned v=0; v<tags.size(); v++) {
10821
10822 for (unsigned j=0; j<tags[v].n_data; j++) {
10823 char tagname[256];
10824
10825 if (tags[v].n_data == 1)
10826 sprintf(tagname, "%s", tags[v].name);
10827 else
10828 sprintf(tagname, "%s[%d]", tags[v].name, j);
10829
10830 bool checked = false;
10831#if 0
10832 for (int index=0; index<MAX_VARS; index++) {
10833 if (equal_ustring(vars[index].event_name, events[e].c_str()) && equal_ustring(vars[index].var_name, tagname)) {
10834 checked = true;
10835 break;
10836 }
10837 }
10838#endif
10839
10840 r->rsprintf("<tr align=left>\n");
10841 r->rsprintf("<td><input type=checkbox %s name=\"sel%d\" value=\"%s:%s\"></td>\n", checked?"checked":"", i++, events[e].c_str(), tagname);
10842 r->rsprintf("<td>%s</td>\n", eqname.c_str());
10843 r->rsprintf("<td>%s</td>\n", events[e].c_str());
10844 r->rsprintf("<td>%s</td>\n", tagname);
10845 r->rsprintf("</tr>\n");
10846 }
10847 }
10848 }
10849 }
10850
10851 r->rsprintf("<tr>\n");
10852 r->rsprintf("<td></td>\n");
10853 r->rsprintf("<td>\n");
10854 r->rsprintf("<input type=hidden name=seln value=%d>\n", i);
10855 r->rsprintf("<input type=submit value=\"Add Selected\">\n");
10856 r->rsprintf("</td>\n");
10857 r->rsprintf("</tr>\n");
10858 }
10859
10860 r->rsprintf("<tr><td colspan=11 style='text-align:left'>New history: displayed_value = formula(history_value)</td></tr>\n");
10861 r->rsprintf("<tr><td colspan=11 style='text-align:left'>Old history: displayed_value = offset + factor*(history_value - voffset)</td></tr>\n");
10862 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");
10863 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");
10864 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");
10865 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");
10866
10867 r->rsprintf("<tr>\n");
10868 r->rsprintf("<th>Col<th>Event<th>Variable<th>Formula<th>Colour<th>Label<th>Raw<th>Order");
10869 if (hp.show_factor) {
10870 r->rsprintf("<th>Factor<th>Offset<th>VOffset");
10871 }
10872 r->rsprintf("</tr>\n");
10873
10874 //print_vars(vars);
10875
10876 size_t nvars = hp.vars.size();
10877 for (size_t index = 0; index <= nvars; index++) {
10878
10879 r->rsprintf("<tr>");
10880
10881 if (index < nvars) {
10882 if (hp.vars[index].colour.empty())
10883 hp.vars[index].colour = NextHistPlotColour(hp);
10884 r->rsprintf("<td style=\"background-color:%s\">&nbsp;<td>\n", hp.vars[index].colour.c_str());
10885 } else {
10886 r->rsprintf("<td>&nbsp;<td>\n");
10887 }
10888
10889 /* event and variable selection */
10890
10891 r->rsprintf("<select name=\"event%d\" size=1 onChange=\"document.form1.submit()\">\n", (int)index);
10892
10893 /* enumerate events */
10894
10895 /* empty option */
10896 r->rsprintf("<option value=\"/empty\">&lt;empty&gt;\n");
10897
10898 if (index==nvars) { // last "empty" entry
10899 for (unsigned e=0; e<events.size(); e++) {
10900 const char *p = events[e].c_str();
10901 r->rsprintf("<option value=\"%s\">%s\n", p, p);
10902 }
10903 } else if ((int)events.size() > max_display_events) { // too many events
10904 r->rsprintf("<option selected value=\"%s\">%s\n", hp.vars[index].event_name.c_str(), hp.vars[index].event_name.c_str());
10905 r->rsprintf("<option>(%d events omitted)\n", (int)events.size());
10906 } else { // show all events
10907 bool found = false;
10908 for (unsigned e=0; e<events.size(); e++) {
10909 const char *s = "";
10910 const char *p = events[e].c_str();
10911 if (equal_ustring(hp.vars[index].event_name.c_str(), p)) {
10912 s = "selected";
10913 found = true;
10914 }
10915 r->rsprintf("<option %s value=\"%s\">%s\n", s, p, p);
10916 }
10917 if (!found) {
10918 const char *p = hp.vars[index].event_name.c_str();
10919 r->rsprintf("<option selected value=\"%s\">%s\n", p, p);
10920 }
10921 }
10922
10923 r->rsprintf("</select></td>\n");
10924
10925 //if (hp.vars[index].order <= 0)
10926 // hp.vars[index].order = (index+1)*10;
10927
10928 if (index < nvars) {
10929 bool found_tag = false;
10930 std::string selected_tag = hp.vars[index].tag_name;
10931
10932 r->rsprintf("<td><select name=\"var%d\">\n", (int)index);
10933
10934 std::vector<TAG> tags;
10935
10936 status = mh->hs_get_tags(hp.vars[index].event_name.c_str(), t, &tags);
10937
10938 if (status == HS_SUCCESS && tags.size() > 0) {
10939
10940 if (/* DISABLES CODE */ (0)) {
10941 printf("Compare %d\n", cmp_names("AAA", "BBB"));
10942 printf("Compare %d\n", cmp_names("BBB", "AAA"));
10943 printf("Compare %d\n", cmp_names("AAA", "AAA"));
10944 printf("Compare %d\n", cmp_names("A", "AAA"));
10945 printf("Compare %d\n", cmp_names("A111", "A1"));
10946 printf("Compare %d\n", cmp_names("A111", "A2"));
10947 printf("Compare %d\n", cmp_names("A111", "A222"));
10948 printf("Compare %d\n", cmp_names("A111a", "A111b"));
10949 }
10950
10951 if (sort_vars)
10952 std::sort(tags.begin(), tags.end(), cmp_tags);
10953
10954 if (/* DISABLES CODE */ (0)) {
10955 printf("Event [%s] %d tags\n", hp.vars[index].event_name.c_str(), (int)tags.size());
10956
10957 for (unsigned v=0; v<tags.size(); v++) {
10958 printf("tag[%d] [%s]\n", v, tags[v].name);
10959 }
10960 }
10961
10962 unsigned count_tags = 0;
10963 for (unsigned v=0; v<tags.size(); v++)
10964 count_tags += tags[v].n_data;
10965
10966 //printf("output %d option tags\n", count_tags);
10967
10968 if ((int)count_tags < max_display_tags) {
10969 for (unsigned v=0; v<tags.size(); v++) {
10970
10971 for (unsigned j=0; j<tags[v].n_data; j++) {
10972 std::string tagname;
10973
10974 if (tags[v].n_data == 1)
10975 tagname = tags[v].name;
10976 else {
10977 char buf[256];
10978 sprintf(buf, "[%d]", j);
10979 tagname = std::string(tags[v].name) + buf;
10980 }
10981
10982 if (equal_ustring(selected_tag.c_str(), tagname.c_str())) {
10983 r->rsprintf("<option selected value=\"%s\">%s\n", tagname.c_str(), tagname.c_str());
10984 found_tag = true;
10985 }
10986 else
10987 r->rsprintf("<option value=\"%s\">%s\n", tagname.c_str(), tagname.c_str());
10988
10989 //printf("%d [%s] [%s] [%s][%s] %d\n", (int)index, vars[index].event_name, tagname, vars[index].var_name, selected_var, found_var);
10990 }
10991 }
10992 }
10993 }
10994
10995 if (!found_tag)
10996 if (hp.vars[index].tag_name.length() > 0)
10997 r->rsprintf("<option selected value=\"%s\">%s\n", hp.vars[index].tag_name.c_str(), hp.vars[index].tag_name.c_str());
10998
10999 r->rsprintf("</select></td>\n");
11000 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());
11001 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());
11002 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());
11003 if (hp.vars[index].show_raw_value)
11004 r->rsprintf("<td><input type=checkbox checked name=\"raw%d\" value=1></td>", (int)index);
11005 else
11006 r->rsprintf("<td><input type=checkbox name=\"raw%d\" value=1></td>", (int)index);
11007 r->rsprintf("<td><input type=text size=3 maxlength=32 name=\"ord%d\" value=\"%d\"></td>\n", (int)index, hp.vars[index].order);
11008 if (hp.show_factor) {
11009 r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"factor%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].factor);
11010 r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"offset%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].offset);
11011 r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"voffset%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].voffset);
11012 } else {
11013 r->rsprintf("<input type=hidden name=\"factor%d\" value=\"%f\">\n", (int)index, hp.vars[index].factor);
11014 r->rsprintf("<input type=hidden name=\"offset%d\" value=\"%f\">\n", (int)index, hp.vars[index].offset);
11015 r->rsprintf("<input type=hidden name=\"voffset%d\" value=\"%f\">\n", (int)index, hp.vars[index].voffset);
11016 }
11017 } else {
11018 r->rsprintf("<td colspan=2><input type=submit name=cmdx value=\"List all variables\"></td>\n");
11019 }
11020
11021 r->rsprintf("</tr>\n");
11022 }
11023
11024 r->rsprintf("</table>\n");
11025 //r->rsprintf("</form>\n");
11026 r->rsprintf("</div>\n"); // closing for <div id="mmain">
11027 r->rsprintf("</form>\n");
11028 r->rsprintf("</body></html>\r\n");
11029}
11030
11031/*------------------------------------------------------------------*/
11032
11033void export_hist(MVOdb* odb, Return* r, const char *group, const char *panel, time_t endtime, int scale, int index, int labels)
11034{
11035 //HNDLE hDB, hkey, hkeypanel;
11036 //int size;
11037 int status;
11038 //char str[256];
11039
11040 int debug = 0;
11041
11042 ss_tzset(); // required for localtime_r()
11043
11044#if 0
11046
11047 /* check panel name in ODB */
11048 sprintf(str, "/History/Display/%s/%s", group, panel);
11049 db_find_key(hDB, 0, str, &hkeypanel);
11050 if (!hkeypanel) {
11051 sprintf(str, "Cannot find /History/Display/%s/%s in ODB\n", group, panel);
11052 show_error(r, str);
11053 return;
11054 }
11055
11056 /* get runmarker flag */
11057 BOOL runmarker = 1;
11058 size = sizeof(runmarker);
11059 db_get_value(hDB, hkeypanel, "Show run markers", &runmarker, &size, TID_BOOL, TRUE);
11060
11061 if (scale == 0) {
11062 /* get timescale */
11063 std::string ts = "1h";
11064 status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
11065 if (status != DB_SUCCESS) {
11066 /* delete old integer key */
11067 db_delete(hDB, hkeypanel, "Timescale");
11068
11069 ts = "1h";
11070 status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
11071 }
11072
11073 scale = time_to_sec(ts.c_str());
11074 }
11075#endif
11076
11077 time_t now = ss_time();
11078
11079 if (endtime == 0)
11080 endtime = now;
11081
11082 HistoryData hsxxx;
11083 HistoryData* hsdata = &hsxxx;
11084
11085 HistPlot hp;
11086 LoadHistPlotFromOdb(odb, &hp, group, panel);
11087
11088 time_t starttime = endtime - scale;
11089
11090 //printf("start %.0f, end %.0f, scale %.0f\n", (double)starttime, (double)endtime, (double)scale);
11091
11092 status = read_history(hp, /*hDB, group, panel,*/ index, hp.show_run_markers, starttime, endtime, 0, hsdata);
11093 if (status != HS_SUCCESS) {
11094 char str[256];
11095 sprintf(str, "History error, status %d\n", status);
11096 show_error(r, str);
11097 return;
11098 }
11099
11100 if (debug)
11101 hsdata->Print();
11102
11103 int *i_var = (int *)malloc(sizeof(int)*hsdata->nvars);
11104
11105 assert(i_var != NULL);
11106
11107 for (int i = 0; i < hsdata->nvars; i++)
11108 i_var[i] = -1;
11109
11110 time_t t = 0;
11111
11112 /* find first time where all variables are available */
11113 for (int i = 0; i < hsdata->nvars; i++)
11114 if (hsdata->odb_index[i] >= 0)
11115 if (hsdata->num_entries[i] > 0)
11116 if ((t == 0) || (hsdata->t[i][0] > t))
11117 t = hsdata->t[i][0];
11118
11119 if (t == 0 && hsdata->nvars > 1) {
11120 show_error(r, "No history available for choosen period");
11121 free(i_var);
11122 return;
11123 }
11124
11125 int run_index = -1;
11126 int state_index = -1;
11127 int n_run_number = 0;
11128 time_t* t_run_number = NULL;
11129 if (hp.show_run_markers)
11130 for (int i = 0; i < hsdata->nvars; i++) {
11131 if (hsdata->odb_index[i] == -2) {
11132 n_run_number = hsdata->num_entries[i];
11133 t_run_number = hsdata->t[i];
11134 run_index = i;
11135 } else if (hsdata->odb_index[i] == -1) {
11136 state_index = i;
11137 }
11138 }
11139
11140 //printf("runmarker %d, state %d, run %d\n", runmarker, state_index, run_index);
11141
11142 /* header */
11143 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
11144 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
11145 r->rsprintf("Accept-Ranges: bytes\r\n");
11146 r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
11147 r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
11148 r->rsprintf("Content-Type: text/plain\r\n");
11149 r->rsprintf("Content-disposition: attachment; filename=\"export.csv\"\r\n");
11150 r->rsprintf("\r\n");
11151
11152 /* output header line with variable names */
11153 if (hp.show_run_markers && t_run_number)
11154 r->rsprintf("Time, Timestamp, Run, Run State, ");
11155 else
11156 r->rsprintf("Time, Timestamp, ");
11157
11158 for (int i = 0, first = 1; i < hsdata->nvars; i++) {
11159 if (hsdata->odb_index[i] < 0)
11160 continue;
11161 if (hsdata->num_entries[i] <= 0)
11162 continue;
11163 if (!first)
11164 r->rsprintf(", ");
11165 first = 0;
11166 r->rsprintf("%s", hsdata->var_names[i]);
11167 }
11168 r->rsprintf("\n");
11169
11170 int i_run = 0;
11171
11172 do {
11173
11174 if (debug)
11175 printf("hsdata %p, t %d, irun %d\n", hsdata, (int)t, i_run);
11176
11177 /* find run number/state which is valid for t */
11178 if (hp.show_run_markers && t_run_number)
11179 while (i_run < n_run_number-1 && t_run_number[i_run+1] <= t)
11180 i_run++;
11181
11182 //printf("irun %d\n", i_run);
11183
11184 /* find index for all variables which is valid for t */
11185 for (int i = 0; i < hsdata->nvars; i++)
11186 while (hsdata->num_entries[i] > 0 && i_var[i] < hsdata->num_entries[i] - 1 && hsdata->t[i][i_var[i]+1] <= t)
11187 i_var[i]++;
11188
11189 /* finish if last point for all variables reached */
11190 bool done = true;
11191 for (int i = 0 ; i < hsdata->nvars ; i++)
11192 if (hsdata->num_entries[i] > 0 && i_var[i] < hsdata->num_entries[i]) {
11193 done = false;
11194 break;
11195 }
11196
11197 if (debug) {
11198 printf("step to time %d: ", (int)t);
11199 for (int i = 0; i < hsdata->nvars; i++) {
11200 printf(" [%d] %d, ", hsdata->num_entries[i], i_var[i]);
11201 }
11202 printf(" done: %d\n", done);
11203 }
11204
11205 if (done)
11206 break;
11207
11208 struct tm tms;
11209 localtime_r(&t, &tms);
11210
11211 char fmt[256];
11212 //strcpy(fmt, "%c");
11213 strcpy(fmt, "%Y.%m.%d %H:%M:%S");
11214 char str[256];
11215 strftime(str, sizeof(str), fmt, &tms);
11216
11217 if (t_run_number && run_index>=0 && state_index>=0) {
11218 if (t_run_number[i_run] <= t)
11219 r->rsprintf("%s, %d, %.0f, %.0f, ", str, (int)t, hsdata->v[run_index][i_run], hsdata->v[state_index][i_run]);
11220 else
11221 r->rsprintf("%s, %d, N/A, N/A, ", str, (int)t);
11222 } else
11223 r->rsprintf("%s, %d, ", str, (int)t);
11224
11225 if (debug) {
11226 for (int i= 0 ; i < hsdata->nvars ; i++)
11227 printf(" %d (%g)", i_var[i], hsdata->v[i][i_var[i]]);
11228 printf("\n");
11229 }
11230
11231 for (int i=0, first=1 ; i<hsdata->nvars ; i++) {
11232 if (i_var[i] < 0)
11233 continue;
11234 if (hsdata->odb_index[i] < 0)
11235 continue;
11236 if (!first)
11237 r->rsprintf(", ");
11238 first = 0;
11239 //r->rsprintf("(%d %g)", i_var[i], hsdata->v[i][i_var[i]]);
11240 r->rsprintf("%g", hsdata->v[i][i_var[i]]);
11241 }
11242 r->rsprintf("\n");
11243
11244 /* find next t as smallest delta t */
11245 int dt = -1;
11246 for (int i = 0 ; i < hsdata->nvars ; i++)
11247 if (i_var[i]>=0 && hsdata->odb_index[i]>=0 && hsdata->num_entries[i]>0 && i_var[i]<hsdata->num_entries[i]-1) {
11248 int xdt = hsdata->t[i][i_var[i]+1] - t;
11249 if (debug)
11250 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);
11251 if (dt <= 0 || xdt < dt)
11252 dt = xdt;
11253 }
11254
11255 if (debug)
11256 printf("dt %d\n", dt);
11257
11258 if (dt <= 0)
11259 break;
11260
11261 t += dt;
11262
11263 } while (1);
11264
11265 free(i_var);
11266}
11267
11268/*------------------------------------------------------------------*/
11269
11270void show_hist_page(MVOdb* odb, Param* p, Return* r, const char *dec_path, char *buffer, int *buffer_size, int refresh)
11271{
11272 HNDLE hDB, hkey, hikeyp, hkeyp, hkeybutton;
11273 KEY key, ikey;
11274 int i, j, k, scale, index, width, size, status, labels;
11275 char hgroup[256], hpanel[256], hcmd[256];
11276 const char def_button[][NAME_LENGTH] = { "10m", "1h", "3h", "12h", "24h", "3d", "7d" };
11277
11279
11280 hcmd[0] = hgroup[0] = hpanel[0] = 0;
11281
11282 if (p->getparam("group") && *p->getparam("group"))
11283 mstrlcpy(hgroup, p->getparam("group"), sizeof(hgroup));
11284 if (p->getparam("panel") && *p->getparam("panel"))
11285 mstrlcpy(hpanel, p->getparam("panel"), sizeof(hpanel));
11286 if (p->getparam("hcmd") && *p->getparam("hcmd"))
11287 mstrlcpy(hcmd, p->getparam("hcmd"), sizeof(hcmd));
11288
11289 if (equal_ustring(hcmd, "Reset")) {
11290 std::string redir;
11291 //sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
11292 redir += "?cmd=oldhistory&group=";
11293 redir += hgroup;
11294 redir += "&panel=";
11295 redir += hpanel;
11296 redirect(r, redir.c_str());
11297 return;
11298 }
11299
11300 if (equal_ustring(hcmd, "Query")) {
11301 show_query_page(p, r);
11302 return;
11303 }
11304
11305 if (equal_ustring(hcmd, "Cancel")) {
11306 //sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
11307 if (p->getparam("redir") && *p->getparam("redir"))
11308 redirect(r, p->getparam("redir"));
11309 else {
11310 std::string redir;
11311 redir += "?cmd=oldhistory&group=";
11312 redir += hgroup;
11313 redir += "&panel=";
11314 redir += hpanel;
11315 redirect(r, redir.c_str());
11316 }
11317 return;
11318 }
11319
11320 if (equal_ustring(hcmd, "Config") ||
11321 equal_ustring(hcmd, "Save")
11322 || equal_ustring(hcmd, "Clear history cache")
11323 || equal_ustring(hcmd, "Refresh")) {
11324
11325 show_hist_config_page(odb, p, r, hgroup, hpanel);
11326 return;
11327 }
11328
11329 if (equal_ustring(hcmd, "New")) {
11330 show_header(r, "History", "GET", "", 0);
11331
11332 r->rsprintf("<table class=\"dialogTable\">");
11333 r->rsprintf("<tr><th class=\"subStatusTitle\" colspan=2>New History Item</th><tr>");
11334 r->rsprintf("<tr><td align=center colspan=2>\n");
11335 r->rsprintf("Select group: &nbsp;&nbsp;");
11336 r->rsprintf("<select id=\"group\" name=\"group\">\n");
11337
11338 /* list existing groups */
11339 db_find_key(hDB, 0, "/History/Display", &hkey);
11340 i = 0;
11341 if (hkey) {
11342 for (i = 0;; i++) {
11343 db_enum_link(hDB, hkey, i, &hkeyp);
11344
11345 if (!hkeyp)
11346 break;
11347
11348 db_get_key(hDB, hkeyp, &key);
11349 if (equal_ustring(hgroup, key.name))
11350 r->rsprintf("<option selected>%s</option>\n", key.name);
11351 else
11352 r->rsprintf("<option>%s</option>\n", key.name);
11353 }
11354 }
11355 if (!hkey || i == 0)
11356 r->rsprintf("<option>Default</option>\n");
11357 r->rsprintf("</select><p>\n");
11358
11359 r->rsprintf("Or enter new group name: &nbsp;&nbsp;");
11360 r->rsprintf("<input type=text size=15 maxlength=31 id=new_group name=new_group>\n");
11361
11362 r->rsprintf("<tr><td align=center colspan=2>\n");
11363 r->rsprintf("<br>Panel name: &nbsp;&nbsp;");
11364 r->rsprintf("<input type=text size=15 maxlength=31 id=panel name=panel><br><br>\n");
11365 r->rsprintf("</td></tr>\n");
11366
11367 r->rsprintf("<tr><td align=center colspan=2>");
11368 std::string str = "?cmd=oldhistory&hcmd=createnew";
11369 str += "&new_group='+document.getElementById('new_group').value+'";
11370 str += "&group='+document.getElementById('group').value+'";
11371 str += "&panel='+document.getElementById('panel').value+'";
11372 r->rsprintf("<input type=button value=Submit onclick=\"window.location.search='%s'\">\n", str.c_str());
11373 r->rsprintf("</td></tr>\n");
11374
11375 r->rsprintf("</table>\r\n");
11376 r->rsprintf("</div>\n"); // closing for <div id="mmain">
11377 r->rsprintf("</form>\n");
11378 r->rsprintf("</body></html>\r\n");
11379 return;
11380 }
11381
11382 if (equal_ustring(hcmd, "Delete Panel")) {
11383 std::string path;
11384 //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
11385 path += "/History/Display/";
11386 path += hgroup;
11387 path += "/";
11388 path += hpanel;
11389 db_delete(hDB, 0, path.c_str());
11390 redirect(r, "?cmd=oldhistory");
11391 return;
11392 }
11393
11394 if (equal_ustring(hcmd, "createnew")) {
11395
11396 /* strip leading/trailing spaces */
11397 while (hpanel[0] == ' ') {
11398 char str[256];
11399 mstrlcpy(str, hpanel+1, sizeof(str));
11400 mstrlcpy(hpanel, str, sizeof(hpanel));
11401 }
11402 while (strlen(hpanel)> 1 && hpanel[strlen(hpanel)-1] == ' ')
11403 hpanel[strlen(hpanel)-1] = 0;
11404
11405 /* use new group if present */
11406 if (p->isparam("new_group") && *p->getparam("new_group"))
11407 mstrlcpy(hgroup, p->getparam("new_group"), sizeof(hgroup));
11408
11409 /* configure that panel */
11410 show_hist_config_page(odb, p, r, hgroup, hpanel);
11411 return;
11412 }
11413
11414 const char* pscale = p->getparam("scale");
11415 if (pscale == NULL || *pscale == 0)
11416 pscale = p->getparam("hscale");
11417 const char* pwidth = p->getparam("width");
11418 if (pwidth == NULL || *pwidth == 0)
11419 pwidth = p->getparam("hwidth");
11420 const char* pheight = p->getparam("height");
11421 if (pheight == NULL || *pheight == 0)
11422 pheight = p->getparam("hheight");
11423 const char* pindex = p->getparam("index");
11424 if (pindex == NULL || *pindex == 0)
11425 pindex = p->getparam("hindex");
11426
11427 labels = 1;
11428 if (*p->getparam("labels") && atoi(p->getparam("labels")) == 0)
11429 labels = 0;
11430
11431 std::string bgcolor = "FFFFFF";
11432 if (*p->getparam("bgcolor"))
11433 bgcolor = p->xgetparam("bgcolor");
11434
11435 std::string fgcolor = "000000";
11436 if (*p->getparam("fgcolor"))
11437 fgcolor = p->xgetparam("fgcolor");
11438
11439 std::string gridcolor = "A0A0A0";
11440 if (*p->getparam("gcolor"))
11441 gridcolor = p->xgetparam("gcolor");
11442
11443 /* evaluate scale and offset */
11444
11445 time_t endtime = 0;
11446 if (p->isparam("time"))
11447 endtime = string_to_time(p->getparam("time"));
11448 else if (p->isparam("htime"))
11449 endtime = string_to_time(p->getparam("htime"));
11450
11451 if (pscale && *pscale)
11452 scale = time_to_sec(pscale);
11453 else
11454 scale = 0;
11455
11456 index = -1;
11457 if (pindex && *pindex)
11458 index = atoi(pindex);
11459
11460#ifdef BROKEN
11461 if (equal_ustring(hcmd, "Create ELog")) {
11462 std::string xurl;
11463 status = db_get_value_string(hDB, 0, "/Elog/URL", 0, &xurl, FALSE);
11464 if (status == DB_SUCCESS) {
11465 char url[256];
11466 get_elog_url(url, sizeof(url));
11467
11468 /*---- use external ELOG ----*/
11469 fsize = 100000;
11470 char* fbuffer = (char*)M_MALLOC(fsize);
11471 assert(fbuffer != NULL);
11472
11473 int width = 640;
11474 int height = 400;
11475
11476 if (equal_ustring(pmag, "Large")) {
11477 width = 1024;
11478 height = 768;
11479 } else if (equal_ustring(pmag, "Small")) {
11480 width = 320;
11481 height = 200;
11482 } else if (atoi(pmag) > 0) {
11483 width = atoi(pmag);
11484 height = 200;
11485 }
11486
11487 printf("hereA\n");
11488 generate_hist_graph(odb, r, hgroup, hpanel, fbuffer, &fsize, width, height, endtime, scale, index, labels, bgcolor.c_str(), fgcolor.c_str(), gridcolor.c_str());
11489
11490 /* save temporary file */
11491 std::string dir;
11492 db_get_value_string(hDB, 0, "/Elog/Logbook Dir", 0, &dir, TRUE);
11493 if (dir.length() > 0 && dir[dir.length()-1] != DIR_SEPARATOR)
11494 dir += DIR_SEPARATOR_STR;
11495
11496 time_t now = time(NULL);
11497 localtime_r(&now, &tms);
11498
11499 std::string file_name = msprintf("%02d%02d%02d_%02d%02d%02d_%s.gif",
11500 tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday,
11501 tms.tm_hour, tms.tm_min, tms.tm_sec, hpanel);
11502 std::string fname = dir + file_name;
11503
11504 /* save attachment */
11505 fh = open(fname.c_str(), O_CREAT | O_RDWR | O_BINARY, 0644);
11506 if (fh < 0) {
11507 cm_msg(MERROR, "show_hist_page", "Cannot write attachment file \"%s\", open() errno %d (%s)", fname.c_str(), errno, strerror(errno));
11508 } else {
11509 int wr = write(fh, fbuffer, fsize);
11510 if (wr != fsize) {
11511 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));
11512 }
11513 close(fh);
11514 }
11515
11516 /* redirect to ELOG */
11517 if (strlen(url) > 1 && url[strlen(url)-1] != '/')
11518 mstrlcat(url, "/", sizeof(url));
11519 mstrlcat(url, "?cmd=New&fa=", sizeof(url));
11520 mstrlcat(url, file_name, sizeof(url));
11521 redirect(r, url);
11522
11523 M_FREE(fbuffer);
11524 return;
11525
11526 } else {
11527 /*---- use internal ELOG ----*/
11528 std::string str = msprintf("\\HS\\%s.gif", hpanel);
11529 if (p->getparam("hscale") && *p->getparam("hscale"))
11530 str += msprintf("?scale=%s", p->getparam("hscale"));
11531 if (p->getparam("htime") && *p->getparam("htime")) {
11532 if (strchr(str.c_str(), '?'))
11533 str += "&";
11534 else
11535 str += "?";
11536 str += msprintf("time=%s", p->getparam("htime"));
11537 }
11538 //if (p->getparam("hoffset") && *p->getparam("hoffset")) {
11539 // if (strchr(str, '?'))
11540 // mstrlcat(str, "&", sizeof(str));
11541 // else
11542 // mstrlcat(str, "?", sizeof(str));
11543 // sprintf(str + strlen(str), "offset=%s", p->getparam("hoffset"));
11544 //}
11545 if (p->getparam("hwidth") && *p->getparam("hwidth")) {
11546 if (strchr(str.c_str(), '?'))
11547 str += "&";
11548 else
11549 str += "?";
11550 str += msprintf("width=%s", p->getparam("hwidth"));
11551 }
11552 if (p->getparam("hindex") && *p->getparam("hindex")) {
11553 if (strchr(str.c_str(), '?'))
11554 str += "&";
11555 else
11556 str += "?";
11557 str += msprintf("index=%s", p->getparam("hindex"));
11558 }
11559
11560 show_elog_new(r, hpanel, NULL, FALSE, str.c_str(), "../../EL/");
11561 return;
11562 }
11563 }
11564#endif
11565
11566 if (equal_ustring(hcmd, "Export")) {
11567 export_hist(odb, r, hgroup, hpanel, endtime, scale, index, labels);
11568 return;
11569 }
11570
11571 if (strstr(dec_path, ".gif")) {
11572 int width = 640;
11573 int height = 400;
11574 if (equal_ustring(pwidth, "Large")) {
11575 width = 1024;
11576 height = 768;
11577 } else if (equal_ustring(pwidth, "Small")) {
11578 width = 320;
11579 height = 200;
11580 } else if (atoi(pwidth) > 0) {
11581 width = atoi(pwidth);
11582 if (atoi(pheight) > 0)
11583 height = atoi(pheight);
11584 else
11585 height = (int)(0.625 * width);
11586 }
11587
11588 //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);
11589
11590 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());
11591
11592 return;
11593 }
11594
11595 if (history_mode && index < 0)
11596 return;
11597
11598 time_t now = time(NULL);
11599
11600 /* evaluate offset shift */
11601 if (equal_ustring(p->getparam("shift"), "leftmaxall")) {
11602 if (endtime == 0)
11603 endtime = now;
11604 time_t last_written = 0;
11605 status = get_hist_last_written(odb, hgroup, hpanel, endtime, index, 1, &last_written);
11606 if (status == HS_SUCCESS)
11607 endtime = last_written + scale/2;
11608 }
11609
11610 if (equal_ustring(p->getparam("shift"), "leftmax")) {
11611 if (endtime == 0)
11612 endtime = now;
11613 time_t last_written = 0;
11614 status = get_hist_last_written(odb, hgroup, hpanel, endtime, index, 0, &last_written);
11615 if (status == HS_SUCCESS)
11616 if (last_written != endtime)
11617 endtime = last_written + scale/2;
11618 }
11619
11620 if (equal_ustring(p->getparam("shift"), "left")) {
11621 if (endtime == 0)
11622 endtime = now;
11623 endtime -= scale/2;
11624 //offset -= scale / 2;
11625 }
11626
11627 if (equal_ustring(p->getparam("shift"), "right")) {
11628 if (endtime == 0)
11629 endtime = now;
11630 endtime += scale/2;
11631 if (endtime > now)
11632 endtime = now;
11633 }
11634
11635 if (equal_ustring(p->getparam("shift"), "rightmax")) {
11636 endtime = 0;
11637 }
11638
11639 if (equal_ustring(p->getparam("shift"), "zoomin")) {
11640 if (endtime == 0)
11641 endtime = now;
11642 endtime -= scale / 4;
11643 scale /= 2;
11644 }
11645
11646 if (equal_ustring(p->getparam("shift"), "zoomout")) {
11647 if (endtime == 0)
11648 endtime = now;
11649 endtime += scale / 2;
11650 if (endtime > now)
11651 endtime = now;
11652 scale *= 2;
11653 }
11654
11655 int xrefresh = refresh;
11656 if (endtime != 0)
11657 xrefresh = 0;
11658 show_header(r, hpanel, "GET", "", xrefresh);
11659
11660 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
11661 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
11662 show_navigation_bar(r, "History");
11663
11664 r->rsprintf("<table class=\"mtable\">");
11665 r->rsprintf("<tr><th class=\"mtableheader\" colspan=2>History</th></tr>");
11666
11667 {
11668 /* check if panel exists */
11669 std::string path;
11670 //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
11671 path += "/History/Display/";
11672 path += hgroup;
11673 path += "/";
11674 path += hpanel;
11675 status = db_find_key(hDB, 0, path.c_str(), &hkey);
11676 if (status != DB_SUCCESS && !equal_ustring(hpanel, "All") && !equal_ustring(hpanel,"")) {
11677 r->rsprintf("<h1>Error: History panel \"%s\" in group \"%s\" does not exist</h1>\n", hpanel, hgroup);
11678 r->rsprintf("</table>\r\n");
11679 r->rsprintf("</div>\n"); // closing for <div id="mmain">
11680 r->rsprintf("</form>\n");
11681 r->rsprintf("</body></html>\r\n");
11682 return;
11683 }
11684 }
11685
11686 /* define hidden field for parameters */
11687 if (pscale && *pscale)
11688 r->rsprintf("<input type=hidden name=hscale id=hscale value=%d>\n", scale);
11689 else {
11690 /* if no scale and offset given, get it from default */
11691 if (hpanel[0] && !equal_ustring(hpanel, "All") && hgroup[0]) {
11692 std::string path;
11693 path += "/History/Display/";
11694 path += hgroup;
11695 path += "/";
11696 path += hpanel;
11697 path += "/Timescale";
11698
11699 std::string scalestr = "1h";
11700 status = db_get_value_string(hDB, 0, path.c_str(), 0, &scalestr, TRUE);
11701 if (status != DB_SUCCESS) {
11702 /* delete old integer key */
11703 db_delete(hDB, 0, path.c_str());
11704 scalestr = "1h";
11705 db_get_value_string(hDB, 0, path.c_str(), 0, &scalestr, TRUE);
11706 }
11707
11708 r->rsprintf("<input type=hidden name=hscale id=hscale value=%s>\n", scalestr.c_str());
11709 scale = time_to_sec(scalestr.c_str());
11710 }
11711 }
11712
11713 if (endtime != 0)
11714 r->rsprintf("<input type=hidden name=htime id=htime value=%s>\n", time_to_string(endtime).c_str());
11715 if (pwidth && *pwidth)
11716 r->rsprintf("<input type=hidden name=hwidth id=hwidth value=%s>\n", pwidth);
11717 if (pheight && *pheight)
11718 r->rsprintf("<input type=hidden name=hheight id=hheight value=%s>\n", pheight);
11719 if (pindex && *pindex)
11720 r->rsprintf("<input type=hidden name=hindex id=hindex value=%s>\n", pindex);
11721
11722 r->rsprintf("</td></tr>\n");
11723
11724 if (hgroup[0] == 0) {
11725 /* "New" button */
11726 r->rsprintf("<tr><td colspan=2><input type=\"button\" name=\"New\" value=\"New\" ");
11727 r->rsprintf("onClick=\"window.location.href='?cmd=oldhistory&hcmd=New'\"></td></tr>\n");
11728
11729 /* links for history panels */
11730 r->rsprintf("<tr><td colspan=2 style=\"text-align:left;\">\n");
11731 if (!hpanel[0])
11732 r->rsprintf("<b>Please select panel:</b><br>\n");
11733
11734 /* table for panel selection */
11735 r->rsprintf("<table class=\"historyTable\">");
11736
11737 /* "All" link */
11738 r->rsprintf("<tr><td colspan=2 class=\"titleCell\">\n");
11739 if (equal_ustring(hgroup, "All"))
11740 r->rsprintf("All &nbsp;&nbsp;");
11741 else
11742 r->rsprintf("<a href=\"?cmd=oldhistory&group=All\">ALL</a>\n");
11743 r->rsprintf("</td></tr>\n");
11744
11745 /* Setup History table links */
11746 db_find_key(hDB, 0, "/History/Display", &hkey);
11747 if (!hkey) {
11748 /* create default panel */
11749 char str[256];
11750 strcpy(str, "System:Trigger per sec.");
11751 strcpy(str + 2 * NAME_LENGTH, "System:Trigger kB per sec.");
11752 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Variables", str, 64, 2, TID_STRING);
11753 strcpy(str, "1h");
11754 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Time Scale", str, NAME_LENGTH, 1, TID_STRING);
11755
11756 strcpy(str, "1h");
11757 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Timescale", str, NAME_LENGTH, 1, TID_STRING);
11758 i = 1;
11759 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Zero ylow", &i, sizeof(BOOL), 1, TID_BOOL);
11760 i = 1;
11761 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Show run markers", &i, sizeof(BOOL), 1, TID_BOOL);
11762
11763 strcpy(str, "");
11764 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Formula", str, 64, 1, TID_STRING);
11765 db_set_value_index(hDB, 0, "/History/Display/Default/Trigger rate/Formula", str, 64, 1, TID_STRING, FALSE);
11766 }
11767
11768 db_find_key(hDB, 0, "/History/Display", &hkey);
11769 if (hkey) {
11770 for (i = 0;; i++) {
11771 db_enum_link(hDB, hkey, i, &hkeyp);
11772
11773 if (!hkeyp)
11774 break;
11775
11776 // Group key
11777 db_get_key(hDB, hkeyp, &key);
11778
11779 char enc_name[256];
11780 mstrlcpy(enc_name, key.name, sizeof(enc_name));
11781 urlEncode(enc_name, sizeof(enc_name));
11782
11783 if (equal_ustring(hpanel, key.name))
11784 r->rsprintf("<tr><td class=\"titleCell\">%s</td>\n<td>", key.name);
11785 else
11786 r->rsprintf("<tr><td class=\"titleCell\"><a href=\"?cmd=oldhistory&group=%s\">%s</a></td>\n<td>", enc_name, key.name);
11787
11788 for (j = 0;; j++) {
11789 // scan items
11790 db_enum_link(hDB, hkeyp, j, &hikeyp);
11791
11792 if (!hikeyp) {
11793 r->rsprintf("</tr>");
11794 break;
11795 }
11796 // Item key
11797 db_get_key(hDB, hikeyp, &ikey);
11798
11799 char enc_iname[256];
11800 mstrlcpy(enc_iname, ikey.name, sizeof(enc_iname));
11801 urlEncode(enc_iname, sizeof(enc_iname));
11802
11803 if (equal_ustring(hpanel, ikey.name))
11804 r->rsprintf("<small><b>%s</b></small> &nbsp;", ikey.name);
11805 else
11806 r->rsprintf("<small><a href=\"?cmd=oldhistory&group=%s&panel=%s\">%s</a></small> &nbsp;\n", enc_name, enc_iname, ikey.name);
11807 }
11808 }
11809 }
11810
11811 r->rsprintf("</table></tr>\n");
11812
11813 } else {
11814 int found = 0;
11815
11816 /* show drop-down selectors */
11817 r->rsprintf("<tr><td colspan=2>\n");
11818
11819 r->rsprintf("Group:\n");
11820
11821 r->rsprintf("<select title=\"Select group\" id=\"fgroup\" onChange=\"window.location.search='?cmd=oldhistory&group='+document.getElementById('fgroup').value;\">\n");
11822
11823 db_find_key(hDB, 0, "/History/Display", &hkey);
11824 if (hkey) {
11825 hkeyp = 0;
11826 for (i = 0;; i++) {
11827 db_enum_link(hDB, hkey, i, &hikeyp);
11828
11829 if (!hikeyp)
11830 break;
11831
11832 if (i == 0)
11833 hkeyp = hikeyp;
11834
11835 // Group key
11836 db_get_key(hDB, hikeyp, &key);
11837
11838 if (equal_ustring(key.name, hgroup)) {
11839 r->rsprintf("<option selected value=\"%s\">%s\n", key.name, key.name);
11840 hkeyp = hikeyp;
11841 } else
11842 r->rsprintf("<option value=\"%s\">%s\n", key.name, key.name);
11843 }
11844
11845 if (equal_ustring("ALL", hgroup)) {
11846 r->rsprintf("<option selected value=\"%s\">%s\n", "ALL", "ALL");
11847 } else {
11848 r->rsprintf("<option value=\"%s\">%s\n", "ALL", "ALL");
11849 }
11850
11851 r->rsprintf("</select>\n");
11852 r->rsprintf("&nbsp;&nbsp;Panel:\n");
11853 r->rsprintf("<select title=\"Select panel\" id=\"fpanel\" ");
11854 r->rsprintf("onChange=\"window.location.search='?cmd=oldhistory&group='+document.getElementById('fgroup').value+");
11855 r->rsprintf("'&panel='+document.getElementById('fpanel').value;\">\n");
11856
11857 found = 0;
11858 if (hkeyp) {
11859 for (i = 0;; i++) {
11860 // scan panels
11861 db_enum_link(hDB, hkeyp, i, &hikeyp);
11862
11863 if (!hikeyp)
11864 break;
11865
11866 // Item key
11867 db_get_key(hDB, hikeyp, &key);
11868
11869 if (equal_ustring(hpanel, key.name)) {
11870 r->rsprintf("<option selected value=\"%s\">%s\n", key.name, key.name);
11871 found = 1;
11872 } else
11873 r->rsprintf("<option value=\"%s\">%s\n", key.name, key.name);
11874 }
11875 }
11876
11877 if (found)
11878 r->rsprintf("<option value=\"\">- all -\n");
11879 else
11880 r->rsprintf("<option selected value=\"\">- all -\n");
11881
11882 r->rsprintf("</select>\n");
11883 }
11884
11885 r->rsprintf("<noscript>\n");
11886 r->rsprintf("<input type=submit value=\"Go\">\n");
11887 r->rsprintf("</noscript>\n");
11888
11889 r->rsprintf("&nbsp;&nbsp;<input type=\"button\" name=\"New\" value=\"New\" ");
11890 r->rsprintf("onClick=\"window.location.href='?cmd=oldhistory&hcmd=New&group=%s'\">\n", hgroup);
11891
11892 r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"Reset\" onClick=\"window.location.href='?cmd=oldhistory&hcmd=Reset&group=%s&panel=%s'\">\n", hgroup, hpanel);
11893
11894 r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"Query\" onClick=\"window.location.href='?cmd=oldhistory&hcmd=Query&group=%s&panel=%s'\">\n", hgroup, hpanel);
11895
11896 double xendtime = endtime;
11897 if (xendtime == 0)
11898 xendtime = now;
11899 double xstarttime = xendtime - scale;
11900
11901 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);
11902
11903 r->rsprintf("</td></tr>\n");
11904 }
11905
11906 //printf("hgroup [%s] hpanel [%s]\n", hgroup, hpanel);
11907
11908 /* check if whole group should be displayed */
11909 if (hgroup[0] && !equal_ustring(hgroup, "ALL") && hpanel[0] == 0) {
11910 std::string strwidth = "Small";
11911 db_get_value_string(hDB, 0, "/History/Display Settings/Width Group", 0, &strwidth, TRUE);
11912
11913 std::string path;
11914 path += "/History/Display/";
11915 path += hgroup;
11916 db_find_key(hDB, 0, path.c_str(), &hkey);
11917 if (hkey) {
11918 for (i = 0 ;; i++) { // scan group
11919 db_enum_link(hDB, hkey, i, &hikeyp);
11920
11921 if (!hikeyp)
11922 break;
11923
11924 db_get_key(hDB, hikeyp, &key);
11925
11926 char enc_name[256];
11927 mstrlcpy(enc_name, key.name, sizeof(enc_name));
11928 urlEncode(enc_name, sizeof(enc_name));
11929
11930 std::string ref;
11931 ref += "graph.gif?width=";
11932 ref += strwidth;
11933 ref += "&cmd=oldhistory&group=";
11934 ref += hgroup;
11935 ref += "&panel=";
11936 ref += enc_name;
11937
11938 std::string ref2;
11939 ref2 += "?cmd=oldhistory&group=";
11940 ref2 += hgroup;
11941 ref2 += "&panel=";
11942 ref2 += enc_name;
11943
11944 if (endtime != 0) {
11945 char tmp[256];
11946 sprintf(tmp, "time=%s&scale=%d", time_to_string(endtime).c_str(), scale);
11947 ref += "&";
11948 ref += tmp;
11949 ref2 += "?";
11950 ref2 += tmp;
11951 }
11952
11953 if (i % 2 == 0)
11954 r->rsprintf("<tr><td><a href=\"%s\"><img src=\"%s\"></a>\n", ref2.c_str(), ref.c_str());
11955 else
11956 r->rsprintf("<td><a href=\"%s\"><img src=\"%s\"></a></tr>\n", ref2.c_str(), ref.c_str());
11957 }
11958
11959 } else {
11960 r->rsprintf("Group \"%s\" not found", hgroup);
11961 }
11962 }
11963
11964 /* image panel */
11965 else if (hpanel[0] && !equal_ustring(hpanel, "All")) {
11966 /* navigation links */
11967 r->rsprintf("<tr><td>\n");
11968
11969 std::string path;
11970 path += "/History/Display/";
11971 path += hgroup;
11972 path += "/";
11973 path += hpanel;
11974 path += "/Buttons";
11975 db_find_key(hDB, 0, path.c_str(), &hkeybutton);
11976 if (hkeybutton == 0) {
11977 /* create default buttons */
11978 db_create_key(hDB, 0, path.c_str(), TID_STRING);
11979 status = db_find_key(hDB, 0, path.c_str(), &hkeybutton);
11980 if (status != DB_SUCCESS || !hkey) {
11981 cm_msg(MERROR, "show_hist_page", "Cannot create history panel with invalid ODB path \"%s\"", path.c_str());
11982 return;
11983 }
11984 db_set_data(hDB, hkeybutton, def_button, sizeof(def_button), 7, TID_STRING);
11985 }
11986
11987 r->rsprintf("<script>\n");
11988 r->rsprintf("function histDisp(p) {\n");
11989 r->rsprintf(" var params = '?cmd=oldhistory&group=%s&panel=%s';\n", hgroup, hpanel);
11990 r->rsprintf(" params += '&'+p;\n");
11991 r->rsprintf(" if (document.getElementById(\'hscale\') !== null)\n");
11992 r->rsprintf(" params += '&hscale='+document.getElementById(\'hscale\').value;\n");
11993 r->rsprintf(" if (document.getElementById(\'htime\') !== null)\n");
11994 r->rsprintf(" params += '&htime='+document.getElementById(\'htime\').value;\n");
11995 r->rsprintf(" if (document.getElementById(\'hwdith\') !== null)\n");
11996 r->rsprintf(" params += '&hwidth='+document.getElementById(\'hwidth\').value;\n");
11997 r->rsprintf(" if (document.getElementById(\'hindex\') !== null)\n");
11998 r->rsprintf(" params += '&hindex='+document.getElementById(\'hindex\').value;\n");
11999 r->rsprintf(" window.location.search = params;\n");
12000 r->rsprintf("}\n\n");
12001 r->rsprintf("</script>\n");
12002
12003 db_get_key(hDB, hkeybutton, &key);
12004
12005 for (i = 0; i < key.num_values; i++) {
12006 char str[256];
12007 size = sizeof(str);
12008 db_get_data_index(hDB, hkeybutton, str, &size, i, TID_STRING);
12009 r->rsprintf("<input type=\"button\" title=\"display last %s\" value=%s onclick=\"histDisp('scale=%s')\">\n", str, str, str);
12010 }
12011
12012 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')\">");
12013 r->rsprintf("<input type=\"button\" value=\"<<\" title=\"go back in time to last available data\" onclick=\"histDisp('shift=leftmax')\">");
12014 r->rsprintf("<input type=\"button\" value=\"<\" title=\"go back in time\" onclick=\"histDisp('shift=left')\">");
12015
12016 r->rsprintf("<input type=\"button\" value=\" + \" title=\"zoom in\" onclick=\"histDisp('shift=zoomin')\">");
12017 r->rsprintf("<input type=\"button\" value=\" - \" title=\"zoom out\" onclick=\"histDisp('shift=zoomout')\">");
12018
12019 if (endtime != 0) {
12020 r->rsprintf("<input type=\"button\" value=\">\" title=\"go forward in time\" onclick=\"histDisp('shift=right')\">");
12021 r->rsprintf("<input type=\"button\" value=\">>\" title=\"go to currently updated fresh data\" onclick=\"histDisp('shift=rightmax')\">");
12022 }
12023
12024 r->rsprintf("<td>\n");
12025 r->rsprintf("<input type=\"button\" value=\"Large\" title=\"large display\" onclick=\"histDisp('width=Large')\">\n");
12026 r->rsprintf("<input type=\"button\" value=\"Small\" title=\"large display\" onclick=\"histDisp('width=Small')\">\n");
12027 r->rsprintf("<input type=\"button\" value=\"Create Elog\" title=\"large display\" onclick=\"histDisp('hcmd=Create Elog')\">\n");
12028 r->rsprintf("<input type=\"button\" value=\"Config\" title=\"large display\" onclick=\"histDisp('hcmd=Config')\">\n");
12029 r->rsprintf("<input type=\"button\" value=\"Export\" title=\"large display\" onclick=\"histDisp('hcmd=Export')\">\n");
12030 r->rsprintf("</tr>\n");
12031
12032 char paramstr[256];
12033
12034 paramstr[0] = 0;
12035 sprintf(paramstr + strlen(paramstr), "&scale=%d", scale);
12036 if (endtime != 0)
12037 sprintf(paramstr + strlen(paramstr), "&time=%s", time_to_string(endtime).c_str());
12038 if (pwidth && *pwidth)
12039 sprintf(paramstr + strlen(paramstr), "&width=%s", pwidth);
12040 else {
12041 std::string wi = "640";
12042 db_get_value_string(hDB, 0, "/History/Display Settings/Width Individual", 0, &wi, TRUE);
12043 sprintf(paramstr + strlen(paramstr), "&width=%s", wi.c_str());
12044 }
12045 if (pheight && *pheight)
12046 sprintf(paramstr + strlen(paramstr), "&height=%s", pheight);
12047
12048 /* define image map */
12049 r->rsprintf("<map name=\"%s\">\r\n", hpanel);
12050
12051 if (!(pindex && *pindex)) {
12052 std::string path;
12053 path += "/History/Display/";
12054 path += hgroup;
12055 path += "/";
12056 path += hpanel;
12057 path += "/Variables";
12058 db_find_key(hDB, 0, path.c_str(), &hkey);
12059 if (hkey) {
12060 db_get_key(hDB, hkey, &key);
12061
12062 for (i = 0; i < key.num_values; i++) {
12063 std::string ref;
12064 //if (paramstr[0]) {
12065 // sprintf(ref, "?cmd=oldhistory&group=%s&panel=%s&%s&index=%d", hgroup, hpanel, paramstr, i);
12066 //} else {
12067 // sprintf(ref, "?cmd=oldhistory&group=%s&panel=%s&index=%d", hgroup, hpanel, i);
12068 //}
12069
12070 ref += "?cmd=oldhistory&group=";
12071 ref += hgroup;
12072 ref += "&panel=";
12073 ref += hpanel;
12074 if (paramstr[0]) {
12075 ref += "&";
12076 ref += paramstr;
12077 }
12078 ref += "&index=";
12079 ref += toString(i);
12080
12081 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());
12082 }
12083 }
12084 } else {
12085 std::string ref = "?cmd=oldhistory&group=";
12086 ref += hgroup;
12087 ref += "&panel=";
12088 ref += hpanel;
12089
12090 if (paramstr[0]) {
12091 ref += "&";
12092 ref += paramstr;
12093 }
12094
12095 if (equal_ustring(pwidth, "Large"))
12096 width = 1024;
12097 else if (equal_ustring(pwidth, "Small"))
12098 width = 320;
12099 else if (atoi(pwidth) > 0)
12100 width = atoi(pwidth);
12101 else
12102 width = 640;
12103
12104 r->rsprintf(" <area shape=rect coords=\"%d,%d,%d,%d\" href=\"%s\">\r\n", 0, 0, width, 20, ref.c_str());
12105 }
12106
12107 r->rsprintf("</map>\r\n");
12108
12109 /* Display individual panels */
12110 if (pindex && *pindex)
12111 sprintf(paramstr + strlen(paramstr), "&index=%s", pindex);
12112
12113 std::string ref;
12114 //sprintf(ref, "graph.gif?cmd=oldhistory&group=%s&panel=%s%s", hgroup, hpanel, paramstr);
12115 ref += "graph.gif?cmd=oldhistory&group=";
12116 ref += hgroup;
12117 ref += "&panel=";
12118 ref += hpanel;
12119 ref += paramstr;
12120
12121 /* put reference to graph */
12122 r->rsprintf("<tr><td colspan=2><img src=\"%s\" usemap=\"#%s\"></tr>\n", ref.c_str(), hpanel);
12123 }
12124
12125 else if (equal_ustring(hgroup, "All")) {
12126 /* Display all panels */
12127 db_find_key(hDB, 0, "/History/Display", &hkey);
12128 if (hkey)
12129 for (i = 0, k = 0;; i++) { // scan Groups
12130 db_enum_link(hDB, hkey, i, &hkeyp);
12131
12132 if (!hkeyp)
12133 break;
12134
12135 db_get_key(hDB, hkeyp, &key);
12136
12137 char enc_group_name[256];
12138 mstrlcpy(enc_group_name, key.name, sizeof(enc_group_name));
12139 urlEncode(enc_group_name, sizeof(enc_group_name));
12140
12141 for (j = 0;; j++, k++) {
12142 // scan items
12143 db_enum_link(hDB, hkeyp, j, &hikeyp);
12144
12145 if (!hikeyp)
12146 break;
12147
12148 db_get_key(hDB, hikeyp, &ikey);
12149
12150 char enc_panel_name[256];
12151 mstrlcpy(enc_panel_name, ikey.name, sizeof(enc_panel_name));
12152 urlEncode(enc_panel_name, sizeof(enc_panel_name));
12153
12154 std::string ref;
12155 ref += "graph.gif?width=Small";
12156 ref += "&cmd=oldhistory&group=";
12157 ref += enc_group_name;
12158 ref += "&panel=";
12159 ref += enc_panel_name;
12160
12161 std::string ref2;
12162 ref2 += "?cmd=oldhistory&group=";
12163 ref2 += enc_group_name;
12164 ref2 += "&panel=";
12165 ref2 += enc_panel_name;
12166
12167 if (endtime != 0) {
12168 char tmp[256];
12169 sprintf(tmp, "time=%s&scale=%d", time_to_string(endtime).c_str(), scale);
12170 ref += "&";
12171 ref += tmp;
12172 ref2 += "&";
12173 ref2 += tmp;
12174 }
12175
12176 if (k % 2 == 0)
12177 r->rsprintf("<tr><td><a href=\"%s\"><img src=\"%s\"></a>\n", ref2.c_str(), ref.c_str());
12178 else
12179 r->rsprintf("<td><a href=\"%s\"><img src=\"%s\"></a></tr>\n", ref2.c_str(), ref.c_str());
12180 } // items loop
12181 } // Groups loop
12182 } // All
12183 r->rsprintf("</table>\r\n");
12184 r->rsprintf("</div>\n"); // closing for <div id="mmain">
12185 r->rsprintf("</form>\n");
12186 r->rsprintf("</body></html>\r\n");
12187}
12188
12189
12190/*------------------------------------------------------------------*/
12191
12192void send_icon(Return* r, const char *icon)
12193{
12194 int length;
12195 const unsigned char *picon;
12196 char str[256], format[256];
12197 time_t now;
12198
12199 if (strstr(icon, "favicon.ico") != 0) {
12200 length = sizeof(favicon_ico);
12201 picon = favicon_ico;
12202 } else if (strstr(icon, "favicon.png") != 0) {
12203 length = sizeof(favicon_png);
12204 picon = favicon_png;
12205 } else
12206 return;
12207
12208 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
12209 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12210 r->rsprintf("Accept-Ranges: bytes\r\n");
12211
12212 /* set expiration time to one day */
12213 time(&now);
12214 now += (int) (3600 * 24);
12215 struct tm gmt_tms;
12216 gmtime_r(&now, &gmt_tms);
12217 strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT");
12218 strftime(str, sizeof(str), format, &gmt_tms);
12219 r->rsprintf("Expires: %s\r\n", str);
12220
12221 if (equal_ustring(icon, "favicon.ico"))
12222 r->rsprintf("Content-Type: image/x-icon\r\n");
12223 else
12224 r->rsprintf("Content-Type: image/png\r\n");
12225
12226 r->rsprintf("Content-Length: %d\r\n\r\n", length);
12227
12228 r->rmemcpy(picon, length);
12229}
12230
12231/*------------------------------------------------------------------*/
12232
12234{
12235 std::string cookie_pwd;
12236 std::string cookie_wpwd;
12237 std::string cookie_cpwd;
12238 int refresh = 0;
12239 //int expand_equipment = 0;
12240};
12241
12242/*------------------------------------------------------------------*/
12243
12245{
12246 gMutex.lock();
12247 t->fTimeLocked = GetTimeSec();
12248}
12249
12251{
12253 gMutex.unlock();
12254}
12255
12256/*------------------------------------------------------------------*/
12257
12258void interprete(Param* p, Return* r, Attachment* a, const Cookies* c, const char *dec_path, RequestTrace* t)
12259/********************************************************************\
12260
12261 Routine: interprete
12262
12263 Purpose: Main interpreter of web commands
12264
12265 \********************************************************************/
12266{
12267 int status;
12268 HNDLE hkey, hDB;
12269
12270 //printf("dec_path [%s]\n", dec_path);
12271
12272 if (strstr(dec_path, "favicon.ico") != 0 ||
12273 strstr(dec_path, "favicon.png")) {
12274 send_icon(r, dec_path);
12275 return;
12276 }
12277
12278 const char* password = p->getparam("pwd");
12279 const char* wpassword = p->getparam("wpwd");
12280 const char* command = p->getparam("cmd");
12281
12282 //printf("interprete: dec_path [%s], command [%s]\n", dec_path, command);
12283
12285 MVOdb* odb = gOdb;
12286
12287 if (history_mode) {
12288 if (equal_ustring(command, "history")) {
12289 if (equal_ustring(command, "config")) {
12290 return;
12291 }
12292
12293 Lock(t);
12294 show_hist_page(odb, p, r, dec_path, NULL, NULL, c->refresh);
12295 Unlock(t);
12296 return;
12297 }
12298 return;
12299 }
12300
12301 /* check for password */
12302 db_find_key(hDB, 0, "/Experiment/Security/Password", &hkey);
12303 if (!password[0] && hkey) {
12304 char str[256];
12305 int size = sizeof(str);
12306 db_get_data(hDB, hkey, str, &size, TID_STRING);
12307
12308 /* check for excemption */
12309 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs/mhttpd", &hkey);
12310 if (hkey == 0 && strcmp(c->cookie_pwd.c_str(), str) != 0) {
12311 Lock(t);
12312 show_password_page(r, dec_path, "");
12313 Unlock(t);
12314 return;
12315 }
12316 }
12317
12318 /*---- redirect with cookie if password given --------------------*/
12319
12320 if (password[0]) {
12321 r->rsprintf("HTTP/1.1 302 Found\r\n");
12322 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12323
12324 time_t now;
12325 time(&now);
12326
12327 now += 3600 * 24;
12328
12329 struct tm gmt_tms;
12330 gmtime_r(&now, &gmt_tms);
12331
12332 char str[256];
12333 strftime(str, sizeof(str), "%A, %d-%b-%Y %H:00:00 GMT", &gmt_tms);
12334
12335 r->rsprintf("Set-Cookie: midas_pwd=%s; path=/; expires=%s\r\n",
12336 ss_crypt(password, "mi"), str);
12337
12338 r->rsprintf("Location: ./\n\n<html>redir</html>\r\n");
12339 return;
12340 }
12341
12342 if (wpassword[0]) {
12343 /* check if password correct */
12344 if (!check_web_password(r, hDB, dec_path, ss_crypt(wpassword, "mi"), p->getparam("redir")))
12345 return;
12346
12347 r->rsprintf("HTTP/1.1 302 Found\r\n");
12348 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12349
12350 time_t now;
12351 time(&now);
12352
12353 now += 3600 * 24;
12354
12355 struct tm gmt_tms;
12356 gmtime_r(&now, &gmt_tms);
12357
12358 char str[256];
12359 strftime(str, sizeof(str), "%A, %d-%b-%Y %H:%M:%S GMT", &gmt_tms);
12360
12361 r->rsprintf("Set-Cookie: midas_wpwd=%s; path=/; expires=%s\r\n", ss_crypt(wpassword, "mi"), str);
12362
12363 sprintf(str, "./%s", p->getparam("redir"));
12364 r->rsprintf("Location: %s\n\n<html>redir</html>\r\n", str);
12365 return;
12366 }
12367
12368 /*---- send sound file -------------------------------------------*/
12369
12370 if (strlen(dec_path) > 3 &&
12371 dec_path[strlen(dec_path)-3] == 'm' &&
12372 dec_path[strlen(dec_path)-2] == 'p' &&
12373 dec_path[strlen(dec_path)-1] == '3') {
12374 if (strrchr(dec_path, '/'))
12375 send_resource(r, strrchr(dec_path, '/')+1);
12376 else
12377 send_resource(r, dec_path);
12378 return;
12379 }
12380
12381 /*---- send midas.js and midas.css -------------------------------*/
12382
12383 if (strstr(dec_path, "midas.js")) {
12384 send_resource(r, "midas.js");
12385 return;
12386 }
12387
12388 if (strstr(dec_path, "midas.css")) {
12389 send_resource(r, "midas.css");
12390 return;
12391 }
12392
12393 /*---- send mhttpd.js --------------------------------------------*/
12394
12395 if (strstr(dec_path, "mhttpd.js")) {
12396 send_resource(r, "mhttpd.js");
12397 return;
12398 }
12399
12400 /*---- send obsolete.js ------------------------------------------*/
12401
12402 if (strstr(dec_path, "obsolete.js")) {
12403 send_resource(r, "obsolete.js");
12404 return;
12405 }
12406
12407 /*---- send the obsolete mhttpd.css ------------------------------*/
12408
12409 if (strstr(dec_path, "mhttpd.css")) {
12410 send_resource(r, "mhttpd.css");
12411 return;
12412 }
12413
12414 /*---- send controls.js ------------------------------------------*/
12415
12416 if (strstr(dec_path, "controls.js")) {
12417 send_resource(r, "controls.js");
12418 return;
12419 }
12420
12421 /*---- send example web page -------------------------------------*/
12422
12423 if (equal_ustring(command, "example")) {
12424 send_resource(r, "example.html");
12425 return;
12426 }
12427
12428 /*---- send example custom page -------------------------------------*/
12429
12430 if (equal_ustring(command, "custom_example")) {
12431 send_resource(r, "custom_example.html");
12432 return;
12433 }
12434
12435 if (equal_ustring(command, "plot_example")) {
12436 send_resource(r, "plot_example.html");
12437 return;
12438 }
12439
12440 /*---- script command --------------------------------------------*/
12441
12442 if (p->getparam("script") && *p->getparam("script")) {
12443
12444 std::string str = msprintf("%s?script=%s", dec_path, p->getparam("script"));
12445 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
12446 return;
12447
12448 std::string path;
12449 path += "/Script/";
12450 path += p->getparam("script");
12451
12452 Lock(t);
12453
12454 cm_exec_script(path.c_str());
12455
12456 Unlock(t);
12457
12458 if (p->isparam("redir"))
12459 redirect2(r, p->getparam("redir"));
12460 else
12461 redirect2(r, "");
12462
12463 return;
12464 }
12465
12466 /*---- customscript command --------------------------------------*/
12467
12468 if (p->getparam("customscript") && *p->getparam("customscript")) {
12469
12470 std::string str = msprintf("%s?customscript=%s", dec_path, p->getparam("customscript"));
12471 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
12472 return;
12473
12474 std::string path;
12475 path += "/CustomScript/";
12476 path += p->getparam("customscript");
12477
12478 Lock(t);
12479
12480 cm_exec_script(path.c_str());
12481
12482 Unlock(t);
12483
12484 if (p->isparam("redir"))
12485 redirect2(r, p->getparam("redir"));
12486 else
12487 redirect2(r, str.c_str());
12488
12489 return;
12490 }
12491
12492 /*---- send the new html pages -----------------------------------*/
12493
12494 if (equal_ustring(command, "start")) {
12495 send_resource(r, "start.html");
12496 return;
12497 }
12498
12499 if ((equal_ustring(command, "") || equal_ustring(command, "status")) && strlen(dec_path) == 0) {
12500 if (midas::odb::exists("/Custom/Status")) {
12501 midas::odb custom("/Custom");
12502
12503 std::string filename = custom["Status"];
12504 filename = add_custom_path(filename);
12505
12506 // if custom file exists, send it (like normal web server)
12507 if (ss_file_exist(filename.c_str())) {
12508 send_file(r, filename);
12509 return;
12510 }
12511 } else
12512 send_resource(r, "status.html");
12513 return;
12514 }
12515
12516 if (equal_ustring(command, "eqtable")) {
12517 send_resource(r, "eqtable.html");
12518 return;
12519 }
12520
12521 if (equal_ustring(command, "newODB")) {
12522 send_resource(r, "odb.html");
12523 return;
12524 }
12525
12526 if (equal_ustring(command, "programs")) {
12527 send_resource(r, "programs.html");
12528 return;
12529 }
12530
12531 if (equal_ustring(command, "alarms")) {
12532 send_resource(r, "alarms.html");
12533 return;
12534 }
12535
12536 if (equal_ustring(command, "transition")) {
12537 send_resource(r, "transition.html");
12538 return;
12539 }
12540
12541 if (equal_ustring(command, "messages")) {
12542 send_resource(r, "messages.html");
12543 return;
12544 }
12545
12546 if (equal_ustring(command, "config") &&
12547 !(dec_path[0] == 'H' && dec_path[1] == 'S' && dec_path[2] == '/')) {
12548 send_resource(r, "config.html");
12549 return;
12550 }
12551
12552 if (equal_ustring(command, "chat")) {
12553 send_resource(r, "chat.html");
12554 return;
12555 }
12556
12557 if (equal_ustring(command, "buffers")) {
12558 send_resource(r, "buffers.html");
12559 return;
12560 }
12561
12562 if (equal_ustring(command, "Show elog")) {
12563 send_resource(r, "elog_show.html");
12564 return;
12565 }
12566
12567 if (equal_ustring(command, "Query elog")) {
12568 send_resource(r, "elog_query_form.html");
12569 return;
12570 }
12571
12572 if (equal_ustring(command, "New elog")) {
12573 send_resource(r, "elog_edit.html");
12574 return;
12575 }
12576
12577 if (equal_ustring(command, "Edit elog")) {
12578 send_resource(r, "elog_edit.html");
12579 return;
12580 }
12581
12582 if (equal_ustring(command, "Reply Elog")) {
12583 send_resource(r, "elog_edit.html");
12584 return;
12585 }
12586
12587 if (equal_ustring(command, "Last elog")) {
12588 send_resource(r, "elog_show.html");
12589 return;
12590 }
12591
12592 if (equal_ustring(command, "Submit Query")) {
12593 send_resource(r, "elog_query.html");
12594 return;
12595 }
12596
12597 if (equal_ustring(dec_path, "spinning-wheel.gif")) {
12598 send_resource(r, "spinning-wheel.gif");
12599 return;
12600 }
12601
12602 /*---- java script commands --------------------------------------*/
12603
12604 if (equal_ustring(command, "jset") ||
12605 equal_ustring(command, "jget") ||
12606 equal_ustring(command, "jcopy") ||
12607 equal_ustring(command, "jpaste") ||
12608 equal_ustring(command, "jkey") ||
12609 equal_ustring(command, "jcreate") ||
12610 equal_ustring(command, "jresize") ||
12611 equal_ustring(command, "jlink") ||
12612 equal_ustring(command, "jrename") ||
12613 equal_ustring(command, "jreorder") ||
12614 equal_ustring(command, "jdelete") ||
12615 equal_ustring(command, "jmsg") ||
12616 equal_ustring(command, "jalm") ||
12617 equal_ustring(command, "jgenmsg") ||
12618 equal_ustring(command, "jrpc_rev0") ||
12619 equal_ustring(command, "jrpc_rev1") ||
12620 equal_ustring(command, "jrpc")) {
12621 Lock(t);
12622 javascript_commands(p, r, c->cookie_cpwd.c_str());
12623 Unlock(t);
12624 return;
12625 }
12626
12627 /*---- history editord -------------------------------------------*/
12628
12629 if (equal_ustring(command, "hs_edit")) {
12630 send_resource(r, "hs_edit.html");
12631 return;
12632 }
12633
12634 /*---- history command -------------------------------------------*/
12635
12636 if (equal_ustring(command, "oldhistory")) {
12637 Lock(t);
12638 show_hist_page(odb, p, r, dec_path, NULL, NULL, c->refresh);
12639 Unlock(t);
12640 return;
12641 }
12642
12643 if (equal_ustring(command, "history")) {
12644 send_resource(r, "history.html");
12645 return;
12646 }
12647
12648 /*---- MSCB command ----------------------------------------------*/
12649
12650 if (equal_ustring(command, "MSCB")) {
12651 if (equal_ustring(command, "set")) {
12652 std::string str;
12653 str += dec_path;
12654 str += "?";
12655 str += add_param_to_url("cmd", command);
12656 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
12657 return;
12658 }
12659
12660 Lock(t);
12661
12662#ifdef HAVE_MSCB
12663 show_mscb_page(p, r, c->refresh);
12664#else
12665 show_error(r, "MSCB support not compiled into this version of mhttpd");
12666#endif
12667
12668 Unlock(t);
12669 return;
12670 }
12671
12672 /*---- help command ----------------------------------------------*/
12673
12674 if (equal_ustring(command, "help")) {
12675 Lock(t);
12676 show_help_page(r, dec_path);
12677 Unlock(t);
12678 return;
12679 }
12680
12681 /*---- trigger equipment readout ---------------------------*/
12682
12683 if (strncmp(command, "Trigger", 7) == 0) {
12684 std::string cmd;
12685 cmd += "?cmd=";
12686 cmd += command;
12687 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), cmd.c_str())) {
12688 return;
12689 }
12690
12691 Lock(t);
12692
12693 /* extract equipment name */
12694 char eq_name[NAME_LENGTH];
12695
12696 mstrlcpy(eq_name, command + 8, sizeof(eq_name));
12697 if (strchr(eq_name, ' '))
12698 *strchr(eq_name, ' ') = 0;
12699
12700 /* get frontend name */
12701 std::string path;
12702 path += "/Equipment/";
12703 path += eq_name;
12704 path += "/Common/Frontend name";
12705 char fe_name[NAME_LENGTH];
12706 int size = NAME_LENGTH;
12707 db_get_value(hDB, 0, path.c_str(), fe_name, &size, TID_STRING, TRUE);
12708
12709 /* and ID */
12710 path = "";
12711 path += "/Equipment/";
12712 path += eq_name;
12713 path += "/Common/Event ID";
12714 WORD event_id = 0;
12715 size = sizeof(event_id);
12716 db_get_value(hDB, 0, path.c_str(), &event_id, &size, TID_WORD, TRUE);
12717
12718 if (cm_exist(fe_name, FALSE) != CM_SUCCESS) {
12719 std::string str;
12720 str += "Frontend \"";
12721 str += fe_name;
12722 str += "\" not running!";
12723 show_error(r, str.c_str());
12724 } else {
12725 HNDLE hconn;
12726 status = cm_connect_client(fe_name, &hconn);
12727 if (status != RPC_SUCCESS) {
12728 std::string str;
12729 str += "Cannot connect to frontend \"";
12730 str += fe_name;
12731 str +="\" !";
12732 show_error(r, str.c_str());
12733 } else {
12735 if (status != CM_SUCCESS)
12736 show_error(r, "Error triggering event");
12737 else
12738 redirect(r, "");
12739
12740 //cm_disconnect_client(hconn, FALSE);
12741 }
12742 }
12743
12744 Unlock(t);
12745
12746 return;
12747 }
12748
12749 /*---- switch to next subrun -------------------------------------*/
12750
12751 if (strncmp(command, "Next Subrun", 11) == 0) {
12752 int i = TRUE;
12753 db_set_value(hDB, 0, "/Logger/Next subrun", &i, sizeof(i), 1, TID_BOOL);
12754 redirect(r, "");
12755 return;
12756 }
12757
12758 /*---- cancel command --------------------------------------------*/
12759
12760 if (equal_ustring(command, "cancel")) {
12761 if (p->isparam("redir"))
12762 redirect(r, p->getparam("redir"));
12763 else
12764 redirect(r, "");
12765 return;
12766 }
12767
12768 /*---- set command -----------------------------------------------*/
12769
12770 if (equal_ustring(command, "set")) {
12771 char str[256];
12772 mstrlcpy(str, "?cmd=set", sizeof(str));
12773 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str))
12774 return;
12775
12776 const char* group = p->getparam("group");
12777 int index = atoi(p->getparam("index"));
12778 const char* value = p->getparam("value");
12779
12780 Lock(t);
12781 show_set_page(p, r, group, index, value);
12782 Unlock(t);
12783 return;
12784 }
12785
12786 /*---- find command ----------------------------------------------*/
12787
12788 if (equal_ustring(command, "find")) {
12789 const char* value = p->getparam("value");
12790 Lock(t);
12792 Unlock(t);
12793 return;
12794 }
12795
12796 /*---- CAMAC CNAF command ----------------------------------------*/
12797
12798 if (equal_ustring(command, "CNAF") || strncmp(dec_path, "CNAF", 4) == 0) {
12799 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), "?cmd=CNAF"))
12800 return;
12801
12802 Lock(t);
12803 show_cnaf_page(p, r);
12804 Unlock(t);
12805 return;
12806 }
12807
12808 /*---- ELog command ----------------------------------------------*/
12809
12810 if (equal_ustring(command, "elog")) {
12811 /* redirect to external ELOG if URL present */
12813 BOOL external_elog = FALSE;
12814 std::string external_elog_url;
12815 int size = sizeof(external_elog);
12816 status = db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, TRUE);
12817 status = db_get_value_string(hDB, 0, "/Elog/URL", 0, &external_elog_url, TRUE);
12818 if (external_elog && (external_elog_url.length() > 0)) {
12819 redirect(r, external_elog_url.c_str());
12820 return;
12821 }
12822 send_resource(r, "elog_show.html");
12823 return;
12824 }
12825
12826 // special processing for "Elog last 7d", etc
12827
12828 char cmdx[32];
12829 mstrlcpy(cmdx, command, sizeof(cmdx));
12830 cmdx[9] = 0;
12831
12832 if (equal_ustring(cmdx, "Elog last")) {
12833 // "Elog last 7d", etc
12834 send_resource(r, "elog_query.html");
12835 return;
12836 }
12837
12838 if (equal_ustring(command, "Create ELog from this page")) {
12839 std::string redir;
12840 redir += "?cmd=New+elog";
12841 redir += "&odb_path=";
12842 redir += p->getparam("odb_path");
12843 redirect(r, redir.c_str());
12844 return;
12845 }
12846
12847 if (equal_ustring(command, "Submit elog")) {
12848 Lock(t);
12849 submit_elog(odb, p, r, a);
12850 Unlock(t);
12851 return;
12852 }
12853
12854 if (equal_ustring(command, "elog_att")) {
12855 Lock(t);
12856 show_elog_attachment(p, r, dec_path);
12857 Unlock(t);
12858 return;
12859 }
12860
12861 /*---- accept command --------------------------------------------*/
12862
12863 if (equal_ustring(command, "accept")) {
12864 int refresh = atoi(p->getparam("refr"));
12865
12866 /* redirect with cookie */
12867 r->rsprintf("HTTP/1.1 302 Found\r\n");
12868 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12869 r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
12870
12871 time_t now;
12872 time(&now);
12873
12874 now += 3600 * 24 * 365;
12875
12876 struct tm gmt_tms;
12877 gmtime_r(&now, &gmt_tms);
12878
12879 char str[256];
12880 strftime(str, sizeof(str), "%A, %d-%b-%Y %H:00:00 GMT", &gmt_tms);
12881
12882 r->rsprintf("Set-Cookie: midas_refr=%d; path=/; expires=%s\r\n", refresh, str);
12883 r->rsprintf("Location: ./\r\n\r\n<html>redir</html>\r\n");
12884
12885 return;
12886 }
12887
12888#ifdef OBSOLETE
12889 /*---- slow control display --------------------------------------*/
12890
12891 if (equal_ustring(command, "eqtable")) {
12892 Lock(t);
12893 show_eqtable_page(p, r, c->refresh);
12894 Unlock(t);
12895 return;
12896 }
12897#endif
12898
12899 /*---- sequencer page --------------------------------------------*/
12900
12901 if (equal_ustring(command, "Sequencer")) {
12902 send_resource(r, "sequencer.html");
12903 return;
12904 }
12905
12906 // obsolete
12907 if (equal_ustring(command, "seq")) {
12908 send_resource(r, "sequencer.html");
12909 return;
12910 }
12911
12912 if (equal_ustring(command, "start_script")) {
12913 send_resource(r, "start_script.html");
12914 return;
12915 }
12916
12917 if (equal_ustring(command, "load_script")) {
12918 send_resource(r, "load_script.html");
12919 return;
12920 }
12921
12922 if (equal_ustring(command, "edit_script")) {
12923 send_resource(r, "edit_script.html");
12924 return;
12925 }
12926
12927 /*---- show ODB --------------------------------------------------*/
12928
12929 if (equal_ustring(command, "oldOdb")) {
12930 int write_access = TRUE;
12931 db_find_key(hDB, 0, "/Experiment/Security/Web Password", &hkey);
12932 if (hkey) {
12933 char str[256];
12934 int size = sizeof(str);
12935 db_get_data(hDB, hkey, str, &size, TID_STRING);
12936 if (strcmp(c->cookie_wpwd.c_str(), str) == 0)
12937 write_access = TRUE;
12938 else
12939 write_access = FALSE;
12940 }
12941
12942 std::string odb_path;
12943 if (p->getparam("odb_path") && *p->getparam("odb_path"))
12944 odb_path = p->getparam("odb_path");
12945
12946 Lock(t);
12947 show_odb_page(p, r, odb_path.c_str(), write_access);
12948 Unlock(t);
12949 return;
12950 }
12951
12952 /*---- New ODB browser --------------------------------------------*/
12953
12954 if (equal_ustring(command, "odb")) {
12955 send_resource(r, "odb.html");
12956 return;
12957 }
12958
12959 /*---- ODB show open records --------------------------------------*/
12960
12961 if (equal_ustring(command, "odb_sor")) {
12962 send_resource(r, "odb_sor.html");
12963 return;
12964 }
12965
12966 /*---- ODB show clients -------------------------------------------*/
12967
12968 if (equal_ustring(command, "odb_scl")) {
12969 send_resource(r, "odb_scl.html");
12970 return;
12971 }
12972
12973 /*---- old ODB path ----------------------------------------------*/
12974
12975 if ((command[0]==0) && dec_path[0]) {
12976 if (equal_ustring(dec_path, "root")) {
12977 std::string new_url = "./?cmd=odb";
12978 //printf("redirect old odb path url [%s] to [%s]\n", dec_path, new_url.c_str());
12979 redirect_307(r, new_url.c_str());
12980 return;
12981 }
12982 }
12983
12984 if ((command[0]==0) && dec_path[0]) {
12985 HNDLE hkey;
12986 status = db_find_key(hDB, 0, dec_path, &hkey);
12987 //printf("try odb path [%s], status %d\n", dec_path, status);
12988 if (status == DB_SUCCESS) {
12989 int level = 0;
12990 for (const char* s = dec_path; *s; s++) {
12991 if (*s == '/')
12992 level++;
12993 }
12994 std::string new_url;
12995 if (level == 0) {
12996 // Top-level directory like /Logger, (which appears in dec_path as "Logger")
12997 new_url += "./";
12998 } else {
12999 for (int i=0; i<level; i++) {
13000 if (i>0)
13001 new_url += "/";
13002 new_url += "..";
13003 }
13004 }
13005 new_url += "?cmd=odb";
13006 new_url += "&odb_path=";
13007 new_url += urlEncode(dec_path);
13008 //printf("redirect old odb path url [%s] to [%s]\n", dec_path, new_url.c_str());
13009 redirect_307(r, new_url.c_str());
13010 return;
13011 }
13012 }
13013
13014 /*---- event dump ------------------------------------------------*/
13015
13016 if (equal_ustring(command, "event dump")) {
13017 send_resource(r, "event_dump.html");
13018 return;
13019 }
13020
13021 /*---- custom page -----------------------------------------------*/
13022
13023 if (equal_ustring(command, "custom")) {
13024 Lock(t);
13025 show_custom_page(p, r, c->cookie_cpwd.c_str());
13026 Unlock(t);
13027 return;
13028 }
13029
13030 /*---- custom page accessed by direct URL that used to be under /CS/... ----*/
13031
13032 if (db_find_key(hDB, 0, "/Custom", &hkey) == DB_SUCCESS && dec_path[0]) {
13033 std::string odb_path;
13034 std::string value;
13035 int status;
13036
13037 odb_path = "";
13038 odb_path += "/Custom/Images/";
13039 odb_path += dec_path;
13040 odb_path += "/Background";
13041
13042 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13043
13044 //printf("Try custom gif [%s] status %d\n", odb_path.c_str(), status);
13045
13046 if (status == DB_SUCCESS) {
13047 if (strstr(dec_path, "..")) {
13048 std::string str;
13049 str += "Invalid custom gif name \'";
13050 str += dec_path;
13051 str += "\' contains \'..\'";
13052 show_error_404(r, str.c_str());
13053 return;
13054 }
13055
13056 Lock(t);
13057 show_custom_gif(r, dec_path);
13058 Unlock(t);
13059 return;
13060 }
13061
13062 bool found_custom = false;
13063
13064 odb_path = "";
13065 odb_path += "/Custom/";
13066 odb_path += dec_path;
13067
13068 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13069
13070 //printf("Try [%s] status %d\n", odb_path.c_str(), status);
13071
13072 if (status == DB_SUCCESS) {
13073 found_custom = true;
13074 } else {
13075 odb_path = "";
13076 odb_path += "/Custom/";
13077 odb_path += dec_path;
13078 odb_path += "&";
13079
13080 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13081
13082 //printf("Try [%s] status %d\n", odb_path.c_str(), status);
13083
13084 if (status == DB_SUCCESS) {
13085 found_custom = true;
13086 } else {
13087 odb_path = "";
13088 odb_path += "/Custom/";
13089 odb_path += dec_path;
13090 odb_path += "!";
13091
13092 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13093
13094 //printf("Try [%s] status %d\n", odb_path.c_str(), status);
13095
13096 if (status == DB_SUCCESS) {
13097 found_custom = true;
13098 }
13099 }
13100 }
13101
13102 if (found_custom) {
13103 //printf("custom file: serving [%s] value [%s]\n", dec_path, value.c_str());
13104 if (strstr(dec_path, "..")) {
13105 std::string str;
13106 str += "Invalid custom page name \'";
13107 str += dec_path;
13108 str += "\' contains \'..\'";
13109 show_error_404(r, str.c_str());
13110 return;
13111 }
13112
13113 p->setparam("page", dec_path);
13114 Lock(t);
13115 show_custom_page(p, r, c->cookie_cpwd.c_str());
13116 Unlock(t);
13117 return;
13118 }
13119 }
13120
13121 /* new custom pages */
13122 if (db_find_key(hDB, 0, "/Custom", &hkey) == DB_SUCCESS && dec_path[0]) {
13123 std::string custom_path;
13124 status = db_get_value_string(hDB, 0, "/Custom/Path", 0, &custom_path, TRUE);
13125 if ((status == DB_SUCCESS) && (custom_path.length() > 0)) {
13126 if (strstr(dec_path, "..")) {
13127 std::string str;
13128 str += "Invalid custom file name \'";
13129 str += dec_path;
13130 str += "\' contains \'..\'";
13131 show_error_404(r, str.c_str());
13132 return;
13133 }
13134
13135 std::string full_filename = add_custom_path(dec_path);
13136
13137 // if custom file exists, send it (like normal web server)
13138 if (ss_file_exist(full_filename.c_str())) {
13139 send_file(r, full_filename);
13140 return;
13141 }
13142 }
13143 }
13144
13145 /*---- redirect if web page --------------------------------------*/
13146
13147 //if (strlen(command) > 0) {
13148 // if (send_resource(r, std::string(command) + ".html", false))
13149 // return;
13150 //}
13151
13152 /*---- serve url as a resource file ------------------------------*/
13153
13154 if (strlen(p->getparam("path")) > 0) {
13155 if (send_resource(r, p->getparam("path"), false)) {
13156 return;
13157 }
13158 }
13159
13160 /*---- show status -----------------------------------------------*/
13161
13162 if (elog_mode) {
13163 redirect(r, "EL/");
13164 return;
13165 }
13166
13167 /* header */
13168 r->rsprintf("HTTP/1.1 400 Bad Request\r\n");
13169 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
13170 r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
13171 r->rsprintf("\r\n");
13172 r->rsprintf("Error: Invalid URL \"%s\" or query \"%s\" or command \"%s\"\n", p->getparam("path"), p->getparam("query"), command);
13173}
13174
13175/*------------------------------------------------------------------*/
13176
13177void decode_query(Param* pp, const char *query_string)
13178{
13179 int len = strlen(query_string);
13180 char *buf = (char *)malloc(len+1);
13181 assert(buf != NULL);
13182 memcpy(buf, query_string, len+1);
13183 char* p = buf;
13184 p = strtok(p, "&");
13185 while (p != NULL) {
13186 char *pitem = p;
13187 p = strchr(p, '=');
13188 if (p != NULL) {
13189 *p++ = 0;
13190 urlDecode(pitem); // parameter name
13191 if (!equal_ustring(pitem, "format"))
13192 urlDecode(p); // parameter value
13193
13194 pp->setparam(pitem, p); // decoded query parameters
13195
13196 p = strtok(NULL, "&");
13197 }
13198 }
13199 free(buf);
13200}
13201
13202void decode_get(Return* rr, char *string, const Cookies* c, const char* url, const char* query_string, RequestTrace* t)
13203{
13204 char path[256];
13205
13206 //printf("decode_get: string [%s], decode_url %d, url [%s], query_string [%s]\n", string, decode_url, url, query_string);
13207
13208 Param* param = new Param();
13209
13210 param->initparam();
13211
13212 if (url)
13213 mstrlcpy(path, url + 1, sizeof(path)); /* strip leading '/' */
13214 else {
13215 mstrlcpy(path, string + 1, sizeof(path)); /* strip leading '/' */
13216
13217 if (strchr(path, '?'))
13218 *strchr(path, '?') = 0;
13219 }
13220
13221 param->setparam("path", path);
13222
13223 assert(query_string != NULL);
13224
13225 decode_query(param, query_string);
13226
13227 param->setparam("query", query_string);
13228
13229 char dec_path[256];
13230 mstrlcpy(dec_path, path, sizeof(dec_path));
13231
13232 interprete(param, rr, NULL, c, dec_path, t);
13233
13234 param->freeparam();
13235 delete param;
13236}
13237
13238/*------------------------------------------------------------------*/
13239
13240void decode_post(Return* rr, const char *header, const char *string, const char *boundary, int length, const Cookies* c, const char* url, RequestTrace* t)
13241{
13242 bool debug_decode_post = false;
13243
13244 Param* param = new Param;
13245
13246 param->initparam();
13247
13248 char path[256];
13249
13250 if (url)
13251 mstrlcpy(path, url + 1, sizeof(path)); /* strip leading '/' */
13252 else {
13253 mstrlcpy(path, header + 1, sizeof(path)); /* strip leading '/' */
13254 if (strchr(path, '?'))
13255 *strchr(path, '?') = 0;
13256 if (strchr(path, ' '))
13257 *strchr(path, ' ') = 0;
13258 }
13259 param->setparam("path", path); // undecoded path
13260
13261 Attachment* a = new Attachment;
13262
13263 const char* pinit = string;
13264
13265 /* return if no boundary defined */
13266 if (!boundary[0])
13267 return;
13268
13269 if (strstr(string, boundary))
13270 string = strstr(string, boundary) + strlen(boundary);
13271
13272 if (debug_decode_post)
13273 printf("decode_post: -->[%s]<--\n", string);
13274
13275 do {
13276 //printf("decode_post: [%s]\n", string);
13277 if (strstr(string, "name=")) {
13278 const char* pitem = strstr(string, "name=") + 5;
13279 if (*pitem == '\"')
13280 pitem++;
13281
13282 //printf("decode_post: pitem [%s]\n", pitem);
13283
13284 if (strncmp(pitem, "attfile", 7) == 0) {
13285 int n = pitem[7] - '1';
13286
13287 char file_name[256];
13288 file_name[0] = 0;
13289
13290 /* evaluate file attachment */
13291 if (strstr(pitem, "filename=")) {
13292 const char* p = strstr(pitem, "filename=") + 9;
13293 if (*p == '\"')
13294 p++;
13295 if (strstr(p, "\r\n\r\n"))
13296 string = strstr(p, "\r\n\r\n") + 4;
13297 else if (strstr(p, "\r\r\n\r\r\n"))
13298 string = strstr(p, "\r\r\n\r\r\n") + 6;
13299
13300 mstrlcpy(file_name, p, sizeof(file_name));
13301
13302 char* pp = file_name;
13303 if (strchr(pp, '\"'))
13304 *strchr(pp, '\"') = 0;
13305
13306 /* set attachment filename */
13307 char str[256];
13308 sprintf(str, "attachment%d", n);
13309 if (debug_decode_post)
13310 printf("decode_post: [%s] = [%s]\n", str, file_name);
13311 param->setparam(str, file_name); // file_name should be decoded?
13312 }
13313
13314 /* find next boundary */
13315 const char* ptmp = string;
13316 const char* p = NULL;
13317 do {
13318 while (*ptmp != '-')
13319 ptmp++;
13320
13321 p = strstr(ptmp, boundary);
13322 if (p != NULL) {
13323 while (*p == '-')
13324 p--;
13325 if (*p == 10)
13326 p--;
13327 if (*p == 13)
13328 p--;
13329 p++;
13330 break;
13331 } else
13332 ptmp += strlen(ptmp);
13333
13334 } while (TRUE);
13335
13336 /* save pointer to file */
13337 if (file_name[0]) {
13338 size_t size = (POINTER_T) p - (POINTER_T) string;
13339 char* buf = (char*)malloc(size+1);
13340 if (!buf) {
13341 return;
13342 }
13343 memcpy(buf, string, size);
13344 buf[size] = 0; // make sure string is NUL terminated
13345 a->attachment_buffer[n] = buf;
13346 a->attachment_size[n] = size;
13347 if (debug_decode_post)
13348 printf("decode_post: attachment[%d] size %d data --->[%s]<---\n", n, (int)a->attachment_size[n], a->attachment_buffer[n]);
13349 }
13350
13351 string = strstr(p, boundary) + strlen(boundary);
13352 } else {
13353 const char* p = pitem;
13354 if (strstr(p, "\r\n\r\n"))
13355 p = strstr(p, "\r\n\r\n") + 4;
13356 else if (strstr(p, "\r\r\n\r\r\n"))
13357 p = strstr(p, "\r\r\n\r\r\n") + 6;
13358
13359 char* ppitem = (char*)strchr(pitem, '\"'); // NB: defeat "const char* string"
13360 if (ppitem)
13361 *ppitem = 0;
13362
13363 char* pb = (char*)(strstr(p, boundary)); // NB: defeat "const char* string"
13364 if (pb) {
13365 string = pb + strlen(boundary);
13366 *pb = 0;
13367 char* ptmp = (char*)(p + (strlen(p) - 1)); // NB: defeat "const char* string"
13368 while (*ptmp == '-' || *ptmp == '\n' || *ptmp == '\r')
13369 *ptmp-- = 0;
13370 } else {
13371 show_error(rr, "Invalid POST request");
13372 return;
13373 }
13374 if (debug_decode_post)
13375 printf("decode_post: [%s] = [%s]\n", pitem, p);
13376 param->setparam(pitem, p); // in decode_post()
13377 }
13378
13379 while (*string == '-' || *string == '\n' || *string == '\r')
13380 string++;
13381 }
13382
13383 } while ((POINTER_T) string - (POINTER_T) pinit < length);
13384
13385 char dec_path[256];
13386 mstrlcpy(dec_path, path, sizeof(dec_path));
13387
13388 interprete(param, rr, a, c, dec_path, t);
13389
13390 delete a;
13391 delete param;
13392}
13393
13394/*------------------------------------------------------------------*/
13395
13397{
13398 HNDLE hDB, hKeyEq, hKey;
13399 RUNINFO_STR(runinfo_str);
13400 int i, status;
13401 KEY key;
13402
13403 /* check /Runinfo structure */
13405 assert(status == DB_SUCCESS);
13406
13407 status = db_check_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str(), FALSE);
13408 if (status == DB_STRUCT_MISMATCH) {
13409 status = db_check_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str(), TRUE);
13410 if (status == DB_SUCCESS) {
13411 cm_msg(MINFO, "check_odb_records", "ODB subtree /Runinfo corrected successfully");
13412 } else {
13413 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Runinfo, db_check_record() status %d", status);
13414 return 0;
13415 }
13416 } else if (status == DB_NO_KEY) {
13417 cm_msg(MERROR, "check_odb_records", "ODB subtree /Runinfo does not exist");
13418 status = db_create_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str());
13419 if (status == DB_SUCCESS) {
13420 cm_msg(MINFO, "check_odb_records", "ODB subtree /Runinfo created successfully");
13421 } else {
13422 cm_msg(MERROR, "check_odb_records", "Cannot create ODB subtree /Runinfo, db_create_record() status %d", status);
13423 return 0;
13424 }
13425 } else if (status != DB_SUCCESS) {
13426 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Runinfo, db_check_record() status %d", status);
13427 return 0;
13428 }
13429
13430 /* check /Equipment/<name>/Common structures */
13431 if (db_find_key(hDB, 0, "/equipment", &hKeyEq) == DB_SUCCESS) {
13432 for (i = 0 ;; i++) {
13433 db_enum_key(hDB, hKeyEq, i, &hKey);
13434 if (!hKey)
13435 break;
13436 db_get_key(hDB, hKey, &key);
13437
13439 if (status == DB_STRUCT_MISMATCH) {
13441 if (status == DB_SUCCESS) {
13442 cm_msg(MINFO, "check_odb_records", "ODB subtree /Equipment/%s/Common corrected successfully", key.name);
13443 } else {
13444 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Equipment/%s/Common, db_check_record() status %d", key.name, status);
13445 }
13446 } else if (status != DB_SUCCESS) {
13447 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Equipment/%s/Common, db_check_record() status %d", key.name, status);
13448 }
13449 }
13450 }
13451
13452 return CM_SUCCESS;
13453}
13454
13455
13456/*------------------------------------------------------------------*/
13457
13458std::atomic_bool _abort{false};
13459
13460void ctrlc_handler(int sig)
13461{
13462 _abort = true;
13463}
13464
13465/*------------------------------------------------------------------*/
13466
13467#ifdef HAVE_MONGOOSE6
13468static std::vector<std::string> gUserAllowedHosts;
13469#endif
13470static std::vector<std::string> gAllowedHosts;
13471#ifdef HAVE_MONGOOSE6
13472static const std::string gOdbAllowedHosts = "/Experiment/Security/mhttpd hosts/Allowed hosts";
13473#endif
13474
13475#ifdef HAVE_MONGOOSE6
13476static void load_allowed_hosts(HNDLE hDB, HNDLE hKey, int index, void* info)
13477{
13478 if (hKey != 0)
13479 cm_msg(MINFO, "load_allowed_hosts", "Reloading mhttpd hosts access control list via hotlink callback");
13480
13481 gAllowedHosts.clear();
13482
13483 // copy the user allowed hosts
13484 for (unsigned int i=0; i<gUserAllowedHosts.size(); i++)
13485 gAllowedHosts.push_back(gUserAllowedHosts[i]);
13486
13487 int total = 0;
13488 int last = 0;
13489 for (int i=0; ; i++) {
13490 std::string s;
13491 int status = db_get_value_string(hDB, 0, gOdbAllowedHosts.c_str(), i, &s, FALSE);
13492 //printf("get %d, status %d, string [%s]\n", i, status, s.c_str());
13493 if (status != DB_SUCCESS) {
13494 total = i;
13495 break;
13496 }
13497
13498 if (s.length() < 1) // skip emties
13499 continue;
13500
13501 if (s[0] == '#') // skip commented-out entries
13502 continue;
13503
13504 //printf("add allowed hosts %d [%s]\n", i, s.c_str());
13505 gAllowedHosts.push_back(s);
13506 last = i;
13507 }
13508
13509 //printf("total %d, last %d\n", total, last);
13510
13511 if (total - last < 5) {
13512 int new_size = last + 10;
13513 //printf("new size %d\n", new_size);
13514 int status = db_resize_string(hDB, 0, gOdbAllowedHosts.c_str(), new_size, 256);
13515 if (status != DB_SUCCESS) {
13516 cm_msg(MERROR, "load_allowed_hosts", "Cannot resize the allowed hosts access control list, db_resize_string(%d) status %d", new_size, status);
13517 }
13518 }
13519}
13520
13521static int init_allowed_hosts()
13522{
13523 HNDLE hDB;
13524 HNDLE hKey;
13525 int status;
13526
13528
13529 // create "allowed hosts" so we can watch it
13530
13531 std::string s;
13532 status = db_get_value_string(hDB, 0, gOdbAllowedHosts.c_str(), 0, &s, TRUE);
13533
13534 if (status != DB_SUCCESS) {
13535 cm_msg(MERROR, "init_allowed_hosts", "Cannot create the mhttpd hosts access control list, db_get_value_string() status %d", status);
13536 return status;
13537 }
13538
13539 status = db_find_key(hDB, 0, gOdbAllowedHosts.c_str(), &hKey);
13540
13541 if (status != DB_SUCCESS || hKey == 0) {
13542 cm_msg(MERROR, "init_allowed_hosts", "Cannot find the mhttpd hosts access control list, db_find_key() status %d", status);
13543 return status;
13544 }
13545
13546 load_allowed_hosts(hDB, 0, 0, NULL);
13547
13548 status = db_watch(hDB, hKey, load_allowed_hosts, NULL);
13549
13550 if (status != DB_SUCCESS) {
13551 cm_msg(MERROR, "init_allowed_hosts", "Cannot watch the mhttpd hosts access control list, db_watch() status %d", status);
13552 return status;
13553 }
13554
13555 return SUCCESS;
13556}
13557
13558 int check_midas_acl(const struct sockaddr *sa, int len) {
13559 // access control list is empty?
13560 if (gAllowedHosts.size() == 0)
13561 return 1;
13562
13563 char hname[NI_MAXHOST];
13564 hname[0] = 0;
13565
13566 int status;
13567 const char* status_string = "success";
13568
13569 status = getnameinfo(sa, len, hname, sizeof(hname), NULL, 0, 0);
13570
13571 if (status)
13572 status_string = gai_strerror(status);
13573
13574 //printf("connection from [%s], status %d (%s)\n", hname, status, status_string);
13575
13576 if (status != 0) {
13577 printf("Rejecting connection from \'%s\', getnameinfo() status %d (%s)\n", hname, status, status_string);
13578 return 0;
13579 }
13580
13581 /* always permit localhost */
13582 if (strcmp(hname, "localhost.localdomain") == 0)
13583 return 1;
13584 if (strcmp(hname, "localhost") == 0)
13585 return 1;
13586
13587 for (unsigned int i=0 ; i<gAllowedHosts.size() ; i++)
13588 if (gAllowedHosts[i] == hname) {
13589 return 1;
13590 }
13591
13592 printf("Rejecting connection from \'%s\'\n", hname);
13593 return 0;
13594 }
13595
13596int open_listening_socket(int port)
13597{
13598 int status;
13599 struct sockaddr_in bind_addr;
13600
13601 /* create a new socket */
13602 int lsock = socket(AF_INET, SOCK_STREAM, 0);
13603
13604 if (lsock == -1) {
13605 printf("Cannot create socket, socket() errno %d (%s)\n", errno, strerror(errno));
13606 return -1;
13607 }
13608
13609 /* bind local node name and port to socket */
13610 memset(&bind_addr, 0, sizeof(bind_addr));
13611 bind_addr.sin_family = AF_INET;
13612 bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
13613 bind_addr.sin_port = htons((short) port);
13614
13615 /* try reusing address */
13616 int flag = 1;
13617 status = setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
13618
13619 if (status < 0) {
13620 printf("Cannot setsockopt(SOL_SOCKET, SO_REUSEADDR), errno %d (%s)\n", errno, strerror(errno));
13621 return -1;
13622 }
13623
13624 status = bind(lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
13625
13626 if (status < 0) {
13627 printf("Cannot bind() to port %d, bind() errno %d (%s)\n", port, errno, strerror(errno));
13628 return -1;
13629 }
13630
13631 /* listen for connection */
13632 status = listen(lsock, SOMAXCONN);
13633 if (status < 0) {
13634 printf("Cannot listen() on port %d, errno %d (%s), bye!\n", port, errno, strerror(errno));
13635 return -1;
13636 }
13637
13638 printf("mhttpd is listening on port %d\n", port);
13639
13640 return lsock;
13641}
13642#endif
13643
13644/*------------------------------------------------------------------*/
13645
13646int try_file_mg(const char* try_dir, const char* filename, std::string& path, FILE** fpp, bool trace)
13647{
13648 if (fpp)
13649 *fpp = NULL;
13650 if (!try_dir)
13651 return SS_FILE_ERROR;
13652 if (strlen(try_dir) < 1)
13653 return SS_FILE_ERROR;
13654
13655 path = try_dir;
13656 if (path[path.length()-1] != DIR_SEPARATOR)
13657 path += DIR_SEPARATOR_STR;
13658 path += filename;
13659
13660 FILE* fp = fopen(path.c_str(), "r");
13661
13662 if (trace) {
13663 if (fp)
13664 printf("file \"%s\": OK!\n", path.c_str());
13665 else
13666 printf("file \"%s\": not found.\n", path.c_str());
13667 }
13668
13669 if (!fp)
13670 return SS_FILE_ERROR;
13671 else if (fpp)
13672 *fpp = fp;
13673 else
13674 fclose(fp);
13675
13676 return SUCCESS;
13677}
13678
13679int find_file_mg(const char* filename, std::string& path, FILE** fpp, bool trace)
13680{
13681 std::string exptdir = cm_get_path();
13682
13683 if (try_file_mg(".", filename, path, fpp, trace) == SUCCESS)
13684 return SUCCESS;
13685
13686 if (try_file_mg(getenv("MIDAS_DIR"), filename, path, fpp, trace) == SUCCESS)
13687 return SUCCESS;
13688
13689 if (try_file_mg(exptdir.c_str(), filename, path, fpp, trace) == SUCCESS)
13690 return SUCCESS;
13691
13692 if (try_file_mg(getenv("MIDASSYS"), filename, path, fpp, trace) == SUCCESS)
13693 return SUCCESS;
13694
13695 // setup default filename
13696 try_file_mg(exptdir.c_str(), filename, path, NULL, false);
13697 return SS_FILE_ERROR;
13698}
13699
13700#ifdef HAVE_MONGOOSE6
13701#include "mongoose6.h"
13702#endif
13703
13704#ifdef HAVE_MONGOOSE616
13705#undef closesocket
13706#include "mongoose616.h"
13707// cs_md5() in not in mongoose.h
13708extern void cs_md5(char buf[33], ...);
13709#endif
13710
13711static bool verbose_mg = false;
13712static bool trace_mg = false;
13713static bool trace_mg_recv = false;
13714static bool trace_mg_send = false;
13715static bool trace_mg_verbose = false;
13716#ifdef HAVE_MONGOOSE616
13717static bool multithread_mg = true;
13718#endif
13719
13720#ifdef HAVE_MONGOOSE6
13721static struct mg_mgr mgr_mg;
13722#endif
13723
13725 std::string username;
13726 std::string realm;
13727 std::string password;
13728};
13729
13730class Auth {
13731public:
13732 std::string realm;
13733 std::string passwd_filename;
13734 std::vector<AuthEntry> passwords;
13735public:
13736 int Init();
13737};
13738
13739static bool read_passwords(Auth* auth);
13740
13742{
13743 std::string exptname = cm_get_experiment_name();
13744
13745 if (!exptname.empty())
13746 realm = exptname;
13747 else
13748 realm = "midas";
13749
13750 bool ok = read_passwords(this);
13751 if (!ok) {
13752 cm_msg(MERROR, "mongoose", "mongoose web server password file \"%s\" has no passwords for realm \"%s\"", passwd_filename.c_str(), realm.c_str());
13753 cm_msg(MERROR, "mongoose", "please add passwords by running: htdigest %s %s midas", passwd_filename.c_str(), realm.c_str());
13754 return SS_FILE_ERROR;
13755 }
13756
13757 return SUCCESS;
13758}
13759
13760static Auth *gAuthMg = NULL;
13761
13762static void xmg_mkmd5resp(const char *method, size_t method_len, const char *uri,
13763 size_t uri_len, const char *ha1, size_t ha1_len,
13764 const char *nonce, size_t nonce_len, const char *nc,
13765 size_t nc_len, const char *cnonce, size_t cnonce_len,
13766 const char *qop, size_t qop_len, char *resp) {
13767 static const char colon[] = ":";
13768 static const size_t one = 1;
13769 char ha2[33];
13770
13771 cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
13772 cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
13773 nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
13774 colon, one, ha2, sizeof(ha2) - 1, NULL);
13775}
13776
13777/*
13778 * Check for authentication timeout.
13779 * Clients send time stamp encoded in nonce. Make sure it is not too old,
13780 * to prevent replay attacks.
13781 * Assumption: nonce is a hexadecimal number of seconds since 1970.
13782 */
13783static int xmg_check_nonce(const char *nonce) {
13784 unsigned long now = (unsigned long) time(NULL);
13785 unsigned long val = (unsigned long) strtoul(nonce, NULL, 16);
13786 return now < val || now - val < 3600;
13787}
13788
13789/*
13790 * Authenticate HTTP request against opened passwords file.
13791 * Returns 1 if authenticated, 0 otherwise.
13792 */
13793
13795 const char *domain) {
13796 mg_printf(c,
13797 "HTTP/1.1 401 Unauthorized\r\n"
13798 "WWW-Authenticate: Digest qop=\"auth\", "
13799 "realm=\"%s\", nonce=\"%lu\"\r\n"
13800 "Content-Length: 0\r\n\r\n",
13801 domain, (unsigned long) time(NULL));
13802}
13803
13804static bool read_passwords(Auth* auth)
13805{
13806 std::string path;
13807 FILE *fp;
13808 int status = find_file_mg("htpasswd.txt", path, &fp, trace_mg||verbose_mg);
13809
13810 auth->passwd_filename = path;
13811 auth->passwords.clear();
13812
13813 if (status != SUCCESS || fp == NULL) {
13814 cm_msg(MERROR, "mongoose", "mongoose web server cannot find password file \"%s\"", path.c_str());
13815 cm_msg(MERROR, "mongoose", "please create password file: touch %s", path.c_str());
13816 return false;
13817 }
13818
13819 bool have_realm = false;
13820 char buf[256];
13821
13822 /*
13823 * Read passwords file line by line. If should have htdigest format,
13824 * i.e. each line should be a colon-separated sequence:
13825 * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD
13826 */
13827 while (fgets(buf, sizeof(buf), fp) != NULL) {
13828 char f_user[256];
13829 char f_domain[256];
13830 char f_ha1[256];
13831
13832 if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3) {
13833 AuthEntry e;
13834 e.realm = f_domain;
13835 e.username = f_user;
13836 e.password = f_ha1;
13837
13838 if (e.realm == auth->realm) {
13839 have_realm = true;
13840 auth->passwords.push_back(e);
13841 }
13842 }
13843 }
13844
13845 fclose(fp);
13846
13847 return have_realm;
13848}
13849
13850#ifdef HAVE_MONGOOSE6
13851std::string find_var_mg(struct mg_str *hdr, const char* var_name)
13852{
13853 assert(!"this code is untested!");
13854
13855 char* buf = NULL;
13856 int buf_size = 0;
13857
13858 while (1) {
13859 if (buf_size == 0) {
13860 buf_size = 256;
13861 buf = (char*)malloc(buf_size);
13862 assert(buf != NULL);
13863 }
13864
13865 int size = mg_http_parse_header(hdr, var_name, buf, buf_size);
13866
13867 if (size <= 0) {
13868 free(buf);
13869 return "";
13870 }
13871
13872 if (size < buf_size) {
13873 std::string s = buf;
13874 free(buf);
13875 return s;
13876 }
13877
13878 buf_size = buf_size*2 + 16;
13879 buf = (char*)realloc(buf, buf_size);
13880 assert(buf != NULL);
13881 }
13882}
13883#endif
13884
13885#ifdef HAVE_MONGOOSE616
13886std::string find_var_mg(struct mg_str *hdr, const char* var_name)
13887{
13888 char* buf = NULL;
13889 int buf_size = 0;
13890 int size = mg_http_parse_header2(hdr, var_name, &buf, buf_size);
13891 if (size <= 0)
13892 return "";
13893 assert(buf != NULL);
13894 std::string s = buf;
13895 free(buf);
13896 return s;
13897}
13898#endif
13899
13900static std::string check_digest_auth(struct http_message *hm, Auth* auth)
13901{
13902 char expected_response[33];
13903
13904 //printf("HereA!\n");
13905
13906 /* Parse "Authorization:" header, fail fast on parse error */
13907 struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
13908
13909 if (!hdr)
13910 return "";
13911
13912 //printf("HereB!\n");
13913
13914 std::string user = find_var_mg(hdr, "username");
13915 std::string cnonce = find_var_mg(hdr, "cnonce");
13916 std::string response = find_var_mg(hdr, "response");
13917 std::string uri = find_var_mg(hdr, "uri");
13918 std::string qop = find_var_mg(hdr, "qop");
13919 std::string nc = find_var_mg(hdr, "nc");
13920 std::string nonce = find_var_mg(hdr, "nonce");
13921
13922 if (user.length()<1) return "";
13923 if (cnonce.length()<1) return "";
13924 if (response.length()<1) return "";
13925 if (uri.length()<1) return "";
13926 if (qop.length()<1) return "";
13927 if (nc.length()<1) return "";
13928 if (nonce.length()<1) return "";
13929
13930 if (xmg_check_nonce(nonce.c_str()) == 0) return "";
13931 //printf("HereB8!\n");
13932
13933 //printf("HereC!\n");
13934
13935 const char* uri_end = strchr(hm->uri.p, ' ');
13936 if (!uri_end) return "";
13937
13938 size_t uri_length = uri_end - hm->uri.p;
13939
13940 if (uri_length != uri.length())
13941 return "";
13942
13943 int cmp = strncmp(hm->uri.p, uri.c_str(), uri_length);
13944
13945 //printf("check URI: message %d %d [%d] authorization [%s]\n", (int)hm->uri.len, uri_length, cmp, uri);
13946
13947 if (cmp != 0)
13948 return "";
13949
13950 for (unsigned i=0; i<auth->passwords.size(); i++) {
13951 AuthEntry* e = &auth->passwords[i];
13952 if (e->username != user)
13953 continue;
13954 if (e->realm != auth->realm)
13955 continue;
13956 const char* f_ha1 = e->password.c_str();
13957 int uri_len = hm->uri.len;
13958 if (hm->uri.p[uri_len] == '?')
13959 uri_len += hm->query_string.len + 1; // "+1" accounts for the "?" character
13960 xmg_mkmd5resp(hm->method.p, hm->method.len,
13961 hm->uri.p, uri_len,
13962 f_ha1, strlen(f_ha1),
13963 nonce.c_str(), nonce.length(),
13964 nc.c_str(), nc.length(),
13965 cnonce.c_str(), cnonce.length(),
13966 qop.c_str(), qop.length(),
13967 expected_response);
13968 int cmp = strcasecmp(response.c_str(), expected_response);
13969 //printf("digest_auth: expected %s, got %s, cmp %d\n", expected_response, response.c_str(), cmp);
13970 if (cmp == 0) {
13971 return e->username;
13972 }
13973 }
13974
13975 return "";
13976}
13977
13978#ifdef HAVE_MONGOOSE616
13979
13980struct HostlistCacheEntry
13981{
13982 time_t time_created = 0;
13983 time_t time_last_used = 0;
13984 int count_used = 0;
13985 bool ipv4 = false;
13986 bool ipv6 = false;
13987 uint32_t ipv4addr = 0;
13988 struct in6_addr ipv6addr;
13989 std::string hostname;
13990 int gai_status = 0;
13991 std::string gai_strerror;
13992 bool ok = false;
13993};
13994
13995static std::vector<HostlistCacheEntry*> gHostlistCache;
13996
13997static void print_hostlist_cache()
13998{
13999 time_t now = time(NULL);
14000
14001 for (unsigned i=0; i<gHostlistCache.size(); i++) {
14002 HostlistCacheEntry* e = gHostlistCache[i];
14003 if (!e) {
14004 // empty slot
14005 continue;
14006 }
14007
14008 printf("%3d: %s \"%s\", ok %d, count_used %d, age created: %d, last_used %d",
14009 i,
14010 e->ipv4?"IPv4":(e->ipv6?"IPv6":"????"),
14011 e->hostname.c_str(),
14012 e->ok,
14013 e->count_used,
14014 (int)(now - e->time_created),
14015 (int)(now - e->time_last_used));
14016
14017 if (e->gai_status) {
14018 printf(", getnameinfo() status %d (%s)", e->gai_status, e->gai_strerror.c_str());
14019 }
14020
14021 printf("\n");
14022 }
14023}
14024
14025static bool mongoose_check_hostlist(const union socket_address *sa)
14026{
14027 time_t now = time(NULL);
14028 bool ipv4 = false;
14029 bool ipv6 = false;
14030 uint32_t ipv4addr = 0;
14031 struct in6_addr ipv6addr;
14032
14033 if (sa->sa.sa_family == AF_INET) {
14034 ipv4 = true;
14035 ipv4addr = sa->sin.sin_addr.s_addr;
14036 } else if (sa->sa.sa_family == AF_INET6) {
14037 ipv6 = true;
14038 memcpy(&ipv6addr, &sa->sin6.sin6_addr, sizeof(ipv6addr));
14039 } else {
14040 printf("Rejecting connection from unknown address family %d (AF_xxx)\n", sa->sa.sa_family);
14041 return false;
14042 }
14043
14044 for (unsigned i=0; i<gHostlistCache.size(); i++) {
14045 HostlistCacheEntry* e = gHostlistCache[i];
14046 if (!e) {
14047 // empty slot
14048 continue;
14049 }
14050
14051 if ((ipv4 == e->ipv4) && (ipv4addr == e->ipv4addr)) {
14052 // IPv4 address match
14053 e->time_last_used = now;
14054 e->count_used++;
14055 return e->ok;
14056 }
14057
14058 if ((ipv6 == e->ipv6) && (memcmp(&ipv6addr, &e->ipv6addr, sizeof(ipv6addr)) == 0)) {
14059 // IPv6 address match
14060 e->time_last_used = now;
14061 e->count_used++;
14062 return e->ok;
14063 }
14064
14065 // not this one. maybe expire old entries?
14066
14067 if (e->time_last_used < now - 24*60*60) {
14068 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);
14069 gHostlistCache[i] = NULL;
14070 delete e;
14071 }
14072 }
14073
14074 // not found in cache
14075
14076 assert(ipv4 || ipv6);
14077
14078 HostlistCacheEntry* e = new HostlistCacheEntry;
14079
14080 bool found = false;
14081 for (unsigned i=0; i<gHostlistCache.size(); i++) {
14082 if (gHostlistCache[i] == NULL) {
14083 gHostlistCache[i] = e;
14084 found = true;
14085 }
14086 }
14087 if (!found) {
14088 gHostlistCache.push_back(e);
14089 }
14090
14091 e->time_created = now;
14092 e->time_last_used = now;
14093 e->count_used = 1;
14094 e->ipv4 = ipv4;
14095 e->ipv6 = ipv6;
14096 if (ipv4)
14097 e->ipv4addr = ipv4addr;
14098 if (ipv6)
14099 memcpy(&e->ipv6addr, &ipv6addr, sizeof(ipv6addr));
14100 e->ok = false;
14101
14102 char hname[NI_MAXHOST];
14103 hname[0] = 0;
14104
14105 e->gai_status = getnameinfo(&sa->sa, sizeof(*sa), hname, sizeof(hname), NULL, 0, 0);
14106
14107 if (e->gai_status) {
14108 e->gai_strerror = gai_strerror(e->gai_status);
14109
14110 printf("Rejecting connection from \'%s\', getnameinfo() status %d (%s)\n", hname, e->gai_status, e->gai_strerror.c_str());
14111
14112 e->ok = false;
14113 return e->ok;
14114 }
14115
14116 printf("connection from \"%s\"\n", hname);
14117
14118 e->hostname = hname;
14119
14120 /* always permit localhost */
14121 if (e->hostname == "localhost.localdomain")
14122 e->ok = true;
14123 else if (e->hostname == "localhost")
14124 e->ok = true;
14125 else {
14126 for (unsigned int i=0 ; i<gAllowedHosts.size() ; i++) {
14127 if (e->hostname == gAllowedHosts[i]) {
14128 e->ok = true;
14129 }
14130 }
14131 }
14132
14133 if (!e->ok) {
14134 printf("Rejecting connection from \'%s\'\n", hname);
14135 }
14136
14137 print_hostlist_cache();
14138
14139 return e->ok;
14140}
14141
14142#endif
14143
14144static std::string mgstr(const mg_str* s)
14145{
14146 return std::string(s->p, s->len);
14147}
14148
14149static const std::string find_header_mg(const struct http_message *msg, const char* name)
14150{
14151 size_t nlen = strlen(name);
14152 for (int i=0; i<MG_MAX_HTTP_HEADERS; i++) {
14153 if (msg->header_names[i].len != nlen)
14154 continue;
14155 if (strncmp(msg->header_names[i].p, name, nlen) != 0)
14156 continue;
14157 return mgstr(&msg->header_values[i]);
14158 }
14159 return "";
14160}
14161
14162static const std::string find_cookie_mg(const struct http_message *msg, const char* cookie_name)
14163{
14164 const std::string cookies = find_header_mg(msg, "Cookie");
14165 if (cookies.length() < 1)
14166 return "";
14167 const char* p = strstr(cookies.c_str(), cookie_name);
14168 if (!p)
14169 return "";
14170 const char* v = p+strlen(cookie_name);
14171 if (*v != '=')
14172 return "";
14173 v++;
14174 //printf("cookie [%s] value [%s]\n", cookie_name, v);
14175 return v;
14176}
14177
14178// Generic event handler
14179
14180static void handle_event_mg(struct mg_connection *nc, int ev, void *ev_data)
14181{
14182 struct mbuf *io = &nc->recv_mbuf;
14183 switch (ev) {
14184 case MG_EV_POLL: // periodic call from loop_mg() via mg_mgr_poll()
14185 break;
14186 case MG_EV_ACCEPT:
14187 if (trace_mg)
14188 printf("handle_event_mg: nc %p, ev %d, ev_data %p -> accept\n", nc, ev, ev_data);
14189 break;
14190 case MG_EV_RECV:
14191 if (trace_mg)
14192 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);
14193#if 0
14194 // This event handler implements simple TCP echo server
14195 mg_send(nc, io->buf, io->len); // Echo received data back
14196 mbuf_remove(io, io->len); // Discard data from recv buffer
14197#endif
14198 break;
14199 case MG_EV_SEND:
14200 if (trace_mg)
14201 printf("handle_event_mg: nc %p, ev %d, ev_data %p -> send %d bytes\n", nc, ev, ev_data, *(int*)ev_data);
14202 break;
14203 case MG_EV_CLOSE:
14204 if (trace_mg)
14205 printf("handle_event_mg: nc %p, ev %d, ev_data %p -> close\n", nc, ev, ev_data);
14206 break;
14207 default:
14208 if (trace_mg)
14209 printf("handle_event_mg: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
14210 break;
14211 }
14212}
14213
14215{
14216 // extract password cookies
14217
14218 char cookie_pwd[256]; // general access password
14219 char cookie_wpwd[256]; // "write mode" password
14220 char cookie_cpwd[256]; // custom page and javascript password
14221
14222 cookie_pwd[0] = 0;
14223 cookie_wpwd[0] = 0;
14224 cookie_cpwd[0] = 0;
14225
14226 std::string s = find_cookie_mg(msg, "midas_pwd");
14227 if (s.length() > 0) {
14228 mstrlcpy(cookie_pwd, s.c_str(), sizeof(cookie_pwd));
14229 cookie_pwd[strcspn(cookie_pwd, " ;\r\n")] = 0;
14230 }
14231
14232 s = find_cookie_mg(msg, "midas_wpwd");
14233 if (s.length()) {
14234 mstrlcpy(cookie_wpwd, s.c_str(), sizeof(cookie_pwd));
14235 cookie_wpwd[strcspn(cookie_wpwd, " ;\r\n")] = 0;
14236 }
14237
14238 s = find_cookie_mg(msg, "cpwd");
14239 if (s.length()) {
14240 mstrlcpy(cookie_cpwd, s.c_str(), sizeof(cookie_pwd));
14241 cookie_cpwd[strcspn(cookie_cpwd, " ;\r\n")] = 0;
14242 }
14243
14244 // extract refresh rate
14245 c->refresh = DEFAULT_REFRESH;
14246 s = find_cookie_mg(msg, "midas_refr");
14247 if (s.length() > 0)
14248 c->refresh = atoi(s.c_str());
14249
14250 // extract equipment expand flag
14251 //c->expand_equipment = 0;
14252 //s = find_cookie_mg(msg, "midas_expeq");
14253 //if (s.length() > 0)
14254 // c->expand_equipment = atoi(s.c_str());
14255
14256 c->cookie_pwd = cookie_pwd;
14257 c->cookie_wpwd = cookie_wpwd;
14258 c->cookie_cpwd = cookie_cpwd;
14259}
14260
14261#define RESPONSE_SENT 1
14262#define RESPONSE_QUEUED 2
14263#define RESPONSE_501 3
14264
14265static int handle_decode_get(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
14266{
14267 Cookies cookies;
14268
14269 decode_cookies(&cookies, msg);
14270
14271 // lock shared structures
14272
14273#ifdef HAVE_MONGOOSE6
14274 int status = ss_mutex_wait_for(request_mutex, 0);
14275 assert(status == SS_SUCCESS);
14276#endif
14277
14278 //t->fTimeLocked = GetTimeSec();
14279
14280 // prepare return buffer
14281
14282 Return *rr = new Return();
14283
14284 rr->zero();
14285
14286 // call midas
14287
14288 decode_get(rr, NULL, &cookies, uri, query_string, t);
14289
14290 if (trace_mg)
14291 printf("handle_decode_get: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14292
14294
14295 if (rr->return_length == -1) {
14296 delete rr;
14297#ifdef HAVE_MONGOOSE6
14298 //t->fTimeUnlocked = GetTimeSec();
14299 ss_mutex_release(request_mutex);
14300#endif
14301 return RESPONSE_501;
14302 }
14303
14304 if (rr->return_length == 0)
14305 rr->return_length = strlen(rr->return_buffer);
14306
14307 //t->fTimeUnlocked = GetTimeSec();
14308
14309#ifdef HAVE_MONGOOSE6
14310 ss_mutex_release(request_mutex);
14311#endif
14312
14313 mg_send(nc, rr->return_buffer, rr->return_length);
14314
14315 if (!strstr(rr->return_buffer, "Content-Length")) {
14316 // cannot do pipelined http if response generated by mhttpd
14317 // decode_get() has no Content-Length header.
14318 // must close the connection.
14320 }
14321
14322 t->fTimeSent = GetTimeSec();
14323
14324 delete rr;
14325
14326 return RESPONSE_SENT;
14327}
14328
14329#ifdef HAVE_MONGOOSE616
14330
14331static uint32_t s_ncseqno = 1;
14332
14333struct MongooseNcUserData
14334{
14335 uint32_t ncseqno = 0;
14336
14337 MongooseNcUserData() // ctor
14338 {
14339 ncseqno = s_ncseqno++;
14340 //printf("MongooseNcUserData::ctor! ncseqno %d\n", ncseqno);
14341 }
14342
14343 ~MongooseNcUserData() // ctor
14344 {
14345 //printf("MongooseNcUserData::dtor! ncseqno %d\n", ncseqno);
14346 }
14347};
14348
14349static uint32_t GetNcSeqno(const mg_connection* nc)
14350{
14351 if (nc == NULL)
14352 return 0;
14353 if (nc->user_data == NULL)
14354 return 0;
14355 const MongooseNcUserData* ncud = (const MongooseNcUserData*)nc->user_data;
14356 return ncud->ncseqno;
14357}
14358
14359static uint32_t s_wseqno = 1;
14360
14361struct MongooseWorkObject
14362{
14363 uint32_t wseqno = 0;
14364 mg_connection* nc = NULL;
14365 uint32_t wncseqno = 0;
14366 bool http_get = false;
14367 bool http_post = false;
14368 bool mjsonrpc = false;
14369 Cookies cookies;
14370 std::string origin;
14371 std::string uri;
14372 std::string query_string;
14373 std::string post_body;
14374 std::string post_boundary;
14375 RequestTrace* t = NULL;
14376 bool send_done = false;
14377
14378 MongooseWorkObject(mg_connection* xnc) // ctor
14379 {
14380 wseqno = s_wseqno++;
14381 nc = xnc;
14382 wncseqno = GetNcSeqno(nc);
14383
14384 //printf("MongooseWorkObject::ctor! wseqno %d, nc %p, wncseqno %d\n", wseqno, nc, wncseqno);
14385 }
14386
14387 ~MongooseWorkObject() // dtor
14388 {
14389 //printf("MongooseWorkObject::dtor! wseqno %d, nc %p, wncseqno %d\n", wseqno, nc, wncseqno);
14390
14391 // poison pointers
14392 nc = NULL;
14393 t = NULL;
14394 }
14395};
14396
14397struct MongooseThreadObject
14398{
14399 std::atomic_bool fIsRunning{false};
14400 std::thread* fThread = NULL; // thread
14401 void* fNc = NULL; // thread is attached to this network connection
14402 std::mutex fMutex;
14403 std::deque<MongooseWorkObject*> fQueue;
14404 std::condition_variable fNotify;
14405
14406 //MongooseThreadObject() // ctor
14407 //{
14408 // printf("MongooseThreadObject %p created!\n", this);
14409 //}
14410
14411 //~MongooseThreadObject() // dtor
14412 //{
14413 // printf("MongooseThreadObject %p destroyed!\n", this);
14414 //}
14415};
14416
14417static std::vector<MongooseThreadObject*> gMongooseThreads;
14418
14419static void mongoose_thread(MongooseThreadObject*);
14420
14421MongooseThreadObject* FindThread(void* nc)
14422{
14423 //printf("FindThread: nc %p, thread %s\n", nc, ss_tid_to_string(ss_gettid()).c_str());
14424
14425 MongooseThreadObject* last_not_connected = NULL;
14426
14427 for (auto it : gMongooseThreads) {
14428 MongooseThreadObject* to = it;
14429 if (to->fNc == nc) {
14430 //printf("to %p, nc %p: found thread\n", to, nc);
14431 return to;
14432 }
14433 if (to->fNc == NULL) {
14434 last_not_connected = to;
14435 }
14436 }
14437
14438 if (last_not_connected) {
14439 MongooseThreadObject* to = last_not_connected;
14440 to->fNc = nc;
14441 //printf("to %p, nc %p: reusing thread\n", to, nc);
14442 return to;
14443 }
14444
14445 MongooseThreadObject* to = new MongooseThreadObject();
14446
14447 to->fNc = nc;
14448
14449 //printf("to %p, nc %p: new thread\n", to, nc);
14450
14451 gMongooseThreads.push_back(to);
14452
14453 printf("Mongoose web server is using %d threads \r", (int)gMongooseThreads.size());
14454 fflush(stdout);
14455
14456 to->fThread = new std::thread(mongoose_thread, to);
14457
14458 return to;
14459}
14460
14461void FreeThread(void* nc)
14462{
14463 //printf("FreeThread, nc %p\n", nc);
14464
14465 for (auto it : gMongooseThreads) {
14466 MongooseThreadObject* to = it;
14467 if (to->fNc == nc) {
14468 //printf("to %p, nc %p: connection closed\n", to, nc);
14469 to->fNc = NULL;
14470 return;
14471 }
14472 }
14473
14474 //printf("to %p, nc %p: connection closed, but no thread\n", nullptr, nc);
14475}
14476
14477static void mongoose_queue(mg_connection* nc, MongooseWorkObject* w)
14478{
14479 w->nc = nc;
14480 MongooseThreadObject* to = FindThread(nc);
14481 assert(to->fNc == nc);
14482 to->fMutex.lock();
14483 to->fQueue.push_back(w);
14484 to->fMutex.unlock();
14485 to->fNotify.notify_one();
14486}
14487
14488static void mongoose_send(mg_connection* nc, MongooseWorkObject* w, const char* p1, size_t s1, const char* p2, size_t s2, bool close_flag = false);
14489
14490static int queue_decode_get(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
14491{
14492 MongooseWorkObject* w = new MongooseWorkObject(nc);
14493 w->http_get = true;
14494 decode_cookies(&w->cookies, msg);
14495 w->uri = uri;
14496 w->query_string = query_string;
14497 w->t = t;
14498
14499 mongoose_queue(nc, w);
14500
14501 return RESPONSE_QUEUED;
14502}
14503
14504static int queue_decode_post(struct mg_connection *nc, const http_message* msg, const char* boundary, const char* uri, const char* query_string, RequestTrace* t)
14505{
14506 MongooseWorkObject* w = new MongooseWorkObject(nc);
14507 w->http_post = true;
14508 decode_cookies(&w->cookies, msg);
14509 w->uri = uri;
14510 w->query_string = query_string;
14511 w->post_body = mgstr(&msg->body);
14512 w->post_boundary = boundary;
14513 w->t = t;
14514
14515 mongoose_queue(nc, w);
14516
14517 return RESPONSE_QUEUED;
14518}
14519
14520static int queue_mjsonrpc(struct mg_connection *nc, const std::string& origin, const std::string& post_body, RequestTrace* t)
14521{
14522 MongooseWorkObject* w = new MongooseWorkObject(nc);
14523 w->mjsonrpc = true;
14524 w->origin = origin;
14525 w->post_body = post_body;
14526 w->t = t;
14527
14528 mongoose_queue(nc, w);
14529
14530 return RESPONSE_QUEUED;
14531}
14532
14533static int thread_http_get(mg_connection *nc, MongooseWorkObject *w)
14534{
14535 // lock shared structures
14536
14537 //int status = ss_mutex_wait_for(request_mutex, 0);
14538 //assert(status == SS_SUCCESS);
14539
14540 //w->t->fTimeLocked = GetTimeSec();
14541
14542 // prepare return buffer
14543
14544 Return *rr = new Return();
14545
14546 rr->zero();
14547
14548 // call midas
14549
14550 decode_get(rr, NULL, &w->cookies, w->uri.c_str(), w->query_string.c_str(), w->t);
14551
14552 if (trace_mg)
14553 printf("handle_decode_get: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14554
14555 w->t->fTimeProcessed = GetTimeSec();
14556
14557 if (rr->return_length == -1) {
14558 delete rr;
14559 //w->t->fTimeUnlocked = GetTimeSec();
14560 //ss_mutex_release(request_mutex);
14561 return RESPONSE_501;
14562 }
14563
14564 if (rr->return_length == 0)
14565 rr->return_length = strlen(rr->return_buffer);
14566
14567 //w->t->fTimeUnlocked = GetTimeSec();
14568
14569 //ss_mutex_release(request_mutex);
14570
14571 bool close_flag = false;
14572
14573 if (!strstr(rr->return_buffer, "Content-Length")) {
14574 // cannot do pipelined http if response generated by mhttpd
14575 // decode_get() has no Content-Length header.
14576 // must close the connection.
14577 close_flag = true;
14578 }
14579
14580 mongoose_send(nc, w, rr->return_buffer, rr->return_length, NULL, 0, close_flag);
14581
14582 w->t->fTimeSent = GetTimeSec();
14583
14584 delete rr;
14585
14586 return RESPONSE_SENT;
14587}
14588
14589static int thread_http_post(mg_connection *nc, MongooseWorkObject *w)
14590{
14591 const char* post_data = w->post_body.c_str();
14592 int post_data_len = w->post_body.length();
14593
14594 // lock shared strctures
14595
14596 //int status = ss_mutex_wait_for(request_mutex, 0);
14597 //assert(status == SS_SUCCESS);
14598
14599 // prepare return buffer
14600
14601 Return* rr = new Return;
14602
14603 rr->zero();
14604
14605 //printf("post_data_len %d, data [%s], boundary [%s]\n", post_data_len, post_data, boundary);
14606
14607 decode_post(rr, NULL, (char*)post_data, w->post_boundary.c_str(), post_data_len, &w->cookies, w->uri.c_str(), w->t);
14608
14609 if (trace_mg)
14610 printf("handle_decode_post: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14611
14612 if (rr->return_length == -1) {
14613 //ss_mutex_release(request_mutex);
14614 delete rr;
14615 return RESPONSE_501;
14616 }
14617
14618 if (rr->return_length == 0)
14619 rr->return_length = strlen(rr->return_buffer);
14620
14621 //ss_mutex_release(request_mutex);
14622
14623 bool close_flag = false;
14624 if (!strstr(rr->return_buffer, "Content-Length")) {
14625 // cannot do pipelined http if response generated by mhttpd
14626 // decode_get() has no Content-Length header.
14627 // must close the connection.
14628 close_flag = true;
14629 }
14630
14631 mongoose_send(nc, w, rr->return_buffer, rr->return_length, NULL, 0, close_flag);
14632
14633 delete rr;
14634
14635 return RESPONSE_SENT;
14636
14637}
14638
14639static int thread_mjsonrpc(mg_connection *nc, MongooseWorkObject *w)
14640{
14641 w->t->fRPC = w->post_body;
14642
14643 //int status = ss_mutex_wait_for(request_mutex, 0);
14644 //assert(status == SS_SUCCESS);
14645
14646 //gMutex.lock();
14647 //w->t->fTimeLocked = GetTimeSec();
14648
14649 MJsonNode* reply = mjsonrpc_decode_post_data(w->post_body.c_str());
14650
14651 //w->t->fTimeUnlocked = GetTimeSec();
14652 //gMutex.unlock();
14653
14654 //ss_mutex_release(request_mutex);
14655
14656 if (reply->GetType() == MJSON_ARRAYBUFFER) {
14657 const char* ptr;
14658 size_t size;
14659 reply->GetArrayBuffer(&ptr, &size);
14660
14661 std::string headers;
14662 headers += "HTTP/1.1 200 OK\n";
14663 if (w->origin.length() > 0)
14664 headers += "Access-Control-Allow-Origin: " + w->origin + "\n";
14665 else
14666 headers += "Access-Control-Allow-Origin: *\n";
14667 headers += "Access-Control-Allow-Credentials: true\n";
14668 headers += "Content-Length: " + toString(size) + "\n";
14669 headers += "Content-Type: application/octet-stream\n";
14670 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14671
14672 //printf("sending headers: %s\n", headers.c_str());
14673 //printf("sending reply: %s\n", reply_string.c_str());
14674
14675 std::string send = headers + "\n";
14676
14677 w->t->fTimeProcessed = GetTimeSec();
14678
14679 mongoose_send(nc, w, send.c_str(), send.length(), ptr, size);
14680
14681 w->t->fTimeSent = GetTimeSec();
14682
14683 delete reply;
14684
14685 return RESPONSE_SENT;
14686 }
14687
14688 std::string reply_string = reply->Stringify();
14689 int reply_length = reply_string.length();
14690
14691 std::string headers;
14692 headers += "HTTP/1.1 200 OK\n";
14693 if (w->origin.length() > 0)
14694 headers += "Access-Control-Allow-Origin: " + w->origin + "\n";
14695 else
14696 headers += "Access-Control-Allow-Origin: *\n";
14697 headers += "Access-Control-Allow-Credentials: true\n";
14698 headers += "Content-Length: " + toString(reply_length) + "\n";
14699 headers += "Content-Type: application/json\n";
14700 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14701
14702 if (trace_mg_verbose) {
14703 printf("-----------------------\nSending headers: %s", headers.c_str());
14704 std::string r = reply_string.substr(0, 128);
14705 printf("-----------------------\nSending reply (%d bytes): %s\n\n\n", (int)reply_string.size(), r.c_str());
14706 }
14707
14708 std::string send = headers + "\n" + reply_string;
14709
14710 w->t->fTimeProcessed = GetTimeSec();
14711
14712 mongoose_send(nc, w, send.c_str(), send.length(), NULL, 0);
14713
14714 w->t->fTimeSent = GetTimeSec();
14715
14716 delete reply;
14717
14718 return RESPONSE_SENT;
14719}
14720
14721static int thread_work_function(mg_connection *nc, MongooseWorkObject *w)
14722{
14723 if (w->http_get)
14724 return thread_http_get(nc, w);
14725 else if (w->http_post)
14726 return thread_http_post(nc, w);
14727 else if (w->mjsonrpc)
14728 return thread_mjsonrpc(nc, w);
14729 else
14730 return RESPONSE_501;
14731}
14732
14733#endif
14734
14735static int handle_decode_post(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
14736{
14737
14738 char boundary[256];
14739 boundary[0] = 0;
14740 const std::string ct = find_header_mg(msg, "Content-Type");
14741 if (ct.length() > 0) {
14742 const char* s = strstr(ct.c_str(), "boundary=");
14743 if (s)
14744 mstrlcpy(boundary, s+9, sizeof(boundary));
14745 }
14746
14747#ifdef HAVE_MONGOOSE616
14748 if (multithread_mg)
14749 return queue_decode_post(nc, msg, boundary, uri, query_string, t);
14750#endif
14751
14752 Cookies cookies;
14753
14754 decode_cookies(&cookies, msg);
14755
14756 const char* post_data = msg->body.p;
14757 int post_data_len = msg->body.len;
14758
14759 // lock shared strctures
14760
14761#ifdef HAVE_MONGOOSE6
14762 int status = ss_mutex_wait_for(request_mutex, 0);
14763 assert(status == SS_SUCCESS);
14764#endif
14765
14766 // prepare return buffer
14767
14768 Return* rr = new Return;
14769
14770 rr->zero();
14771
14772 //printf("post_data_len %d, data [%s], boundary [%s]\n", post_data_len, post_data, boundary);
14773
14774 decode_post(rr, NULL, (char*)post_data, boundary, post_data_len, &cookies, uri, t);
14775
14776 if (trace_mg)
14777 printf("handle_decode_post: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14778
14779 if (rr->return_length == -1) {
14780#ifdef HAVE_MONGOOSE6
14781 ss_mutex_release(request_mutex);
14782#endif
14783 delete rr;
14784 return RESPONSE_501;
14785 }
14786
14787 if (rr->return_length == 0)
14788 rr->return_length = strlen(rr->return_buffer);
14789
14790#ifdef HAVE_MONGOOSE6
14791 ss_mutex_release(request_mutex);
14792#endif
14793
14794 mg_send(nc, rr->return_buffer, rr->return_length);
14795
14796 if (!strstr(rr->return_buffer, "Content-Length")) {
14797 // cannot do pipelined http if response generated by mhttpd
14798 // decode_get() has no Content-Length header.
14799 // must close the connection.
14801 }
14802
14803 delete rr;
14804
14805 return RESPONSE_SENT;
14806}
14807
14808static int handle_http_get(struct mg_connection *nc, const http_message* msg, const char* uri, RequestTrace* t)
14809{
14810 std::string query_string = mgstr(&msg->query_string);
14811
14812 if (trace_mg||verbose_mg)
14813 printf("handle_http_get: uri [%s], query [%s]\n", uri, query_string.c_str());
14814
14815 if (query_string == "mjsonrpc_schema") {
14816 MJsonNode* s = mjsonrpc_get_schema();
14817 std::string reply = s->Stringify();
14818 delete s;
14819
14820 int reply_length = reply.length();
14821
14822 const std::string origin_header = find_header_mg(msg, "Origin");
14823
14824 std::string headers;
14825 headers += "HTTP/1.1 200 OK\n";
14826 if (origin_header.length() > 0)
14827 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14828 else
14829 headers += "Access-Control-Allow-Origin: *\n";
14830 headers += "Access-Control-Allow-Credentials: true\n";
14831 headers += "Content-Length: " + toString(reply_length) + "\n";
14832 headers += "Content-Type: application/json\n";
14833 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14834
14835 //printf("sending headers: %s\n", headers.c_str());
14836 //printf("sending reply: %s\n", reply.c_str());
14837
14838 std::string send = headers + "\n" + reply;
14839
14841
14842 mg_send(nc, send.c_str(), send.length());
14843
14844 t->fTimeSent = GetTimeSec();
14845
14846 return RESPONSE_SENT;
14847 }
14848
14849 if (query_string == "mjsonrpc_schema_text") {
14850 MJsonNode* s = mjsonrpc_get_schema();
14851 std::string reply = mjsonrpc_schema_to_text(s);
14852 delete s;
14853
14854 int reply_length = reply.length();
14855
14856 const std::string origin_header = find_header_mg(msg, "Origin");
14857
14858 std::string headers;
14859 headers += "HTTP/1.1 200 OK\n";
14860 if (origin_header.length() > 0)
14861 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14862 else
14863 headers += "Access-Control-Allow-Origin: *\n";
14864 headers += "Access-Control-Allow-Credentials: true\n";
14865 headers += "Content-Length: " + toString(reply_length) + "\n";
14866 headers += "Content-Type: text/plain\n";
14867 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14868
14869 //printf("sending headers: %s\n", headers.c_str());
14870 //printf("sending reply: %s\n", reply.c_str());
14871
14872 std::string send = headers + "\n" + reply;
14873
14875
14876 mg_send(nc, send.c_str(), send.length());
14877
14878 t->fTimeSent = GetTimeSec();
14879
14880 return RESPONSE_SENT;
14881 }
14882
14883#ifdef HAVE_MONGOOSE616
14884 if (multithread_mg)
14885 return queue_decode_get(nc, msg, uri, query_string.c_str(), t);
14886#endif
14887
14888 return handle_decode_get(nc, msg, uri, query_string.c_str(), t);
14889}
14890
14891static int handle_http_post(struct mg_connection *nc, const http_message* msg, const char* uri, RequestTrace* t)
14892{
14893 std::string query_string = mgstr(&msg->query_string);
14894 std::string post_data = mgstr(&msg->body);
14895
14896 if (trace_mg||verbose_mg)
14897 printf("handle_http_post: uri [%s], query [%s], post data %d bytes\n", uri, query_string.c_str(), (int)post_data.length());
14898 if (trace_mg_verbose)
14899 printf("handle_http_post: post data = \n%s\n", post_data.c_str());
14900
14901 if (query_string.substr(0, 8) == "mjsonrpc") { // ignore any parameter after "mjsonrpc"
14902 const std::string origin_header = find_header_mg(msg, "Origin");
14903 const std::string ctype_header = find_header_mg(msg, "Content-Type");
14904
14905 if (strstr(ctype_header.c_str(), "application/json") == NULL) {
14906 std::string headers;
14907 headers += "HTTP/1.1 415 Unsupported Media Type\n";
14908 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14909
14910 //printf("sending headers: %s\n", headers.c_str());
14911 //printf("sending reply: %s\n", reply.c_str());
14912
14913 if (trace_mg_verbose)
14914 printf("handle_http_post: unsupported media type \"%s\"\n", ctype_header.c_str());
14915
14916 std::string send = headers + "\n";
14917
14919
14920 mg_send(nc, send.c_str(), send.length());
14921
14922 t->fTimeSent = GetTimeSec();
14923
14924 return RESPONSE_SENT;
14925 }
14926
14927#ifdef HAVE_MONGOOSE616
14928 if (multithread_mg)
14929 return queue_mjsonrpc(nc, origin_header, post_data, t);
14930#endif
14931
14932 //printf("post body: %s\n", post_data.c_str());
14933
14934 t->fRPC = post_data;
14935
14936#ifdef HAVE_MONGOOSE6
14937 int status = ss_mutex_wait_for(request_mutex, 0);
14938 assert(status == SS_SUCCESS);
14939#endif
14940
14941 //t->fTimeLocked = GetTimeSec();
14942
14943 MJsonNode* reply = mjsonrpc_decode_post_data(post_data.c_str());
14944
14945 //t->fTimeUnlocked = GetTimeSec();
14946
14947#ifdef HAVE_MONGOOSE6
14948 ss_mutex_release(request_mutex);
14949#endif
14950
14951 if (reply->GetType() == MJSON_ARRAYBUFFER) {
14952 const char* ptr;
14953 size_t size;
14954 reply->GetArrayBuffer(&ptr, &size);
14955
14956 std::string headers;
14957 headers += "HTTP/1.1 200 OK\n";
14958 if (origin_header.length() > 0)
14959 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14960 else
14961 headers += "Access-Control-Allow-Origin: *\n";
14962 headers += "Access-Control-Allow-Credentials: true\n";
14963 headers += "Content-Length: " + toString(size) + "\n";
14964 headers += "Content-Type: application/octet-stream\n";
14965 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14966
14967 //printf("sending headers: %s\n", headers.c_str());
14968 //printf("sending reply: %s\n", reply_string.c_str());
14969
14970 std::string send = headers + "\n";
14971
14973
14974 mg_send(nc, send.c_str(), send.length());
14975 mg_send(nc, ptr, size);
14976
14977 t->fTimeSent = GetTimeSec();
14978
14979 delete reply;
14980
14981 return RESPONSE_SENT;
14982 }
14983
14984 std::string reply_string = reply->Stringify();
14985 int reply_length = reply_string.length();
14986
14987 std::string headers;
14988 headers += "HTTP/1.1 200 OK\n";
14989 if (origin_header.length() > 0)
14990 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14991 else
14992 headers += "Access-Control-Allow-Origin: *\n";
14993 headers += "Access-Control-Allow-Credentials: true\n";
14994 headers += "Content-Length: " + toString(reply_length) + "\n";
14995 headers += "Content-Type: application/json\n";
14996 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14997
14998 //printf("sending headers: %s\n", headers.c_str());
14999 //printf("sending reply: %s\n", reply_string.c_str());
15000
15001 std::string send = headers + "\n" + reply_string;
15002
15004
15005 mg_send(nc, send.c_str(), send.length());
15006
15007 t->fTimeSent = GetTimeSec();
15008
15009 delete reply;
15010
15011 return RESPONSE_SENT;
15012 }
15013
15014 return handle_decode_post(nc, msg, uri, query_string.c_str(), t);
15015}
15016
15018{
15019 //
15020 // JSON-RPC CORS pre-flight request, see
15021 // https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
15022 //
15023 // OPTIONS /resources/post-here/ HTTP/1.1
15024 // Host: bar.other
15025 // User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
15026 // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
15027 // Accept-Language: en-us,en;q=0.5
15028 // Accept-Encoding: gzip,deflate
15029 // Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
15030 // Connection: keep-alive
15031 // Origin: http://foo.example
15032 // Access-Control-Request-Method: POST
15033 // Access-Control-Request-Headers: X-PINGOTHER
15034 //
15035 // HTTP/1.1 200 OK
15036 // Date: Mon, 01 Dec 2008 01:15:39 GMT
15037 // Server: Apache/2.0.61 (Unix)
15038 // Access-Control-Allow-Origin: http://foo.example
15039 // Access-Control-Allow-Methods: POST, GET, OPTIONS
15040 // Access-Control-Allow-Headers: X-PINGOTHER
15041 // Access-Control-Max-Age: 1728000
15042 // Vary: Accept-Encoding, Origin
15043 // Content-Encoding: gzip
15044 // Content-Length: 0
15045 // Keep-Alive: timeout=2, max=100
15046 // Connection: Keep-Alive
15047 // Content-Type: text/plain
15048 //
15049
15050 const std::string origin_header = find_header_mg(msg, "Origin");
15051
15052 if (trace_mg||verbose_mg)
15053 printf("handle_http_options_cors: origin [%s]\n", origin_header.c_str());
15054
15055 std::string headers;
15056 headers += "HTTP/1.1 200 OK\n";
15057 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
15058 if (origin_header.length() > 0)
15059 headers += "Access-Control-Allow-Origin: " + origin_header + "\n";
15060 else
15061 headers += "Access-Control-Allow-Origin: *\n";
15062 headers += "Access-Control-Allow-Headers: Content-Type\n";
15063 headers += "Access-Control-Allow-Credentials: true\n";
15064 headers += "Access-Control-Max-Age: 120\n";
15065 headers += "Content-Length: 0\n";
15066 headers += "Content-Type: text/plain\n";
15067 //printf("sending headers: %s\n", headers.c_str());
15068 //printf("sending reply: %s\n", reply.c_str());
15069
15070 std::string send = headers + "\n";
15071
15073
15074 mg_send(nc, send.c_str(), send.length());
15075
15076 t->fTimeSent = GetTimeSec();
15077}
15078
15079// HTTP event handler
15080
15081static bool mongoose_passwords_enabled(const struct mg_connection *nc);
15082
15083#ifdef HAVE_MONGOOSE616
15084static MVOdb* gProxyOdb = NULL;
15085#endif
15086
15088{
15089 std::string method = mgstr(&msg->method);
15090 std::string query_string = mgstr(&msg->query_string);
15091 std::string uri_encoded = mgstr(&msg->uri);
15092 std::string uri = UrlDecode(uri_encoded.c_str());
15093
15094 if (trace_mg)
15095 printf("handle_http_message: method [%s] uri [%s] proto [%s]\n", method.c_str(), uri.c_str(), mgstr(&msg->proto).c_str());
15096
15097 RequestTrace* t = new RequestTrace;
15099 t->fMethod = method;
15100 t->fUri = uri;
15101 t->fQuery = query_string;
15102
15103 // process OPTIONS for Cross-origin (CORS) preflight request
15104 // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
15105 if (method == "OPTIONS" && query_string == "mjsonrpc" && mg_get_http_header(msg, "Access-Control-Request-Method") != NULL) {
15106 handle_http_options_cors(nc, msg, t);
15107 t->fCompleted = true;
15109 return;
15110 }
15111
15113 std::string username = check_digest_auth(msg, gAuthMg);
15114
15115 // Cannot re-read the password file - it is not thread safe to do so
15116 // unless I lock gAuthMg for each call check_digest_auth() and if I do so,
15117 // I will serialize (single-thread) all the http requests and defeat
15118 // the whole point of multithreading the web server. K.O.
15119 //
15121 //if (username.length() < 1) {
15122 // bool ok = read_passwords(&gAuthMg);
15123 // if (ok)
15124 // username = check_digest_auth(msg, &gAuthMg);
15125 //}
15126
15127 if (trace_mg)
15128 printf("handle_http_message: auth user: \"%s\"\n", username.c_str());
15129
15130 if (username.length() == 0) {
15131 if (trace_mg||verbose_mg)
15132 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());
15133
15135 t->fCompleted = true;
15137 return;
15138 }
15139 t->fAuthOk = true;
15140 } else {
15141 t->fAuthOk = true;
15142 }
15143
15144#ifdef HAVE_MONGOOSE616
15145 if (gProxyOdb && starts_with(uri, "/proxy/")) {
15146 std::string::size_type p1 = uri.find("/", 1);
15147 if (p1 == uri.length()-1) {
15148 std::string response = "404 Not Found (Proxy name is missing)";
15149 mg_send_head(nc, 404, response.length(), NULL);
15150 mg_send(nc, response.c_str(), response.length());
15151 delete t;
15152 return;
15153 }
15154 std::string::size_type p2 = uri.find("/", p1+1);
15155 if (p2 == std::string::npos) {
15156 std::string response = "404 Not Found (Proxy URL should end with a slash)";
15157 mg_send_head(nc, 404, response.length(), NULL);
15158 mg_send(nc, response.c_str(), response.length());
15159 delete t;
15160 return;
15161 }
15162 std::string p = uri.substr(p1+1, p2-p1-1);
15163 //printf("uri [%s], p1: %d, p2: %d, substr: [%s]\n", uri.c_str(), (int)p1, (int)p2, p.c_str());
15164 if (p.length() < 1) {
15165 std::string response = "404 Not Found (Double-slash or Proxy name is too short)";
15166 mg_send_head(nc, 404, response.length(), NULL);
15167 mg_send(nc, response.c_str(), response.length());
15168 delete t;
15169 return;
15170 }
15171 std::string destination;
15172 gProxyOdb->RS(p.c_str(), &destination);
15173 if (destination.length() < 1) {
15174 std::string response = "404 Not Found (Proxy not found in ODB)";
15175 mg_send_head(nc, 404, response.length(), NULL);
15176 mg_send(nc, response.c_str(), response.length());
15177 delete t;
15178 return;
15179 } else if (destination[0] == '#') {
15180 std::string response = "404 Not Found (Proxy commented-out in ODB)";
15181 mg_send_head(nc, 404, response.length(), NULL);
15182 mg_send(nc, response.c_str(), response.length());
15183 delete t;
15184 return;
15185 } else if (ends_with_char(destination, '/')) {
15186 std::string response = "404 Not Found (Proxy address should not end with a slash)";
15187 mg_send_head(nc, 404, response.length(), NULL);
15188 mg_send(nc, response.c_str(), response.length());
15189 delete t;
15190 return;
15191 } else if (!starts_with(destination, "http")) {
15192 std::string response = "404 Not Found (Proxy address does not start with http";
15193 mg_send_head(nc, 404, response.length(), NULL);
15194 mg_send(nc, response.c_str(), response.length());
15195 delete t;
15196 return;
15197 } else {
15198 std::string m;
15199 m += "/proxy";
15200 m += "/";
15201 m += p;
15202 mg_str mount = mg_mk_str(m.c_str());
15203 mg_str upstream = mg_mk_str(destination.c_str());
15204 if (verbose_mg||trace_mg) {
15205 printf("proxy: uri [%s] mount [%s] upstream [%s]\n", uri.c_str(), mgstr(&mount).c_str(), mgstr(&upstream).c_str());
15206 }
15207 mg_http_reverse_proxy(nc, msg, mount, upstream);
15208 delete t;
15209 return;
15210 }
15211 }
15212#endif
15213
15214 int response = RESPONSE_501;
15215
15216 if (method == "GET")
15217 response = handle_http_get(nc, msg, uri.c_str(), t);
15218 else if (method == "POST")
15219 response = handle_http_post(nc, msg, uri.c_str(), t);
15220
15221 if (response == RESPONSE_501) {
15222 if (trace_mg||verbose_mg)
15223 printf("handle_http_message: sending 501 Not Implemented error\n");
15224
15225 std::string response = "501 Not Implemented";
15226 mg_send_head(nc, 501, response.length(), NULL); // 501 Not Implemented
15227 mg_send(nc, response.c_str(), response.length());
15228 }
15229
15230 if (response != RESPONSE_QUEUED) {
15231 t->fCompleted = true;
15233 }
15234}
15235
15236#ifdef HAVE_MONGOOSE6
15237
15238static void handle_http_event_mg(struct mg_connection *nc, int ev, void *ev_data)
15239{
15240 switch (ev) {
15241 case MG_EV_HTTP_REQUEST:
15242 if (trace_mg)
15243 printf("handle_http_event_mg: nc %p, ev %d, ev_data %p -> http request\n", nc, ev, ev_data);
15244 handle_http_message(nc, (http_message*)ev_data);
15245 break;
15246 default:
15247 if (trace_mg)
15248 printf("handle_http_event_mg: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
15249 break;
15250 }
15251}
15252
15253static void handle_http_redirect(struct mg_connection *nc, int ev, void *ev_data)
15254{
15255 switch (ev) {
15256 case MG_EV_HTTP_REQUEST:
15257 {
15258 http_message* msg = (http_message*)ev_data;
15259 if (trace_mg)
15260 printf("handle_http_redirect: nc %p, ev %d, ev_data %p -> http request\n", nc, ev, ev_data);
15261
15262 mg_printf(nc, "HTTP/1.1 302 Found\r\nLocation: https://%s%s\r\n\r\n",
15263 ((std::string*)(nc->user_data))->c_str(),
15264 mgstr(&msg->uri).c_str());
15266 }
15267 break;
15268 default:
15269 if (trace_mg)
15270 printf("handle_http_redirect: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
15271 }
15272}
15273
15274#endif
15275
15276#ifdef HAVE_MONGOOSE616
15277
15278// from mongoose examples/multithreaded/multithreaded.c
15279
15280//static sock_t s_sock[2];
15281static std::atomic_bool s_shutdown{false};
15282static struct mg_mgr s_mgr;
15283static std::atomic_int s_rseqno{1};
15284static std::mutex s_mg_broadcast_mutex;
15285
15286#if 0
15287// This info is passed to the worker thread
15288struct work_request {
15289 void* nc;
15290 MongooseWorkObject* w;
15291};
15292#endif
15293
15294// This info is passed by the worker thread to mg_broadcast
15295struct work_result {
15296 mg_connection* nc = NULL;
15297 uint32_t check = 0x12345678;
15298 int rseqno = 0;
15299 MongooseWorkObject* w = NULL;
15300 const char* p1 = NULL;
15301 size_t s1 = 0;
15302 const char* p2 = NULL;
15303 size_t s2 = 0;
15304 bool close_flag = false;
15305 bool send_501 = false;
15306};
15307
15308#if 0
15309static void mongoose_queue(void *nc, MongooseWorkObject *w)
15310{
15311 struct work_request req = {nc, w};
15312
15313 //printf("nc: %p: wseqno: %d, queue work object!\n", nc, w->wseqno);
15314
15315 if (write(s_sock[0], &req, sizeof(req)) < 0) {
15316 fprintf(stderr, "mongoose_queue: Error: write(s_sock(0)) error %d (%s)\n", errno, strerror(errno));
15317 abort();
15318 }
15319}
15320#endif
15321
15322static void on_work_complete(struct mg_connection *nc, int ev, void *ev_data)
15323{
15324 (void) ev;
15325 struct work_result *res = (struct work_result *)ev_data;
15326
15327 assert(res != NULL);
15328 assert(res->w != NULL);
15329
15330 //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);
15331
15332 // check for correct connection, note that nc objects are reused and after a socket is closed
15333 // and a new one is opened, the same nc address and the same nc->sock can now refer to a completely
15334 // different tcp connection. So we check ncseqno instead of nc. K.O.
15335
15336 //if (res->nc != nc)
15337 //return;
15338
15339 if (GetNcSeqno(nc) != res->w->wncseqno)
15340 return;
15341
15342 //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);
15343
15344 if (res->send_501) {
15345 std::string response = "501 Not Implemented";
15346 mg_send_head(nc, 501, response.length(), NULL); // 501 Not Implemented
15347 mg_send(nc, response.c_str(), response.length());
15348 }
15349
15350 if (res->s1 > 0)
15351 mg_send(nc, res->p1, res->s1);
15352
15353 if (res->s2 > 0)
15354 mg_send(nc, res->p2, res->s2);
15355
15356 if (res->close_flag) {
15357 // cannot do pipelined http if response generated by mhttpd
15358 // decode_get() has no Content-Length header.
15359 // must close the connection.
15361 }
15362
15363 res->w->send_done = true;
15364}
15365
15366static void mongoose_send(mg_connection* nc, MongooseWorkObject* w, const char* p1, size_t s1, const char* p2, size_t s2, bool close_flag)
15367{
15368 //printf("nc: %p: send %d and %d\n", nc, (int)s1, (int)s2);
15369 struct work_result res;
15370 res.nc = nc;
15371 res.w = w;
15372 res.rseqno = s_rseqno++; // thread-asfe, s_rseqno is std::atomic_int
15373 res.p1 = p1;
15374 res.s1 = s1;
15375 res.p2 = p2;
15376 res.s2 = s2;
15377 res.close_flag = close_flag;
15378 res.send_501 = false;
15379 //printf("nc: %p: call mg_broadcast()\n", nc);
15380
15381 // NB: mg_broadcast() is advertised as thread-safe, but it is not.
15382 //
15383 // in mongoose 6.16, mg_brodacast() and mg_mgr_handle_ctl_sock() have several problems:
15384 //
15385 // a) "wrong thread" read from mgr->ctl[0], defeating the handshake
15386 //
15387 // b) "lost messages". if more than one message is written to mgr->ctl[0], the second message
15388 // will be "eaten" by mg_mgr_handle_ctl_sock() because of mistatch between number of bytes read and written
15389 // in the two functions. mg_mgr_handle_ctl_sock() always reads about 8000 bytes while mg_broadcast()
15390 // writes 8 bytes per message, (per examples/multithreaded/multithreaded.c. mhttpd messages are a bit longer).
15391 // So if multiple messages are present in the msg->ctl[0] pipe, the read call (of about 8000 bytes)
15392 // in mg_mgr_handle_ctl_sock() will return several messages (last message may be truncated)
15393 // but only the first message will be processed by the code. any additional messages are ignored.
15394 //
15395 // Problems (a) and (b) are easy to fix by using a mutex to serialize mg_broadcast().
15396 //
15397 // c) if the mg_broadcast() message contains pointers to the data buffer to be sent out,
15398 // the caller of mg_broadcast() should not free these data buffers until mg_send() is called
15399 // in "on_work_complete()". In theory, the caller of mg_broadcast() could wait until on_work_complete()
15400 // sets a "done" flag. In practice, if the corresponding network connection is closed before
15401 // mg_mgr_handle_ctl_sock() has looped over it, on_work_complete() will never run
15402 // and the "done" flag will never be set. (Of course, network connections are permitted to close
15403 // at any time without warning, but) the firefox browser closes the network connections "a lot"
15404 // especially when user pressed the "page reload" button at the moment when HTTP transations
15405 // are "in flight". (google-chrome tends to permit these "lame duck" transactions to complete and mongoose
15406 // does not see unexpected socket closures, at least not as many).
15407 //
15408 // To fix problem (c) I need to know when mg_mgr_handle_ctl_sock()'s loop over network connections
15409 // has completed (two cases: (a) my on_work_complete() was hopefully called and finished,
15410 // and (b) the "right" network connection was already closed (for whatever reason) and my on_work_complete()
15411 // was never called).
15412 //
15413 // My solution is to change the handshake between mg_broadcast() and mg_mgr_handle_ctl_sock() by sending
15414 // the handshake reply after looping over the network connections instead of after reading the message
15415 // from msg->ctl[1].
15416 //
15417 // This requires a modification to the code in mongoose.c. If this change is lost/undone, nothing will work.
15418 //
15419
15420 s_mg_broadcast_mutex.lock();
15421 mg_broadcast(&s_mgr, on_work_complete, (void *)&res, sizeof(res));
15422 s_mg_broadcast_mutex.unlock();
15423}
15424
15425static void mongoose_send_501(mg_connection* nc, MongooseWorkObject* w)
15426{
15427 struct work_result res;
15428 res.nc = nc;
15429 res.w = w;
15430 res.rseqno = s_rseqno++; // thread-asfe, s_rseqno is std::atomic_int
15431 res.p1 = 0;
15432 res.s1 = 0;
15433 res.p2 = 0;
15434 res.s2 = 0;
15435 res.close_flag = false;
15436 res.send_501 = true;
15437 //printf("nc: %p, call mg_broadcast()\n", nc);
15438
15439 s_mg_broadcast_mutex.lock();
15440 mg_broadcast(&s_mgr, on_work_complete, (void *)&res, sizeof(res));
15441 s_mg_broadcast_mutex.unlock();
15442}
15443
15444#if 0
15445void *worker_thread_proc(void *param)
15446{
15447 //struct mg_mgr *mgr = (struct mg_mgr *) param;
15448 struct work_request req = {0};
15449
15450 while ((! _abort) && (! s_shutdown)) {
15451 int rd = read(s_sock[1], &req, sizeof(req));
15452 if (rd == 0) {
15453 // socket closed, shutdown the thread
15454 break;
15455 }
15456 if (rd < 0) {
15457 if (_abort || s_shutdown) {
15458 return NULL;
15459 }
15460 fprintf(stderr, "worker_thread_proc: Error: read(s_sock(1)) returned %d, error %d (%s)\n", rd, errno, strerror(errno));
15461 abort();
15462 return NULL;
15463 }
15464
15465 //printf("nc: %p: received request!\n", req.nc);
15466
15467 int response = thread_work_function(req.nc, req.w);
15468
15469 if (response == RESPONSE_501) {
15470 if (trace_mg||verbose_mg)
15471 printf("handle_http_message: sending 501 Not Implemented error\n");
15472 mongoose_send_501(req.nc, req.w);
15473 }
15474
15475 req.w->t->fCompleted = true;
15476 gTraceBuf->AddTraceMTS(req.w->t);
15477
15478 //printf("nc: %p: wseqno: %d, delete work object!\n", req.nc, req.w->wseqno);
15479
15480 delete req.w;
15481 req.w = NULL;
15482 }
15483 return NULL;
15484}
15485#endif
15486
15487static void mongoose_thread(MongooseThreadObject* to)
15488{
15489 //printf("to %p, nc %p: thread %p started!\n", to, to->fNc, to->fThread);
15490
15491 std::unique_lock<std::mutex> ulm(to->fMutex, std::defer_lock);
15492
15493 to->fIsRunning = true;
15494
15495 while ((! _abort) && (! s_shutdown)) {
15496 MongooseWorkObject *w = NULL;
15497
15498 ulm.lock();
15499 while (to->fQueue.empty()) {
15500 //printf("to %p, nc %p, thread %p: waiting!\n", to, to->fNc, to->fThread);
15501 to->fNotify.wait(ulm);
15502 if (_abort || s_shutdown) {
15503 break;
15504 }
15505 }
15506
15507 if (_abort || s_shutdown) {
15508 break;
15509 }
15510
15511 w = to->fQueue.front();
15512 to->fQueue.pop_front();
15513 ulm.unlock();
15514
15515 //printf("to %p, nc %p: wseqno: %d, received request!\n", to, w->nc, w->wseqno);
15516
15517 int response = thread_work_function(w->nc, w);
15518
15519 if (response == RESPONSE_501) {
15520 if (trace_mg||verbose_mg)
15521 printf("handle_http_message: sending 501 Not Implemented error\n");
15522 mongoose_send_501(w->nc, w);
15523 }
15524
15525 // mg_broadcast() called on_work_complete() on the main thread and waited until it finished
15526
15527 if (!w->send_done) {
15528 // NB: careful here, if connection nc was closed, pointer nc points to nowhere! do not dereference it!
15529 // NB: stay quiet about it, nothing special about network connctions closing and opening as they wish.
15530 //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);
15531 }
15532
15533 w->t->fCompleted = true;
15534 gTraceBuf->AddTraceMTS(w->t);
15535
15536 //printf("nc: %p: wseqno: %d, delete work object!\n", w->nc, w->wseqno);
15537
15538 delete w;
15539 }
15540
15541 to->fIsRunning = false;
15542
15543 //printf("to %p, nc %p: thread %p finished!\n", to, to->fNc, to->fThread);
15544}
15545
15546static bool mongoose_hostlist_enabled(const struct mg_connection *nc);
15547
15548static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
15549{
15550 (void) nc;
15551 (void) ev_data;
15552
15553 //if (trace_mg && ev != 0) {
15554 // printf("ev_handler: connection %p, event %d\n", nc, ev);
15555 //}
15556
15557 switch (ev) {
15558 case 0:
15559 break;
15560 default: {
15561 if (trace_mg) {
15562 printf("ev_handler: connection %p, event %d\n", nc, ev);
15563 }
15564 break;
15565 }
15566 case MG_EV_ACCEPT:
15567 assert(nc->user_data == NULL);
15568 nc->user_data = new MongooseNcUserData();
15569
15570 if (trace_mg) {
15571 printf("ev_handler: connection %p, MG_EV_ACCEPT, user_data %p, ncseqno %d\n", nc, nc->user_data, GetNcSeqno(nc));
15572 }
15573 if (s_shutdown) {
15574 //printf("XXX nc %p!\n", nc);
15576 } else if (mongoose_hostlist_enabled(nc)) {
15577 if (!mongoose_check_hostlist(&nc->sa)) {
15579 }
15580 }
15581 break;
15582 case MG_EV_RECV:
15583 if (trace_mg_recv) {
15584 printf("ev_handler: connection %p, MG_EV_RECV, %d bytes\n", nc, *(int*)ev_data);
15585 }
15586 if (s_shutdown) {
15587 //printf("RRR nc %p!\n", nc);
15589 }
15590 break;
15591 case MG_EV_SEND:
15592 if (trace_mg_send) {
15593 printf("ev_handler: connection %p, MG_EV_SEND, %d bytes\n", nc, *(int*)ev_data);
15594 }
15595 break;
15596 case MG_EV_HTTP_CHUNK: {
15597 if (trace_mg) {
15598 printf("ev_handler: connection %p, MG_EV_HTTP_CHUNK\n", nc);
15599 }
15600 if (s_shutdown) {
15601 //printf("RRR1 nc %p!\n", nc);
15603 }
15604 break;
15605 }
15606 case MG_EV_HTTP_REQUEST: {
15607 struct http_message* msg = (struct http_message*)ev_data;
15608 if (trace_mg) {
15609 printf("ev_handler: connection %p, MG_EV_HTTP_REQUEST \"%s\" \"%s\"\n", nc, mgstr(&msg->method).c_str(), mgstr(&msg->uri).c_str());
15610 }
15611 if (s_shutdown) {
15612 //printf("RRR2 nc %p!\n", nc);
15614 } else {
15615 handle_http_message(nc, msg);
15616 }
15617 break;
15618 }
15619 case MG_EV_CLOSE: {
15620 if (trace_mg) {
15621 printf("ev_handler: connection %p, MG_EV_CLOSE, user_data %p, ncseqno %d\n", nc, nc->user_data, GetNcSeqno(nc));
15622 }
15623 //printf("CCC nc %p!\n", nc);
15624 FreeThread(nc);
15625 if (nc->user_data) {
15626 MongooseNcUserData* ncud = (MongooseNcUserData*)nc->user_data;
15627 nc->user_data = NULL;
15628 delete ncud;
15629 ncud = NULL;
15630 }
15631 }
15632 }
15633}
15634
15635#define FLAG_HTTPS MG_F_USER_1
15636#define FLAG_PASSWORDS MG_F_USER_2
15637#define FLAG_HOSTLIST MG_F_USER_3
15638
15639static bool mongoose_passwords_enabled(const struct mg_connection *nc)
15640{
15641 int flags = 0;
15642 if (nc && nc->listener) {
15643 flags = nc->listener->flags;
15644 }
15645 //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);
15646 return flags & FLAG_PASSWORDS;
15647}
15648
15649static bool mongoose_hostlist_enabled(const struct mg_connection *nc)
15650{
15651 int flags = 0;
15652 if (nc && nc->listener) {
15653 flags = nc->listener->flags;
15654 }
15655 //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);
15656 return flags & FLAG_HOSTLIST;
15657}
15658
15659static int mongoose_listen(const char* address, int flags)
15660{
15661#if MG_ENABLE_SSL
15662#else
15663 if (flags & FLAG_HTTPS) {
15664 cm_msg(MERROR, "mongoose_listen", "https port \"%s\" requested, but mhttpd compiled without MG_ENABLE_SSL", address);
15665 return SS_SOCKET_ERROR;
15666 }
15667#endif
15668
15669 struct mg_connection *nc = mg_bind(&s_mgr, address, ev_handler);
15670 if (nc == NULL) {
15671 cm_msg(MERROR, "mongoose_listen", "Cannot mg_bind address \"%s\"", address);
15672 return SS_SOCKET_ERROR;
15673 }
15674
15675 if (flags & FLAG_HTTPS) {
15676#if MG_ENABLE_SSL
15677 std::string cert_file;
15678
15679 int status = find_file_mg("ssl_cert.pem", cert_file, NULL, trace_mg);
15680
15681 if (status != SUCCESS) {
15682 cm_msg(MERROR, "mongoose_listen", "cannot find SSL certificate file \"%s\"", cert_file.c_str());
15683 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");
15684 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");
15685 return SS_FILE_ERROR;
15686 }
15687
15688 printf("Mongoose web server will use https certificate file \"%s\"\n", cert_file.c_str());
15689
15690 const char* errmsg = mg_set_ssl(nc, cert_file.c_str(), NULL);
15691 if (errmsg) {
15692 cm_msg(MERROR, "mongoose_listen", "Cannot enable https with certificate file \"%s\", error: %s", cert_file.c_str(), errmsg);
15693 return SS_SOCKET_ERROR;
15694 }
15695
15696 // NB: where is the warning that the SSL certificate has expired?!? K.O.
15697#else
15698 abort(); // cannot happen!
15699#endif
15700 }
15701
15703
15704 nc->flags |= flags;
15705
15706 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");
15707
15708 return SUCCESS;
15709}
15710
15711static int mongoose_init(MVOdb* odb, bool no_passwords, bool no_hostlist, const std::vector<std::string>& user_hostlist)
15712{
15713 bool enable_localhost_port = true;
15714 int localhost_port = 8080;
15715 bool localhost_port_passwords = false;
15716
15717 bool enable_insecure_port = false;
15718 int insecure_port = 8081;
15719 bool insecure_port_passwords = true;
15720 bool insecure_port_hostlist = true;
15721
15722 bool enable_https_port = false;
15723 int https_port = 8443;
15724 bool https_port_passwords = true;
15725 bool https_port_hostlist = false;
15726
15727 std::vector<std::string> hostlist;
15728 hostlist.push_back("localhost");
15729
15730 bool enable_ipv6 = true;
15731
15732 odb->RB("Enable localhost port", &enable_localhost_port, true);
15733 odb->RI("localhost port", &localhost_port, true);
15734 odb->RB("localhost port passwords", &localhost_port_passwords, true);
15735 odb->RB("Enable insecure port", &enable_insecure_port, true);
15736 odb->RI("insecure port", &insecure_port, true);
15737 odb->RB("insecure port passwords", &insecure_port_passwords, true);
15738 odb->RB("insecure port host list", &insecure_port_hostlist, true);
15739 odb->RB("Enable https port", &enable_https_port, true);
15740 odb->RI("https port", &https_port, true);
15741 odb->RB("https port passwords", &https_port_passwords, true);
15742 odb->RB("https port host list", &https_port_hostlist, true);
15743 odb->RSA("Host list", &hostlist, true, 10, 256);
15744 odb->RB("Enable IPv6", &enable_ipv6, true);
15745
15746 // populate the MIME.types table
15747 gProxyOdb = odb->Chdir("Proxy", true);
15748 std::string proxy_example = "#http://localhost:8080";
15749 gProxyOdb->RS("example", &proxy_example, true);
15750
15751 // populate the MIME.types table
15752 SaveMimetypes(odb->Chdir("mime.types", true));
15753
15754 if (!no_passwords
15755 && ((enable_localhost_port && localhost_port_passwords)
15756 || (enable_insecure_port && insecure_port_passwords)
15757 || (enable_https_port && https_port_passwords))) {
15758 gAuthMg = new Auth();
15759 int status = gAuthMg->Init();
15760 if (status != SUCCESS) {
15761 printf("mongoose_init: Error: Cannot initialize authorization object!\n");
15762 return status;
15763 }
15764 printf("HTTP Digest authentication with realm \"%s\" and password file \"%s\"\n", gAuthMg->realm.c_str(), gAuthMg->passwd_filename.c_str());
15765 } else {
15766 printf("Password protection is off\n");
15767 }
15768
15769 if (!no_hostlist
15770 && ((enable_insecure_port && insecure_port_hostlist)
15771 || (enable_https_port && https_port_hostlist))) {
15772 gAllowedHosts.clear();
15773
15774 // copy the user allowed hosts
15775 for (unsigned int i=0; i<user_hostlist.size(); i++)
15776 gAllowedHosts.push_back(user_hostlist[i]);
15777
15778 for (unsigned i=0; i<hostlist.size(); i++) {
15779 std::string s = hostlist[i];
15780 if (s.length() < 1) // skip emties
15781 continue;
15782
15783 if (s[0] == '#') // skip commented-out entries
15784 continue;
15785
15786 //printf("add allowed hosts %d [%s]\n", i, s.c_str());
15787 gAllowedHosts.push_back(s);
15788 }
15789
15790 printf("Hostlist active, connections will be accepted only from: ");
15791 for (unsigned i=0; i<gAllowedHosts.size(); i++) {
15792 if (i>0)
15793 printf(", ");
15794 printf("%s", gAllowedHosts[i].c_str());
15795 }
15796 printf("\n");
15797 } else {
15798 printf("Hostlist off, connections from anywhere will be accepted\n");
15799 }
15800
15801 mg_mgr_init(&s_mgr, NULL);
15802
15803 bool listen_failed = false;
15804
15805 if (enable_localhost_port) {
15806 char str[256];
15807 sprintf(str, "localhost:%d", localhost_port);
15808 int status = mongoose_listen(str, 0);
15809 if (status != SUCCESS)
15810 listen_failed = true;
15811 if (enable_ipv6) {
15812 sprintf(str, "[::1]:%d", localhost_port);
15813 status = mongoose_listen(str, 0);
15814 if (status != SUCCESS)
15815 listen_failed = true;
15816 }
15817 }
15818
15819 if (enable_insecure_port) {
15820 char str[256];
15821 int flags = 0;
15822 if (insecure_port_passwords)
15823 flags |= FLAG_PASSWORDS;
15824 if (insecure_port_hostlist)
15825 flags |= FLAG_HOSTLIST;
15826 if (enable_ipv6) {
15827 sprintf(str, "[::]:%d", insecure_port);
15828 int status = mongoose_listen(str, flags);
15829 if (status != SUCCESS)
15830 listen_failed = true;
15831 } else {
15832 sprintf(str, "%d", insecure_port);
15833 int status = mongoose_listen(str, flags);
15834 if (status != SUCCESS)
15835 listen_failed = true;
15836 }
15837 }
15838
15839 if (enable_https_port) {
15840 char str[256];
15841 int flags = 0;
15842 if (https_port_passwords)
15843 flags |= FLAG_PASSWORDS;
15844 if (https_port_hostlist)
15845 flags |= FLAG_HOSTLIST;
15846 flags |= FLAG_HTTPS;
15847 if (enable_ipv6) {
15848 sprintf(str, "[::]:%d", https_port);
15849 int status = mongoose_listen(str, flags);
15850 if (status != SUCCESS)
15851 listen_failed = true;
15852 } else {
15853 sprintf(str, "%d", https_port);
15854 int status = mongoose_listen(str, flags);
15855 if (status != SUCCESS)
15856 listen_failed = true;
15857 }
15858 }
15859
15860 if (listen_failed) {
15861 cm_msg(MERROR, "mongoose_init", "Failed to listen on a TCP port enabled in ODB /WebServer");
15862 return SS_SOCKET_ERROR;
15863 }
15864
15865 return SUCCESS;
15866}
15867
15868static void mongoose_poll(int msec = 200)
15869{
15870 mg_mgr_poll(&s_mgr, msec);
15871}
15872
15873static void mongoose_cleanup()
15874{
15875 printf("Mongoose web server shutting down\n");
15876
15877 s_shutdown = true;
15878
15879 // close listener sockets
15880 if (s_mgr.active_connections) {
15881 struct mg_connection* nc = s_mgr.active_connections;
15882 while (nc) {
15883 //printf("nc %p, next %p, user_data %p, listener %p, flags %lu\n", nc, nc->next, nc->user_data, nc->listener, nc->flags);
15884 if (nc->flags & MG_F_LISTENING) {
15886 }
15887 nc = nc->next;
15888 }
15889 }
15890
15891 // tell threads to shut down
15892 for (auto it : gMongooseThreads) {
15893 MongooseThreadObject* to = it;
15894 to->fNotify.notify_one();
15895 }
15896
15897 // wait until all threads stop
15898 for (int i=0; i<10; i++) {
15899 int count_running = 0;
15900 for (auto it : gMongooseThreads) {
15901 MongooseThreadObject* to = it;
15902 //printf("AAA6C %p thread %p running %d!\n", to, to->fThread, to->fIsRunning);
15903 if (to->fIsRunning) {
15904 count_running++;
15905 }
15906 }
15907 printf("Mongoose web server shutting down, %d threads still running\n", count_running);
15908 if (count_running == 0)
15909 break;
15910 mongoose_poll(1000);
15911 }
15912
15913 // delete thread objects
15914 for (auto it : gMongooseThreads) {
15915 MongooseThreadObject* to = it;
15916 //printf("AAA7B %p thread %p running %d!\n", to, to->fThread, to->fIsRunning);
15917 if (to->fIsRunning) {
15918 cm_msg(MERROR, "mongoose", "thread failed to shut down");
15919 continue;
15920 }
15921 to->fThread->join();
15922 delete to->fThread;
15923 delete to;
15924 }
15925 gMongooseThreads.clear();
15926
15927 mg_mgr_free(&s_mgr);
15928
15929 //closesocket(s_sock[0]);
15930 //closesocket(s_sock[1]);
15931
15932 // make leak sanitizer happy!
15933 for (auto e : gHostlistCache) {
15934 delete e;
15935 }
15936 gHostlistCache.clear();
15937 if (gProxyOdb) {
15938 delete gProxyOdb;
15939 gProxyOdb = NULL;
15940 }
15941 if (gMimeTypesOdb) {
15942 delete gMimeTypesOdb;
15943 gMimeTypesOdb = NULL;
15944 }
15945
15946 printf("Mongoose web server shut down\n");
15947}
15948
15949#endif
15950
15951#ifdef HAVE_MONGOOSE6
15952
15953static bool mongoose_passwords_enabled(const struct mg_connection *nc)
15954{
15955 return true;
15956}
15957
15958int start_mg(int user_http_port, int user_https_port, int socket_priviledged_port, int verbose)
15959{
15960 HNDLE hDB;
15961 int size;
15962 int status;
15963
15964 //if (verbose)
15965 // trace_mg = true;
15966
15967 if (verbose)
15968 verbose_mg = true;
15969
15971 assert(status == CM_SUCCESS);
15972
15973 int http_port = 8080;
15974 int https_port = 8443;
15975 int http_redirect_to_https = 1;
15976
15977 size = sizeof(http_port);
15978 db_get_value(hDB, 0, "/Experiment/midas http port", &http_port, &size, TID_INT, TRUE);
15979
15980 size = sizeof(https_port);
15981 db_get_value(hDB, 0, "/Experiment/midas https port", &https_port, &size, TID_INT, TRUE);
15982
15983 size = sizeof(http_redirect_to_https);
15984 db_get_value(hDB, 0, "/Experiment/http redirect to https", &http_redirect_to_https, &size, TID_BOOL, TRUE);
15985
15986 bool need_cert_file = false;
15987 bool need_password_file = false;
15988
15989 if (user_http_port)
15990 http_port = user_http_port;
15991
15992 if (user_https_port)
15993 https_port = user_https_port;
15994
15995 if (https_port) {
15996 need_cert_file = true;
15997 need_password_file = true;
15998 }
15999
16000 if (!https_port)
16001 http_redirect_to_https = 0;
16002
16003 if (http_port && !http_redirect_to_https) {
16004 // no passwords serving over http unless
16005 // http is just a redict to https
16006 need_password_file = false;
16007 }
16008
16009 if (socket_priviledged_port >= 0) {
16010 // no passwords if serving unencrypted http on port 80
16011 need_password_file = false;
16012 printf("Mongoose web server password portection is disabled: serving unencrypted http on port 80\n");
16013 }
16014
16015 bool have_at_least_one_port = false;
16016
16017 std::string cert_file;
16018
16019 if (need_cert_file) {
16020 status = find_file_mg("ssl_cert.pem", cert_file, NULL, trace_mg);
16021
16022 if (status != SUCCESS) {
16023 cm_msg(MERROR, "mongoose", "cannot find SSL certificate file \"%s\"", cert_file.c_str());
16024 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");
16025 return SS_FILE_ERROR;
16026 }
16027
16028 printf("Mongoose web server will use SSL certificate file \"%s\"\n", cert_file.c_str());
16029 }
16030
16031 if (need_password_file) {
16032 gAuthMg = new Auth();
16033 status = gAuthMg->Init();
16034 if (status != SUCCESS) {
16035 printf("Error: Cannot initialize authorization object!\n");
16036 return status;
16037 }
16038 printf("Mongoose web server will use authentication realm \"%s\", password file \"%s\"\n", gAuthMg->realm.c_str(), gAuthMg->passwd_filename.c_str());
16039 } else {
16040 printf("Mongoose web server will not use password protection\n");
16041 }
16042
16043 if (trace_mg)
16044 printf("start_mg!\n");
16045
16046#ifndef OS_WINNT
16047 signal(SIGPIPE, SIG_IGN);
16048#endif
16049
16050 if (!gTraceBuf) {
16052 }
16053
16054 if (!request_mutex) {
16055 status = ss_mutex_create(&request_mutex, FALSE);
16056 assert(status==SS_SUCCESS || status==SS_CREATED);
16057 }
16058
16059 mg_mgr_init(&mgr_mg, NULL);
16060
16061 // use socket bound to priviledged port (setuid-mode)
16062 if (socket_priviledged_port >= 0) {
16063 struct mg_connection* nc = mg_add_sock(&mgr_mg, socket_priviledged_port, handle_event_mg);
16064 if (nc == NULL) {
16065 cm_msg(MERROR, "mongoose", "Cannot create mg_connection for set-uid-root privileged port");
16066 return SS_SOCKET_ERROR;
16067 }
16068
16069 nc->flags |= MG_F_LISTENING;
16070#ifdef MG_ENABLE_THREADS
16072#endif
16074 mg_register_http_endpoint(nc, "/", handle_http_event_mg);
16075
16076 have_at_least_one_port = true;
16077 printf("mongoose web server is listening on the set-uid-root privileged port\n");
16078 }
16079
16080 if (http_port != 80) { // port 80 is already handled by socket_priviledged_port
16081 char str[256];
16082 sprintf(str, "%d", http_port);
16083 struct mg_connection* nc = mg_bind(&mgr_mg, str, handle_event_mg);
16084 if (nc == NULL) {
16085 cm_msg(MERROR, "mongoose", "Cannot bind to port %d", http_port);
16086 return SS_SOCKET_ERROR;
16087 }
16088
16089#ifdef MG_ENABLE_THREADS
16091#endif
16093
16094 if (http_redirect_to_https) {
16095 std::string hostname = ss_gethostname();
16096 char str[256];
16097 sprintf(str, "%d", https_port);
16098 std::string s = hostname + ":" + std::string(str);
16099 nc->user_data = new std::string(s);
16100 mg_register_http_endpoint(nc, "/", handle_http_redirect);
16101 printf("mongoose web server is redirecting HTTP port %d to https://%s\n", http_port, s.c_str());
16102 } else {
16103 mg_register_http_endpoint(nc, "/", handle_http_event_mg);
16104 }
16105
16106 have_at_least_one_port = true;
16107 printf("mongoose web server is listening on the HTTP port %d\n", http_port);
16108 }
16109
16110 if (https_port) {
16111#ifdef MG_ENABLE_SSL
16112 char str[256];
16113 sprintf(str, "%d", https_port);
16114 struct mg_connection* nc = mg_bind(&mgr_mg, str, handle_event_mg);
16115 if (nc == NULL) {
16116 cm_msg(MERROR, "mongoose", "Cannot bind to port %d", https_port);
16117 return SS_SOCKET_ERROR;
16118 }
16119
16120 mg_set_ssl(nc, cert_file.c_str(), NULL);
16121#ifdef MG_ENABLE_THREADS
16123#endif
16125 mg_register_http_endpoint(nc, "/", handle_http_event_mg);
16126
16127 have_at_least_one_port = true;
16128 printf("mongoose web server is listening on the HTTPS port %d\n", https_port);
16129#else
16130 cm_msg(MERROR, "mongoose", "https port %d requested, but mhttpd compiled without MG_ENABLE_SSL", https_port);
16131 return SS_SOCKET_ERROR;
16132#endif
16133 }
16134
16135 if (!have_at_least_one_port) {
16136 cm_msg(MERROR, "mongoose", "cannot start: no ports defined");
16137 return SS_FILE_ERROR;
16138 }
16139
16140 return SUCCESS;
16141}
16142
16143int stop_mg()
16144{
16145 if (trace_mg)
16146 printf("stop_mg!\n");
16147
16148 // Stop the server.
16149 mg_mgr_free(&mgr_mg);
16150
16151 if (trace_mg)
16152 printf("stop_mg done!\n");
16153 return SUCCESS;
16154}
16155
16156int loop_mg()
16157{
16158 int status = SUCCESS;
16159
16160 /* establish Ctrl-C handler - will set _abort to TRUE */
16162
16163 while (!_abort) {
16164
16165 /* cm_yield() is not thread safe, need to take a lock */
16166
16167#ifdef HAVE_MONGOOSE6
16168 status = ss_mutex_wait_for(request_mutex, 0);
16169#endif
16170 gMutex.lock();
16171
16172 /* check for shutdown message */
16173 status = cm_yield(0);
16174 if (status == RPC_SHUTDOWN)
16175 break;
16176
16177 gMutex.unlock();
16178#ifdef HAVE_MONGOOSE6
16179 status = ss_mutex_release(request_mutex);
16180#endif
16181
16182 //ss_sleep(10);
16183
16184 mg_mgr_poll(&mgr_mg, 10);
16185 }
16186
16187 return status;
16188}
16189#endif
16190
16191static MJsonNode* get_http_trace(const MJsonNode* params)
16192{
16193 if (!params) {
16194 MJSO *doc = MJSO::I();
16195 doc->D("get current value of mhttpd http_trace");
16196 doc->P(NULL, 0, "there are no input parameters");
16197 doc->R(NULL, MJSON_INT, "current value of http_trace");
16198 return doc;
16199 }
16200
16201 return mjsonrpc_make_result("http_trace", MJsonNode::MakeInt(http_trace));
16202}
16203
16204static MJsonNode* set_http_trace(const MJsonNode* params)
16205{
16206 if (!params) {
16207 MJSO* doc = MJSO::I();
16208 doc->D("set new value of mhttpd http_trace");
16209 doc->P(NULL, MJSON_INT, "new value of http_trace");
16210 doc->R(NULL, MJSON_INT, "new value of http_trace");
16211 return doc;
16212 }
16213
16214 http_trace = params->GetInt();
16215 return mjsonrpc_make_result("http_trace", MJsonNode::MakeInt(http_trace));
16216}
16217
16219{
16220 mjsonrpc_add_handler("set_http_trace", set_http_trace);
16221 mjsonrpc_add_handler("get_http_trace", get_http_trace);
16222}
16223
16224/*------------------------------------------------------------------*/
16225
16226int main(int argc, const char *argv[])
16227{
16228 int status;
16229 int daemon = FALSE;
16230#ifdef HAVE_MONGOOSE6
16231 int user_http_port = 0;
16232 int user_https_port = 0;
16233#endif
16234#ifdef HAVE_MONGOOSE616
16235 bool no_passwords = false;
16236 bool no_hostlist = false;
16237#endif
16238 const char *myname = "mhttpd";
16239
16240 setbuf(stdout, NULL);
16241 setbuf(stderr, NULL);
16242#ifdef SIGPIPE
16243 /* avoid getting killed by "Broken pipe" signals */
16244 signal(SIGPIPE, SIG_IGN);
16245#endif
16246
16247#ifdef HAVE_MONGOOSE6
16248 //
16249 // if running setuid-root, unconditionally bind to port 80.
16250 //
16251
16252 int socket_priviledged_port = -1;
16253
16254#ifdef OS_UNIX
16255 // in setuid-root mode bind to priviledged port
16256 if (getuid() != geteuid()) {
16257 int port80 = 80;
16258
16259 printf("mhttpd is running in setuid-root mode.\n");
16260
16261 socket_priviledged_port = open_listening_socket(port80);
16262 if (socket_priviledged_port < 0) {
16263 printf("Cannot open listening socket on TCP port %d, aborting.\n", port80);
16264 exit(1);
16265 }
16266
16267 // give up root privilege
16268 status = setuid(getuid());
16269 if (status != 0) {
16270 printf("Cannot give up root privelege, aborting.\n");
16271 exit(1);
16272 }
16273 status = setuid(getuid());
16274 if (status != 0) {
16275 printf("Cannot give up root privelege, aborting.\n");
16276 exit(1);
16277 }
16278 }
16279#endif
16280#endif
16281
16282 char midas_hostname[256];
16283 char midas_expt[256];
16284
16285 /* get default from environment */
16286 cm_get_environment(midas_hostname, sizeof(midas_hostname), midas_expt, sizeof(midas_expt));
16287
16288 /* parse command line parameters */
16289#ifdef HAVE_MONGOOSE6
16290 gUserAllowedHosts.clear();
16291#else
16292 std::vector<std::string> user_hostlist;
16293#endif
16294 for (int i = 1; i < argc; i++) {
16295 if (argv[i][0] == '-' && argv[i][1] == 'D')
16296 daemon = TRUE;
16297 else if (argv[i][0] == '-' && argv[i][1] == 'v')
16298 verbose = TRUE;
16299 else if (argv[i][0] == '-' && argv[i][1] == 'E')
16300 elog_mode = TRUE;
16301 else if (argv[i][0] == '-' && argv[i][1] == 'H') {
16303#ifdef HAVE_MONGOOSE6
16304 } else if (strcmp(argv[i], "--http") == 0) {
16305 if (argv[i+1]) {
16306 user_http_port = atoi(argv[i+1]);
16307 }
16308 } else if (strcmp(argv[i], "--https") == 0) {
16309 if (argv[i+1]) {
16310 user_https_port = atoi(argv[i+1]);
16311 }
16312#endif
16313 } else if (strcmp(argv[i], "--trace-mg") == 0) {
16314 trace_mg = true;
16315 trace_mg_recv = true;
16316 trace_mg_send = true;
16317 } else if (strcmp(argv[i], "--trace-mg-verbose") == 0) {
16318 trace_mg_verbose = true;
16319 } else if (strcmp(argv[i], "--no-trace-mg-recv") == 0) {
16320 trace_mg_recv = false;
16321 } else if (strcmp(argv[i], "--no-trace-mg-send") == 0) {
16322 trace_mg_send = false;
16323 } else if (strcmp(argv[i], "--verbose-mg") == 0) {
16324 verbose_mg = true;
16325#ifdef HAVE_MONGOOSE616
16326 } else if (strcmp(argv[i], "--no-multithread") == 0) {
16327 multithread_mg = false;
16328 } else if (strcmp(argv[i], "--no-passwords") == 0) {
16329 no_passwords = true;
16330 } else if (strcmp(argv[i], "--no-hostlist") == 0) {
16331 no_hostlist = true;
16332#endif
16333 } else if (argv[i][0] == '-') {
16334 if (i + 1 >= argc || argv[i + 1][0] == '-')
16335 goto usage;
16336 if (argv[i][1] == 'h')
16337 mstrlcpy(midas_hostname, argv[++i], sizeof(midas_hostname));
16338 else if (argv[i][1] == 'e')
16339 mstrlcpy(midas_expt, argv[++i], sizeof(midas_hostname));
16340 else if (argv[i][1] == 'a') {
16341#ifdef HAVE_MONGOOSE6
16342 gUserAllowedHosts.push_back(argv[++i]);
16343#else
16344 user_hostlist.push_back(argv[++i]);
16345#endif
16346 } else if (argv[i][1] == 'p') {
16347 printf("Option \"-p port_number\" for the old web server is obsolete.\n");
16348 printf("mongoose web server is the new default, port number is set in ODB or with \"--http port_number\".\n");
16349 printf("To run the obsolete old web server, please use \"--oldserver\" switch.\n");
16350 return 1;
16351 } else {
16352 usage:
16353 printf("usage: %s [-h Hostname[:port]] [-e Experiment] [-v] [-D] [-a Hostname]\n\n", argv[0]);
16354 printf(" -a add hostname to the hostlist of hosts allowed to connect to mhttpd\n");
16355 printf(" -e experiment to connect to\n");
16356 printf(" -h connect to midas server (mserver) on given host\n");
16357 printf(" -v display verbose HTTP communication\n");
16358 printf(" -D become a daemon\n");
16359 printf(" -E only display ELog system\n");
16360 printf(" -H only display history plots\n");
16361#ifdef HAVE_MONGOOSE6
16362 printf(" --http port - bind to specified HTTP port (default is ODB \"/Experiment/midas http port\")\n");
16363 printf(" --https port - bind to specified HTTP port (default is ODB \"/Experiment/midas https port\")\n");
16364#endif
16365 printf(" --verbose-mg - trace mongoose web requests\n");
16366 printf(" --trace-mg - trace mongoose events\n");
16367 printf(" --no-trace-mg-recv - do not trace mongoose recv events\n");
16368 printf(" --no-trace-mg-send - dop not trace mongoose send events\n");
16369#ifdef HAVE_MONGOOSE616
16370 printf(" --no-multithread - disable mongoose multithreading\n");
16371 printf(" --no-passwords - disable password protection\n");
16372 printf(" --no-hostlist - disable access control host list\n");
16373#endif
16374 return 0;
16375 }
16376 }
16377 }
16378
16379 if (daemon) {
16380 printf("Becoming a daemon...\n");
16382 }
16383
16384#ifdef OS_LINUX
16385 /* write PID file */
16386 FILE *f = fopen("/var/run/mhttpd.pid", "w");
16387 if (f != NULL) {
16388 fprintf(f, "%d", ss_getpid());
16389 fclose(f);
16390 }
16391#endif
16392
16393 if (history_mode)
16394 myname = "mhttpd_history";
16395
16396 /*---- connect to experiment ----*/
16397 status = cm_connect_experiment1(midas_hostname, midas_expt, myname, NULL,
16400 return 1;
16401 else if (status == DB_INVALID_HANDLE) {
16402 std::string s = cm_get_error(status);
16403 puts(s.c_str());
16404 } else if (status != CM_SUCCESS) {
16405 std::string s = cm_get_error(status);
16406 puts(s.c_str());
16407 return 1;
16408 }
16409
16410 /* mhttpd needs the watchdog thread until we are sure
16411 * we do not have any long sleeps anywhere in the mhttpd code.
16412 * this includes reads from the history files or databases,
16413 * that can take arbitrary long time */
16415
16416 /* Get ODB handles */
16417
16418 HNDLE hDB;
16419
16421
16422 MVOdb *odb = MakeMidasOdb(hDB);
16423 gOdb = odb;
16424
16425 /* do ODB record checking */
16426 if (!check_odb_records(odb)) {
16427 // check_odb_records() fails with nothing printed to the terminal
16428 // because mhttpd does not print cm_msg(MERROR, ...) messages to the terminal.
16429 // At least print something!
16430 printf("check_odb_records() failed, see messages and midas.log, bye!\n");
16432 return 1;
16433 }
16434
16435#ifdef HAVE_MONGOOSE6
16436 if (init_allowed_hosts() != SUCCESS) {
16437 printf("init_allowed_hosts() failed, see messages and midas.log, bye!\n");
16439 return 1;
16440 }
16441
16442 if (verbose) {
16443 if (gAllowedHosts.size() > 0) {
16444 printf("mhttpd allowed hosts list: ");
16445 for (unsigned int i=0; i<gAllowedHosts.size(); i++) {
16446 if (i>0)
16447 printf(", ");
16448 printf("%s", gAllowedHosts[i].c_str());
16449 }
16450 printf("\n");
16451 } else {
16452 printf("mhttpd allowed hosts list is empty\n");
16453 }
16454 }
16455
16456 // populate the MIME.types table
16457 SaveMimetypes(odb->Chdir("WebServer/mime.types", true));
16458#endif
16459
16460 /* initialize odb entries needed for mhttpd and midas web pages */
16461 init_mhttpd_odb(odb);
16462
16463 /* initialize menu buttons */
16464 init_menu_buttons(odb);
16465
16466 /* initialize elog odb entries */
16467 init_elog_odb();
16468
16469 /* initialize the JSON RPC handlers */
16470 mjsonrpc_init();
16472
16474
16475#ifdef HAVE_MONGOOSE6
16476 status = start_mg(user_http_port, user_https_port, socket_priviledged_port, verbose);
16477 if (status != SUCCESS) {
16478 // At least print something!
16479 printf("could not start the mongoose web server, see messages and midas.log, bye!\n");
16481 return 1;
16482 }
16483#endif
16484
16485#ifdef HAVE_MONGOOSE616
16486
16487#ifdef SIGPIPE
16488#ifdef SIG_IGN
16489 signal(SIGPIPE, SIG_IGN);
16490#endif
16491#endif
16492
16493 if (!gTraceBuf) {
16495 }
16496
16497 //if (!request_mutex) {
16498 // status = ss_mutex_create(&request_mutex, FALSE);
16499 // assert(status==SS_SUCCESS || status==SS_CREATED);
16500 //}
16501
16502 /* establish Ctrl-C handler - will set _abort to TRUE */
16504
16505 MVOdb* o = odb->Chdir("WebServer", true);
16506 status = mongoose_init(o, no_passwords, no_hostlist, user_hostlist);
16507 if (status != SUCCESS) {
16508 // At least print something!
16509 printf("Error: Could not start the mongoose web server, see messages and midas.log, bye!\n");
16511 return 1;
16512 }
16513
16514 delete o;
16515#endif
16516
16517#ifdef HAVE_MONGOOSE6
16518 loop_mg();
16519 stop_mg();
16520#endif
16521
16522#ifdef HAVE_MONGOOSE616
16523 while (!_abort) {
16524
16525 /* cm_yield() is not thread safe, need to take a lock */
16526
16527 //status = ss_mutex_wait_for(request_mutex, 0);
16528 gMutex.lock();
16529
16530 /* check for shutdown message */
16531 status = cm_yield(0);
16532 if (status == RPC_SHUTDOWN)
16533 break;
16534
16535 gMutex.unlock();
16536 //status = ss_mutex_release(request_mutex);
16537
16538 //ss_sleep(10);
16539
16540 mongoose_poll(10);
16541 }
16542
16543 mongoose_cleanup();
16544#endif
16545
16546 if (gMh) {
16547 delete gMh;
16548 gMh = NULL;
16549 gMhkey = 0;
16550 }
16551
16552 mjsonrpc_exit();
16554 return 0;
16555}
16556
16557/* emacs
16558 * Local Variables:
16559 * tab-width: 8
16560 * c-basic-offset: 3
16561 * indent-tabs-mode: nil
16562 * End:
16563 */
#define FALSE
Definition cfortran.h:309
char * attachment_buffer[3]
Definition mhttpd.cxx:69
size_t attachment_size[3]
Definition mhttpd.cxx:70
~Attachment()
Definition mhttpd.cxx:79
void clear(int i)
Definition mhttpd.cxx:85
std::vector< AuthEntry > passwords
Definition mhttpd.cxx:13734
std::string passwd_filename
Definition mhttpd.cxx:13733
std::string realm
Definition mhttpd.cxx:13732
int Init()
Definition mhttpd.cxx:13741
virtual int hs_read(time_t start_time, time_t end_time, time_t interval, int num_var, const char *const event_name[], const char *const tag_name[], const int var_index[], int num_entries[], time_t *time_buffer[], double *data_buffer[], int status[])=0
see hs_read(), returns HS_SUCCESS
virtual int hs_disconnect()=0
disconnect from history, returns HS_SUCCESS
virtual int hs_get_events(time_t time_from, std::vector< std::string > *pevents)=0
get list of events that exist(ed) at given time and later (value 0 means "return all events from begi...
virtual int hs_get_tags(const char *event_name, time_t time_from, std::vector< TAG > *ptags)=0
get list of history variables for given event (use event names returned by hs_get_events()) that exis...
virtual int hs_get_last_written(time_t start_time, int num_var, const char *const event_name[], const char *const tag_name[], const int var_index[], time_t last_written[])=0
virtual int hs_clear_cache()=0
clear internal cache, returns HS_SUCCESS
void initparam()
Definition mhttpd.cxx:719
void unsetparam(const char *param)
Definition mhttpd.cxx:819
const char * getparam(const char *param)
Definition mhttpd.cxx:776
char _param[MAX_PARAM][PARAM_LENGTH]
Definition mhttpd.cxx:704
void setparam(const char *param, const char *value)
Definition mhttpd.cxx:726
~Param()
Definition mhttpd.cxx:714
char * _value[MAX_PARAM]
Definition mhttpd.cxx:705
BOOL isparam(const char *param)
Definition mhttpd.cxx:805
void printparam()
Definition mhttpd.cxx:767
std::string xgetparam(const char *param)
Definition mhttpd.cxx:796
Param()
Definition mhttpd.cxx:709
void freeparam()
Definition mhttpd.cxx:756
char _text[TEXT_SIZE]
Definition mhttpd.cxx:706
MUTEX_T * fMutex
Definition mhttpd.cxx:440
void AddTrace(RequestTrace *t)
Definition mhttpd.cxx:458
void AddTraceMTS(RequestTrace *t)
Definition mhttpd.cxx:463
std::vector< RequestTrace * > fBuf
Definition mhttpd.cxx:441
std::string fQuery
Definition mhttpd.cxx:396
std::string fMethod
Definition mhttpd.cxx:394
void PrintTrace0() const
Definition mhttpd.cxx:413
std::string fUri
Definition mhttpd.cxx:395
double fTimeSent
Definition mhttpd.cxx:392
std::string fResource
Definition mhttpd.cxx:398
bool fCompleted
Definition mhttpd.cxx:393
double fTimeReceived
Definition mhttpd.cxx:388
std::string fRPC
Definition mhttpd.cxx:397
double fTimeUnlocked
Definition mhttpd.cxx:390
double fTimeLocked
Definition mhttpd.cxx:389
double fTimeProcessed
Definition mhttpd.cxx:391
Return()
Definition mhttpd.cxx:524
va_end(argptr)
~Return()
Definition mhttpd.cxx:534
else strcpy(return_buffer+strlen_retbuf, str)
int return_grow(size_t len)
Definition mhttpd.cxx:556
return_grow(strlen(str))
vsprintf(str,(char *) format, argptr)
void rread(const char *filename, int fh, int len)
Definition mhttpd.cxx:588
int strlen_retbuf
Definition mhttpd.cxx:520
size_t return_size
Definition mhttpd.cxx:517
void zero()
Definition mhttpd.cxx:549
void char str[10000]
Definition mhttpd.cxx:672
void rsprintf(const char *format,...) MATTRPRINTF(2
void rmemcpy(const void *buf, int len)
Definition mhttpd.cxx:578
va_start(argptr, format)
int return_length
Definition mhttpd.cxx:521
void rsputs2(const char *str)
Definition mhttpd.cxx:621
void rsputs(const char *str)
Definition mhttpd.cxx:602
void reset()
Definition mhttpd.cxx:544
char * return_buffer
Definition mhttpd.cxx:518
assert(strlen(str)< sizeof(str))
static bool exists(const std::string &name)
Definition odbxx.cxx:76
static void usage()
int done
TRIGGER_SETTINGS ts
INT al_get_alarms(std::string *presult)
Definition alarm.cxx:877
INT cm_yield(INT millisec)
Definition midas.cxx:5660
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3027
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2782
INT cm_start_watchdog_thread()
Definition midas.cxx:7366
INT cm_connect_experiment1(const char *host_name, const char *default_exp_name, const char *client_name, void(*func)(char *), INT odb_size, DWORD watchdog_timeout)
Definition midas.cxx:2313
std::string cm_expand_env(const char *str)
Definition midas.cxx:7721
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5479
INT cm_disconnect_experiment(void)
Definition midas.cxx:2862
std::string cm_get_exptab_filename()
Definition midas.cxx:1804
std::string cm_get_path()
Definition midas.cxx:1553
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5861
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2150
const char * cm_get_version()
Definition midas.cxx:1492
std::string cm_get_experiment_name()
Definition midas.cxx:1596
const char * cm_get_revision()
Definition midas.cxx:1500
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7531
INT el_submit(int run, const char *author, const char *type, const char *syst, const char *subject, const char *text, const char *reply_to, const char *encoding, const char *afilename1, const char *buffer1, INT buffer_size1, const char *afilename2, const char *buffer2, INT buffer_size2, const char *afilename3, const char *buffer3, INT buffer_size3, char *tag, INT tag_size)
Definition elog.cxx:126
#define CM_SUCCESS
Definition midas.h:582
#define CM_WRONG_PASSWORD
Definition midas.h:589
#define DB_STRUCT_MISMATCH
Definition midas.h:655
#define DB_OUT_OF_RANGE
Definition midas.h:652
#define DB_INVALID_HANDLE
Definition midas.h:636
#define DB_NO_ACCESS
Definition midas.h:649
#define DB_SUCCESS
Definition midas.h:632
#define DB_NO_KEY
Definition midas.h:643
#define DB_NO_MORE_SUBKEYS
Definition midas.h:647
#define SS_SUCCESS
Definition midas.h:664
#define SS_FILE_ERROR
Definition midas.h:670
#define SS_CREATED
Definition midas.h:665
#define SS_SOCKET_ERROR
Definition midas.h:674
#define RPC_SHUTDOWN
Definition midas.h:708
#define RPC_SUCCESS
Definition midas.h:699
#define RPC_NET_ERROR
Definition midas.h:702
#define HS_UNDEFINED_VAR
Definition midas.h:734
#define HS_SUCCESS
Definition midas.h:728
#define HS_FILE_ERROR
Definition midas.h:729
#define EL_SUCCESS
Definition midas.h:746
unsigned short int WORD
Definition mcstd.h:49
unsigned int DWORD
Definition mcstd.h:51
#define SUCCESS
Definition mcstd.h:54
#define TID_DOUBLE
Definition midas.h:343
#define TID_KEY
Definition midas.h:349
#define TID_BOOL
Definition midas.h:340
#define MODE_EXCLUSIVE
Definition midas.h:373
#define MT_INFO
Definition midas.h:543
#define TID_WORD
Definition midas.h:332
#define STATE_STOPPED
Definition midas.h:305
#define MINFO
Definition midas.h:560
#define MODE_DELETE
Definition midas.h:372
#define TID_LINK
Definition midas.h:350
#define TID_STRING
Definition midas.h:346
#define MODE_WRITE
Definition midas.h:371
#define MERROR
Definition midas.h:559
#define STATE_RUNNING
Definition midas.h:307
#define MODE_READ
Definition midas.h:370
#define TID_INT
Definition midas.h:338
#define TID_FLOAT
Definition midas.h:341
void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c)
Definition mgd.cxx:2471
void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
Definition mgd.cxx:2299
gdImagePtr gdImageCreate(int sx, int sy)
Definition mgd.cxx:417
void gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, const char *s, int color)
Definition mgd.cxx:877
void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
Definition mgd.cxx:2307
void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
Definition mgd.cxx:638
gdImagePtr gdImageCreateFromGif(FILE *fd)
Definition mgd.cxx:1888
int x
Definition mgd.h:121
char * data
Definition mgd.h:54
int w
Definition mgd.h:64
void gdImageStringUp(gdImagePtr im, gdFontPtr f, int x, int y, const char *s, int color)
Definition mgd.cxx:888
void gdImageDestroy(gdImagePtr im)
Definition mgd.cxx:439
gdFontPtr gdFontMediumBold
Definition mgd.cxx:408
int h
Definition mgd.h:65
gdFontPtr gdFontGiant
Definition mgd.cxx:409
int gdImageColorClosest(gdImagePtr im, int r, int g, int b)
Definition mgd.cxx:455
void gdImageGif(gdImagePtr im, gdGifBuffer *buffer)
Definition mgd.cxx:1118
void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
Definition mgd.cxx:738
int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)
Definition mgd.cxx:492
void gdImageInterlace(gdImagePtr im, int interlaceArg)
Definition mgd.cxx:2619
int y
Definition mgd.h:121
gdFontPtr gdFontSmall
Definition mgd.cxx:410
void gdImageFill(gdImagePtr im, int x, int y, int color)
Definition mgd.cxx:964
void gdImageColorTransparent(gdImagePtr im, int color)
Definition mgd.cxx:533
void mjsonrpc_init()
void D(const char *description)
Definition mjsonrpc.cxx:303
MJsonNode * mjsonrpc_decode_post_data(const char *post_data)
void P(const char *name, int mjson_type, const char *description)
Definition mjsonrpc.cxx:340
MJsonNode * mjsonrpc_make_result(MJsonNode *node)
Definition mjsonrpc.cxx:135
void mjsonrpc_exit()
static MJSO * I()
Definition mjsonrpc.cxx:298
MJsonNode * mjsonrpc_get_schema()
void mjsonrpc_set_std_mutex(void *mutex)
std::string mjsonrpc_schema_to_text(const MJsonNode *schema)
void mjsonrpc_add_handler(const char *method, mjsonrpc_handler_t *handler, bool needs_locking)
void R(const char *name, int mjson_type, const char *description)
Definition mjsonrpc.cxx:348
#define MAX(a, b)
Definition midas.h:509
int mg_printf(struct mg_connection *, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
time_t mg_mgr_poll(struct mg_mgr *, int milli)
void mg_mgr_free(struct mg_mgr *)
char * cs_md5(char buf[33],...)
void * user_data
Definition mongoose6.h:1265
struct mg_str header_names[MG_MAX_HTTP_HEADERS]
Definition mongoose6.h:2090
#define MG_MAX_HTTP_HEADERS
Definition mongoose6.h:2031
#define MG_F_CLOSE_IMMEDIATELY
Definition mongoose6.h:1289
void mbuf_remove(struct mbuf *, size_t data_size)
void mg_mgr_init(struct mg_mgr *mgr, void *user_data)
void mg_send_head(struct mg_connection *n, int status_code, int64_t content_length, const char *extra_headers)
struct mg_str uri
Definition mongoose6.h:2072
#define MG_EV_RECV
Definition mongoose6.h:1222
const char * mg_set_ssl(struct mg_connection *nc, const char *cert, const char *ca_cert)
void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t)
struct mg_str header_values[MG_MAX_HTTP_HEADERS]
Definition mongoose6.h:2091
union socket_address sa
Definition mongoose6.h:1253
struct mg_str method
Definition mongoose6.h:2071
size_t len
Definition mongoose6.h:835
size_t len
Definition mongoose6.h:1207
#define MG_F_LISTENING
Definition mongoose6.h:1278
void mg_send(struct mg_connection *, const void *buf, int len)
struct mg_connection * next
Definition mongoose6.h:1247
struct mbuf recv_mbuf
Definition mongoose6.h:1255
#define MG_EV_POLL
Definition mongoose6.h:1219
void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, mg_event_handler_t handler)
struct mg_str query_string
Definition mongoose6.h:2087
#define MG_EV_HTTP_CHUNK
Definition mongoose6.h:2116
struct sockaddr sin6
Definition mongoose6.h:1200
mg_event_handler_t f
Definition mongoose6.h:1272
void mg_set_protocol_http_websocket(struct mg_connection *nc)
#define MG_EV_SEND
Definition mongoose6.h:1223
unsigned long flags
Definition mongoose6.h:1276
struct mg_connection * listener
Definition mongoose6.h:1248
struct mg_str proto
Definition mongoose6.h:2073
struct mg_str body
Definition mongoose6.h:2094
struct sockaddr sa
Definition mongoose6.h:1195
char * buf
Definition mongoose6.h:834
struct mg_connection * mg_bind(struct mg_mgr *, const char *, mg_event_handler_t)
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, size_t buf_size)
struct mg_str mg_mk_str(const char *s)
#define MG_EV_CLOSE
Definition mongoose6.h:1224
void mg_enable_multithreading(struct mg_connection *nc)
struct mg_str * mg_get_http_header(struct http_message *hm, const char *name)
const char * p
Definition mongoose6.h:1206
#define MG_EV_ACCEPT
Definition mongoose6.h:1220
struct mg_connection * mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t)
struct sockaddr_in sin
Definition mongoose6.h:1196
#define MG_F_SEND_AND_CLOSE
Definition mongoose6.h:1288
#define MG_EV_HTTP_REQUEST
Definition mongoose6.h:2114
#define O_BINARY
Definition msystem.h:226
std::string ss_gethostname()
Definition system.cxx:5784
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3229
int ss_isnan(double x)
Definition system.cxx:8039
time_t ss_mktime(struct tm *tms)
Definition system.cxx:3437
DWORD ss_millitime()
Definition system.cxx:3465
int ss_file_exist(const char *path)
Definition system.cxx:7196
int ss_isfin(double x)
Definition system.cxx:8044
std::string ss_getcwd()
Definition system.cxx:5848
INT ss_getpid(void)
Definition system.cxx:1379
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:3013
void ss_tzset()
Definition system.cxx:3427
INT recv_string(int sock, char *buffer, DWORD buffer_size, INT millisec)
Definition system.cxx:5471
std::string ss_replace_env_variables(const std::string &inputPath)
Definition system.cxx:2284
INT ss_daemon_init(BOOL keep_stdout)
Definition system.cxx:2073
DWORD ss_time()
Definition system.cxx:3534
INT ss_sleep(INT millisec)
Definition system.cxx:3700
char * ss_crypt(const char *buf, const char *salt)
Definition system.cxx:7969
void * ss_ctrlc_handler(void(*func)(int))
Definition system.cxx:3971
INT ss_timezone()
Definition system.cxx:3652
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3109
INT cm_msg1(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format,...)
Definition midas.cxx:989
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:518
std::string cm_get_error(INT code)
Definition midas.cxx:469
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:931
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1280
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:553
#define TELL(fh)
Definition msystem.h:250
#define WORD_SWAP(x)
Definition msystem.h:65
#define DWORD_SWAP(x)
Definition msystem.h:74
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3285
INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:11005
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6917
INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4293
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5185
INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT idx)
Definition odb.cxx:6385
std::string strcomb1(const char **list)
Definition odb.cxx:668
INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7449
INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
Definition odb.cxx:4775
INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align, const char *rec_str)
Definition odb.cxx:11834
INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT *buffer_size, const char *path)
Definition odb.cxx:8230
INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7773
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6563
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3392
INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
Definition odb.cxx:13003
INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
Definition odb.cxx:9055
INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level, INT(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:4544
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6043
INT db_get_link(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6096
INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string *s, BOOL create, int create_string_length)
Definition odb.cxx:13967
INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10941
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7668
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13845
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7239
INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5495
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:3999
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10865
INT db_copy_json_obsolete(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int save_keys, int follow_links, int recurse)
Definition odb.cxx:10529
INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type)
Definition odb.cxx:5028
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4256
INT db_get_link_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6680
INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
Definition odb.cxx:6285
INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD *delta)
Definition odb.cxx:6156
INT db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT idx, DWORD type, BOOL trunc)
Definition odb.cxx:5135
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5357
INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
Definition odb.cxx:14058
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12831
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11336
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7523
INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
Definition odb.cxx:3688
#define RPC_CNAF16
Definition mrpc.h:129
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13926
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11958
#define RPC_JRPC
Definition mrpc.h:134
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11704
const char * rpc_tid_name(INT id)
Definition midas.cxx:11895
#define RPC_CNAF24
Definition mrpc.h:130
#define RPC_MANUAL_TRIG
Definition mrpc.h:132
INT rpc_tid_size(INT id)
Definition midas.cxx:11888
static std::string q(const char *s)
#define HS_GET_INACTIVE
Definition history.h:37
#define HS_GET_READER
Definition history.h:35
int hs_read_event_list(std::vector< std::string > *pevents)
int hs_find_reader_channel(HNDLE hDB, HNDLE *hKeyOut, int debug_flag)
int hs_get_history(HNDLE hDB, HNDLE hKey, int flags, int debug_flag, MidasHistoryInterface **mh)
int main()
Definition hwtest.cxx:23
void ** info
Definition fesimdaq.cxx:41
HNDLE hKey
INT run_number[2]
Definition mana.cxx:246
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
BOOL debug
debug printouts
Definition mana.cxx:254
BOOL daemon
Definition mana.cxx:258
INT type
Definition mana.cxx:269
HNDLE hDB
main ODB handle
Definition mana.cxx:207
char addr[128]
Definition mcnaf.cxx:104
double count
Definition mdump.cxx:33
KEY key
Definition mdump.cxx:34
INT i
Definition mdump.cxx:32
char response[10000]
Definition melog.cxx:90
#define closesocket(s)
Definition melog.cxx:29
static int offset
Definition mgd.cxx:1500
static void handle_event_mg(struct mg_connection *nc, int ev, void *ev_data)
Definition mhttpd.cxx:14180
static bool trace_mg
Definition mhttpd.cxx:13712
static void SaveHistPlotToOdb(MVOdb *odb, const HistPlot &hp, const char *group, const char *panel)
Definition mhttpd.cxx:10413
static bool trace_mg_send
Definition mhttpd.cxx:13714
#define LOG2
Definition mhttpd.cxx:7637
BOOL is_editable(char *eq_name, char *var_name)
Definition mhttpd.cxx:2536
#define LN10
Definition mhttpd.cxx:7636
const unsigned char favicon_png[]
Definition mhttpd.cxx:217
#define WEB_BUFFER_SIZE
Definition mhttpd.cxx:511
void haxis(gdImagePtr im, gdFont *font, int col, int gcol, int x1, int y1, int width, int minor, int major, int text, int label, int grid, double xmin, double xmax)
Definition mhttpd.cxx:7640
static bool cmp_vars(const HistVar &a, const HistVar &b)
Definition mhttpd.cxx:10109
void output_key(Param *p, Return *r, HNDLE hkey, int index, const char *format)
Definition mhttpd.cxx:4381
static MVOdb * gOdb
Definition mhttpd.cxx:48
void get_elog_url(char *url, int len)
static void DeleteHistPlotDeleted(HistPlot &hp)
Definition mhttpd.cxx:10489
static BOOL history_mode
Definition mhttpd.cxx:95
static void LoadHistPlotFromParam(HistPlot *hp, Param *p)
Definition mhttpd.cxx:10308
static void add_rpc_functions()
Definition mhttpd.cxx:16218
#define RESPONSE_501
Definition mhttpd.cxx:14263
INT check_odb_records(MVOdb *odb)
Definition mhttpd.cxx:13396
static int handle_http_post(struct mg_connection *nc, const http_message *msg, const char *uri, RequestTrace *t)
Definition mhttpd.cxx:14891
std::string get_content_type(const char *filename)
Definition mhttpd.cxx:1135
static const std::string find_header_mg(const struct http_message *msg, const char *name)
Definition mhttpd.cxx:14149
static std::mutex gMutex
Definition mhttpd.cxx:47
static void xmg_mkmd5resp(const char *method, size_t method_len, const char *uri, size_t uri_len, const char *ha1, size_t ha1_len, const char *nonce, size_t nonce_len, const char *nc, size_t nc_len, const char *cnonce, size_t cnonce_len, const char *qop, size_t qop_len, char *resp)
Definition mhttpd.cxx:13762
#define HTTP_ENCODING
Definition mhttpd.cxx:213
void strencode(Return *r, const char *text)
Definition mhttpd.cxx:2024
static MVOdb * gMimeTypesOdb
Definition mhttpd.cxx:179
void init_elog_odb()
Definition mhttpd.cxx:1983
static const char default_type_list[20][NAME_LENGTH]
Definition mhttpd.cxx:101
void show_eqtable_page(Param *pp, Return *r, int refresh)
Definition mhttpd.cxx:2564
static void history_watch_callback(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition mhttpd.cxx:8153
void show_custom_file(Return *r, const char *name)
Definition mhttpd.cxx:3616
void show_find_page(Return *r, const char *value)
Definition mhttpd.cxx:7575
#define MAX_GROUPS
Definition mhttpd.cxx:52
BOOL check_web_password(Return *r, HNDLE hDB, const char *dec_path, const char *password, const char *redir)
Definition mhttpd.cxx:6744
bool send_fp(Return *r, const std::string &path, FILE *fp)
Definition mhttpd.cxx:1166
std::vector< std::string > get_resource_paths()
Definition mhttpd.cxx:991
static void SplitEventAndTagNames(std::string var_name, std::string &event_name, std::string &tag_name)
Definition mhttpd.cxx:10159
void decode_cookies(Cookies *c, const http_message *msg)
Definition mhttpd.cxx:14214
char * stristr(const char *str, const char *pattern)
Definition mhttpd.cxx:341
void submit_elog(MVOdb *odb, Param *pp, Return *r, Attachment *a)
Definition mhttpd.cxx:2253
void show_query_page(Param *p, Return *r)
Definition mhttpd.cxx:9799
static void urlEncode(char *ps, int ps_size)
Definition mhttpd.cxx:921
static std::string NextHistPlotColour(const HistPlot &hp)
Definition mhttpd.cxx:10124
std::string strencode2(const char *text)
Definition mhttpd.cxx:2052
void decode_get(Return *rr, char *string, const Cookies *c, const char *url, const char *query_string, RequestTrace *t)
Definition mhttpd.cxx:13202
static MJsonNode * get_http_trace(const MJsonNode *params)
Definition mhttpd.cxx:16191
void show_error(Return *r, const char *error)
Definition mhttpd.cxx:1843
static HNDLE gMhkey
Definition mhttpd.cxx:8161
static bool read_passwords(Auth *auth)
Definition mhttpd.cxx:13804
void redirect2(Return *r, const char *path)
Definition mhttpd.cxx:1448
static void show_cnaf_page(Param *p, Return *rr)
Definition mhttpd.cxx:5662
static void SaveMimetypes(MVOdb *odb)
Definition mhttpd.cxx:203
#define MAX_PARAM
Definition mhttpd.cxx:697
#define DEFAULT_REFRESH
Definition mhttpd.cxx:39
void show_navigation_bar(Return *r, const char *cur_page)
Definition mhttpd.cxx:1871
#define LOG5
Definition mhttpd.cxx:7638
void ctrlc_handler(int sig)
Definition mhttpd.cxx:13460
static void PrintHistPlot(const HistPlot &hp)
Definition mhttpd.cxx:10114
void javascript_commands(Param *p, Return *r, const char *cookie_cpwd)
Definition mhttpd.cxx:4456
#define MAX_VARS
Definition mhttpd.cxx:53
static int handle_decode_post(struct mg_connection *nc, const http_message *msg, const char *uri, const char *query_string, RequestTrace *t)
Definition mhttpd.cxx:14735
std::string time_to_string(time_t t)
Definition mhttpd.cxx:8141
static std::string mgstr(const mg_str *s)
Definition mhttpd.cxx:14144
int vaxis(gdImagePtr im, gdFont *font, int col, int gcol, int x1, int y1, int width, int minor, int major, int text, int label, int grid, double ymin, double ymax, BOOL logaxis)
Definition mhttpd.cxx:7914
static bool mongoose_passwords_enabled(const struct mg_connection *nc)
static bool verbose_mg
Definition mhttpd.cxx:13711
static int cmp_names(const void *a, const void *b)
Definition mhttpd.cxx:9983
INT sendmail(const char *from_host, const char *smtp_host, const char *from, const char *to, const char *subject, const char *text)
Definition mhttpd.cxx:1255
int find_file_mg(const char *filename, std::string &path, FILE **fpp, bool trace)
Definition mhttpd.cxx:13679
void decode_query(Param *pp, const char *query_string)
Definition mhttpd.cxx:13177
static bool gDoReloadHistory
Definition mhttpd.cxx:8151
static std::string check_digest_auth(struct http_message *hm, Auth *auth)
Definition mhttpd.cxx:13900
static MidasHistoryInterface * gMh
Definition mhttpd.cxx:8160
static bool trace_mg_verbose
Definition mhttpd.cxx:13715
static bool gDoSetupHistoryWatch
Definition mhttpd.cxx:8150
int get_hist_last_written(MVOdb *odb, const char *group, const char *panel, time_t endtime, int index, int want_all, time_t *plastwritten)
Definition mhttpd.cxx:8529
void taxis(gdImagePtr im, gdFont *font, int col, int gcol, int x1, int y1, int width, int xr, int minor, int major, int text, int label, int grid, double xmin, double xmax)
Definition mhttpd.cxx:7793
void show_password_page(Return *r, const char *dec_path, const char *password)
Definition mhttpd.cxx:6710
time_t string_to_time(const char *str)
Definition mhttpd.cxx:8125
void show_header(Return *r, const char *title, const char *method, const char *path, int refresh)
Definition mhttpd.cxx:1783
bool send_resource(Return *r, const std::string &name, bool generate_404=true)
Definition mhttpd.cxx:1231
void strencode3(Return *r, const char *text)
Definition mhttpd.cxx:2083
static std::vector< std::string > gAllowedHosts
Definition mhttpd.cxx:13470
bool send_file(Return *r, const std::string &path, bool generate_404=true)
Definition mhttpd.cxx:1212
static int xmg_check_nonce(const char *nonce)
Definition mhttpd.cxx:13783
void show_text_header(Return *r)
Definition mhttpd.cxx:1831
char * find_odb_tag(char *p, char *path, char *format, int *edit, char *type, char *pwd, char *tail)
Definition mhttpd.cxx:3098
static std::string UrlDecode(const char *p)
Definition mhttpd.cxx:843
void strencode4(Return *r, const char *text)
Definition mhttpd.cxx:2108
void show_elog_attachment(Param *p, Return *r, const char *path)
Definition mhttpd.cxx:2488
void Unlock(RequestTrace *t)
Definition mhttpd.cxx:12250
int evaluate_src(char *key, char *src, double *fvalue)
Definition mhttpd.cxx:3504
void redirect(Return *r, const char *path)
Definition mhttpd.cxx:1411
static void urlDecode(char *p)
Definition mhttpd.cxx:882
static std::string add_param_to_url(const char *name, const char *value)
Definition mhttpd.cxx:9788
#define READ_HISTORY_DATA
Definition mhttpd.cxx:8328
bool starts_with(const std::string &s1, const char *s2)
Definition mhttpd.cxx:4440
static const char * cgif_bar_str[]
Definition mhttpd.cxx:3475
#define PARAM_LENGTH
Definition mhttpd.cxx:698
#define RESPONSE_QUEUED
Definition mhttpd.cxx:14262
static MidasHistoryInterface * get_history(bool reset=false)
Definition mhttpd.cxx:8165
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)
Definition mhttpd.cxx:3282
static int handle_http_get(struct mg_connection *nc, const http_message *msg, const char *uri, RequestTrace *t)
Definition mhttpd.cxx:14808
#define READ_HISTORY_RUNMARKER
Definition mhttpd.cxx:8329
std::string add_custom_path(const std::string &filename)
Definition mhttpd.cxx:3577
void sec_to_label(char *result, int sec, int base, int force_date)
Definition mhttpd.cxx:7758
void show_odb_page(Param *pp, Return *r, const char *dec_path, int write_access)
Definition mhttpd.cxx:6801
void send_icon(Return *r, const char *icon)
Definition mhttpd.cxx:12192
static BOOL elog_mode
Definition mhttpd.cxx:94
void Lock(RequestTrace *t)
Definition mhttpd.cxx:12244
void show_set_page(Param *pp, Return *r, const char *group, int index, const char *value)
Definition mhttpd.cxx:7441
void decode_post(Return *rr, const char *header, const char *string, const char *boundary, int length, const Cookies *c, const char *url, RequestTrace *t)
Definition mhttpd.cxx:13240
#define DELETE(x)
Definition mhttpd.cxx:8243
void show_hist_config_page(MVOdb *odb, Param *p, Return *r, const char *hgroup, const char *hpanel)
Definition mhttpd.cxx:10527
static const std::string find_cookie_mg(const struct http_message *msg, const char *cookie_name)
Definition mhttpd.cxx:14162
void do_jrpc(Param *p, Return *r)
Definition mhttpd.cxx:4327
const unsigned char favicon_ico[]
Definition mhttpd.cxx:282
void init_mhttpd_odb(MVOdb *odb)
Definition mhttpd.cxx:1935
void check_obsolete_odb(HNDLE hDB, const char *odb_path)
Definition mhttpd.cxx:1885
void do_jrpc_rev1(Param *p, Return *r)
Definition mhttpd.cxx:4146
static RequestTraceBuf * gTraceBuf
Definition mhttpd.cxx:506
const char * mhttpd_revision(void)
Definition mhttpd.cxx:836
void show_hist_page(MVOdb *odb, Param *p, Return *r, const char *dec_path, char *buffer, int *buffer_size, int refresh)
Definition mhttpd.cxx:11270
static int handle_decode_get(struct mg_connection *nc, const http_message *msg, const char *uri, const char *query_string, RequestTrace *t)
Definition mhttpd.cxx:14265
void gen_odb_attachment(Return *r, const char *path, std::string &bout)
Definition mhttpd.cxx:2139
#define RESPONSE_SENT
Definition mhttpd.cxx:14261
bool open_resource_file(const char *filename, std::string *ppath, FILE **pfp)
Definition mhttpd.cxx:1031
void redirect_307(Return *r, const char *path)
Definition mhttpd.cxx:1435
static std::string GetMimetype(const std::string &ext)
Definition mhttpd.cxx:181
static double GetTimeSec()
Definition mhttpd.cxx:376
void show_custom_page(Param *pp, Return *r, const char *cookie_cpwd)
Definition mhttpd.cxx:5428
static MJsonNode * set_http_trace(const MJsonNode *params)
Definition mhttpd.cxx:16204
const bool cmp_tags(const TAG &a, const TAG &b)
Definition mhttpd.cxx:10051
const bool cmp_events1(const std::string &a, const std::string &b)
Definition mhttpd.cxx:10046
#define READ_HISTORY_LAST_WRITTEN
Definition mhttpd.cxx:8330
int read_history(const HistPlot &hp, int index, int flags, time_t tstart, time_t tend, time_t scale, HistoryData *data)
Definition mhttpd.cxx:8364
static std::string toString(int i)
Definition mhttpd.cxx:57
const MimetypeTableEntry gMimetypeTable[]
Definition mhttpd.cxx:130
int time_to_sec(const char *str)
Definition mhttpd.cxx:8100
static BOOL verbose
Definition mhttpd.cxx:96
void show_help_page(Return *r, const char *dec_path)
Definition mhttpd.cxx:1544
void do_jrpc_rev0(Param *p, Return *r)
Definition mhttpd.cxx:4021
INT search_callback(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *info)
Definition mhttpd.cxx:1461
void init_menu_buttons(MVOdb *odb)
Definition mhttpd.cxx:1894
std::atomic_bool _abort
Definition mhttpd.cxx:13458
void generate_hist_graph(MVOdb *odb, Return *rr, const char *hgroup, const char *hpanel, char *buffer, int *buffer_size, int width, int height, time_t xendtime, int scale, int index, int labels, const char *bgcolor, const char *fgcolor, const char *gridcolor)
Definition mhttpd.cxx:8612
#define ALLOC(t, n)
Definition mhttpd.cxx:8242
static const char * cgif_label_str[]
Definition mhttpd.cxx:3435
time_t mktime_with_dst(const struct tm *ptms)
Definition mhttpd.cxx:9753
const char * mname[]
Definition midas.cxx:144
int try_file_mg(const char *try_dir, const char *filename, std::string &path, FILE **fpp, bool trace)
Definition mhttpd.cxx:13646
void export_hist(MVOdb *odb, Return *r, const char *group, const char *panel, time_t endtime, int scale, int index, int labels)
Definition mhttpd.cxx:11033
#define STRDUP(x)
Definition mhttpd.cxx:8245
static int http_trace
Definition mhttpd.cxx:435
static void xmg_http_send_digest_auth_request(struct mg_connection *c, const char *domain)
Definition mhttpd.cxx:13794
void interprete(Param *p, Return *r, Attachment *a, const Cookies *c, const char *dec_path, RequestTrace *t)
Definition mhttpd.cxx:12258
static const char default_system_list[20][NAME_LENGTH]
Definition mhttpd.cxx:116
static void handle_http_options_cors(struct mg_connection *nc, const http_message *msg, RequestTrace *t)
Definition mhttpd.cxx:15017
#define DELETEA(x, n)
Definition mhttpd.cxx:8244
static Auth * gAuthMg
Definition mhttpd.cxx:13760
static void SortHistPlotVars(HistPlot &hp)
Definition mhttpd.cxx:10506
const bool cmp_events(const std::string &a, const std::string &b)
Definition mhttpd.cxx:10041
int xdb_get_data_index(HNDLE hDB, const char *str, void *value, int size, int index, int tid)
Definition mhttpd.cxx:10070
void show_error_404(Return *r, const char *error)
Definition mhttpd.cxx:1858
static void handle_http_message(struct mg_connection *nc, http_message *msg)
Definition mhttpd.cxx:15087
static int xdb_find_key(HNDLE hDB, HNDLE dir, const char *str, HNDLE *hKey, int tid, int size)
Definition mhttpd.cxx:10086
static void AddHistPlotSelectedParam(HistPlot &hp, Param *p)
Definition mhttpd.cxx:10385
void show_custom_gif(Return *rr, const char *name)
Definition mhttpd.cxx:3679
static void LoadHistPlotFromOdb(MVOdb *odb, HistPlot *hp, const char *group, const char *panel)
Definition mhttpd.cxx:10203
static bool trace_mg_recv
Definition mhttpd.cxx:13713
static int NextHistPlotOrder(const HistPlot &hp)
Definition mhttpd.cxx:10150
#define TEXT_SIZE
Definition mhttpd.cxx:699
bool ends_with_char(const std::string &s, char c)
Definition midas.cxx:412
std::string msprintf(const char *format,...)
Definition midas.cxx:419
#define DIR_SEPARATOR
Definition midas.h:193
INT HNDLE
Definition midas.h:132
#define M_MALLOC(x)
Definition midas.h:1551
#define RPC_OUT
Definition midas.h:1580
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
#define RPC_IN
Definition midas.h:1579
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MAX_ID
Definition midas.h:1607
int INT
Definition midas.h:129
#define MATTRPRINTF(a, b)
Definition midas.h:221
#define CNAF_INHIBIT_SET
Definition midas.h:494
#define MAX_ODB_PATH
Definition midas.h:277
#define CNAF_CRATE_ZINIT
Definition midas.h:497
#define M_FREE(x)
Definition midas.h:1553
#define TRUE
Definition midas.h:182
#define CNAF
Definition midas.h:491
#define CNAF_CRATE_CLEAR
Definition midas.h:496
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define EQUIPMENT_COMMON_STR
Definition midas.h:1112
#define POINTER_T
Definition midas.h:166
std::vector< std::string > STRING_LIST
Definition midas.h:246
INT MUTEX_T
Definition midas.h:237
#define RUNINFO_STR(_name)
Definition midas.h:1409
#define NAME_LENGTH
Definition midas.h:272
#define CNAF_INHIBIT_CLEAR
Definition midas.h:495
MidasHistoryInterface * mh
#define end
#define read(n, a, f)
#define event_id
#define write(n, a, f, d)
#define name(x)
Definition midas_macro.h:24
static FILE * fp
#define SOMAXCONN
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, size_t buf_size)
int gettimeofday(struct timeval *tp, void *tzp)
timeval tv
Definition msysmon.cxx:1095
MUTEX_T * tm
Definition odbedit.cxx:39
char pwd[256]
Definition odbedit.cxx:24
double total[100]
Definition odbhist.cxx:42
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
char file_name[256]
Definition odbhist.cxx:41
DWORD status
Definition odbhist.cxx:39
char var_name[256]
Definition odbhist.cxx:41
Definition mhttpd.cxx:13724
std::string password
Definition mhttpd.cxx:13727
std::string username
Definition mhttpd.cxx:13725
std::string realm
Definition mhttpd.cxx:13726
char bgcolor[8]
Definition mhttpd.cxx:3498
double max
Definition mhttpd.cxx:3496
char src[256]
Definition mhttpd.cxx:3493
BOOL logscale
Definition mhttpd.cxx:3495
double min
Definition mhttpd.cxx:3496
int width
Definition mhttpd.cxx:3494
char fgcolor[8]
Definition mhttpd.cxx:3497
int height
Definition mhttpd.cxx:3494
int direction
Definition mhttpd.cxx:3494
char bdcolor[8]
Definition mhttpd.cxx:3499
char format[32]
Definition mhttpd.cxx:3449
char src[256]
Definition mhttpd.cxx:3448
char bgcolor[8]
Definition mhttpd.cxx:3453
char font[32]
Definition mhttpd.cxx:3450
char fgcolor[8]
Definition mhttpd.cxx:3452
std::string cookie_pwd
Definition mhttpd.cxx:12235
int refresh
Definition mhttpd.cxx:12238
std::string cookie_wpwd
Definition mhttpd.cxx:12236
std::string cookie_cpwd
Definition mhttpd.cxx:12237
std::string timescale
Definition mhttpd.cxx:8348
bool enable_factor
Definition mhttpd.cxx:8357
bool show_fill
Definition mhttpd.cxx:8355
double minimum
Definition mhttpd.cxx:8349
double maximum
Definition mhttpd.cxx:8350
bool show_factor
Definition mhttpd.cxx:8356
bool zero_ylow
Definition mhttpd.cxx:8351
bool show_values
Definition mhttpd.cxx:8354
bool log_axis
Definition mhttpd.cxx:8352
std::vector< HistVar > vars
Definition mhttpd.cxx:8359
bool show_run_markers
Definition mhttpd.cxx:8353
double factor
Definition mhttpd.cxx:8341
std::string tag_name
Definition mhttpd.cxx:8335
double offset
Definition mhttpd.cxx:8342
int order
Definition mhttpd.cxx:8340
std::string formula
Definition mhttpd.cxx:8336
std::string label
Definition mhttpd.cxx:8338
double voffset
Definition mhttpd.cxx:8343
std::string colour
Definition mhttpd.cxx:8337
std::string event_name
Definition mhttpd.cxx:8334
bool show_raw_value
Definition mhttpd.cxx:8339
int * odb_index
Definition mhttpd.cxx:8254
time_t tstart
Definition mhttpd.cxx:8263
void Free()
Definition mhttpd.cxx:8285
int alloc_nvars
Definition mhttpd.cxx:8250
void Allocate(int xnvars)
Definition mhttpd.cxx:8267
time_t tend
Definition mhttpd.cxx:8264
double ** v
Definition mhttpd.cxx:8258
void Print() const
Definition mhttpd.cxx:8300
time_t ** t
Definition mhttpd.cxx:8257
char ** var_names
Definition mhttpd.cxx:8252
int * var_index
Definition mhttpd.cxx:8253
char ** event_names
Definition mhttpd.cxx:8251
int * status
Definition mhttpd.cxx:8255
bool have_last_written
Definition mhttpd.cxx:8260
time_t * last_written
Definition mhttpd.cxx:8261
int * num_entries
Definition mhttpd.cxx:8256
time_t scale
Definition mhttpd.cxx:8265
Definition midas.h:1027
INT num_values
Definition midas.h:1029
DWORD type
Definition midas.h:1028
INT total_size
Definition midas.h:1032
WORD access_mode
Definition midas.h:1034
INT last_written
Definition midas.h:1038
char name[NAME_LENGTH]
Definition midas.h:1030
INT item_size
Definition midas.h:1033
Definition mhttpd.cxx:125
std::string mimetype
Definition mhttpd.cxx:127
std::string ext
Definition mhttpd.cxx:126
Definition midas.h:1233
char name[NAME_LENGTH]
Definition midas.h:1234
Definition mgd.h:58
Definition mgd.h:120
Return * r
Definition mhttpd.cxx:1457
const char * search_name
Definition mhttpd.cxx:1458
double d
Definition system.cxx:1313
char c
Definition system.cxx:1312
static double e(void)
Definition tinyexpr.c:136
static te_expr * base(state *s)
Definition tinyexpr.c:357
static te_expr * list(state *s)
Definition tinyexpr.c:567