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 recv_string(s, str, strsize, 3000);
1289 if (verbose)
1290 puts(str);
1291
1292 /* drain server messages */
1293 do {
1294 str[0] = 0;
1295 recv_string(s, str, strsize, 300);
1296 if (verbose)
1297 puts(str);
1298 } while (str[0]);
1299
1300 sprintf(str, "HELO %s\r\n", from_host);
1301 send(s, str, strlen(str), 0);
1302 if (verbose)
1303 puts(str);
1304 recv_string(s, str, strsize, 3000);
1305 if (verbose)
1306 puts(str);
1307
1308 if (strchr(from, '<')) {
1309 mstrlcpy(buf, strchr(from, '<') + 1, sizeof(buf));
1310 if (strchr(buf, '>'))
1311 *strchr(buf, '>') = 0;
1312 } else
1313 mstrlcpy(buf, from, sizeof(buf));
1314
1315 sprintf(str, "MAIL FROM: %s\n", buf);
1316 send(s, str, strlen(str), 0);
1317 if (verbose)
1318 puts(str);
1319 recv_string(s, str, strsize, 3000);
1320 if (verbose)
1321 puts(str);
1322
1323 sprintf(str, "RCPT TO: <%s>\r\n", to);
1324 send(s, str, strlen(str), 0);
1325 if (verbose)
1326 puts(str);
1327 recv_string(s, str, strsize, 3000);
1328 if (verbose)
1329 puts(str);
1330
1331 sprintf(str, "DATA\r\n");
1332 send(s, str, strlen(str), 0);
1333 if (verbose)
1334 puts(str);
1335 recv_string(s, str, strsize, 3000);
1336 if (verbose)
1337 puts(str);
1338
1339 sprintf(str, "To: %s\r\nFrom: %s\r\nSubject: %s\r\n", to, from, subject);
1340 send(s, str, strlen(str), 0);
1341 if (verbose)
1342 puts(str);
1343
1344 sprintf(str, "X-Mailer: mhttpd revision %s\r\n", mhttpd_revision());
1345 send(s, str, strlen(str), 0);
1346 if (verbose)
1347 puts(str);
1348
1349 ss_tzset(); // required for localtime_r()
1350 time_t now;
1351 time(&now);
1352 struct tm tms;
1353 localtime_r(&now, &tms);
1354 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", &tms);
1355 offset = (-(int) timezone);
1356 if (tms.tm_isdst)
1357 offset += 3600;
1358 sprintf(str, "Date: %s %+03d%02d\r\n", buf, (int) (offset / 3600),
1359 (int) ((abs((int) offset) / 60) % 60));
1360 send(s, str, strlen(str), 0);
1361 if (verbose)
1362 puts(str);
1363
1364 sprintf(str, "Content-Type: TEXT/PLAIN; charset=US-ASCII\r\n\r\n");
1365 send(s, str, strlen(str), 0);
1366 if (verbose)
1367 puts(str);
1368
1369 /* analyze text for "." at beginning of line */
1370 const char* p = text;
1371 str[0] = 0;
1372 while (strstr(p, "\r\n.\r\n")) {
1373 i = (POINTER_T) strstr(p, "\r\n.\r\n") - (POINTER_T) p + 1;
1374 mstrlcat(str, p, i);
1375 p += i + 4;
1376 mstrlcat(str, "\r\n..\r\n", strsize);
1377 }
1378 mstrlcat(str, p, strsize);
1379 mstrlcat(str, "\r\n", strsize);
1380 send(s, str, strlen(str), 0);
1381 if (verbose)
1382 puts(str);
1383
1384 /* send ".<CR>" to signal end of message */
1385 sprintf(str, ".\r\n");
1386 send(s, str, strlen(str), 0);
1387 if (verbose)
1388 puts(str);
1389 recv_string(s, str, strsize, 3000);
1390 if (verbose)
1391 puts(str);
1392
1393 sprintf(str, "QUIT\n");
1394 send(s, str, strlen(str), 0);
1395 if (verbose)
1396 puts(str);
1397 recv_string(s, str, strsize, 3000);
1398 if (verbose)
1399 puts(str);
1400
1401 closesocket(s);
1402 free(str);
1403
1404 return 1;
1405}
1406
1407/*------------------------------------------------------------------*/
1408
1409void redirect(Return *r, const char *path)
1410{
1411 char str[256];
1412
1413 //printf("redirect to [%s]\n", path);
1414
1415 mstrlcpy(str, path, sizeof(str));
1416 if (str[0] == 0)
1417 strcpy(str, "./");
1418
1419 /* redirect */
1420 r->rsprintf("HTTP/1.1 302 Found\r\n");
1421 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1422 r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
1423
1424 if (strncmp(path, "http:", 5) == 0)
1425 r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
1426 else if (strncmp(path, "https:", 6) == 0)
1427 r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
1428 else {
1429 r->rsprintf("Location: %s\r\n\r\n<html>redir</html>\r\n", str);
1430 }
1431}
1432
1433void redirect_307(Return *r, const char *path)
1434{
1435 //printf("redirect_307 to [%s]\n", path);
1436
1437 /* redirect */
1438 r->rsprintf("HTTP/1.1 307 Temporary Redirect\r\n");
1439 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1440 r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
1441 r->rsprintf("Location: %s\r\n", path);
1442 r->rsprintf("\r\n");
1443 r->rsprintf("<html>redirect to %s</html>\r\n", path);
1444}
1445
1446void redirect2(Return* r, const char *path)
1447{
1448 redirect(r, path);
1449}
1450
1451/*------------------------------------------------------------------*/
1452
1454{
1456 const char* search_name;
1457};
1458
1460{
1461 search_data* sinfo = (search_data*)info;
1462 int i;
1463 INT size, status;
1464
1465 Return* r = sinfo->r;
1466 const char* search_name = sinfo->search_name;
1467
1468 /* convert strings to uppercase */
1469
1470 char xstr1[MAX_ODB_PATH];
1471 for (i = 0; key->name[i]; i++)
1472 xstr1[i] = toupper(key->name[i]);
1473 xstr1[i] = 0;
1474
1475 char str2[MAX_ODB_PATH];
1476 for (i = 0; search_name[i] ; i++)
1477 str2[i] = toupper(search_name[i]);
1478 str2[i] = 0;
1479
1480 if (strstr(xstr1, str2) != NULL) {
1481 char data[10000];
1482 std::string path = db_get_path(hDB, hKey).substr(1);
1483 std::string path_encoded = urlEncode(path.c_str());
1484
1485 if (key->type == TID_KEY || key->type == TID_LINK) {
1486 /* for keys, don't display data value */
1487 r->rsprintf("<tr><td class=\"ODBkey\"><a href=\"?cmd=odb&odb_path=/%s\">/%s</a></tr>\n", path_encoded.c_str(), path.c_str());
1488 } else {
1489 /* strip variable name from path */
1490 char* p = const_cast<char *>(path.data() + path.length() - 1);
1491 while (*p && *p != '/')
1492 *p-- = 0;
1493 if (*p == '/')
1494 *p = 0;
1495
1496 /* display single value */
1497 if (key->num_values == 1) {
1498 size = sizeof(data);
1499 status = db_get_data(hDB, hKey, data, &size, key->type);
1500 std::string data_str;
1501 if (status == DB_NO_ACCESS)
1502 data_str = "<no read access>";
1503 else
1504 data_str = db_sprintf(data, key->item_size, 0, key->type);
1505
1506 r->rsprintf("<tr><td class=\"ODBkey\">");
1507 r->rsprintf("<a href=\"?cmd=odb&odb_path=/%s\">/%s/%s</a></td>", path_encoded.c_str(), path.c_str(), key->name);
1508 r->rsprintf("<td class=\"ODBvalue\">%s</td></tr>\n", data_str.c_str());
1509 } else {
1510 /* display first value */
1511 i = key->num_values;
1512 if (i > 10)
1513 i = 11;
1514 r->rsprintf("<tr><td rowspan=%d class=\"ODBkey\">", i);
1515 r->rsprintf("<a href=\"?cmd=odb&odb_path=/%s\">/%s/%s\n", path_encoded.c_str(), path.c_str(), key->name);
1516
1517 for (int i = 0; i < key->num_values; i++) {
1518 size = sizeof(data);
1519 db_get_data_index(hDB, hKey, data, &size, i, key->type);
1520
1521 std::string data_str = db_sprintf(data, key->item_size, 0, key->type);
1522
1523 if (i > 0)
1524 r->rsprintf("<tr>");
1525
1526 r->rsprintf("<td class=\"ODBvalue\">[%d] %s</td></tr>\n", i, data_str.c_str());
1527
1528 if (i > 8) {
1529 r->rsprintf("<tr><td class=\"ODBvalue\">... [%d] values ...</td></tr>\n", key->num_values - i - 1);
1530 break;
1531 }
1532 }
1533 }
1534 }
1535 }
1536
1537 return SUCCESS;
1538}
1539
1540/*------------------------------------------------------------------*/
1541
1542void show_help_page(Return* r, const char* dec_path)
1543{
1544 const char *s;
1545 char str[256];
1546 int status;
1547
1548 show_header(r, "Help", "", "./", 0);
1549 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
1550 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
1551 show_navigation_bar(r, "Help");
1552
1553 r->rsprintf("<table class=\"mtable\" style=\"width: 95%%\">\n");
1554 r->rsprintf(" <tr>\n");
1555 r->rsprintf(" <td class=\"mtableheader\">MIDAS Help Page</td>\n");
1556 r->rsprintf(" </tr>\n");
1557 r->rsprintf(" <tr>\n");
1558 r->rsprintf(" <td>\n");
1559 r->rsprintf(" <table>\n");
1560
1561 r->rsprintf(" <tr>\n");
1562 r->rsprintf(" <td style=\"text-align:right;\">Documentation:</td>\n");
1563 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://midas.triumf.ca\">https://midas.triumf.ca</a></td>\n");
1564 r->rsprintf(" </tr>\n");
1565 r->rsprintf(" <tr>\n");
1566 r->rsprintf(" <td style=\"text-align:right;\">Discussion Forum:</td>\n");
1567 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://midas.triumf.ca/forum/\">https://midas.triumf.ca/forum/</a></td>\n");
1568 r->rsprintf(" </tr>\n");
1569 r->rsprintf(" <tr>\n");
1570 r->rsprintf(" <td style=\"text-align:right;\">Code:</td>\n");
1571 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://bitbucket.org/tmidas/midas/\">https://bitbucket.org/tmidas/midas/</a></td>\n");
1572 r->rsprintf(" </tr>\n");
1573 r->rsprintf(" <tr>\n");
1574 r->rsprintf(" <td style=\"text-align:right;\">Report a bug:</td>\n");
1575 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"https://bitbucket.org/tmidas/midas/issues/\">https://bitbucket.org/tmidas/midas/issues/</a></td>\n");
1576 r->rsprintf(" </tr>\n");
1577
1578 r->rsprintf(" <tr>\n");
1579 r->rsprintf(" <td style=\"text-align:right;\">Version:</td>\n");
1580 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_version());
1581 r->rsprintf(" </tr>\n");
1582 r->rsprintf(" <tr>\n");
1583 r->rsprintf(" <td style=\"text-align:right;\">Revision:</td>\n");
1584 std::string rev = cm_get_revision();
1585 std::string url = "https://bitbucket.org/tmidas/midas/commits/";
1586 // rev format looks like this:
1587 // Fri Nov 24 10:15:54 2017 -0800 - midas-2017-07-c-171-gb8928d5c-dirty on branch develop
1588 // -gXXX is the commit hash
1589 // -dirty should be removed from the hash url, if present
1590 // " " before "on branch" should be removed from the hash url
1591 std::string::size_type pos = rev.find("-g");
1592 if (pos != std::string::npos) {
1593 std::string hash = rev.substr(pos+2);
1594 pos = hash.find("-dirty");
1595 if (pos != std::string::npos) {
1596 hash = hash.substr(0, pos);
1597 }
1598 pos = hash.find(" ");
1599 if (pos != std::string::npos) {
1600 hash = hash.substr(0, pos);
1601 }
1602 url += hash;
1603 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"%s\">%s</a></td>\n", url.c_str(), rev.c_str());
1604 } else {
1605 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", rev.c_str());
1606 }
1607 r->rsprintf(" </tr>\n");
1608
1609 r->rsprintf(" <tr>\n");
1610 r->rsprintf(" <td style=\"text-align:right;\">MIDASSYS:</td>\n");
1611 s = getenv("MIDASSYS");
1612 if (!s) s = "(unset)";
1613 mstrlcpy(str, s, sizeof(str));
1614 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", str);
1615 r->rsprintf(" </tr>\n");
1616
1617 r->rsprintf(" <tr>\n");
1618 r->rsprintf(" <td style=\"text-align:right;\">mhttpd current directory:</td>\n");
1619 std::string cwd = ss_getcwd();
1620 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cwd.c_str());
1621 r->rsprintf(" </tr>\n");
1622
1623 r->rsprintf(" <tr>\n");
1624 r->rsprintf(" <td style=\"text-align:right;\">Exptab file:</td>\n");
1625 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_exptab_filename().c_str());
1626 r->rsprintf(" </tr>\n");
1627
1628 r->rsprintf(" <tr>\n");
1629 r->rsprintf(" <td style=\"text-align:right;\">Experiment:</td>\n");
1630 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_experiment_name().c_str());
1631 r->rsprintf(" </tr>\n");
1632
1633 r->rsprintf(" <tr>\n");
1634 r->rsprintf(" <td style=\"text-align:right;\">Experiment directory:</td>\n");
1635 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_path().c_str());
1636 r->rsprintf(" </tr>\n");
1637
1640
1641 if (status == CM_SUCCESS) {
1642 if (list.size() == 1) {
1643 r->rsprintf(" <tr>\n");
1644 r->rsprintf(" <td style=\"text-align:right;\">System logfile:</td>\n");
1645 std::string s;
1646 cm_msg_get_logfile("midas", 0, &s, NULL, NULL);
1647 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", s.c_str());
1648 r->rsprintf(" </tr>\n");
1649 } else {
1650 r->rsprintf(" <tr>\n");
1651 r->rsprintf(" <td style=\"text-align:right;\">Logfiles:</td>\n");
1652 r->rsprintf(" <td style=\"text-align:left;\">\n");
1653 for (unsigned i=0 ; i<list.size() ; i++) {
1654 if (i>0)
1655 r->rsputs("<br />\n");
1656 std::string s;
1657 cm_msg_get_logfile(list[i].c_str(), 0, &s, NULL, NULL);
1658 r->rsputs(s.c_str());
1659 }
1660 r->rsprintf("\n </td>\n");
1661 r->rsprintf(" </tr>\n");
1662 }
1663 }
1664
1665 r->rsprintf(" <tr>\n");
1666 r->rsprintf(" <td style=\"text-align:right;\">Image history:</td>\n");
1667 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", cm_get_history_path("IMAGE").c_str());
1668 r->rsprintf(" </tr>\n");
1669
1670 r->rsprintf(" <tr>\n");
1671 r->rsprintf(" <td style=\"text-align:right;\">Resource paths:</td>\n");
1672 r->rsprintf(" <td style=\"text-align:left;\">");
1673 std::vector<std::string> resource_paths = get_resource_paths();
1674 for (unsigned i=0; i<resource_paths.size(); i++) {
1675 if (i>0)
1676 r->rsputs("<br>");
1677 r->rsputs(resource_paths[i].c_str());
1678 std::string exp = cm_expand_env(resource_paths[i].c_str());
1679 //printf("%d %d [%s] [%s]\n", resource_paths[i].length(), exp.length(), resource_paths[i].c_str(), exp.c_str());
1680 if (exp != resource_paths[i]) {
1681 r->rsputs(" (");
1682 r->rsputs(exp.c_str());
1683 r->rsputs(")");
1684 }
1685 }
1686 r->rsprintf(" </td>\n");
1687 r->rsprintf(" </tr>\n");
1688
1689 std::string path;
1690
1691 r->rsprintf(" <tr>\n");
1692 r->rsprintf(" <td style=\"text-align:right;\">midas.css:</td>\n");
1693 if (open_resource_file("midas.css", &path, NULL))
1694 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1695 else
1696 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1697 r->rsprintf(" </tr>\n");
1698
1699 r->rsprintf(" <tr>\n");
1700 r->rsprintf(" <td style=\"text-align:right;\">midas.js:</td>\n");
1701 if (open_resource_file("midas.js", &path, NULL))
1702 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1703 else
1704 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1705 r->rsprintf(" </tr>\n");
1706
1707 r->rsprintf(" <tr>\n");
1708 r->rsprintf(" <td style=\"text-align:right;\">controls.js:</td>\n");
1709 if (open_resource_file("controls.js", &path, NULL))
1710 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1711 else
1712 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1713 r->rsprintf(" </tr>\n");
1714
1715 r->rsprintf(" <tr>\n");
1716 r->rsprintf(" <td style=\"text-align:right;\">mhttpd.js:</td>\n");
1717 if (open_resource_file("mhttpd.js", &path, NULL))
1718 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1719 else
1720 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1721 r->rsprintf(" </tr>\n");
1722
1723 r->rsprintf(" <tr>\n");
1724 r->rsprintf(" <td style=\"text-align:right;\">obsolete.js:</td>\n");
1725 if (open_resource_file("obsolete.js", &path, NULL))
1726 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1727 else
1728 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1729 r->rsprintf(" </tr>\n");
1730
1731 r->rsprintf(" <tr>\n");
1732 r->rsprintf(" <td style=\"text-align:right;\">Obsolete mhttpd.css:</td>\n");
1733 if (open_resource_file("mhttpd.css", &path, NULL))
1734 r->rsprintf(" <td style=\"text-align:left;\">%s</td>\n", path.c_str());
1735 else
1736 r->rsprintf(" <td style=\"text-align:left;\">NOT FOUND</td>\n");
1737 r->rsprintf(" </tr>\n");
1738
1739 r->rsprintf(" <tr>\n");
1740 r->rsprintf(" <td style=\"text-align:right;\">JSON-RPC schema:</td>\n");
1741 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?mjsonrpc_schema\">json format</a> or <a href=\"?mjsonrpc_schema_text\">text table format</a></td>\n");
1742 r->rsprintf(" </tr>\n");
1743
1744 r->rsprintf(" <tr>\n");
1745 r->rsprintf(" <td style=\"text-align:right;\">JavaScript examples:</td>\n");
1746 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?cmd=example\">example.html</a></td>\n");
1747 r->rsprintf(" </tr>\n");
1748
1749 r->rsprintf(" <tr>\n");
1750 r->rsprintf(" <td style=\"text-align:right;\">Custom page example:</td>\n");
1751 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?cmd=custom_example\">custom_example.html</a></td>\n");
1752 r->rsprintf(" </tr>\n");
1753
1754 r->rsprintf(" <tr>\n");
1755 r->rsprintf(" <td style=\"text-align:right;\">MPlot custom plot examples:</td>\n");
1756 r->rsprintf(" <td style=\"text-align:left;\"><a href=\"?cmd=plot_example\">plot_example.html</a></td>\n");
1757 r->rsprintf(" </tr>\n");
1758
1759 r->rsprintf(" </table>\n");
1760 r->rsprintf(" </td>\n");
1761 r->rsprintf(" </tr>\n");
1762 r->rsprintf("</table>\n");
1763
1764 r->rsprintf("<table class=\"mtable\" style=\"width: 95%%\">\n");
1765 r->rsprintf(" <tr>\n");
1766 r->rsprintf(" <td class=\"mtableheader\">Contributions</td>\n");
1767 r->rsprintf(" </tr>\n");
1768 r->rsprintf(" <tr>\n");
1769 r->rsprintf(" <td>\n");
1770 r->rsprintf("Pierre-Andre&nbsp;Amaudruz - Sergio&nbsp;Ballestrero - Suzannah&nbsp;Daviel - Peter&nbsp;Green - Qing&nbsp;Gu - Greg&nbsp;Hackman - Gertjan&nbsp;Hofman - Paul&nbsp;Knowles - Exaos&nbsp;Lee - Thomas&nbsp;Lindner - Shuoyi&nbsp;Ma - Rudi&nbsp;Meier - Bill&nbsp;Mills - Glenn&nbsp;Moloney - Dave&nbsp;Morris - John&nbsp;M&nbsp;O'Donnell - Konstantin&nbsp;Olchanski - Chris&nbsp;Pearson - Renee&nbsp;Poutissou - Stefan&nbsp;Ritt - Zaher&nbsp;Salman - Ryu&nbsp;Sawada - Tamsen&nbsp;Schurman - Ben&nbsp;Smith - Andreas&nbsp;Suter - Jan&nbsp;M.&nbsp;Wouters - Piotr&nbsp;Adam&nbsp;Zolnierczuk\n");
1771 r->rsprintf(" </td>\n");
1772 r->rsprintf(" </tr>\n");
1773 r->rsprintf("</table>\n");
1774
1775 r->rsprintf("</div></form>\n");
1776 r->rsprintf("</body></html>\r\n");
1777}
1778
1779/*------------------------------------------------------------------*/
1780
1781void show_header(Return* r, const char *title, const char *method, const char *path, int refresh)
1782{
1783 HNDLE hDB;
1784 time_t now;
1785 char str[256];
1786
1788
1789 /* header */
1790 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1791 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1792 r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
1793 r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
1794 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
1795
1796 r->rsprintf("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n");
1797 r->rsprintf("<html><head>\n");
1798
1799 /* style sheet */
1800 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
1801 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
1802 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
1803
1804 /* auto refresh */
1805 if (refresh > 0)
1806 r->rsprintf("<meta http-equiv=\"Refresh\" content=\"%02d\">\n", refresh);
1807
1808 r->rsprintf("<title>%s</title></head>\n", title);
1809
1810 mstrlcpy(str, path, sizeof(str));
1811 urlEncode(str, sizeof(str));
1812
1813 if (equal_ustring(method, "POST"))
1814 r->rsprintf
1815 ("<body><form name=\"form1\" method=\"POST\" action=\"%s\" enctype=\"multipart/form-data\">\n\n",
1816 str);
1817 else if (equal_ustring(method, "GET"))
1818 r->rsprintf("<body><form name=\"form1\" method=\"GET\" action=\"%s\">\n\n", str);
1819
1820 /* title row */
1821
1822 std::string exptname;
1823 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &exptname, TRUE);
1824 time(&now);
1825}
1826
1827/*------------------------------------------------------------------*/
1828
1830{
1831 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1832 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1833 r->rsprintf("Access-Control-Allow-Origin: *\r\n");
1834 r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
1835 r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
1836 r->rsprintf("Content-Type: text/plain; charset=%s\r\n\r\n", HTTP_ENCODING);
1837}
1838
1839/*------------------------------------------------------------------*/
1840
1841void show_error(Return* r, const char *error)
1842{
1843 /* header */
1844 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
1845 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1846 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
1847
1848 r->rsprintf("<html><head>\n");
1849 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
1850 r->rsprintf("<title>MIDAS error</title></head>\n");
1851 r->rsprintf("<body><H1>%s</H1></body></html>\n", error);
1852}
1853
1854/*------------------------------------------------------------------*/
1855
1856void show_error_404(Return* r, const char *error)
1857{
1858 /* header */
1859 r->rsprintf("HTTP/1.1 404 Not Found\r\n");
1860 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
1861 r->rsprintf("Content-Type: text/plain\r\n");
1862 r->rsprintf("\r\n");
1863
1864 r->rsprintf("MIDAS error: %s\n", error);
1865}
1866
1867/*------------------------------------------------------------------*/
1868
1869void show_navigation_bar(Return* r, const char *cur_page)
1870{
1871 r->rsprintf("<script>\n");
1872 r->rsprintf("window.addEventListener(\"load\", function(e) { mhttpd_init('%s', 1000); });\n", cur_page);
1873 r->rsprintf("</script>\n");
1874
1875 r->rsprintf("<!-- header and side navigation will be filled in mhttpd_init -->\n");
1876 r->rsprintf("<div id=\"mheader\"></div>\n");
1877 r->rsprintf("<div id=\"msidenav\"></div>\n");
1878 r->rsprintf("<div id=\"mmain\">\n");
1879}
1880
1881/*------------------------------------------------------------------*/
1882
1883void check_obsolete_odb(HNDLE hDB, const char* odb_path)
1884{
1885 HNDLE hKey;
1886 int status = db_find_key(hDB, 0, odb_path, &hKey);
1887 if (status == DB_SUCCESS) {
1888 cm_msg(MERROR, "check_obsolete_odb", "ODB \"%s\" is obsolete, please delete it.", odb_path);
1889 }
1890}
1891
1892void init_menu_buttons(MVOdb* odb)
1893{
1894 HNDLE hDB;
1895 BOOL true_value = TRUE;
1896 BOOL false_value = FALSE;
1897 int size = sizeof(true_value);
1899 db_get_value(hDB, 0, "/Experiment/Menu/Status", &true_value, &size, TID_BOOL, TRUE);
1900 db_get_value(hDB, 0, "/Experiment/Menu/Start", &false_value, &size, TID_BOOL, TRUE);
1901 db_get_value(hDB, 0, "/Experiment/Menu/Transition", &true_value, &size, TID_BOOL, TRUE);
1902 db_get_value(hDB, 0, "/Experiment/Menu/ODB", &true_value, &size, TID_BOOL, TRUE);
1903 db_get_value(hDB, 0, "/Experiment/Menu/OldODB", &true_value, &size, TID_BOOL, TRUE);
1904 db_get_value(hDB, 0, "/Experiment/Menu/Messages", &true_value, &size, TID_BOOL, TRUE);
1905 db_get_value(hDB, 0, "/Experiment/Menu/Chat", &true_value, &size, TID_BOOL, TRUE);
1906 db_get_value(hDB, 0, "/Experiment/Menu/Elog", &true_value, &size, TID_BOOL, TRUE);
1907 db_get_value(hDB, 0, "/Experiment/Menu/Alarms", &true_value, &size, TID_BOOL, TRUE);
1908 db_get_value(hDB, 0, "/Experiment/Menu/Programs", &true_value, &size, TID_BOOL, TRUE);
1909 db_get_value(hDB, 0, "/Experiment/Menu/Buffers", &true_value, &size, TID_BOOL, TRUE);
1910 db_get_value(hDB, 0, "/Experiment/Menu/History", &true_value, &size, TID_BOOL, TRUE);
1911 db_get_value(hDB, 0, "/Experiment/Menu/OldHistory", &true_value, &size, TID_BOOL, TRUE);
1912 db_get_value(hDB, 0, "/Experiment/Menu/MSCB", &true_value, &size, TID_BOOL, TRUE);
1913 db_get_value(hDB, 0, "/Experiment/Menu/Sequencer", &true_value, &size, TID_BOOL, TRUE);
1914 db_get_value(hDB, 0, "/Experiment/Menu/PySequencer",&true_value, &size, TID_BOOL, TRUE);
1915 db_get_value(hDB, 0, "/Experiment/Menu/Event Dump", &true_value, &size, TID_BOOL, TRUE);
1916 db_get_value(hDB, 0, "/Experiment/Menu/Config", &true_value, &size, TID_BOOL, TRUE);
1917 db_get_value(hDB, 0, "/Experiment/Menu/Example", &false_value, &size, TID_BOOL, TRUE);
1918 db_get_value(hDB, 0, "/Experiment/Menu/Help", &true_value, &size, TID_BOOL, TRUE);
1919
1920 //std::string buf;
1921 //status = db_get_value_string(hDB, 0, "/Experiment/Menu buttons", 0, &buf, FALSE);
1922 //if (status == DB_SUCCESS) {
1923 // cm_msg(MERROR, "init_menu_buttons", "ODB \"/Experiment/Menu buttons\" is obsolete, please delete it.");
1924 //}
1925
1926 check_obsolete_odb(hDB, "/Experiment/Menu buttons");
1927 check_obsolete_odb(hDB, "/Experiment/Menu/OldSequencer");
1928 check_obsolete_odb(hDB, "/Experiment/Menu/NewSequencer");
1929}
1930
1931/*------------------------------------------------------------------*/
1932
1933void init_mhttpd_odb(MVOdb* odb)
1934{
1935 HNDLE hDB;
1936 HNDLE hKey;
1937 int status;
1938 std::string s;
1940
1941 status = db_find_key(hDB, 0, "/Experiment/Base URL", &hKey);
1942 if (status == DB_SUCCESS) {
1943 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/Base URL\" is obsolete, please delete it.");
1944 }
1945
1946 status = db_find_key(hDB, 0, "/Experiment/CSS File", &hKey);
1947 if (status == DB_SUCCESS) {
1948 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/CSS File\" is obsolete, please delete it.");
1949 }
1950
1951 status = db_find_key(hDB, 0, "/Experiment/JS File", &hKey);
1952 if (status == DB_SUCCESS) {
1953 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/JS File\" is obsolete, please delete it.");
1954 }
1955
1956 status = db_find_key(hDB, 0, "/Experiment/Start-Stop Buttons", &hKey);
1957 if (status == DB_SUCCESS) {
1958 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Experiment/Start-Stop Buttons\" is obsolete, please delete it.");
1959 }
1960
1961 bool xdefault = true;
1962 odb->RB("Experiment/Pause-Resume Buttons", &xdefault, true);
1963
1964#ifdef HAVE_MONGOOSE616
1965 check_obsolete_odb(hDB, "/Experiment/midas http port");
1966 check_obsolete_odb(hDB, "/Experiment/midas https port");
1967 check_obsolete_odb(hDB, "/Experiment/http redirect to https");
1968 check_obsolete_odb(hDB, "/Experiment/Security/mhttpd hosts");
1969#endif
1970
1971 status = db_find_key(hDB, 0, "/Logger/Message file", &hKey);
1972 if (status == DB_SUCCESS) {
1973 cm_msg(MERROR, "init_mhttpd_odb", "ODB \"/Logger/Message file\" is obsolete, please delete it and use \"/Logger/Message dir\" and \"/Logger/message file date format\" instead.");
1974 }
1975
1976 check_obsolete_odb(hDB, "/Logger/Watchdog timeout");
1977}
1978
1979/*------------------------------------------------------------------*/
1980
1982{
1983 HNDLE hDB;
1984 int size;
1985 HNDLE hkey;
1987
1988 BOOL external_elog = FALSE;
1989 std::string external_elog_url;
1990
1991 size = sizeof(external_elog);
1992 db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, TRUE);
1993 db_get_value_string(hDB, 0, "/Elog/URL", 0, &external_elog_url, TRUE);
1994
1995 BOOL allow_delete = FALSE;
1996 BOOL allow_edit = FALSE;
1997 size = sizeof(BOOL);
1998 db_get_value(hDB, 0, "/Elog/Allow delete", &allow_delete, &size, TID_BOOL, TRUE);
1999 db_get_value(hDB, 0, "/Elog/Allow edit", &allow_edit, &size, TID_BOOL, TRUE);
2000 //db_get_value(hDB, 0, "/Elog/Display run number", &display_run_number, &size, TID_BOOL, TRUE);
2001
2002 if (db_find_key(hDB, 0, "/Elog/Buttons", &hkey) != DB_SUCCESS) {
2003 const char def_button[][NAME_LENGTH] = { "8h", "24h", "7d" };
2004 db_set_value(hDB, 0, "/Elog/Buttons", def_button, NAME_LENGTH*3, 3, TID_STRING);
2005 }
2006
2007
2008 /* get type list from ODB */
2009 size = 20 * NAME_LENGTH;
2010 if (db_find_key(hDB, 0, "/Elog/Types", &hkey) != DB_SUCCESS) {
2011 db_set_value(hDB, 0, "/Elog/Types", default_type_list, NAME_LENGTH * 20, 20, TID_STRING);
2012 }
2013
2014 /* get system list from ODB */
2015 size = 20 * NAME_LENGTH;
2016 if (db_find_key(hDB, 0, "/Elog/Systems", &hkey) != DB_SUCCESS)
2017 db_set_value(hDB, 0, "/Elog/Systems", default_system_list, NAME_LENGTH * 20, 20, TID_STRING);
2018}
2019
2020/*------------------------------------------------------------------*/
2021
2022void strencode(Return* r, const char *text)
2023{
2024 size_t len = strlen(text);
2025 for (size_t i = 0; i < len; i++) {
2026 switch (text[i]) {
2027 case '\n':
2028 r->rsprintf("<br>\n");
2029 break;
2030 case '<':
2031 r->rsprintf("&lt;");
2032 break;
2033 case '>':
2034 r->rsprintf("&gt;");
2035 break;
2036 case '&':
2037 r->rsprintf("&amp;");
2038 break;
2039 case '\"':
2040 r->rsprintf("&quot;");
2041 break;
2042 default:
2043 r->rsprintf("%c", text[i]);
2044 }
2045 }
2046}
2047
2048/*------------------------------------------------------------------*/
2049
2050std::string strencode2(const char *text)
2051{
2052 std::string b;
2053 size_t len = strlen(text);
2054 for (size_t i = 0; i < len; i++) {
2055 switch (text[i]) {
2056 case '\n':
2057 b += "<br>\n";
2058 break;
2059 case '<':
2060 b += "&lt;";
2061 break;
2062 case '>':
2063 b += "&gt;";
2064 break;
2065 case '&':
2066 b += "&amp;";
2067 break;
2068 case '\"':
2069 b += "&quot;";
2070 break;
2071 default:
2072 b += text[i];
2073 break;
2074 }
2075 }
2076 return b;
2077}
2078
2079/*------------------------------------------------------------------*/
2080
2081void strencode3(Return* r, const char *text)
2082{
2083 size_t len = strlen(text);
2084 for (size_t i = 0; i < len; i++) {
2085 switch (text[i]) {
2086 case '<':
2087 r->rsprintf("&lt;");
2088 break;
2089 case '>':
2090 r->rsprintf("&gt;");
2091 break;
2092 case '&':
2093 r->rsprintf("&amp;");
2094 break;
2095 case '\"':
2096 r->rsprintf("&quot;");
2097 break;
2098 default:
2099 r->rsprintf("%c", text[i]);
2100 }
2101 }
2102}
2103
2104/*------------------------------------------------------------------*/
2105
2106void strencode4(Return* r, const char *text)
2107{
2108 size_t len = strlen(text);
2109 for (size_t i = 0; i < len; i++) {
2110 switch (text[i]) {
2111 case '\n':
2112 r->rsprintf("<br>\n");
2113 break;
2114 case '<':
2115 r->rsprintf("&lt;");
2116 break;
2117 case '>':
2118 r->rsprintf("&gt;");
2119 break;
2120 case '&':
2121 r->rsprintf("&amp;");
2122 break;
2123 case '\"':
2124 r->rsprintf("&quot;");
2125 break;
2126 case ' ':
2127 r->rsprintf("&nbsp;");
2128 break;
2129 default:
2130 r->rsprintf("%c", text[i]);
2131 }
2132 }
2133}
2134
2135/*------------------------------------------------------------------*/
2136
2137void gen_odb_attachment(Return* r, const char *path, std::string& bout)
2138{
2139 HNDLE hDB, hkeyroot, hkey;
2140 KEY key;
2141 INT i, j, size;
2142 char data[1024];
2143 time_t now;
2144
2146 db_find_key(hDB, 0, path, &hkeyroot);
2147 assert(hkeyroot);
2148
2149 /* title row */
2150 //size = sizeof(str);
2151 //str[0] = 0;
2152 //db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
2153 time(&now);
2154
2155 bout += "<table border=3 cellpadding=1 class=\"dialogTable\">\n";
2156 char ctimebuf[32];
2157 ctime_r(&now, ctimebuf);
2158 bout += msprintf("<tr><th colspan=2>%s</tr>\n", ctimebuf);
2159 bout += msprintf("<tr><th colspan=2>%s</tr>\n", path);
2160
2161 /* enumerate subkeys */
2162 for (i = 0;; i++) {
2163 db_enum_link(hDB, hkeyroot, i, &hkey);
2164 if (!hkey)
2165 break;
2166 db_get_key(hDB, hkey, &key);
2167
2168 /* resolve links */
2169 if (key.type == TID_LINK) {
2170 db_enum_key(hDB, hkeyroot, i, &hkey);
2171 db_get_key(hDB, hkey, &key);
2172 }
2173
2174 if (key.type == TID_KEY) {
2175 /* for keys, don't display data value */
2176 bout += msprintf("<tr><td colspan=2>%s</td></tr>\n", key.name);
2177 } else {
2178 /* display single value */
2179 if (key.num_values == 1) {
2180 size = sizeof(data);
2181 db_get_data(hDB, hkey, data, &size, key.type);
2182 //printf("data size %d [%s]\n", size, data);
2183 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2184 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2185
2186 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2187 data_str = "(empty)";
2188 hex_str = "";
2189 }
2190
2191 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
2192 //sprintf(b, "<tr><td>%s</td><td>%s (%s)</td></tr>\n", key.name, data_str, hex_str);
2193 bout += "<tr><td>";
2194 bout += key.name;
2195 bout += "</td><td>";
2196 bout += data_str;
2197 bout += " (";
2198 bout += hex_str;
2199 bout += ")</td></tr>\n";
2200 } else {
2201 bout += msprintf("<tr><td>%s</td><td>", key.name);
2202 bout += strencode2(data_str.c_str());
2203 bout += "</td></tr>\n";
2204 }
2205 } else {
2206 /* display first value */
2207 bout += msprintf("<tr><td rowspan=%d>%s</td>\n", key.num_values, key.name);
2208
2209 for (j = 0; j < key.num_values; j++) {
2210 size = sizeof(data);
2211 db_get_data_index(hDB, hkey, data, &size, j, key.type);
2212 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2213 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2214
2215 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2216 data_str = "(empty)";
2217 hex_str = "";
2218 }
2219
2220 if (j > 0) {
2221 bout += "<tr>";
2222 }
2223
2224 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
2225 //sprintf(b, "<td>[%d] %s (%s)<br></td></tr>\n", j, data_str, hex_str);
2226 bout += "<td>[";
2227 bout += toString(j);
2228 bout += "] ";
2229 bout += data_str;
2230 bout += " (";
2231 bout += hex_str;
2232 bout += ")<br></td></tr>\n";
2233 } else {
2234 //sprintf(b, "<td>[%d] %s<br></td></tr>\n", j, data_str);
2235 bout += "<td>[";
2236 bout += toString(j);
2237 bout += "] ";
2238 bout += data_str;
2239 bout += "<br></td></tr>\n";
2240 }
2241 }
2242 }
2243 }
2244 }
2245
2246 bout += "</table>\n";
2247}
2248
2249/*------------------------------------------------------------------*/
2250
2251void submit_elog(MVOdb* odb, Param* pp, Return* r, Attachment* a)
2252{
2253 char path[256], path1[256];
2254 char mail_to[256], mail_from[256], mail_list[256],
2255 smtp_host[256], tag[80], mail_param[1000];
2256 char *p, *pitem;
2257 HNDLE hDB, hkey;
2258 char att_file[3][256];
2259 int fh, size, n_mail;
2260 char mhttpd_full_url[256];
2261
2263 mstrlcpy(att_file[0], pp->getparam("attachment0"), sizeof(att_file[0]));
2264 mstrlcpy(att_file[1], pp->getparam("attachment1"), sizeof(att_file[1]));
2265 mstrlcpy(att_file[2], pp->getparam("attachment2"), sizeof(att_file[2]));
2266
2267 /* check for valid attachment files */
2268 for (int i = 0; i < 3; i++) {
2269 char str[256];
2270 sprintf(str, "attachment%d", i);
2271 //printf("submit_elog: att %d, [%s] param [%s], size %d\n", i, str, pp->getparam(str), a->_attachment_size[i]);
2272 if (pp->getparam(str) && *pp->getparam(str) && a->attachment_size[i] == 0) {
2273 /* replace '\' by '/' */
2274 mstrlcpy(path, pp->getparam(str), sizeof(path));
2275 mstrlcpy(path1, path, sizeof(path1));
2276 while (strchr(path, '\\'))
2277 *strchr(path, '\\') = '/';
2278
2279 /* check if valid ODB tree */
2280 if (db_find_key(hDB, 0, path, &hkey) == DB_SUCCESS) {
2281 std::string bout;
2282 gen_odb_attachment(r, path, bout);
2283 int bufsize = bout.length()+1;
2284 char* buf = (char*)M_MALLOC(bufsize);
2285 memcpy(buf, bout.c_str(), bufsize);
2286 mstrlcpy(att_file[i], path, sizeof(att_file[0]));
2287 mstrlcat(att_file[i], ".html", sizeof(att_file[0]));
2288 a->attachment_buffer[i] = buf;
2289 a->attachment_size[i] = bufsize;
2290 }
2291 /* check if local file */
2292 else if ((fh = open(path1, O_RDONLY | O_BINARY)) >= 0) {
2293 size = lseek(fh, 0, SEEK_END);
2294 char* buf = (char*)M_MALLOC(size);
2295 lseek(fh, 0, SEEK_SET);
2296 int rd = read(fh, buf, size);
2297 if (rd < 0)
2298 rd = 0;
2299 close(fh);
2300 mstrlcpy(att_file[i], path, sizeof(att_file[0]));
2301 a->attachment_buffer[i] = buf;
2302 a->attachment_size[i] = rd;
2303 } else if (strncmp(path, "/HS/", 4) == 0) {
2304 char* buf = (char*)M_MALLOC(100000);
2305 size = 100000;
2306 mstrlcpy(str, path + 4, sizeof(str));
2307 if (strchr(str, '?')) {
2308 p = strchr(str, '?') + 1;
2309 p = strtok(p, "&");
2310 while (p != NULL) {
2311 pitem = p;
2312 p = strchr(p, '=');
2313 if (p != NULL) {
2314 *p++ = 0;
2315 urlDecode(pitem); // parameter name
2316 urlDecode(p); // parameter value
2317
2318 pp->setparam(pitem, p);
2319
2320 p = strtok(NULL, "&");
2321 }
2322 }
2323 *strchr(str, '?') = 0;
2324 }
2325 show_hist_page(odb, pp, r, "image.gif", buf, &size, 0);
2326 mstrlcpy(att_file[i], str, sizeof(att_file[0]));
2327 a->attachment_buffer[i] = buf;
2328 a->attachment_size[i] = size;
2329 pp->unsetparam("scale");
2330 pp->unsetparam("offset");
2331 pp->unsetparam("width");
2332 pp->unsetparam("index");
2333 } else {
2334 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
2335 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
2336 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
2337
2338 r->rsprintf("<html><head>\n");
2339 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
2340 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
2341 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
2342 r->rsprintf("<title>ELog Error</title></head>\n");
2343 r->rsprintf("<i>Error: Attachment file <i>%s</i> not valid.</i><p>\n", pp->getparam(str));
2344 r->rsprintf("Please go back and enter a proper filename (use the <b>Browse</b> button).\n");
2345 r->rsprintf("<body></body></html>\n");
2346 return;
2347 }
2348 }
2349 }
2350
2351 int edit = atoi(pp->getparam("edit"));
2352 //printf("submit_elog: edit [%s] %d, orig [%s]\n", pp->getparam("edit"), edit, pp->getparam("orig"));
2353
2354 tag[0] = 0;
2355 if (edit) {
2356 mstrlcpy(tag, pp->getparam("orig"), sizeof(tag));
2357 }
2358
2359 int status = el_submit(atoi(pp->getparam("run")),
2360 pp->getparam("author"),
2361 pp->getparam("type"),
2362 pp->getparam("system"),
2363 pp->getparam("subject"),
2364 pp->getparam("text"),
2365 pp->getparam("orig"),
2366 *pp->getparam("html") ? "HTML" : "plain",
2367 att_file[0], a->attachment_buffer[0], a->attachment_size[0],
2368 att_file[1], a->attachment_buffer[1], a->attachment_size[1],
2369 att_file[2], a->attachment_buffer[2], a->attachment_size[2],
2370 tag, sizeof(tag));
2371
2372 //printf("el_submit status %d, tag [%s]\n", status, tag);
2373
2374 if (status != EL_SUCCESS) {
2375 cm_msg(MERROR, "submit_elog", "el_submit() returned status %d", status);
2376 }
2377
2378 /* supersede host name with "/Elog/Host name" */
2379 std::string elog_host_name;
2380 db_get_value_string(hDB, 0, "/Elog/Host name", 0, &elog_host_name, TRUE);
2381
2382 // K.O. FIXME: we cannot guess the Elog URL like this because
2383 // we do not know if access is through a proxy or redirect
2384 // we do not know if it's http: or https:, etc. Better
2385 // to read the whole "mhttpd_full_url" string from ODB.
2386 sprintf(mhttpd_full_url, "http://%s/", elog_host_name.c_str());
2387
2388 /* check for mail submissions */
2389 mail_param[0] = 0;
2390 n_mail = 0;
2391
2392 for (int index = 0; index <= 1; index++) {
2393 std::string str;
2394 str += "/Elog/Email ";
2395 if (index == 0)
2396 str += pp->getparam("type");
2397 else
2398 str += pp->getparam("system");
2399
2400 if (db_find_key(hDB, 0, str.c_str(), &hkey) == DB_SUCCESS) {
2401 size = sizeof(mail_list);
2402 db_get_data(hDB, hkey, mail_list, &size, TID_STRING);
2403
2404 if (db_find_key(hDB, 0, "/Elog/SMTP host", &hkey) != DB_SUCCESS) {
2405 show_error(r, "No SMTP host defined under /Elog/SMTP host");
2406 return;
2407 }
2408 size = sizeof(smtp_host);
2409 db_get_data(hDB, hkey, smtp_host, &size, TID_STRING);
2410
2411 p = strtok(mail_list, ",");
2412 while (1) {
2413 mstrlcpy(mail_to, p, sizeof(mail_to));
2414
2415 std::string exptname;
2416 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &exptname, TRUE);
2417
2418 sprintf(mail_from, "MIDAS %s <MIDAS@%s>", exptname.c_str(), elog_host_name.c_str());
2419
2420 std::string mail_text;
2421 mail_text += "A new entry has been submitted by ";
2422 mail_text += pp->getparam("author");
2423 mail_text += "\n";
2424 mail_text += "\n";
2425
2426 mail_text += "Experiment : ";
2427 mail_text += exptname.c_str();
2428 mail_text += "\n";
2429
2430 mail_text += "Type : ";
2431 mail_text += pp->getparam("type");
2432 mail_text += "\n";
2433
2434 mail_text += "System : ";
2435 mail_text += pp->getparam("system");
2436 mail_text += "\n";
2437
2438 mail_text += "Subject : ";
2439 mail_text += pp->getparam("subject");
2440 mail_text += "\n";
2441
2442 mail_text += "Link : ";
2443 mail_text += mhttpd_full_url;
2444 mail_text += "/EL/";
2445 mail_text += tag;
2446 mail_text += "\n";
2447
2448 mail_text += "\n";
2449
2450 mail_text += pp->getparam("text");
2451 mail_text += "\n";
2452
2453 sendmail(elog_host_name.c_str(), smtp_host, mail_from, mail_to, pp->getparam("type"), mail_text.c_str());
2454
2455 if (mail_param[0] == 0)
2456 mstrlcpy(mail_param, "?", sizeof(mail_param));
2457 else
2458 mstrlcat(mail_param, "&", sizeof(mail_param));
2459 sprintf(mail_param + strlen(mail_param), "mail%d=%s", n_mail++, mail_to);
2460
2461 p = strtok(NULL, ",");
2462 if (!p)
2463 break;
2464 while (*p == ' ')
2465 p++;
2466 }
2467 }
2468 }
2469
2470 r->rsprintf("HTTP/1.1 302 Found\r\n");
2471 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
2472
2473 //if (mail_param[0])
2474 // r->rsprintf("Location: ../EL/%s?%s\n\n<html>redir</html>\r\n", tag, mail_param + 1);
2475 //else
2476 // r->rsprintf("Location: ../EL/%s\n\n<html>redir</html>\r\n", tag);
2477
2478 if (mail_param[0])
2479 r->rsprintf("Location: ?cmd=Show+elog&tag=%s&%s\n\n<html>redir</html>\r\n", tag, mail_param + 1);
2480 else
2481 r->rsprintf("Location: ?cmd=Show+elog&tag=%s\n\n<html>redir</html>\r\n", tag);
2482}
2483
2484/*------------------------------------------------------------------*/
2485
2486void show_elog_attachment(Param* p, Return* r, const char* path)
2487{
2488 HNDLE hDB;
2489 int size;
2490 int status;
2491 char file_name[256];
2492
2494 file_name[0] = 0;
2495 if (hDB > 0) {
2496 size = sizeof(file_name);
2497 memset(file_name, 0, size);
2498
2499 status = db_get_value(hDB, 0, "/Logger/Elog dir", file_name, &size, TID_STRING, FALSE);
2500 if (status != DB_SUCCESS)
2501 db_get_value(hDB, 0, "/Logger/Data dir", file_name, &size, TID_STRING, TRUE);
2502
2503 if (file_name[0] != 0)
2504 if (file_name[strlen(file_name) - 1] != DIR_SEPARATOR)
2505 mstrlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
2506 }
2507 mstrlcat(file_name, path, sizeof(file_name));
2508
2509 int fh = open(file_name, O_RDONLY | O_BINARY);
2510 if (fh > 0) {
2511 lseek(fh, 0, SEEK_END);
2512 int length = TELL(fh);
2513 lseek(fh, 0, SEEK_SET);
2514
2515 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
2516 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
2517 r->rsprintf("Accept-Ranges: bytes\r\n");
2518 //r->rsprintf("Content-disposition: attachment; filename=%s\r\n", path);
2519
2520 r->rsprintf("Content-Type: %s\r\n", get_content_type(file_name).c_str());
2521
2522 r->rsprintf("Content-Length: %d\r\n\r\n", length);
2523
2524 r->rread(file_name, fh, length);
2525
2526 close(fh);
2527 }
2528
2529 return;
2530}
2531
2532/*------------------------------------------------------------------*/
2533
2534BOOL is_editable(char *eq_name, char *var_name)
2535{
2536 HNDLE hDB, hkey;
2537 KEY key;
2538 char str[256];
2539 int i, size;
2540
2542 sprintf(str, "/Equipment/%s/Settings/Editable", eq_name);
2543 db_find_key(hDB, 0, str, &hkey);
2544
2545 /* if no editable entry found, use default */
2546 if (!hkey) {
2547 return (equal_ustring(var_name, "Demand") ||
2548 equal_ustring(var_name, "Output") || strncmp(var_name, "D_", 2) == 0);
2549 }
2550
2551 db_get_key(hDB, hkey, &key);
2552 for (i = 0; i < key.num_values; i++) {
2553 size = sizeof(str);
2554 db_get_data_index(hDB, hkey, str, &size, i, TID_STRING);
2556 return TRUE;
2557 }
2558 return FALSE;
2559}
2560
2561#ifdef OBSOLETE
2562void show_eqtable_page(Param* pp, Return* r, int refresh)
2563{
2564 int i, j, k, colspan, size, n_var, i_edit, i_set, line;
2565 char eq_name[32], group[32];
2566 char group_name[MAX_GROUPS][32], data[256], style[80];
2567 HNDLE hDB;
2568 char odb_path[256];
2569
2571
2572 /* check if variable to edit */
2573 i_edit = -1;
2574 if (equal_ustring(pp->getparam("cmd"), "Edit"))
2575 i_edit = atoi(pp->getparam("index"));
2576
2577 /* check if variable to set */
2578 i_set = -1;
2579 if (equal_ustring(pp->getparam("cmd"), "Set"))
2580 i_set = atoi(pp->getparam("index"));
2581
2582 /* get equipment and group */
2583 if (pp->getparam("eq"))
2584 mstrlcpy(eq_name, pp->getparam("eq"), sizeof(eq_name));
2585 mstrlcpy(group, "All", sizeof(group));
2586 if (pp->getparam("group") && *pp->getparam("group"))
2587 mstrlcpy(group, pp->getparam("group"), sizeof(group));
2588
2589#if 0
2590 /* check for "names" in settings */
2591 if (eq_name[0]) {
2592 sprintf(str, "/Equipment/%s/Settings", eq_name);
2593 HNDLE hkeyset;
2594 db_find_key(hDB, 0, str, &hkeyset);
2595 HNDLE hkeynames = 0;
2596 if (hkeyset) {
2597 for (i = 0;; i++) {
2598 db_enum_link(hDB, hkeyset, i, &hkeynames);
2599
2600 if (!hkeynames)
2601 break;
2602
2603 KEY key;
2604 db_get_key(hDB, hkeynames, &key);
2605
2606 if (strncmp(key.name, "Names", 5) == 0)
2607 break;
2608 }
2609 }
2610
2611 /* redirect if no names found */
2612 if (!hkeyset || !hkeynames) {
2613 /* redirect */
2614 sprintf(str, "?cmd=odb&odb_path=/Equipment/%s/Variables", eq_name);
2615 redirect(r, str);
2616 return;
2617 }
2618 }
2619#endif
2620
2621 show_header(r, "MIDAS slow control", "", group, i_edit == -1 ? refresh : 0);
2622 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
2623 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
2624 r->rsprintf("<script type=\"text/javascript\" src=\"obsolete.js\"></script>\n");
2625 show_navigation_bar(r, "SC");
2626
2627 /*---- menu buttons ----*/
2628
2629 r->rsprintf("<tr><td colspan=15>\n");
2630
2631 if (equal_ustring(pp->getparam("cmd"), "Edit"))
2632 r->rsprintf("<input type=submit name=cmd value=Set>\n");
2633
2634 r->rsprintf("</tr>\n\n");
2635 r->rsprintf("</table>"); //end header table
2636
2637 r->rsprintf("<table class=\"ODBtable\" style=\"max-width:700px;\">"); //body table
2638
2639 /*---- enumerate SC equipment ----*/
2640
2641 r->rsprintf("<tr><td class=\"subStatusTitle\" colspan=15><i>Equipment:</i> &nbsp;&nbsp;\n");
2642
2643 HNDLE hkeyeqroot;
2644 db_find_key(hDB, 0, "/Equipment", &hkeyeqroot);
2645 if (hkeyeqroot)
2646 for (i = 0;; i++) {
2647 HNDLE hkeyeq;
2648 db_enum_link(hDB, hkeyeqroot, i, &hkeyeq);
2649
2650 if (!hkeyeq)
2651 break;
2652
2653 KEY eqkey;
2654 db_get_key(hDB, hkeyeq, &eqkey);
2655
2656 HNDLE hkeyset;
2657 db_find_key(hDB, hkeyeq, "Settings", &hkeyset);
2658 if (hkeyset) {
2659 for (j = 0;; j++) {
2660 HNDLE hkeynames;
2661 db_enum_link(hDB, hkeyset, j, &hkeynames);
2662
2663 if (!hkeynames)
2664 break;
2665
2666 KEY key;
2667 db_get_key(hDB, hkeynames, &key);
2668
2669 if (strncmp(key.name, "Names", 5) == 0) {
2670 if (equal_ustring(eq_name, eqkey.name))
2671 r->rsprintf("<b>%s</b> &nbsp;&nbsp;", eqkey.name);
2672 else {
2673 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eqkey.name).c_str(), eqkey.name);
2674 }
2675 break;
2676 }
2677 }
2678 }
2679 }
2680 r->rsprintf("</tr>\n");
2681
2682 if (!eq_name[0]) {
2683 r->rsprintf("</table>");
2684 return;
2685 }
2686
2687 /*---- display SC ----*/
2688
2689 n_var = 0;
2690 std::string names_path = msprintf("/Equipment/%s/Settings/Names", eq_name);
2691 HNDLE hkeyeqnames;
2692 db_find_key(hDB, 0, names_path.c_str(), &hkeyeqnames);
2693
2694 if (hkeyeqnames) {
2695
2696 /*---- single name array ----*/
2697 r->rsprintf("<tr><td colspan=15><i>Groups:</i> &nbsp;&nbsp;");
2698
2699 /* "all" group */
2700 if (equal_ustring(group, "All"))
2701 r->rsprintf("<b>All</b> &nbsp;&nbsp;");
2702 else
2703 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">All</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2704
2705 /* collect groups */
2706
2707 memset(group_name, 0, sizeof(group_name));
2708 KEY key;
2709 db_get_key(hDB, hkeyeqnames, &key);
2710
2711 for (int level = 0; ; level++) {
2712 bool next_level = false;
2713 for (i = 0; i < key.num_values; i++) {
2714 char name_str[256];
2715 size = sizeof(name_str);
2716 db_get_data_index(hDB, hkeyeqnames, name_str, &size, i, TID_STRING);
2717
2718 char *s = strchr(name_str, '%');
2719 for (int k=0; s && k<level; k++)
2720 s = strchr(s+1, '%');
2721
2722 if (s) {
2723 *s = 0;
2724 if (strchr(s+1, '%'))
2725 next_level = true;
2726
2727 //printf("try group [%s] name [%s], level %d, %d\n", name_str, s+1, level, next_level);
2728
2729 for (j = 0; j < MAX_GROUPS; j++) {
2730 if (equal_ustring(group_name[j], name_str) || group_name[j][0] == 0)
2731 break;
2732 }
2733 if ((j < MAX_GROUPS) && (group_name[j][0] == 0))
2734 mstrlcpy(group_name[j], name_str, sizeof(group_name[0]));
2735 }
2736 }
2737
2738 if (!next_level)
2739 break;
2740 }
2741
2742 for (i = 0; i < MAX_GROUPS && group_name[i][0]; i++) {
2743 if (equal_ustring(group_name[i], group))
2744 r->rsprintf("<b>%s</b> &nbsp;&nbsp;", group_name[i]);
2745 else {
2746 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s&group=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str(), urlEncode(group_name[i]).c_str(), group_name[i]);
2747 }
2748 }
2749
2750 r->rsprintf("<i>ODB:</i> &nbsp;&nbsp;");
2751 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Common\">Common</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2752 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Settings\">Settings</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2753 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Variables\">Variables</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2754 r->rsprintf("</tr>\n");
2755
2756 /* count variables */
2757 std::string vars_path = msprintf("/Equipment/%s/Variables", eq_name);
2758 HNDLE hkeyvar;
2759 db_find_key(hDB, 0, vars_path.c_str(), &hkeyvar);
2760 if (!hkeyvar) {
2761 r->rsprintf("</table>");
2762 return;
2763 }
2764 for (i = 0;; i++) {
2765 HNDLE hkey;
2766 db_enum_link(hDB, hkeyvar, i, &hkey);
2767 if (!hkey)
2768 break;
2769 }
2770
2771 if (i == 0 || i > 15) {
2772 r->rsprintf("</table>");
2773 return;
2774 }
2775
2776 /* title row */
2777 colspan = 15 - i;
2778 r->rsprintf("<tr class=\"subStatusTitle\"><th colspan=%d>Names", colspan);
2779
2780 /* display entries for this group */
2781 for (int i = 0;; i++) {
2782 HNDLE hkey;
2783 db_enum_link(hDB, hkeyvar, i, &hkey);
2784
2785 if (!hkey)
2786 break;
2787
2788 KEY key;
2789 db_get_key(hDB, hkey, &key);
2790 r->rsprintf("<th>%s", key.name);
2791 }
2792
2793 r->rsprintf("</tr>\n");
2794
2795 /* data for current group */
2796 std::string names_path = msprintf("/Equipment/%s/Settings/Names", eq_name);
2797 int num_values = 0;
2798 HNDLE hnames_key;
2799 db_find_key(hDB, 0, names_path.c_str(), &hnames_key);
2800 if (hnames_key) {
2801 KEY names_key;
2802 db_get_key(hDB, hnames_key, &names_key);
2803 num_values = names_key.num_values;
2804 }
2805 for (int i = 0; i < num_values; i++) {
2806 char names_str[256];
2807 size = sizeof(names_str);
2808 db_get_data_index(hDB, hnames_key, names_str, &size, i, TID_STRING);
2809
2810 char name[NAME_LENGTH+32];
2811 mstrlcpy(name, names_str, sizeof(name));
2812
2813 //printf("group [%s], name [%s], str [%s]\n", group, name, names_str);
2814
2815 if (!equal_ustring(group, "All")) {
2816 // check if name starts with the name of the group we want to display
2817 char *s = strstr(name, group);
2818 if (s != name)
2819 continue;
2820 if (name[strlen(group)] != '%')
2821 continue;
2822 }
2823
2824 if (strlen(name) < 1)
2825 sprintf(name, "[%d]", i);
2826
2827 if (i % 2 == 0)
2828 r->rsprintf("<tr class=\"ODBtableEven\"><td colspan=%d><nobr>%s</nobr>", colspan, name);
2829 else
2830 r->rsprintf("<tr class=\"ODBtableOdd\"><td colspan=%d><nobr>%s</nobr>", colspan, name);
2831
2832 for (int j = 0;; j++) {
2833 HNDLE hkey;
2834 db_enum_link(hDB, hkeyvar, j, &hkey);
2835 if (!hkey)
2836 break;
2837
2838 KEY varkey;
2839 db_get_key(hDB, hkey, &varkey);
2840
2841 /* check if "variables" array is shorter than the "names" array */
2842 if (i >= varkey.num_values)
2843 continue;
2844
2845 size = sizeof(data);
2846 db_get_data_index(hDB, hkey, data, &size, i, varkey.type);
2847 std::string data_str = db_sprintf(data, varkey.item_size, 0, varkey.type);
2848
2849 if (is_editable(eq_name, varkey.name)) {
2850 if (n_var == i_set) {
2851 /* set value */
2852 char str[256];
2853 mstrlcpy(str, pp->getparam("value"), sizeof(str));
2854 db_sscanf(str, data, &size, 0, varkey.type);
2855 db_set_data_index(hDB, hkey, data, size, i, varkey.type);
2856
2857 /* redirect (so that 'reload' does not reset value) */
2858 r->reset();
2859 redirect(r, group);
2860 return;
2861 }
2862 if (n_var == i_edit) {
2863 r->rsprintf("<td align=center>");
2864 r->rsprintf("<input type=text size=10 maxlenth=80 name=value value=\"%s\">\n", data_str.c_str());
2865 r->rsprintf("<input type=submit size=20 name=cmd value=Set>\n");
2866 r->rsprintf("<input type=hidden name=index value=%d>\n", i_edit);
2867 n_var++;
2868 } else {
2869 sprintf(odb_path, "Equipment/%s/Variables/%s[%d]", eq_name, varkey.name, i);
2870 r->rsprintf("<td align=center>");
2871 r->rsprintf("<a href=\"#\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\', 0);return false;\" >%s</a>", odb_path, data_str.c_str());
2872 n_var++;
2873 }
2874 } else
2875 r->rsprintf("<td align=center>%s", data_str.c_str());
2876 }
2877
2878 r->rsprintf("</tr>\n");
2879 }
2880 } else {
2881 /*---- multiple name arrays ----*/
2882 r->rsprintf("<tr><td colspan=15><i>Groups:</i> ");
2883
2884 /* "all" group */
2885 if (equal_ustring(group, "All"))
2886 r->rsprintf("<b>All</b> &nbsp;&nbsp;");
2887 else
2888 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s\">All</a> &nbsp;&nbsp;", eq_name);
2889
2890 /* groups from Variables tree */
2891
2892 std::string vars_path = msprintf("/Equipment/%s/Variables", eq_name);
2893 HNDLE hkeyvar;
2894 db_find_key(hDB, 0, vars_path.c_str(), &hkeyvar);
2895
2896 if (hkeyvar) {
2897 for (int i = 0;; i++) {
2898 HNDLE hkey;
2899 db_enum_link(hDB, hkeyvar, i, &hkey);
2900
2901 if (!hkey)
2902 break;
2903
2904 KEY key;
2905 db_get_key(hDB, hkey, &key);
2906
2907 if (equal_ustring(key.name, group)) {
2908 r->rsprintf("<b>%s</b> &nbsp;&nbsp;", key.name);
2909 } else {
2910 r->rsprintf("<a href=\"?cmd=eqtable&eq=%s&group=%s\">%s</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str(), urlEncode(key.name).c_str(), key.name);
2911 }
2912 }
2913 }
2914
2915 r->rsprintf("<i>ODB:</i> &nbsp;&nbsp;");
2916 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Common\">Common</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2917 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Settings\">Settings</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2918 r->rsprintf("<a href=\"?cmd=odb&odb_path=Equipment/%s/Variables\">Variables</a> &nbsp;&nbsp;", urlEncode(eq_name).c_str());
2919 r->rsprintf("</tr>\n");
2920
2921 /* enumerate variable arrays */
2922 line = 0;
2923 for (i = 0;; i++) {
2924 HNDLE hkey;
2925 db_enum_link(hDB, hkeyvar, i, &hkey);
2926
2927 if (line % 2 == 0)
2928 mstrlcpy(style, "ODBtableEven", sizeof(style));
2929 else
2930 mstrlcpy(style, "ODBtableOdd", sizeof(style));
2931
2932 if (!hkey)
2933 break;
2934
2935 KEY varkey;
2936 db_get_key(hDB, hkey, &varkey);
2937
2938 if (!equal_ustring(group, "All") && !equal_ustring(varkey.name, group))
2939 continue;
2940
2941 /* title row */
2942 r->rsprintf("<tr class=\"subStatusTitle\"><th colspan=9>Names<th>%s</tr>\n", varkey.name);
2943
2944 if (varkey.type == TID_KEY) {
2945 HNDLE hkeyroot = hkey;
2946
2947 /* enumerate subkeys */
2948 for (j = 0;; j++) {
2949 db_enum_key(hDB, hkeyroot, j, &hkey);
2950 if (!hkey)
2951 break;
2952
2953 KEY key;
2954 db_get_key(hDB, hkey, &key);
2955
2956 if (key.type == TID_KEY) {
2957 /* for keys, don't display data value */
2958 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<br></tr>\n", style, key.name);
2959 } else {
2960 /* display single value */
2961 if (key.num_values == 1) {
2962 size = sizeof(data);
2963 db_get_data(hDB, hkey, data, &size, key.type);
2964
2965 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2966 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2967
2968 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2969 data_str = "(empty)";
2970 hex_str = "";
2971 }
2972
2973 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
2974 r->rsprintf
2975 ("<tr class=\"%s\" ><td colspan=9>%s<td align=center>%s (%s)<br></tr>\n",
2976 style, key.name, data_str.c_str(), hex_str.c_str());
2977 else
2978 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<td align=center>%s<br></tr>\n",
2979 style, key.name, data_str.c_str());
2980 line++;
2981 } else {
2982 /* display first value */
2983 r->rsprintf("<tr class=\"%s\"><td colspan=9 rowspan=%d>%s\n", style, key.num_values,
2984 key.name);
2985
2986 for (k = 0; k < key.num_values; k++) {
2987 size = sizeof(data);
2988 db_get_data_index(hDB, hkey, data, &size, k, key.type);
2989 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
2990 std::string hex_str = db_sprintfh(data, key.item_size, 0, key.type);
2991
2992 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
2993 data_str = "(empty)";
2994 hex_str = "";
2995 }
2996
2997 if (k > 0)
2998 r->rsprintf("<tr>");
2999
3000 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
3001 r->rsprintf("<td>[%d] %s (%s)<br></tr>\n", k, data_str.c_str(), hex_str.c_str());
3002 else
3003 r->rsprintf("<td>[%d] %s<br></tr>\n", k, data_str.c_str());
3004 line++;
3005 }
3006 }
3007 }
3008 }
3009 } else {
3010 /* data for current group */
3011 std::string names_path = msprintf("/Equipment/%s/Settings/Names %s", eq_name, varkey.name);
3012 HNDLE hkeyset;
3013 db_find_key(hDB, 0, names_path.c_str(), &hkeyset);
3014 KEY key;
3015 if (hkeyset)
3016 db_get_key(hDB, hkeyset, &key);
3017
3018 if (varkey.num_values > 1000)
3019 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s<td align=center><i>... %d values ...</i>",
3020 style, varkey.name, varkey.num_values);
3021 else {
3022 for (j = 0; j < varkey.num_values; j++) {
3023
3024 if (line % 2 == 0)
3025 mstrlcpy(style, "ODBtableEven", sizeof(style));
3026 else
3027 mstrlcpy(style, "ODBtableOdd", sizeof(style));
3028
3029 char name[NAME_LENGTH+32];
3030 if (hkeyset && j<key.num_values) {
3031 size = sizeof(name);
3032 db_get_data_index(hDB, hkeyset, name, &size, j, TID_STRING);
3033 } else {
3034 sprintf(name, "%s[%d]", varkey.name, j);
3035 }
3036
3037 if (strlen(name) < 1) {
3038 sprintf(name, "%s[%d]", varkey.name, j);
3039 }
3040
3041 r->rsprintf("<tr class=\"%s\"><td colspan=9>%s", style, name);
3042
3043 size = sizeof(data);
3044 db_get_data_index(hDB, hkey, data, &size, j, varkey.type);
3045 std::string data_str = db_sprintf(data, varkey.item_size, 0, varkey.type);
3046
3047 if (is_editable(eq_name, varkey.name)) {
3048 if (n_var == i_set) {
3049 /* set value */
3050 char str[256];
3051 mstrlcpy(str, pp->getparam("value"), sizeof(str));
3052 db_sscanf(str, data, &size, 0, varkey.type);
3053 db_set_data_index(hDB, hkey, data, size, j, varkey.type);
3054
3055 /* redirect (so that 'reload' does not reset value) */
3056 r->reset();
3057 sprintf(str, "%s", group);
3058 redirect(r, str);
3059 return;
3060 }
3061 if (n_var == i_edit) {
3062 r->rsprintf("<td align=center><input type=text size=10 maxlenth=80 name=value value=\"%s\">\n", data_str.c_str());
3063 r->rsprintf("<input type=submit size=20 name=cmd value=Set></tr>\n");
3064 r->rsprintf("<input type=hidden name=index value=%d>\n", i_edit);
3065 r->rsprintf("<input type=hidden name=cmd value=Set>\n");
3066 n_var++;
3067 } else {
3068 sprintf(odb_path, "Equipment/%s/Variables/%s[%d]", eq_name, varkey.name, j);
3069
3070 r->rsprintf("<td align=cernter>");
3071 r->rsprintf("<a href=\"#\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\', 0);return false;\" >%s</a>", odb_path, data_str.c_str());
3072 n_var++;
3073 }
3074
3075 } else
3076 r->rsprintf("<td align=center>%s\n", data_str.c_str());
3077 r->rsprintf("</tr>\n");
3078 line++;
3079 }
3080 }
3081
3082 r->rsprintf("</tr>\n");
3083 }
3084 }
3085 }
3086
3087 r->rsprintf("</table>\n");
3088 r->rsprintf("</div>\n"); // closing for <div id="mmain">
3089 r->rsprintf("</form>\n");
3090 r->rsprintf("</body></html>\r\n");
3091}
3092#endif
3093
3094/*------------------------------------------------------------------*/
3095
3096char *find_odb_tag(char *p, char *path, char *format, int *edit, char *type, char *pwd, char *tail)
3097{
3098 char str[256], *ps, *pt;
3099 BOOL in_script;
3100
3101 *edit = 0;
3102 *tail = 0;
3103 *format = 0;
3104 pwd[0] = 0;
3105 in_script = FALSE;
3106 strcpy(type, "text");
3107 do {
3108 while (*p && *p != '<')
3109 p++;
3110
3111 /* return if end of string reached */
3112 if (!*p)
3113 return NULL;
3114
3115 p++;
3116 while (*p && ((*p == ' ') || iscntrl(*p)))
3117 p++;
3118
3119 strncpy(str, p, 6);
3120 str[6] = 0;
3121 if (equal_ustring(str, "script"))
3122 in_script = TRUE;
3123
3124 strncpy(str, p, 7);
3125 str[7] = 0;
3126 if (equal_ustring(str, "/script"))
3127 in_script = FALSE;
3128
3129 strncpy(str, p, 4);
3130 str[4] = 0;
3131 if (equal_ustring(str, "odb ")) {
3132 ps = p - 1;
3133 p += 4;
3134 while (*p && ((*p == ' ') || iscntrl(*p)))
3135 p++;
3136
3137 do {
3138 strncpy(str, p, 7);
3139 str[7] = 0;
3140 if (equal_ustring(str, "format=")) {
3141 p += 7;
3142 if (*p == '\"') {
3143 p++;
3144 while (*p && *p != '\"')
3145 *format++ = *p++;
3146 *format = 0;
3147 if (*p == '\"')
3148 p++;
3149 } else {
3150 while (*p && *p != ' ' && *p != '>')
3151 *format++ = *p++;
3152 *format = 0;
3153 }
3154
3155 } else {
3156
3157 strncpy(str, p, 4);
3158 str[4] = 0;
3159 if (equal_ustring(str, "src=")) {
3160 p += 4;
3161 if (*p == '\"') {
3162 p++;
3163 while (*p && *p != '\"')
3164 *path++ = *p++;
3165 *path = 0;
3166 if (*p == '\"')
3167 p++;
3168 } else {
3169 while (*p && *p != ' ' && *p != '>')
3170 *path++ = *p++;
3171 *path = 0;
3172 }
3173 } else {
3174
3175 if (in_script)
3176 break;
3177
3178 strncpy(str, p, 5);
3179 str[5] = 0;
3180 if (equal_ustring(str, "edit=")) {
3181 p += 5;
3182
3183 if (*p == '\"') {
3184 p++;
3185 *edit = atoi(p);
3186 if (*p == '\"')
3187 p++;
3188 } else {
3189 *edit = atoi(p);
3190 while (*p && *p != ' ' && *p != '>')
3191 p++;
3192 }
3193
3194 } else {
3195
3196 strncpy(str, p, 5);
3197 str[5] = 0;
3198 if (equal_ustring(str, "type=")) {
3199 p += 5;
3200 if (*p == '\"') {
3201 p++;
3202 while (*p && *p != '\"')
3203 *type++ = *p++;
3204 *type = 0;
3205 if (*p == '\"')
3206 p++;
3207 } else {
3208 while (*p && *p != ' ' && *p != '>')
3209 *type++ = *p++;
3210 *type = 0;
3211 }
3212 } else {
3213 strncpy(str, p, 4);
3214 str[4] = 0;
3215 if (equal_ustring(str, "pwd=")) {
3216 p += 4;
3217 if (*p == '\"') {
3218 p++;
3219 while (*p && *p != '\"')
3220 *pwd++ = *p++;
3221 *pwd = 0;
3222 if (*p == '\"')
3223 p++;
3224 } else {
3225 while (*p && *p != ' ' && *p != '>')
3226 *pwd++ = *p++;
3227 *pwd = 0;
3228 }
3229 } else {
3230 if (strchr(p, '=')) {
3231 mstrlcpy(str, p, sizeof(str));
3232 pt = strchr(str, '=')+1;
3233 if (*pt == '\"') {
3234 pt++;
3235 while (*pt && *pt != '\"')
3236 pt++;
3237 if (*pt == '\"')
3238 pt++;
3239 *pt = 0;
3240 } else {
3241 while (*pt && *pt != ' ' && *pt != '>')
3242 pt++;
3243 *pt = 0;
3244 }
3245 if (tail[0]) {
3246 mstrlcat(tail, " ", 256);
3247 mstrlcat(tail, str, 256);
3248 } else {
3249 mstrlcat(tail, str, 256);
3250 }
3251 p += strlen(str);
3252 }
3253 }
3254 }
3255 }
3256 }
3257 }
3258
3259 while (*p && ((*p == ' ') || iscntrl(*p)))
3260 p++;
3261
3262 if (*p == '<') {
3263 cm_msg(MERROR, "find_odb_tag", "Invalid odb tag '%s'", ps);
3264 return NULL;
3265 }
3266 } while (*p != '>');
3267
3268 return ps;
3269 }
3270
3271 while (*p && *p != '>')
3272 p++;
3273
3274 } while (1);
3275
3276}
3277
3278/*------------------------------------------------------------------*/
3279
3280void show_odb_tag(Param* pp, Return* r, const char *path, const char *keypath1, const char *format, int n_var, int edit, char *type, char *pwd, char *tail)
3281{
3282 int size, index, i_edit, i_set;
3283 char data[TEXT_SIZE], full_keypath[256], keypath[256], *p;
3284 HNDLE hDB, hkey;
3285 KEY key;
3286
3287 /* check if variable to edit */
3288 i_edit = -1;
3289 if (equal_ustring(pp->getparam("cmd"), "Edit"))
3290 i_edit = atoi(pp->getparam("index"));
3291
3292 /* check if variable to set */
3293 i_set = -1;
3294 if (equal_ustring(pp->getparam("cmd"), "Set"))
3295 i_set = atoi(pp->getparam("index"));
3296
3297 /* check if path contains index */
3298 mstrlcpy(full_keypath, keypath1, sizeof(full_keypath));
3299 mstrlcpy(keypath, keypath1, sizeof(keypath));
3300 index = 0;
3301
3302 if (strchr(keypath, '[') && strchr(keypath, ']')) {
3303 for (p = strchr(keypath, '[') + 1; *p && *p != ']'; p++)
3304 if (!isdigit(*p))
3305 break;
3306
3307 if (*p && *p == ']') {
3308 index = atoi(strchr(keypath, '[') + 1);
3309 *strchr(keypath, '[') = 0;
3310 }
3311 }
3312
3314 db_find_key(hDB, 0, keypath, &hkey);
3315 if (!hkey)
3316 r->rsprintf("<b>Key \"%s\" not found in ODB</b>\n", keypath);
3317 else {
3318 db_get_key(hDB, hkey, &key);
3319 size = sizeof(data);
3320 db_get_data_index(hDB, hkey, data, &size, index, key.type);
3321
3322 std::string data_str;
3323 if (format && strlen(format)>0)
3324 data_str = db_sprintff(format, data, key.item_size, 0, key.type);
3325 else
3326 data_str= db_sprintf(data, key.item_size, 0, key.type);
3327
3328 if (equal_ustring(type, "checkbox")) {
3329
3330 if (pp->isparam("cbi"))
3331 i_set = atoi(pp->getparam("cbi"));
3332 if (n_var == i_set) {
3333 /* toggle state */
3334 if (key.type == TID_BOOL) {
3335 if (data_str[0] == 'y')
3336 data_str = "n";
3337 else
3338 data_str = "y";
3339 } else {
3340 if (atoi(data_str.c_str()) > 0)
3341 data_str = "0";
3342 else
3343 data_str = "1";
3344 }
3345
3346 db_sscanf(data_str.c_str(), data, &size, 0, key.type);
3347 db_set_data_index(hDB, hkey, data, size, index, key.type);
3348 }
3349
3350 std::string options;
3351 if (data_str[0] == 'y' || atoi(data_str.c_str()) > 0)
3352 options += "checked ";
3353 if (!edit)
3354 options += "disabled ";
3355 else {
3356 if (edit == 1) {
3357 options += "onClick=\"o=document.createElement('input');o.type='hidden';o.name='cbi';o.value='";
3358 options += msprintf("%d", n_var);
3359 options += "';document.form1.appendChild(o);";
3360 options += "document.form1.submit();\" ";
3361 }
3362 }
3363
3364 if (tail[0])
3365 options += tail;
3366
3367 r->rsprintf("<input type=\"checkbox\" %s>\n", options.c_str());
3368
3369 } else { // checkbox
3370
3371 if (edit == 1) {
3372 if (n_var == i_set) {
3373 /* set value */
3374 char str[256];
3375 mstrlcpy(str, pp->getparam("value"), sizeof(str));
3376 db_sscanf(str, data, &size, 0, key.type);
3377 db_set_data_index(hDB, hkey, data, size, index, key.type);
3378
3379 /* read back value */
3380 size = sizeof(data);
3381 db_get_data_index(hDB, hkey, data, &size, index, key.type);
3382 data_str = db_sprintf(data, key.item_size, 0, key.type);
3383 }
3384
3385 if (n_var == i_edit) {
3386 r->rsprintf("<input type=text size=10 maxlength=80 name=value value=\"%s\">\n", data_str.c_str());
3387 r->rsprintf("<input type=submit size=20 name=cmd value=Set>\n");
3388 r->rsprintf("<input type=hidden name=index value=%d>\n", n_var);
3389 r->rsprintf("<input type=hidden name=cmd value=Set>\n");
3390 } else {
3391 if (edit == 2) {
3392 /* edit handling through user supplied JavaScript */
3393 r->rsprintf("<a href=\"#\" %s>", tail);
3394 } else {
3395 /* edit handling through form submission */
3396 if (pwd[0]) {
3397 r->rsprintf("<a onClick=\"promptpwd('%s?cmd=Edit&index=%d&pnam=%s')\" href=\"#\">", path, n_var, pwd);
3398 } else {
3399 r->rsprintf("<a href=\"%s?cmd=Edit&index=%d\" %s>", path, n_var, tail);
3400 }
3401 }
3402
3403 r->rsputs(data_str.c_str());
3404 r->rsprintf("</a>");
3405 }
3406 } else if (edit == 2) {
3407 r->rsprintf("<a href=\"#\" onclick=\"ODBEdit('%s')\">\n", full_keypath);
3408 r->rsputs(data_str.c_str());
3409 r->rsprintf("</a>");
3410 }
3411 else
3412 r->rsputs(data_str.c_str());
3413 }
3414 }
3415}
3416
3417/*------------------------------------------------------------------*/
3418
3419/* add labels using following syntax under /Custom/Images/<name.gif>/Labels/<name>:
3420
3421 [Name] [Description] [Example]
3422
3423 Src ODB path for vairable to display /Equipment/Environment/Variables/Input[0]
3424 Format Formt for float/double %1.2f Deg. C
3425 Font Font to use small | medium | giant
3426 X X-position in pixel 90
3427 Y Y-position from top 67
3428 Align horizontal align left/center/right left
3429 FGColor Foreground color RRGGBB 000000
3430 BGColor Background color RRGGBB FFFFFF
3431*/
3432
3433static const char *cgif_label_str[] = {
3434 "Src = STRING : [256] ",
3435 "Format = STRING : [32] %1.1f",
3436 "Font = STRING : [32] Medium",
3437 "X = INT : 0",
3438 "Y = INT : 0",
3439 "Align = INT : 0",
3440 "FGColor = STRING : [8] 000000",
3441 "BGColor = STRING : [8] FFFFFF",
3442 NULL
3443};
3444
3445typedef struct {
3446 char src[256];
3447 char format[32];
3448 char font[32];
3449 int x, y, align;
3450 char fgcolor[8];
3451 char bgcolor[8];
3452} CGIF_LABEL;
3453
3454/* add labels using following syntax under /Custom/Images/<name.gif>/Bars/<name>:
3455
3456 [Name] [Description] [Example]
3457
3458 Src ODB path for vairable to display /Equipment/Environment/Variables/Input[0]
3459 X X-position in pixel 90
3460 Y Y-position from top 67
3461 Width Width in pixel 20
3462 Height Height in pixel 100
3463 Direction 0(vertical)/1(horiz.) 0
3464 Axis Draw axis 0(none)/1(left)/2(right) 1
3465 Logscale Draw logarithmic axis n
3466 Min Min value for axis 0
3467 Max Max value for axis 10
3468 FGColor Foreground color RRGGBB 000000
3469 BGColor Background color RRGGBB FFFFFF
3470 BDColor Border color RRGGBB 808080
3471*/
3472
3473static const char *cgif_bar_str[] = {
3474 "Src = STRING : [256] ",
3475 "X = INT : 0",
3476 "Y = INT : 0",
3477 "Width = INT : 10",
3478 "Height = INT : 100",
3479 "Direction = INT : 0",
3480 "Axis = INT : 1",
3481 "Logscale = BOOL : n",
3482 "Min = DOUBLE : 0",
3483 "Max = DOUBLE : 10",
3484 "FGColor = STRING : [8] 000000",
3485 "BGColor = STRING : [8] FFFFFF",
3486 "BDColor = STRING : [8] 808080",
3487 NULL
3488};
3489
3490typedef struct {
3491 char src[256];
3492 int x, y, width, height, direction, axis;
3494 double min, max;
3495 char fgcolor[8];
3496 char bgcolor[8];
3497 char bdcolor[8];
3498} CGIF_BAR;
3499
3500/*------------------------------------------------------------------*/
3501
3502int evaluate_src(char *key, char *src, double *fvalue)
3503{
3504 HNDLE hDB, hkeyval;
3505 KEY vkey;
3506 int i, n, size, ivalue;
3507 char str[256], data[256];
3508
3510
3511 /* separate source from operators */
3512 for (i=0 ; i<(int)strlen(src) ; i++)
3513 if (src[i] == '>' || src[i] == '&')
3514 break;
3515 strncpy(str, src, i);
3516 str[i] = 0;
3517
3518 /* strip trailing blanks */
3519 while (strlen(str) > 0 && str[strlen(str)-1] == ' ')
3520 str[strlen(str)-1] = 0;
3521
3522 db_find_key(hDB, 0, str, &hkeyval);
3523 if (!hkeyval) {
3524 cm_msg(MERROR, "evaluate_src", "Invalid Src key \"%s\" for Fill \"%s\"",
3525 src, key);
3526 return 0;
3527 }
3528
3529 db_get_key(hDB, hkeyval, &vkey);
3530 size = sizeof(data);
3531 db_get_value(hDB, 0, src, data, &size, vkey.type, FALSE);
3532 std::string value = db_sprintf(data, size, 0, vkey.type);
3533 if (equal_ustring(value.c_str(), "NAN"))
3534 return 0;
3535
3536 if (vkey.type == TID_BOOL) {
3537 *fvalue = (value[0] == 'y');
3538 } else
3539 *fvalue = atof(value.c_str());
3540
3541 /* evaluate possible operators */
3542 do {
3543 if (src[i] == '>' && src[i+1] == '>') {
3544 i+=2;
3545 n = atoi(src+i);
3546 while (src[i] == ' ' || isdigit(src[i]))
3547 i++;
3548 ivalue = (int)*fvalue;
3549 ivalue >>= n;
3550 *fvalue = ivalue;
3551 }
3552
3553 if (src[i] == '&') {
3554 i+=1;
3555 while (src[i] == ' ')
3556 i++;
3557 if (src[i] == '0' && src[i+1] == 'x')
3558 sscanf(src+2+i, "%x", &n);
3559 else
3560 n = atoi(src+i);
3561 while (src[i] == ' ' || isxdigit(src[i]) || src[i] == 'x')
3562 i++;
3563 ivalue = (int)*fvalue;
3564 ivalue &= n;
3565 *fvalue = ivalue;
3566 }
3567
3568 } while (src[i]);
3569
3570 return 1;
3571}
3572
3573/*------------------------------------------------------------------*/
3574
3575std::string add_custom_path(const std::string& filename)
3576{
3577 // do not append custom path to absolute filenames
3578
3579 if (filename[0] == '/')
3580 return filename;
3581 if (filename[0] == DIR_SEPARATOR)
3582 return filename;
3583
3584 HNDLE hDB;
3586
3587 std::string custom_path = "";
3588
3589 int status = db_get_value_string(hDB, 0, "/Custom/Path", 0, &custom_path, TRUE);
3590
3591 if (status != DB_SUCCESS)
3592 return filename;
3593
3594 if (custom_path.length() < 1)
3595 return filename;
3596
3597 if ((custom_path == DIR_SEPARATOR_STR) || !strchr(custom_path.c_str(), DIR_SEPARATOR)) {
3598 cm_msg(MERROR, "add_custom_path", "ODB /Custom/Path has a forbidden value \"%s\", please change it", custom_path.c_str());
3599 return filename;
3600 }
3601
3602 custom_path = ss_replace_env_variables(custom_path);
3603
3604 std::string full_filename = custom_path;
3605 if (full_filename[full_filename.length()-1] != DIR_SEPARATOR)
3606 full_filename += DIR_SEPARATOR_STR;
3607 full_filename += filename;
3608
3609 return full_filename;
3610}
3611
3612/*------------------------------------------------------------------*/
3613
3614void show_custom_file(Return* r, const char *name)
3615{
3616 char str[256];
3617 std::string filename;
3618 HNDLE hDB;
3619
3621
3622 HNDLE hkey;
3623 sprintf(str, "/Custom/%s", name);
3624 db_find_key(hDB, 0, str, &hkey);
3625
3626 if (!hkey) {
3627 sprintf(str, "/Custom/%s&", name);
3628 db_find_key(hDB, 0, str, &hkey);
3629 if (!hkey) {
3630 sprintf(str, "/Custom/%s!", name);
3631 db_find_key(hDB, 0, str, &hkey);
3632 }
3633 }
3634
3635 if(!hkey){
3636 sprintf(str,"show_custom_file: Invalid custom page: \"/Custom/%s\" not found in ODB", name);
3637 show_error_404(r, str);
3638 return;
3639 }
3640
3641 int status;
3642 KEY key;
3643
3644 status = db_get_key(hDB, hkey, &key);
3645
3646 if (status != DB_SUCCESS) {
3647 char errtext[512];
3648 sprintf(errtext, "show_custom_file: Error: db_get_key() for \"%s\" status %d", str, status);
3649 show_error_404(r, errtext);
3650 return;
3651 }
3652
3653 int size = key.total_size;
3654 char* ctext = (char*)malloc(size);
3655
3656 status = db_get_data(hDB, hkey, ctext, &size, TID_STRING);
3657
3658 if (status != DB_SUCCESS) {
3659 char errtext[512];
3660 sprintf(errtext, "show_custom_file: Error: db_get_data() for \"%s\" status %d", str, status);
3661 show_error_404(r, errtext);
3662 free(ctext);
3663 return;
3664 }
3665
3666 filename = add_custom_path(ctext);
3667
3668 free(ctext);
3669
3670 send_file(r, filename, true);
3671
3672 return;
3673}
3674
3675/*------------------------------------------------------------------*/
3676
3677void show_custom_gif(Return* rr, const char *name)
3678{
3679 char str[256], data[256], src[256];
3680 int i, index, length, status, size, width, height, bgcol, fgcol, bdcol, r, g, b, x, y;
3681 HNDLE hDB, hkeygif, hkeyroot, hkey, hkeyval;
3682 double fvalue, ratio;
3683 KEY key, vkey;
3684 gdImagePtr im;
3685 gdGifBuffer gb;
3686 gdFontPtr pfont;
3687 FILE *f;
3688 CGIF_LABEL label;
3689 CGIF_BAR bar;
3690
3692
3693 /* find image description in ODB */
3694 sprintf(str, "/Custom/Images/%s", name);
3695 db_find_key(hDB, 0, str, &hkeygif);
3696 if (!hkeygif) {
3697
3698 // If we don't have Images directory,
3699 // then just treat this like any other custom file.
3701 return;
3702 }
3703
3704 /* load background image */
3705 std::string filename;
3706 db_get_value_string(hDB, hkeygif, "Background", 0, &filename, FALSE);
3707
3708 std::string full_filename = add_custom_path(filename);
3709
3710 f = fopen(full_filename.c_str(), "rb");
3711 if (f == NULL) {
3712 sprintf(str, "show_custom_gif: Cannot open file \"%s\"", full_filename.c_str());
3713 show_error_404(rr, str);
3714 return;
3715 }
3716
3717 im = gdImageCreateFromGif(f);
3718 fclose(f);
3719
3720 if (im == NULL) {
3721 sprintf(str, "show_custom_gif: File \"%s\" is not a GIF image", filename.c_str());
3722 show_error_404(rr, str);
3723 return;
3724 }
3725
3727
3728 /*---- draw labels ----------------------------------------------*/
3729
3730 db_find_key(hDB, hkeygif, "Labels", &hkeyroot);
3731 if (hkeyroot) {
3732 for (index = 0;; index++) {
3733 db_enum_key(hDB, hkeyroot, index, &hkey);
3734 if (!hkey)
3735 break;
3736 db_get_key(hDB, hkey, &key);
3737
3738 size = sizeof(label);
3739 status = db_get_record1(hDB, hkey, &label, &size, 0, strcomb1(cgif_label_str).c_str());
3740 if (status != DB_SUCCESS) {
3741 cm_msg(MERROR, "show_custom_gif", "Cannot open data record for label \"%s\"",
3742 key.name);
3743 continue;
3744 }
3745
3746 if (label.src[0] == 0) {
3747 cm_msg(MERROR, "show_custom_gif", "Empty Src key for label \"%s\"", key.name);
3748 continue;
3749 }
3750
3751 db_find_key(hDB, 0, label.src, &hkeyval);
3752 if (!hkeyval) {
3753 cm_msg(MERROR, "show_custom_gif", "Invalid Src key \"%s\" for label \"%s\"",
3754 label.src, key.name);
3755 continue;
3756 }
3757
3758 db_get_key(hDB, hkeyval, &vkey);
3759 size = sizeof(data);
3760 status = db_get_value(hDB, 0, label.src, data, &size, vkey.type, FALSE);
3761
3762 std::string value;
3763
3764 if (label.format[0]) {
3765 if (vkey.type == TID_FLOAT)
3766 value = msprintf(label.format, *(((float *) data)));
3767 else if (vkey.type == TID_DOUBLE)
3768 value = msprintf(label.format, *(((double *) data)));
3769 else if (vkey.type == TID_INT)
3770 value = msprintf(label.format, *(((INT *) data)));
3771 else if (vkey.type == TID_BOOL) {
3772 if (strstr(label.format, "%c"))
3773 value = msprintf(label.format, *(((INT *) data)) ? 'y' : 'n');
3774 else
3775 value = msprintf(label.format, *(((INT *) data)));
3776 } else
3777 value = db_sprintf(data, size, 0, vkey.type);
3778 } else
3779 value = db_sprintf(data, size, 0, vkey.type);
3780
3781 sscanf(label.fgcolor, "%02x%02x%02x", &r, &g, &b);
3782 fgcol = gdImageColorAllocate(im, r, g, b);
3783 if (fgcol == -1)
3784 fgcol = gdImageColorClosest(im, r, g, b);
3785
3786 sscanf(label.bgcolor, "%02x%02x%02x", &r, &g, &b);
3787 bgcol = gdImageColorAllocate(im, r, g, b);
3788 if (bgcol == -1)
3789 bgcol = gdImageColorClosest(im, r, g, b);
3790
3791 /* select font */
3792 if (equal_ustring(label.font, "Small"))
3793 pfont = gdFontSmall;
3794 else if (equal_ustring(label.font, "Medium"))
3795 pfont = gdFontMediumBold;
3796 else if (equal_ustring(label.font, "Giant"))
3797 pfont = gdFontGiant;
3798 else
3799 pfont = gdFontMediumBold;
3800
3801 width = value.length() * pfont->w + 5 + 5;
3802 height = pfont->h + 2 + 2;
3803
3804 if (label.align == 0) {
3805 /* left */
3806 gdImageFilledRectangle(im, label.x, label.y, label.x + width,
3807 label.y + height, bgcol);
3808 gdImageRectangle(im, label.x, label.y, label.x + width, label.y + height,
3809 fgcol);
3810 gdImageString(im, pfont, label.x + 5, label.y + 2, value.c_str(), fgcol);
3811 } else if (label.align == 1) {
3812 /* center */
3813 gdImageFilledRectangle(im, label.x - width / 2, label.y, label.x + width / 2,
3814 label.y + height, bgcol);
3815 gdImageRectangle(im, label.x - width / 2, label.y, label.x + width / 2,
3816 label.y + height, fgcol);
3817 gdImageString(im, pfont, label.x + 5 - width / 2, label.y + 2, value.c_str(), fgcol);
3818 } else {
3819 /* right */
3820 gdImageFilledRectangle(im, label.x - width, label.y, label.x,
3821 label.y + height, bgcol);
3822 gdImageRectangle(im, label.x - width, label.y, label.x, label.y + height,
3823 fgcol);
3824 gdImageString(im, pfont, label.x - width + 5, label.y + 2, value.c_str(), fgcol);
3825 }
3826 }
3827 }
3828
3829 /*---- draw bars ------------------------------------------------*/
3830
3831 db_find_key(hDB, hkeygif, "Bars", &hkeyroot);
3832 if (hkeyroot) {
3833 for (index = 0;; index++) {
3834 db_enum_key(hDB, hkeyroot, index, &hkey);
3835 if (!hkey)
3836 break;
3837 db_get_key(hDB, hkey, &key);
3838
3839 size = sizeof(bar);
3840 status = db_get_record1(hDB, hkey, &bar, &size, 0, strcomb1(cgif_bar_str).c_str());
3841 if (status != DB_SUCCESS) {
3842 cm_msg(MERROR, "show_custom_gif", "Cannot open data record for bar \"%s\"",
3843 key.name);
3844 continue;
3845 }
3846
3847 if (bar.src[0] == 0) {
3848 cm_msg(MERROR, "show_custom_gif", "Empty Src key for bar \"%s\"", key.name);
3849 continue;
3850 }
3851
3852 db_find_key(hDB, 0, bar.src, &hkeyval);
3853 if (!hkeyval) {
3854 cm_msg(MERROR, "show_custom_gif", "Invalid Src key \"%s\" for bar \"%s\"",
3855 bar.src, key.name);
3856 continue;
3857 }
3858
3859 db_get_key(hDB, hkeyval, &vkey);
3860 size = sizeof(data);
3861 status = db_get_value(hDB, 0, bar.src, data, &size, vkey.type, FALSE);
3862 std::string value = db_sprintf(data, size, 0, vkey.type);
3863 if (equal_ustring(value.c_str(), "NAN"))
3864 continue;
3865
3866 fvalue = atof(value.c_str());
3867
3868 sscanf(bar.fgcolor, "%02x%02x%02x", &r, &g, &b);
3869 fgcol = gdImageColorAllocate(im, r, g, b);
3870 if (fgcol == -1)
3871 fgcol = gdImageColorClosest(im, r, g, b);
3872
3873 sscanf(bar.bgcolor, "%02x%02x%02x", &r, &g, &b);
3874 bgcol = gdImageColorAllocate(im, r, g, b);
3875 if (bgcol == -1)
3876 bgcol = gdImageColorClosest(im, r, g, b);
3877
3878 sscanf(bar.bdcolor, "%02x%02x%02x", &r, &g, &b);
3879 bdcol = gdImageColorAllocate(im, r, g, b);
3880 if (bdcol == -1)
3881 bdcol = gdImageColorClosest(im, r, g, b);
3882
3883 if (bar.min == bar.max)
3884 bar.max += 1;
3885
3886 if (bar.logscale) {
3887 if (fvalue < 1E-20)
3888 fvalue = 1E-20;
3889 ratio = (log(fvalue) - log(bar.min)) / (log(bar.max) - log(bar.min));
3890 } else
3891 ratio = (fvalue - bar.min) / (bar.max - bar.min);
3892 if (ratio < 0)
3893 ratio = 0;
3894 if (ratio > 1)
3895 ratio = 1;
3896
3897 if (bar.direction == 0) {
3898 /* vertical */
3899 ratio = (bar.height - 2) - ratio * (bar.height - 2);
3900 r = (int) (ratio + 0.5);
3901
3902 gdImageFilledRectangle(im, bar.x, bar.y, bar.x + bar.width,
3903 bar.y + bar.height, bgcol);
3904 gdImageRectangle(im, bar.x, bar.y, bar.x + bar.width, bar.y + bar.height,
3905 bdcol);
3906 gdImageFilledRectangle(im, bar.x + 1, bar.y + r + 1, bar.x + bar.width - 1,
3907 bar.y + bar.height - 1, fgcol);
3908
3909 if (bar.axis == 1)
3910 vaxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y + bar.height, bar.height, -3,
3911 -5, -7, -8, 0, bar.min, bar.max, bar.logscale);
3912 else if (bar.axis == 2)
3913 vaxis(im, gdFontSmall, bdcol, 0, bar.x + bar.width, bar.y + bar.height,
3914 bar.height, 3, 5, 7, 10, 0, bar.min, bar.max, bar.logscale);
3915
3916 } else {
3917 /* horizontal */
3918 ratio = ratio * (bar.height - 2);
3919 r = (int) (ratio + 0.5);
3920
3921 gdImageFilledRectangle(im, bar.x, bar.y, bar.x + bar.height,
3922 bar.y + bar.width, bgcol);
3923 gdImageRectangle(im, bar.x, bar.y, bar.x + bar.height, bar.y + bar.width,
3924 bdcol);
3925 gdImageFilledRectangle(im, bar.x + 1, bar.y + 1, bar.x + r,
3926 bar.y + bar.width - 1, fgcol);
3927
3928 if (bar.axis == 1)
3929 haxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y, bar.height, -3, -5, -7, -18,
3930 0, bar.min, bar.max);
3931 else if (bar.axis == 2)
3932 haxis(im, gdFontSmall, bdcol, 0, bar.x, bar.y + bar.width, bar.height, 3,
3933 5, 7, 8, 0, bar.min, bar.max);
3934 }
3935 }
3936 }
3937
3938 /*---- draw fills -----------------------------------------------*/
3939
3940 db_find_key(hDB, hkeygif, "Fills", &hkeyroot);
3941 if (hkeyroot) {
3942 for (index = 0;; index++) {
3943 db_enum_key(hDB, hkeyroot, index, &hkey);
3944 if (!hkey)
3945 break;
3946 db_get_key(hDB, hkey, &key);
3947
3948 size = sizeof(src);
3949 src[0] = 0;
3950 db_get_value(hDB, hkey, "Src", src, &size, TID_STRING, TRUE);
3951
3952 if (src[0] == 0) {
3953 cm_msg(MERROR, "show_custom_gif", "Empty Src key for Fill \"%s\"", key.name);
3954 continue;
3955 }
3956
3957 if (!evaluate_src(key.name, src, &fvalue))
3958 continue;
3959
3960 x = y = 0;
3961 size = sizeof(x);
3962 db_get_value(hDB, hkey, "X", &x, &size, TID_INT, TRUE);
3963 db_get_value(hDB, hkey, "Y", &y, &size, TID_INT, TRUE);
3964
3965 size = sizeof(data);
3966 status = db_get_value(hDB, hkey, "Limits", data, &size, TID_DOUBLE, FALSE);
3967 if (status != DB_SUCCESS) {
3968 cm_msg(MERROR, "show_custom_gif", "No \"Limits\" entry for Fill \"%s\"",
3969 key.name);
3970 continue;
3971 }
3972 for (i = 0; i < size / (int) sizeof(double); i++)
3973 if (*((double *) data + i) > fvalue)
3974 break;
3975 if (i > 0)
3976 i--;
3977
3978 db_find_key(hDB, hkey, "Fillcolors", &hkeyval);
3979 if (!hkeyval) {
3980 cm_msg(MERROR, "show_custom_gif", "No \"Fillcolors\" entry for Fill \"%s\"",
3981 key.name);
3982 continue;
3983 }
3984
3985 size = sizeof(data);
3986 strcpy(data, "FFFFFF");
3987 status = db_get_data_index(hDB, hkeyval, data, &size, i, TID_STRING);
3988 if (status == DB_SUCCESS) {
3989 sscanf(data, "%02x%02x%02x", &r, &g, &b);
3990 fgcol = gdImageColorAllocate(im, r, g, b);
3991 if (fgcol == -1)
3992 fgcol = gdImageColorClosest(im, r, g, b);
3993 gdImageFill(im, x, y, fgcol);
3994 }
3995 }
3996 }
3997
3998 /* generate GIF */
3999 gdImageInterlace(im, 1);
4000 gdImageGif(im, &gb);
4001 gdImageDestroy(im);
4002 length = gb.size;
4003
4004 rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
4005 rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
4006
4007 rr->rsprintf("Content-Type: image/gif\r\n");
4008 rr->rsprintf("Content-Length: %d\r\n", length);
4009 rr->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
4010 rr->rsprintf("Expires: Fri, 01-Jan-1983 00:00:00 GMT\r\n\r\n");
4011
4012 rr->rmemcpy(gb.data, length);
4013}
4014
4015
4016
4017/*------------------------------------------------------------------*/
4018
4020{
4021 static RPC_LIST rpc_list[] = {
4022 { 9999, "mhttpd_jrpc_rev0", {
4023 {TID_STRING, RPC_IN}, // arg0
4024 {TID_STRING, RPC_IN}, // arg1
4025 {TID_STRING, RPC_IN}, // arg2
4026 {TID_STRING, RPC_IN}, // arg3
4027 {TID_STRING, RPC_IN}, // arg4
4028 {TID_STRING, RPC_IN}, // arg5
4029 {TID_STRING, RPC_IN}, // arg6
4030 {TID_STRING, RPC_IN}, // arg7
4031 {TID_STRING, RPC_IN}, // arg8
4032 {TID_STRING, RPC_IN}, // arg9
4033 {0}} },
4034 { 0 }
4035 };
4036
4037 int count = 0, substring = 0, rpc;
4038
4039 const char *xname = p->getparam("name");
4040 const char *srpc = p->getparam("rpc");
4041
4042 if (!srpc || !xname) {
4044 r->rsprintf("<INVALID_ARGUMENTS>");
4045 return;
4046 }
4047
4048 char sname[256];
4049 mstrlcpy(sname, xname, sizeof(sname));
4050
4051 if (sname[strlen(sname)-1]=='*') {
4052 sname[strlen(sname)-1] = 0;
4053 substring = 1;
4054 }
4055
4056 rpc = atoi(srpc);
4057
4058 if (rpc<RPC_MIN_ID || rpc>RPC_MAX_ID) {
4060 r->rsprintf("<INVALID_RPC_ID>");
4061 return;
4062 }
4063
4064 rpc_list[0].id = rpc;
4066
4068 r->rsprintf("calling rpc %d | ", rpc);
4069
4070 if (1) {
4071 int status, i;
4072 char str[256];
4073 HNDLE hDB, hrootkey, hsubkey, hkey;
4074
4076
4077 /* find client which exports FCNA function */
4078 status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
4079 if (status == DB_SUCCESS) {
4080 for (i=0; ; i++) {
4081 status = db_enum_key(hDB, hrootkey, i, &hsubkey);
4083 break;
4084
4085 sprintf(str, "RPC/%d", rpc);
4086 status = db_find_key(hDB, hsubkey, str, &hkey);
4087 if (status == DB_SUCCESS) {
4088 char client_name[NAME_LENGTH];
4089 HNDLE hconn;
4090 int size;
4091
4092 size = sizeof(client_name);
4093 status = db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, FALSE);
4094 if (status != DB_SUCCESS)
4095 continue;
4096
4097 if (strlen(sname) > 0) {
4098 if (substring) {
4099 if (strstr(client_name, sname) != client_name)
4100 continue;
4101 } else {
4102 if (strcmp(sname, client_name) != 0)
4103 continue;
4104 }
4105 }
4106
4107 count++;
4108
4109 r->rsprintf("client %s", client_name);
4110
4111 status = cm_connect_client(client_name, &hconn);
4112 r->rsprintf(" %d", status);
4113
4114 if (status == RPC_SUCCESS) {
4115 status = rpc_client_call(hconn, rpc,
4116 p->getparam("arg0"),
4117 p->getparam("arg1"),
4118 p->getparam("arg2"),
4119 p->getparam("arg3"),
4120 p->getparam("arg4"),
4121 p->getparam("arg5"),
4122 p->getparam("arg6"),
4123 p->getparam("arg7"),
4124 p->getparam("arg8"),
4125 p->getparam("arg9")
4126 );
4127 r->rsprintf(" %d", status);
4128
4129 //status = cm_disconnect_client(hconn, FALSE);
4130 r->rsprintf(" %d", status);
4131 }
4132
4133 r->rsprintf(" | ");
4134 }
4135 }
4136 }
4137 }
4138
4139 r->rsprintf("rpc %d, called %d clients\n", rpc, count);
4140}
4141
4142/*------------------------------------------------------------------*/
4143
4145{
4146 static RPC_LIST rpc_list[] = {
4147 { 9998, "mhttpd_jrpc_rev1", {
4148 {TID_STRING, RPC_OUT}, // return string
4149 {TID_INT, RPC_IN}, // return string max length
4150 {TID_STRING, RPC_IN}, // arg0
4151 {TID_STRING, RPC_IN}, // arg1
4152 {TID_STRING, RPC_IN}, // arg2
4153 {TID_STRING, RPC_IN}, // arg3
4154 {TID_STRING, RPC_IN}, // arg4
4155 {TID_STRING, RPC_IN}, // arg5
4156 {TID_STRING, RPC_IN}, // arg6
4157 {TID_STRING, RPC_IN}, // arg7
4158 {TID_STRING, RPC_IN}, // arg8
4159 {TID_STRING, RPC_IN}, // arg9
4160 {0}} },
4161 { 0 }
4162 };
4163
4164 int status, substring = 0, rpc;
4165
4166 const char *xname = p->getparam("name");
4167 const char *srpc = p->getparam("rpc");
4168
4169 if (!srpc || !xname) {
4171 r->rsprintf("<INVALID_ARGUMENTS>");
4172 return;
4173 }
4174
4175 char sname[256];
4176 mstrlcpy(sname, xname, sizeof(sname));
4177
4178 if (sname[strlen(sname)-1]=='*') {
4179 sname[strlen(sname)-1] = 0;
4180 substring = 1;
4181 }
4182
4183 rpc = atoi(srpc);
4184
4185 if (rpc<RPC_MIN_ID || rpc>RPC_MAX_ID) {
4187 r->rsprintf("<INVALID_RPC_ID>");
4188 return;
4189 }
4190
4191 rpc_list[0].id = rpc;
4193
4194 //printf("cm_register_functions() for format \'%s\' status %d\n", sformat, status);
4195
4197
4198 std::string reply_header;
4199 std::string reply_body;
4200
4201 //r->rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", HTTP_ENCODING);
4202 //r->rsprintf("<!-- created by MHTTPD on (timestamp) -->\n");
4203 //r->rsprintf("<jrpc_rev1>\n");
4204 //r->rsprintf(" <rpc>%d</rpc>\n", rpc);
4205
4206 if (1) {
4207 HNDLE hDB, hrootkey, hsubkey, hkey;
4208
4210
4211 int buf_length = 1024;
4212
4213 int max_reply_length = atoi(p->getparam("max_reply_length"));
4214 if (max_reply_length > buf_length)
4215 buf_length = max_reply_length;
4216
4217 char* buf = (char*)malloc(buf_length);
4218
4219 assert(buf != NULL);
4220
4221 /* find client which exports our RPC function */
4222 status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
4223 if (status == DB_SUCCESS) {
4224 for (int i=0; ; i++) {
4225 status = db_enum_key(hDB, hrootkey, i, &hsubkey);
4227 break;
4228
4229 char str[256];
4230 sprintf(str, "RPC/%d", rpc);
4231 status = db_find_key(hDB, hsubkey, str, &hkey);
4232 if (status == DB_SUCCESS) {
4233 char client_name[NAME_LENGTH];
4234 HNDLE hconn;
4235 int size;
4236
4237 size = sizeof(client_name);
4238 status = db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, FALSE);
4239 if (status != DB_SUCCESS)
4240 continue;
4241
4242 if (strlen(sname) > 0) {
4243 if (substring) {
4244 if (strstr(client_name, sname) != client_name)
4245 continue;
4246 } else {
4247 if (strcmp(sname, client_name) != 0)
4248 continue;
4249 }
4250 }
4251
4252 //r->rsprintf(" <client>\n");
4253 //r->rsprintf(" <name>%s</name>\n", client_name);
4254
4255 int connect_status = -1;
4256 int call_status = -1;
4257 int call_length = 0;
4258 int disconnect_status = -1;
4259
4260 connect_status = cm_connect_client(client_name, &hconn);
4261
4262 //r->rsprintf(" <connect_status>%d</connect_status>\n", status);
4263
4264 if (connect_status == RPC_SUCCESS) {
4265 buf[0] = 0;
4266
4267 call_status = rpc_client_call(hconn, rpc,
4268 buf,
4269 buf_length,
4270 p->getparam("arg0"),
4271 p->getparam("arg1"),
4272 p->getparam("arg2"),
4273 p->getparam("arg3"),
4274 p->getparam("arg4"),
4275 p->getparam("arg5"),
4276 p->getparam("arg6"),
4277 p->getparam("arg7"),
4278 p->getparam("arg8"),
4279 p->getparam("arg9")
4280 );
4281
4282 //r->rsprintf(" <rpc_status>%d</rpc_status>\n", status);
4284 //r->rsputs("<data>");
4285 //r->rsputs(buf);
4286 //r->rsputs("</data>\n");
4287
4288 if (call_status == RPC_SUCCESS) {
4289 call_length = strlen(buf);
4290 reply_body += buf;
4291 }
4292
4293 //disconnect_status = cm_disconnect_client(hconn, FALSE);
4294 //r->rsprintf(" <disconnect_status>%d</disconnect_status>\n", status);
4295 }
4296
4297 //r->rsprintf(" </client>\n");
4298
4299 if (reply_header.length() > 0)
4300 reply_header += " | ";
4301
4302 char tmp[256];
4303 sprintf(tmp, "%s %d %d %d %d", client_name, connect_status, call_status, disconnect_status, call_length);
4304 reply_header += tmp;
4305 }
4306 }
4307 }
4308
4309 free(buf);
4310 }
4311
4312 //r->rsprintf(" <called_clients>%d</called_clients>\n", count);
4313 //r->rsprintf("</jrpc_rev1>\n");
4314
4315 if (reply_header.length() > 0) {
4316 r->rsputs(reply_header.c_str());
4317 r->rsputs(" || ");
4318 r->rsputs(reply_body.c_str());
4319 r->rsputs("\n");
4320 }
4321}
4322
4323/*------------------------------------------------------------------*/
4324
4326{
4327 int status;
4328
4329 const char *name = p->getparam("name");
4330 const char *cmd = p->getparam("rcmd");
4331 const char *args = p->getparam("rarg");
4332
4333 if (!name || !cmd || !args) {
4335 r->rsprintf("<INVALID_ARGUMENTS>");
4336 return;
4337 }
4338
4340
4341 int buf_length = 1024;
4342
4343 int max_reply_length = atoi(p->getparam("max_reply_length"));
4344 if (max_reply_length > buf_length)
4345 buf_length = max_reply_length;
4346
4347 char* buf = (char*)malloc(buf_length);
4348 assert(buf != NULL);
4349
4350 buf[0] = 0;
4351
4352 HNDLE hconn;
4353
4354 status = cm_connect_client(name, &hconn);
4355
4356 if (status != RPC_SUCCESS) {
4357 r->rsprintf("<RPC_CONNECT_ERROR>%d</RPC_CONNECT_ERROR>", status);
4358 free(buf);
4359 return;
4360 }
4361
4362 status = rpc_client_call(hconn, RPC_JRPC, cmd, args, buf, buf_length);
4363
4364 if (status != RPC_SUCCESS) {
4365 r->rsprintf("<RPC_CALL_ERROR>%d</RPC_CALL_ERROR>", status);
4366 free(buf);
4367 return;
4368 }
4369
4370 r->rsprintf("%s", buf);
4371
4372 //status = cm_disconnect_client(hconn, FALSE);
4373
4374 free(buf);
4375}
4376
4377/*------------------------------------------------------------------*/
4378
4379void output_key(Param* p, Return* r, HNDLE hkey, int index, const char *format)
4380{
4381 int size, i;
4382 HNDLE hDB, hsubkey;
4383 KEY key;
4384 char data[TEXT_SIZE];
4385
4387
4388 db_get_key(hDB, hkey, &key);
4389 if (key.type == TID_KEY) {
4390 for (i=0 ; ; i++) {
4391 db_enum_key(hDB, hkey, i, &hsubkey);
4392 if (!hsubkey)
4393 break;
4394 output_key(p, r, hsubkey, -1, format);
4395 }
4396 } else {
4397 if (key.item_size <= (int)sizeof(data)) {
4398 size = sizeof(data);
4399 db_get_data(hDB, hkey, data, &size, key.type);
4400 if (index == -1) {
4401 for (i=0 ; i<key.num_values ; i++) {
4402 if (p->isparam("name") && atoi(p->getparam("name")) == 1) {
4403 if (key.num_values == 1)
4404 r->rsprintf("%s:", key.name);
4405 else
4406 r->rsprintf("%s[%d]:", key.name, i);
4407 }
4408 std::string data_str;
4409 if (format && format[0])
4410 data_str = db_sprintff(format, data, key.item_size, i, key.type);
4411 else
4412 data_str = db_sprintf(data, key.item_size, i, key.type);
4413 r->rsputs(data_str.c_str());
4414 if (i<key.num_values-1)
4415 r->rsputs("\n");
4416 }
4417 } else {
4418 if (p->isparam("name") && atoi(p->getparam("name")) == 1)
4419 r->rsprintf("%s[%d]:", key.name, index);
4420 if (index >= key.num_values)
4421 r->rsputs("<DB_OUT_OF_RANGE>");
4422 else {
4423 std::string data_str;
4424 if (p->isparam("format"))
4425 data_str = db_sprintff(p->getparam("format"), data, key.item_size, index, key.type);
4426 else
4427 data_str = db_sprintf(data, key.item_size, index, key.type);
4428 r->rsputs(data_str.c_str());
4429 }
4430 }
4431 r->rsputs("\n");
4432 }
4433 }
4434}
4435
4436/*------------------------------------------------------------------*/
4437
4438bool starts_with(const std::string& s1, const char* s2)
4439{
4440 if (s1.length() < strlen(s2))
4441 return false;
4442 return (strncasecmp(s1.c_str(), s2, strlen(s2)) == 0);
4443}
4444
4445//static bool ends_with_char(const std::string& s, char c)
4446//{
4447// if (s.length() < 1)
4448// return false;
4449// return s[s.length()-1] == c;
4450//}
4451
4452/*------------------------------------------------------------------*/
4453
4454void javascript_commands(Param* p, Return* r, const char *cookie_cpwd)
4455{
4456 int status;
4457 int size, i, n, index, type;
4458 unsigned int t;
4459 char str[TEXT_SIZE], format[256], facility[256], user[256];
4460 HNDLE hDB, hkey;
4461 KEY key;
4462 char data[TEXT_SIZE];
4463
4465
4466 // process common parameters
4467
4468 const int ENCODING_NONE = 0;
4469 const int ENCODING_ODB = 1;
4470 const int ENCODING_XML = 2;
4471 const int ENCODING_JSON = 3;
4472
4473 std::string cmd_parameter;
4474 std::string encoding_parameter;
4475 int encoding = ENCODING_NONE; // default encoding
4476 bool jsonp = false; // default is no JSONP wrapper
4477 std::string jsonp_callback; // default is no JSONP
4478 bool single = false; // single encoding
4479 bool multiple = false; // multiple encoding
4480 std::vector<std::string> odb; // multiple odb parameters
4481 //HNDLE hodb; // ODB handle for single odb parameter
4482 //std::vector<HNDLE> hodbm; // ODB handle for multiple odb parameter
4483
4484 if (p->isparam("cmd")) {
4485 cmd_parameter = p->getparam("cmd");
4486 }
4487
4488 if (p->isparam("encoding")) {
4489 encoding_parameter = p->getparam("encoding");
4490 }
4491
4492 if (encoding_parameter.length() > 0) {
4493 if (starts_with(encoding_parameter, "odb"))
4494 encoding = ENCODING_ODB;
4495 else if (starts_with(encoding_parameter, "xml"))
4496 encoding = ENCODING_XML;
4497 else if (starts_with(encoding_parameter, "json"))
4498 encoding = ENCODING_JSON;
4499 }
4500
4501 if (encoding == ENCODING_JSON) {
4502 if (p->isparam("callback")) {
4503 jsonp = true;
4504 jsonp_callback = p->getparam("callback");
4505 }
4506 }
4507
4508 if (p->isparam("odb")) {
4509 single = true;
4510 odb.push_back(p->getparam("odb"));
4511 }
4512
4513 if (p->isparam("odb0")) {
4514 multiple = true;
4515 for (int i=0 ; ; i++) {
4516 char ppath[256];
4517 sprintf(ppath, "odb%d", i);
4518 if (!p->isparam(ppath))
4519 break;
4520 odb.push_back(p->getparam(ppath));
4521 }
4522 }
4523
4524 if (/* DISABLES CODE */ (0)) {
4525 printf("command [%s], encoding %d [%s], jsonp %d, single %d, multiple %d, odb array size %d\n", cmd_parameter.c_str(), encoding, encoding_parameter.c_str(), jsonp, single, multiple, (int)odb.size());
4526 }
4527
4528 /* process "jset" command */
4529 if (equal_ustring(p->getparam("cmd"), "jset")) {
4530
4531 if (*p->getparam("pnam")) {
4532 std::string ppath;
4533 ppath += "/Custom/Pwd/";
4534 ppath += p->getparam("pnam");
4535 str[0] = 0;
4536 db_get_value(hDB, 0, ppath.c_str(), str, &size, TID_STRING, TRUE);
4537 if (!equal_ustring(cookie_cpwd, str)) {
4539 r->rsprintf("Invalid password!");
4540 return;
4541 }
4542 }
4543 mstrlcpy(str, p->getparam("odb"), sizeof(str));
4544 if (strchr(str, '[')) {
4545 if (*(strchr(str, '[')+1) == '*')
4546 index = -1;
4547 else
4548 index = atoi(strchr(str, '[')+1);
4549 *strchr(str, '[') = 0;
4550 } else
4551 index = 0;
4552
4553 if (db_find_key(hDB, 0, str, &hkey) == DB_SUCCESS && p->isparam("value")) {
4554 db_get_key(hDB, hkey, &key);
4555 memset(data, 0, sizeof(data));
4556 if (key.item_size <= (int)sizeof(data)) {
4557 if (index == -1) {
4558 const char* ptr = p->getparam("value");
4559 for (i=0 ; ptr != NULL ; i++) {
4560 size = sizeof(data);
4561 db_sscanf(ptr, data, &size, 0, key.type);
4562 if (strchr(data, ','))
4563 *strchr(data, ',') = 0;
4565 ptr = strchr(ptr, ',');
4566 if (ptr != NULL)
4567 ptr++;
4568 }
4569 } else {
4570 size = sizeof(data);
4571 db_sscanf(p->getparam("value"), data, &size, 0, key.type);
4572
4573 /* extend data size for single string if necessary */
4574 if ((key.type == TID_STRING || key.type == TID_LINK)
4575 && (int) strlen(data) + 1 > key.item_size && key.num_values == 1) {
4576 key.item_size = strlen(data) + 1;
4577 db_set_data(hDB, hkey, data, key.item_size, 1, key.type);
4578 } else
4580 }
4581 }
4582 } else {
4583 if (p->isparam("value") && p->isparam("type") && p->isparam("len")) {
4584 int type = atoi(p->getparam("type"));
4585 if (type == 0) {
4587 r->rsprintf("Invalid type %d!", type);
4588 return;
4589 }
4590 db_create_key(hDB, 0, str, type);
4591 db_find_key(hDB, 0, str, &hkey);
4592 if (!hkey) {
4594 r->rsprintf("Cannot create \'%s\' type %d", str, type);
4595 return;
4596 }
4597 db_get_key(hDB, hkey, &key);
4598 memset(data, 0, sizeof(data));
4599 size = sizeof(data);
4600 db_sscanf(p->getparam("value"), data, &size, 0, key.type);
4601 if (key.type == TID_STRING)
4602 db_set_data(hDB, hkey, data, atoi(p->getparam("len")), 1, TID_STRING);
4603 else {
4604 for (i=0 ; i<atoi(p->getparam("len")) ; i++)
4606 }
4607 }
4608 }
4609
4611 r->rsprintf("OK");
4612 return;
4613 }
4614
4615 /* process "jget" command */
4616 if (equal_ustring(p->getparam("cmd"), "jget")) {
4617
4618 if (p->isparam("odb")) {
4619 mstrlcpy(str, p->getparam("odb"), sizeof(str));
4620 if (strchr(str, '[')) {
4621 if (*(strchr(str, '[')+1) == '*')
4622 index = -1;
4623 else
4624 index = atoi(strchr(str, '[')+1);
4625 *strchr(str, '[') = 0;
4626 } else
4627 index = 0;
4628
4630
4631 status = db_find_key(hDB, 0, str, &hkey);
4632
4633 if (status == DB_SUCCESS)
4634 output_key(p, r, hkey, index, p->getparam("format"));
4635 else
4636 r->rsputs("<DB_NO_KEY>");
4637 }
4638
4639 if (p->isparam("odb0")) {
4641 for (i=0 ; ; i++) {
4642 char ppath[256];
4643 sprintf(ppath, "odb%d", i);
4644 sprintf(format, "format%d", i);
4645 if (p->isparam(ppath)) {
4646 mstrlcpy(str, p->getparam(ppath), sizeof(str));
4647 if (strchr(str, '[')) {
4648 if (*(strchr(str, '[')+1) == '*')
4649 index = -1;
4650 else
4651 index = atoi(strchr(str, '[')+1);
4652 *strchr(str, '[') = 0;
4653 } else
4654 index = 0;
4655 if (i > 0)
4656 r->rsputs("$#----#$\n");
4657 if (db_find_key(hDB, 0, str, &hkey) == DB_SUCCESS)
4658 output_key(p, r, hkey, index, p->getparam(format));
4659 else
4660 r->rsputs("<DB_NO_KEY>");
4661
4662 } else
4663 break;
4664 }
4665 }
4666
4667 return;
4668 }
4669
4670 /* process "jcopy" command */
4671 if (equal_ustring(p->getparam("cmd"), "jcopy")) {
4672
4673 bool fmt_odb = false;
4674 bool fmt_xml = false;
4675 bool fmt_json = true;
4676 bool fmt_jsonp = false;
4677 int follow_links = 1;
4678 int save_keys = 1;
4679 int recurse = 1;
4680 const char* fmt = NULL;
4681 const char* jsonp_callback = "callback";
4682
4683 if (p->isparam("encoding")) {
4684 fmt = p->getparam("encoding");
4685 } else if (p->isparam("format")) {
4686 fmt = p->getparam("format");
4687 }
4688
4689 if (fmt) {
4690 fmt_odb = (equal_ustring(fmt, "odb") > 0);
4691 fmt_xml = (equal_ustring(fmt, "xml") > 0);
4692 fmt_json = (strstr(fmt, "json") != NULL);
4693
4694 if (fmt_odb)
4695 fmt_xml = fmt_json = false;
4696 if (fmt_xml)
4697 fmt_odb = fmt_json = false;
4698 if (fmt_json)
4699 fmt_odb = fmt_xml = false;
4700
4701 if (fmt_json)
4702 fmt_jsonp = (strstr(fmt, "-p") != NULL);
4703 if (fmt_jsonp && p->isparam("callback"))
4704 jsonp_callback = p->getparam("callback");
4705 if (fmt_json && strstr(fmt, "-nofollowlinks"))
4706 follow_links = 0;
4707 if (fmt_json && strstr(fmt, "-nokeys"))
4708 save_keys = 2;
4709 if (fmt_json && strstr(fmt, "-nolastwritten"))
4710 save_keys = 0;
4711 if (fmt_json && strstr(fmt, "-norecurse"))
4712 recurse = 0;
4713 }
4714
4715 if (p->isparam("odb")) {
4716 mstrlcpy(str, p->getparam("odb"), sizeof(str));
4717
4719
4720 if (fmt_json)
4721 status = db_find_link(hDB, 0, str, &hkey);
4722 else
4723 status = db_find_key(hDB, 0, str, &hkey);
4724 if (status == DB_SUCCESS) {
4725
4726 if (fmt_jsonp) {
4727 r->rsputs(jsonp_callback);
4728 r->rsputs("(");
4729 }
4730
4731 int end = 0;
4732 int bufsize = WEB_BUFFER_SIZE;
4733 char* buf = (char *)malloc(bufsize);
4734
4735 if (fmt_xml)
4736 db_copy_xml(hDB, hkey, buf, &bufsize, true);
4737 else if (fmt_json)
4738 db_copy_json_obsolete(hDB, hkey, &buf, &bufsize, &end, save_keys, follow_links, recurse);
4739 else
4740 db_copy(hDB, hkey, buf, &bufsize, (char *)"");
4741
4742 r->rsputs(buf);
4743 free(buf);
4744
4745 if (fmt_jsonp) {
4746 r->rsputs(");\n");
4747 }
4748 } else
4749 r->rsputs("<DB_NO_KEY>");
4750 }
4751
4752 if (p->isparam("odb0")) {
4754 if (fmt_jsonp) {
4755 r->rsputs(jsonp_callback);
4756 r->rsputs("(");
4757 }
4758 if (fmt_xml) {
4759 r->rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", HTTP_ENCODING);
4760 r->rsputs("<jcopy>\n");
4761 r->rsputs("<data>\n");
4762 } else if (fmt_json)
4763 r->rsputs("[\n");
4764 else
4765 r->rsputs("");
4766 for (int i=0 ; ; i++) {
4767 char ppath[256];
4768 sprintf(ppath, "odb%d", i);
4769 if (!p->isparam(ppath))
4770 break;
4771 mstrlcpy(str, p->getparam(ppath), sizeof(str));
4772
4773 if (i > 0) {
4774 if (fmt_xml)
4775 r->rsputs("</data>\n<data>\n");
4776 else if (fmt_json)
4777 r->rsputs(",\n");
4778 else
4779 r->rsputs("$#----#$\n");
4780 }
4781
4782 if (fmt_json)
4783 status = db_find_link(hDB, 0, str, &hkey);
4784 else
4785 status = db_find_key(hDB, 0, str, &hkey);
4786 if (status != DB_SUCCESS) {
4787 if (fmt_xml)
4788 r->rsputs("<DB_NO_KEY/>\n");
4789 else if (fmt_json) {
4790 char tmp[256];
4791 sprintf(tmp, "{ \"/error\" : %d }\n", status);
4792 r->rsputs(tmp);
4793 } else
4794 r->rsputs("<DB_NO_KEY>\n");
4795 continue;
4796 }
4797
4798 int end = 0;
4799 int bufsize = WEB_BUFFER_SIZE;
4800 char* buf = (char *)malloc(bufsize);
4801
4802 if (fmt_xml) {
4803 db_copy_xml(hDB, hkey, buf, &bufsize, true);
4804 const char* s = strstr(buf, "-->");
4805 if (s)
4806 s+=4;
4807 else
4808 s = buf;
4809 r->rsputs(s);
4810 } else if (fmt_json) {
4811 db_copy_json_obsolete(hDB, hkey, &buf, &bufsize, &end, save_keys, follow_links, recurse);
4812 r->rsputs(buf);
4813 } else {
4814 db_copy(hDB, hkey, buf, &bufsize, (char *)"");
4815 r->rsputs(buf);
4816 }
4817
4818 free(buf);
4819 }
4820
4821 if (fmt_xml)
4822 r->rsputs("</data>\n</jcopy>\n");
4823 else if (fmt_json)
4824 r->rsputs("]\n");
4825 else
4826 r->rsputs("");
4827
4828 if (fmt_jsonp) {
4829 r->rsputs(");\n");
4830 }
4831 }
4832 return;
4833 }
4834
4835 /* process "jkey" command */
4836 if (equal_ustring(p->getparam("cmd"), "jkey")) {
4837
4838 // test:
4839 // curl "http://localhost:8080?cmd=jkey&odb0=/runinfo/run+number&odb1=/nonexistant&odb2=/&encoding=json&callback=aaa"
4840
4842
4843 if (jsonp) {
4844 r->rsputs(jsonp_callback.c_str());
4845 r->rsputs("(");
4846 }
4847
4848 if (multiple) {
4849 switch (encoding) {
4850 default:
4851 break;
4852 case ENCODING_JSON:
4853 r->rsprintf("[ ");
4854 break;
4855 }
4856 }
4857
4858 for (unsigned i=0; i<odb.size(); i++) {
4859 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
4860 if (status == DB_SUCCESS)
4861 status = db_get_key(hDB, hkey, &key);
4862 switch (encoding) {
4863 default:
4864 if (multiple && i>0)
4865 r->rsputs("$#----#$\n");
4866 if (status == DB_SUCCESS) {
4867 r->rsprintf("%s\n", key.name);
4868 r->rsprintf("TID_%s\n", rpc_tid_name(key.type));
4869 r->rsprintf("%d\n", key.num_values);
4870 r->rsprintf("%d\n", key.item_size);
4871 r->rsprintf("%d\n", key.last_written);
4872 } else {
4873 r->rsputs("<DB_NO_KEY>\n");
4874 }
4875 break;
4876 case ENCODING_JSON:
4877 if (multiple && i>0)
4878 r->rsprintf(", ");
4879 if (status == DB_SUCCESS) {
4880 r->rsprintf("{ ");
4881 r->rsprintf("\"name\":\"%s\",", key.name);
4882 r->rsprintf("\"type\":%d,", key.type);
4883 r->rsprintf("\"type_name\":\"TID_%s\",", rpc_tid_name(key.type));
4884 r->rsprintf("\"num_values\":%d,", key.num_values);
4885 r->rsprintf("\"item_size\":%d,", key.item_size);
4886 r->rsprintf("\"last_written\":%d", key.last_written);
4887 r->rsprintf(" }");
4888 } else {
4889 r->rsprintf("{ \"/error\":%d }", status);
4890 }
4891 break;
4892 }
4893 }
4894
4895 if (multiple) {
4896 switch (encoding) {
4897 default:
4898 break;
4899 case ENCODING_JSON:
4900 r->rsprintf(" ]");
4901 break;
4902 }
4903 }
4904
4905 if (jsonp) {
4906 r->rsputs(");\n");
4907 }
4908
4909 return;
4910 }
4911
4912 /* process "jcreate" command */
4913 if (equal_ustring(p->getparam("cmd"), "jcreate")) {
4914
4915 // test:
4916 // curl "http://localhost:8080?cmd=jcreate&odb0=/test/foo&type0=7&odb1=/nonexistant&type1=100&odb2=/test/bar&type2=12&encoding=json&callback=aaa"
4917 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo&type=7"
4918 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo70&type=7&arraylen=10"
4919 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo12s&type=12&strlen=32"
4920 // curl "http://localhost:8080?cmd=jcreate&odb=/test/foo12s5&type=12&strlen=32&arraylen=5"
4921 // curl "http://localhost:8080?cmd=jcreate&odb0=/test/foo12s5x&type0=12&strlen0=32&arraylen0=5"
4922
4923
4925
4926 if (jsonp) {
4927 r->rsputs(jsonp_callback.c_str());
4928 r->rsputs("(");
4929 }
4930
4931 if (multiple) {
4932 switch (encoding) {
4933 default:
4934 case ENCODING_JSON:
4935 r->rsprintf("[ ");
4936 break;
4937 }
4938 }
4939
4940 for (unsigned i=0; i<odb.size(); i++) {
4941 HNDLE hkey = 0;
4942 int type = 0;
4943 int arraylength = 0;
4944 int strlength = 0;
4945
4946 if (single) {
4947 type = atoi(p->getparam("type"));
4948 arraylength = atoi(p->getparam("arraylen"));
4949 strlength = atoi(p->getparam("strlen"));
4950 }
4951 else if (multiple) {
4952 char buf[256];
4953 sprintf(buf, "type%d", i);
4954 type = atoi(p->getparam(buf));
4955 sprintf(buf, "arraylen%d", i);
4956 arraylength = atoi(p->getparam(buf));
4957 sprintf(buf, "strlen%d", i);
4958 strlength = atoi(p->getparam(buf));
4959 }
4960
4961 status = db_create_key(hDB, 0, odb[i].c_str(), type);
4962
4963 if (status == DB_SUCCESS) {
4964 status = db_find_link(hDB, 0, odb[i].c_str(), &hkey);
4965 }
4966
4967 if (status == DB_SUCCESS && hkey && type == TID_STRING && strlength > 0) {
4968 char* s = (char*)calloc(strlength, 1); // initialized to zero
4969 status = db_set_data(hDB, hkey, s, strlength, 1, TID_STRING);
4970 free(s);
4971 }
4972
4973 if (status == DB_SUCCESS && hkey && arraylength > 1) {
4974 status = db_set_num_values(hDB, hkey, arraylength);
4975 }
4976
4977 switch (encoding) {
4978 default:
4979 case ENCODING_JSON:
4980 if (multiple && i>0)
4981 r->rsprintf(", ");
4982 r->rsprintf("%d", status);
4983 break;
4984 }
4985 }
4986
4987 if (multiple) {
4988 switch (encoding) {
4989 default:
4990 case ENCODING_JSON:
4991 r->rsprintf(" ]");
4992 break;
4993 }
4994 }
4995
4996 if (jsonp) {
4997 r->rsputs(");\n");
4998 }
4999
5000 return;
5001 }
5002
5003 /* process "jresize" command */
5004 if (equal_ustring(p->getparam("cmd"), "jresize")) {
5005
5006 // test:
5007
5008 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo70&arraylen=5"
5009 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&arraylen=5"
5010 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&strlen=16"
5011 // curl "http://localhost:8080?cmd=jresize&odb=/test/foo12s5&strlen=30&arraylen=10"
5012
5014
5015 if (jsonp) {
5016 r->rsputs(jsonp_callback.c_str());
5017 r->rsputs("(");
5018 }
5019
5020 if (multiple) {
5021 switch (encoding) {
5022 default:
5023 case ENCODING_JSON:
5024 r->rsprintf("[ ");
5025 break;
5026 }
5027 }
5028
5029 for (unsigned i=0; i<odb.size(); i++) {
5030 HNDLE hkey;
5031 KEY key;
5032 int arraylength = 0;
5033 int strlength = 0;
5034
5035 if (single) {
5036 arraylength = atoi(p->getparam("arraylen"));
5037 strlength = atoi(p->getparam("strlen"));
5038 }
5039 else if (multiple) {
5040 char buf[256];
5041 sprintf(buf, "arraylen%d", i);
5042 arraylength = atoi(p->getparam(buf));
5043 sprintf(buf, "strlen%d", i);
5044 strlength = atoi(p->getparam(buf));
5045 }
5046
5047 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
5048
5049 if (status == DB_SUCCESS && hkey) {
5050 status = db_get_key(hDB, hkey, &key);
5051 }
5052
5053 if (status == DB_SUCCESS && hkey && key.type == TID_STRING && strlength > 0) {
5054 int oldsize = key.item_size * key.num_values;
5055 char* olddata = (char*)malloc(oldsize);
5056 int size = oldsize;
5057 status = db_get_data(hDB, hkey, olddata, &size, TID_STRING);
5058
5059 if (status == DB_SUCCESS) {
5060 int newsize = strlength * key.num_values;
5061 char* s = (char*)calloc(newsize, 1); // initialized to zero
5062 for (int k=0; k<key.num_values; k++) {
5063 mstrlcpy(s + strlength*k, olddata + key.item_size*k, strlength);
5064 }
5065
5066 status = db_set_data(hDB, hkey, s, newsize, key.num_values, TID_STRING);
5067 free(s);
5068 }
5069
5070 free(olddata);
5071 }
5072
5073 if (status == DB_SUCCESS && hkey && arraylength > 0) {
5074 status = db_set_num_values(hDB, hkey, arraylength);
5075 }
5076
5077 switch (encoding) {
5078 default:
5079 case ENCODING_JSON:
5080 if (multiple && i>0)
5081 r->rsprintf(", ");
5082 r->rsprintf("%d", status);
5083 break;
5084 }
5085 }
5086
5087 if (multiple) {
5088 switch (encoding) {
5089 default:
5090 case ENCODING_JSON:
5091 r->rsprintf(" ]");
5092 break;
5093 }
5094 }
5095
5096 if (jsonp) {
5097 r->rsputs(");\n");
5098 }
5099
5100 return;
5101 }
5102
5103 /* process "jrename" command */
5104 if (equal_ustring(p->getparam("cmd"), "jrename")) {
5105
5106 // test:
5107 // curl "http://localhost:8080?cmd=jrename&odb0=/test/foo&type0=7&odb1=/nonexistant&type1=100&odb2=/test/bar&type2=12&encoding=json&callback=aaa"
5108 // curl "http://localhost:8080?cmd=jrename&odb=/test/foo&name=foofoo"
5109
5111
5112 if (jsonp) {
5113 r->rsputs(jsonp_callback.c_str());
5114 r->rsputs("(");
5115 }
5116
5117 if (multiple) {
5118 switch (encoding) {
5119 default:
5120 case ENCODING_JSON:
5121 r->rsprintf("[ ");
5122 break;
5123 }
5124 }
5125
5126 for (unsigned i=0; i<odb.size(); i++) {
5127 const char* name = NULL;
5128 if (single)
5129 name = p->getparam("name");
5130 else if (multiple) {
5131 char buf[256];
5132 sprintf(buf, "name%d", i);
5133 name = p->getparam(buf);
5134 }
5135 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
5136 if (status == DB_SUCCESS) {
5137 status = db_rename_key(hDB, hkey, name);
5138 }
5139 switch (encoding) {
5140 default:
5141 case ENCODING_JSON:
5142 if (multiple && i>0)
5143 r->rsprintf(", ");
5144 r->rsprintf("%d", status);
5145 break;
5146 }
5147 }
5148
5149 if (multiple) {
5150 switch (encoding) {
5151 default:
5152 case ENCODING_JSON:
5153 r->rsprintf(" ]");
5154 break;
5155 }
5156 }
5157
5158 if (jsonp) {
5159 r->rsputs(");\n");
5160 }
5161
5162 return;
5163 }
5164
5165 /* process "jlink" command */
5166 if (equal_ustring(p->getparam("cmd"), "jlink")) {
5167
5168 // test:
5169 // curl "http://localhost:8080?cmd=jlink&odb=/test/link&dest=/test/foo"
5170 // curl "http://localhost:8080?cmd=jlink&odb0=/test/link0&dest0=/test/foo&odb1=/test/link1&dest1=/test/foo"
5171
5173
5174 if (jsonp) {
5175 r->rsputs(jsonp_callback.c_str());
5176 r->rsputs("(");
5177 }
5178
5179 if (multiple) {
5180 switch (encoding) {
5181 default:
5182 case ENCODING_JSON:
5183 r->rsprintf("[ ");
5184 break;
5185 }
5186 }
5187
5188 for (unsigned i=0; i<odb.size(); i++) {
5189 const char* dest = NULL;
5190 if (single)
5191 dest = p->getparam("dest");
5192 else if (multiple) {
5193 char buf[256];
5194 sprintf(buf, "dest%d", i);
5195 dest = p->getparam(buf);
5196 }
5197
5198 status = db_create_link(hDB, 0, odb[i].c_str(), dest);
5199
5200 switch (encoding) {
5201 default:
5202 case ENCODING_JSON:
5203 if (multiple && i>0)
5204 r->rsprintf(", ");
5205 r->rsprintf("%d", status);
5206 break;
5207 }
5208 }
5209
5210 if (multiple) {
5211 switch (encoding) {
5212 default:
5213 case ENCODING_JSON:
5214 r->rsprintf(" ]");
5215 break;
5216 }
5217 }
5218
5219 if (jsonp) {
5220 r->rsputs(");\n");
5221 }
5222
5223 return;
5224 }
5225
5226 /* process "jreorder" command */
5227 if (equal_ustring(p->getparam("cmd"), "jreorder")) {
5228
5229 // test:
5230 // curl "http://localhost:8080?cmd=jreorder&odb0=/test/foo&index0=0&odb1=/test/bar&index1=1"
5231 // curl "http://localhost:8080?cmd=jreorder&odb=/test/bar&index=0"
5232
5234
5235 if (jsonp) {
5236 r->rsputs(jsonp_callback.c_str());
5237 r->rsputs("(");
5238 }
5239
5240 if (multiple) {
5241 switch (encoding) {
5242 default:
5243 case ENCODING_JSON:
5244 r->rsprintf("[ ");
5245 break;
5246 }
5247 }
5248
5249 for (unsigned i=0; i<odb.size(); i++) {
5250 int index = 0;
5251 if (single)
5252 index = atoi(p->getparam("index"));
5253 else if (multiple) {
5254 char buf[256];
5255 sprintf(buf, "index%d", i);
5256 index = atoi(p->getparam(buf));
5257 }
5258
5259 status = db_find_key(hDB, 0, odb[i].c_str(), &hkey);
5260 if (status == DB_SUCCESS) {
5261 status = db_reorder_key(hDB, hkey, index);
5262 }
5263
5264 switch (encoding) {
5265 default:
5266 case ENCODING_JSON:
5267 if (multiple && i>0)
5268 r->rsprintf(", ");
5269 r->rsprintf("%d", status);
5270 break;
5271 }
5272 }
5273
5274 if (multiple) {
5275 switch (encoding) {
5276 default:
5277 case ENCODING_JSON:
5278 r->rsprintf(" ]");
5279 break;
5280 }
5281 }
5282
5283 if (jsonp) {
5284 r->rsputs(");\n");
5285 }
5286
5287 return;
5288 }
5289
5290 /* process "jdelete" command */
5291 if (equal_ustring(p->getparam("cmd"), "jdelete")) {
5292
5293 // test:
5294 // curl "http://localhost:8080?cmd=jdelete&odb0=/test/foo&odb1=/nonexistant&odb2=/test/bar&encoding=json&callback=aaa"
5295 // curl "http://localhost:8080?cmd=jdelete&odb=/test/foo"
5296
5298
5299 if (jsonp) {
5300 r->rsputs(jsonp_callback.c_str());
5301 r->rsputs("(");
5302 }
5303
5304 if (multiple) {
5305 switch (encoding) {
5306 default:
5307 case ENCODING_JSON:
5308 r->rsprintf("[ ");
5309 break;
5310 }
5311 }
5312
5313 for (unsigned i=0; i<odb.size(); i++) {
5314 status = db_delete(hDB, 0, odb[i].c_str());
5315 switch (encoding) {
5316 default:
5317 case ENCODING_JSON:
5318 if (multiple && i>0)
5319 r->rsprintf(", ");
5320 r->rsprintf("%d", status);
5321 break;
5322 }
5323 }
5324
5325 if (multiple) {
5326 switch (encoding) {
5327 default:
5328 case ENCODING_JSON:
5329 r->rsprintf(" ]");
5330 break;
5331 }
5332 }
5333
5334 if (jsonp) {
5335 r->rsputs(");\n");
5336 }
5337
5338 return;
5339 }
5340
5341 /* process "jmsg" command */
5342 if (equal_ustring(p->getparam("cmd"), "jmsg")) {
5343
5344 if (p->getparam("f") && *p->getparam("f"))
5345 mstrlcpy(facility, p->getparam("f"), sizeof(facility));
5346 else
5347 mstrlcpy(facility, "midas", sizeof(facility));
5348
5349 n = 1;
5350 if (p->getparam("n") && *p->getparam("n"))
5351 n = atoi(p->getparam("n"));
5352
5353 t = 0;
5354 if (p->getparam("t") && p->getparam("t"))
5355 t = atoi(p->getparam("t"));
5356
5358 char* messages = NULL;
5359 int num_messages = 0;
5360 cm_msg_retrieve2(facility, t, n, &messages, &num_messages);
5361 if (messages) {
5362 r->rsputs(messages);
5363 free(messages);
5364 }
5365 return;
5366 }
5367
5368 /* process "jgenmsg" command */
5369 if (equal_ustring(p->getparam("cmd"), "jgenmsg")) {
5370
5371 if (p->getparam("facility") && *p->getparam("facility"))
5372 mstrlcpy(facility, p->getparam("facility"), sizeof(facility));
5373 else
5374 mstrlcpy(facility, "midas", sizeof(facility));
5375
5376 if (p->getparam("user") && *p->getparam("user"))
5377 mstrlcpy(user, p->getparam("user"), sizeof(user));
5378 else
5379 mstrlcpy(user, "javascript_commands", sizeof(user));
5380
5381 if (p->getparam("type") && *p->getparam("type"))
5382 type = atoi(p->getparam("type"));
5383 else
5384 type = MT_INFO;
5385
5386 if (p->getparam("msg") && *p->getparam("msg")) {
5387 cm_msg1(type, __FILE__, __LINE__, facility, user, "%s", p->getparam("msg"));
5388 }
5389
5391 r->rsputs("Message successfully created\n");
5392 return;
5393 }
5394
5395 /* process "jalm" command */
5396 if (equal_ustring(p->getparam("cmd"), "jalm")) {
5397
5399 std::string alarms;
5400 al_get_alarms(&alarms);
5401 r->rsputs(alarms.c_str());
5402 return;
5403 }
5404
5405 /* process "jrpc" command */
5406 if (equal_ustring(p->getparam("cmd"), "jrpc_rev0")) {
5407 do_jrpc_rev0(p, r);
5408 return;
5409 }
5410
5411 /* process "jrpc" command */
5412 if (equal_ustring(p->getparam("cmd"), "jrpc_rev1")) {
5413 do_jrpc_rev1(p, r);
5414 return;
5415 }
5416
5417 /* process "jrpc" command */
5418 if (equal_ustring(p->getparam("cmd"), "jrpc")) {
5419 do_jrpc(p, r);
5420 return;
5421 }
5422}
5423
5424/*------------------------------------------------------------------*/
5425
5426void show_custom_page(Param* pp, Return* r, const char *cookie_cpwd)
5427{
5428 int size, n_var, index, edit;
5429 char keypath[256], type[32], *p, *ps;
5430 char pwd[256], tail[256];
5431 HNDLE hDB, hkey;
5432 KEY key;
5433 char data[TEXT_SIZE];
5434
5435 std::string path = pp->getparam("page");
5436
5437 if (path[0] == 0) {
5438 show_error_404(r, "show_custom_page: Invalid custom page: \"page\" parameter is empty");
5439 return;
5440 }
5441
5442 if (strstr(path.c_str(), "..")) {
5443 std::string str;
5444 str += "Invalid custom page name \'";
5445 str += path;
5446 str += "\' contains \'..\'";
5447 show_error_404(r, str.c_str());
5448 return;
5449 }
5450
5451 if (strstr(path.c_str(), ".gif")) {
5452 show_custom_gif(r, path.c_str());
5453 return;
5454 }
5455
5456 if (strchr(path.c_str(), '.')) {
5457 show_custom_file(r, path.c_str());
5458 return;
5459 }
5460
5462
5463 std::string xpath = std::string("/Custom/") + path;
5464 db_find_key(hDB, 0, xpath.c_str(), &hkey);
5465 if (!hkey) {
5466 xpath = std::string("/Custom/") + path + "&";
5467 db_find_key(hDB, 0, xpath.c_str(), &hkey);
5468 if (!hkey) {
5469 xpath = std::string("/Custom/") + path + "!";
5470 db_find_key(hDB, 0, xpath.c_str(), &hkey);
5471 }
5472 }
5473
5474 if (hkey) {
5475 char* ctext;
5476 int status;
5477
5478 status = db_get_key(hDB, hkey, &key);
5479 assert(status == DB_SUCCESS);
5480 size = key.total_size;
5481 ctext = (char*)malloc(size);
5482 status = db_get_data(hDB, hkey, ctext, &size, TID_STRING);
5483 if (status != DB_SUCCESS) {
5484 std::string errtext = msprintf("show_custom_page: Error: db_get_data() for \"%s\" status %d", xpath.c_str(), status);
5485 show_error_404(r, errtext.c_str());
5486 free(ctext);
5487 return;
5488 }
5489
5490 std::string content_type = "text/html";
5491
5492 /* check for link */
5493 if (std::string(ctext).substr(0, 5) == "?cmd=") {
5494 redirect(r, ctext);
5495 free(ctext);
5496 return;
5497 }
5498
5499 /* check if filename */
5500 if (strchr(ctext, '\n') == 0) {
5501 std::string full_filename = add_custom_path(ctext);
5502 int fh = open(full_filename.c_str(), O_RDONLY | O_BINARY);
5503 if (fh < 0) {
5504 std::string str = msprintf("show_custom_page: Cannot open file \"%s\", open() errno %d (%s)", full_filename.c_str(), errno, strerror(errno));
5505 show_error_404(r, str.c_str());
5506 free(ctext);
5507 return;
5508 }
5509 free(ctext);
5510 ctext = NULL;
5511 off_t off = lseek(fh, 0, SEEK_END);
5512 if (off < 0) {
5513 std::string str = msprintf("show_custom_page: Cannot open file \"%s\", lseek(SEEK_END) errno %d (%s)", full_filename.c_str(), errno, strerror(errno));
5514 show_error_404(r, str.c_str());
5515 free(ctext);
5516 close(fh);
5517 return;
5518 }
5519 size_t size = off;
5520 lseek(fh, 0, SEEK_SET);
5521 ctext = (char*)malloc(size+1);
5522 ssize_t rd = read(fh, ctext, size);
5523 if (rd > 0) {
5524 ctext[rd] = 0; // make sure string is zero-terminated
5525 size = rd;
5526 } else {
5527 ctext[0] = 0;
5528 size = 0;
5529 }
5530 close(fh);
5531
5532 content_type = get_content_type(full_filename.c_str());
5533 }
5534
5535 /* check for valid password */
5536 if (equal_ustring(pp->getparam("cmd"), "Edit")) {
5537 p = ps = ctext;
5538 n_var = 0;
5539 do {
5540 char format[256];
5541
5542 p = find_odb_tag(ps, keypath, format, &edit, type, pwd, tail);
5543 if (p == NULL)
5544 break;
5545 ps = strchr(p, '>') + 1;
5546
5547 if (pwd[0] && n_var == atoi(pp->getparam("index"))) {
5548 char str[256];
5549 size = NAME_LENGTH;
5550 mstrlcpy(str, path.c_str(), sizeof(str)); // FIXME: overflows "str"
5551 if (strlen(str)>0 && str[strlen(str)-1] == '&')
5552 str[strlen(str)-1] = 0;
5553 std::string ppath;
5554 ppath += "/Custom/Pwd/";
5555 if (pp->getparam("pnam") && *pp->getparam("pnam")) {
5556 ppath += pp->getparam("pnam");
5557 } else {
5558 ppath += str;
5559 }
5560 str[0] = 0;
5561 db_get_value(hDB, 0, ppath.c_str(), str, &size, TID_STRING, TRUE);
5562 if (!equal_ustring(cookie_cpwd, str)) {
5563 show_error_404(r, "show_custom_page: Invalid password!");
5564 free(ctext);
5565 return;
5566 } else
5567 break;
5568 }
5569
5570 n_var++;
5571 } while (p != NULL);
5572 }
5573
5574 /* process toggle command */
5575 if (equal_ustring(pp->getparam("cmd"), "Toggle")) {
5576
5577 if (pp->getparam("pnam") && *pp->getparam("pnam")) {
5578 std::string ppath;
5579 ppath += "/Custom/Pwd/";
5580 ppath += pp->getparam("pnam");
5581 std::string str;
5582 db_get_value_string(hDB, 0, ppath.c_str(), 0, &str, TRUE, 256);
5583 if (!equal_ustring(cookie_cpwd, str.c_str())) {
5584 show_error_404(r, "show_custom_page: Invalid password!");
5585 free(ctext);
5586 return;
5587 }
5588 }
5589 std::string podb = pp->getparam("odb");
5590 std::string::size_type pos = podb.find('[');
5591 if (pos != std::string::npos) {
5592 index = atoi(podb.substr(pos+1).c_str());
5593 podb.resize(pos);
5594 //printf("found index %d in [%s] [%s]\n", index, pp->getparam("odb"), podb.c_str());
5595 } else
5596 index = 0;
5597
5598 if (db_find_key(hDB, 0, podb.c_str(), &hkey)) {
5599 db_get_key(hDB, hkey, &key);
5600 memset(data, 0, sizeof(data));
5601 if (key.item_size <= (int)sizeof(data)) {
5602 size = sizeof(data);
5603 db_get_data_index(hDB, hkey, data, &size, index, key.type);
5604 std::string data_str = db_sprintf(data, size, 0, key.type);
5605 if (atoi(data_str.c_str()) == 0)
5606 db_sscanf("1", data, &size, 0, key.type);
5607 else
5608 db_sscanf("0", data, &size, 0, key.type);
5610 }
5611 }
5612
5613 /* redirect (so that 'reload' does not toggle again) */
5614 redirect(r, path.c_str());
5615 free(ctext);
5616 return;
5617 }
5618
5619 /* HTTP header */
5620 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
5621 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
5622 r->rsprintf("Content-Type: %s; charset=%s\r\n\r\n", content_type.c_str(), HTTP_ENCODING);
5623
5624 /* interprete text, replace <odb> tags with ODB values */
5625 p = ps = ctext;
5626 n_var = 0;
5627 do {
5628 char format[256];
5629 p = find_odb_tag(ps, keypath, format, &edit, type, pwd, tail);
5630 if (p != NULL)
5631 *p = 0;
5632 r->rsputs(ps);
5633
5634 if (p == NULL)
5635 break;
5636 ps = strchr(p + 1, '>') + 1;
5637
5638 show_odb_tag(pp, r, path.c_str(), keypath, format, n_var, edit, type, pwd, tail);
5639 n_var++;
5640
5641 } while (p != NULL);
5642
5643 if (equal_ustring(pp->getparam("cmd"), "Set") || pp->isparam("cbi")) {
5644 /* redirect (so that 'reload' does not change value) */
5645 r->reset();
5646 redirect(r, path.c_str());
5647 }
5648
5649 free(ctext);
5650 ctext = NULL;
5651 } else {
5652 std::string str = msprintf("Invalid custom page: Page \"%s\" not found in ODB", path.c_str());
5653 show_error_404(r, str.c_str());
5654 return;
5655 }
5656}
5657
5658/*------------------------------------------------------------------*/
5659
5660static void show_cnaf_page(Param* p, Return* rr)
5661{
5662 char str[256];
5663 int c, n, a, f, d, q, x, r, ia, id, w;
5664 int i, size, status;
5665 HNDLE hDB, hrootkey, hsubkey, hkey;
5666
5667 static char client_name[NAME_LENGTH];
5668 static HNDLE hconn = 0;
5669
5671
5672 /* find FCNA server if not specified */
5673 if (hconn == 0) {
5674 /* find client which exports FCNA function */
5675 status = db_find_key(hDB, 0, "System/Clients", &hrootkey);
5676 if (status == DB_SUCCESS) {
5677 for (i = 0;; i++) {
5678 status = db_enum_key(hDB, hrootkey, i, &hsubkey);
5680 break;
5681
5682 sprintf(str, "RPC/%d", RPC_CNAF16);
5683 status = db_find_key(hDB, hsubkey, str, &hkey);
5684 if (status == DB_SUCCESS) {
5685 size = sizeof(client_name);
5686 db_get_value(hDB, hsubkey, "Name", client_name, &size, TID_STRING, TRUE);
5687 break;
5688 }
5689 }
5690 }
5691
5692 if (client_name[0]) {
5693 status = cm_connect_client(client_name, &hconn);
5694 if (status != RPC_SUCCESS)
5695 hconn = 0;
5696 }
5697 }
5698
5699 /* header */
5700 rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
5701 rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
5702 rr->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
5703
5704 rr->rsprintf("<html><head>\n");
5705 rr->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
5706 rr->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
5707 rr->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
5708 rr->rsprintf("<title>MIDAS CAMAC interface</title></head>\n");
5709 rr->rsprintf("<body><form method=\"GET\" action=\"CNAF\">\n\n");
5710
5711 /* title row */
5712
5713 size = sizeof(str);
5714 str[0] = 0;
5715 db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
5716
5717 rr->rsprintf("<table border=3 cellpadding=1>\n");
5718 rr->rsprintf("<tr><th colspan=3>MIDAS experiment \"%s\"", str);
5719
5720 if (client_name[0] == 0)
5721 rr->rsprintf("<th colspan=3 class=\"redLight\">No CAMAC server running</tr>\n");
5722 else if (hconn == 0)
5723 rr->rsprintf("<th colspan=3 class=\"redLight\">Cannot connect to %s</tr>\n", client_name);
5724 else
5725 rr->rsprintf("<th colspan=3>CAMAC server: %s</tr>\n", client_name);
5726
5727 /* default values */
5728 c = n = 1;
5729 a = f = d = q = x = 0;
5730 r = 1;
5731 ia = id = w = 0;
5732
5733 /*---- menu buttons ----*/
5734
5735 rr->rsprintf("<tr><td colspan=3>\n");
5736 rr->rsprintf("<input type=submit name=cmd value=Execute>\n");
5737
5738 rr->rsprintf("<td colspan=3>\n");
5739 rr->rsprintf("<input type=submit name=cmd value=ODB>\n");
5740 rr->rsprintf("<input type=submit name=cmd value=Status>\n");
5741 rr->rsprintf("<input type=submit name=cmd value=Help>\n");
5742 rr->rsprintf("</tr>\n\n");
5743
5744 /* header */
5745 rr->rsprintf("<tr><th>N");
5746 rr->rsprintf("<th>A");
5747 rr->rsprintf("<th>F");
5748 rr->rsprintf("<th colspan=3>Data");
5749
5750 /* execute commands */
5751 size = sizeof(d);
5752
5753 const char* cmd = p->getparam("cmd");
5754 if (equal_ustring(cmd, "C cycle")) {
5755 rpc_client_call(hconn, RPC_CNAF16, CNAF_CRATE_CLEAR, 0, 0, 0, 0, 0, &d, &size, &x,
5756 &q);
5757
5758 rr->rsprintf("<tr><td colspan=6 class=\"greenLight\">C cycle executed sucessfully</tr>\n");
5759 } else if (equal_ustring(cmd, "Z cycle")) {
5760 rpc_client_call(hconn, RPC_CNAF16, CNAF_CRATE_ZINIT, 0, 0, 0, 0, 0, &d, &size, &x,
5761 &q);
5762
5763 rr->rsprintf("<tr><td colspan=6 class=\"greenLight\">Z cycle executed sucessfully</tr>\n");
5764 } else if (equal_ustring(cmd, "Clear inhibit")) {
5765 rpc_client_call(hconn, RPC_CNAF16, CNAF_INHIBIT_CLEAR, 0, 0, 0, 0, 0, &d, &size, &x,
5766 &q);
5767
5768 rr->rsprintf
5769 ("<tr><td colspan=6 class=\"greenLight\">Clear inhibit executed sucessfully</tr>\n");
5770 } else if (equal_ustring(cmd, "Set inhibit")) {
5771 rpc_client_call(hconn, RPC_CNAF16, CNAF_INHIBIT_SET, 0, 0, 0, 0, 0, &d, &size, &x,
5772 &q);
5773
5774 rr->rsprintf
5775 ("<tr><td colspan=6 class=\"greenLight\">Set inhibit executed sucessfully</tr>\n");
5776 } else if (equal_ustring(cmd, "Execute")) {
5777 c = atoi(p->getparam("C"));
5778 n = atoi(p->getparam("N"));
5779 a = atoi(p->getparam("A"));
5780 f = atoi(p->getparam("F"));
5781 r = atoi(p->getparam("R"));
5782 w = atoi(p->getparam("W"));
5783 id = atoi(p->getparam("ID"));
5784 ia = atoi(p->getparam("IA"));
5785
5786 const char* pd = p->getparam("D");
5787 if (strncmp(pd, "0x", 2) == 0)
5788 sscanf(pd + 2, "%x", &d);
5789 else
5790 d = atoi(p->getparam("D"));
5791
5792 /* limit repeat range */
5793 if (r == 0)
5794 r = 1;
5795 if (r > 100)
5796 r = 100;
5797 if (w > 1000)
5798 w = 1000;
5799
5800 for (i = 0; i < r; i++) {
5801 status = SUCCESS;
5802
5803 if (hconn) {
5804 size = sizeof(d);
5805 status =
5806 rpc_client_call(hconn, RPC_CNAF24, CNAF, 0, c, n, a, f, &d, &size, &x,
5807 &q);
5808
5809 if (status == RPC_NET_ERROR) {
5810 /* try to reconnect */
5811 //cm_disconnect_client(hconn, FALSE);
5812 status = cm_connect_client(client_name, &hconn);
5813 if (status != RPC_SUCCESS) {
5814 hconn = 0;
5815 client_name[0] = 0;
5816 }
5817
5818 if (hconn) {
5819 status = rpc_client_call(hconn, RPC_CNAF24, CNAF, 0, c, n, a, f, &d, &size, &x, &q);
5820 }
5821 }
5822 }
5823
5824 if (status != SUCCESS) {
5825 rr->rsprintf
5826 ("<tr><td colspan=6 class=\"redLight\">Error executing function, code = %d</tr>",
5827 status);
5828 } else {
5829 rr->rsprintf("<tr align=center><td>%d", n);
5830 rr->rsprintf("<td>%d", a);
5831 rr->rsprintf("<td>%d", f);
5832 rr->rsprintf("<td colspan=3>%d / 0x%04X Q%d X%d", d, d, q, x);
5833 }
5834
5835 d += id;
5836 a += ia;
5837
5838 if (w > 0)
5839 ss_sleep(w);
5840 }
5841 }
5842
5843 /* input fields */
5844 rr->rsprintf
5845 ("<tr align=center><td><input type=text size=3 name=N value=%d>\n",
5846 n);
5847 rr->rsprintf("<td><input type=text size=3 name=A value=%d>\n", a);
5848 rr->rsprintf("<td><input type=text size=3 name=F value=%d>\n", f);
5849 rr->rsprintf
5850 ("<td colspan=3><input type=text size=8 name=D value=%d></tr>\n",
5851 d);
5852
5853 /* control fields */
5854 rr->rsprintf("<tr><td colspan=2>Repeat");
5855 rr->rsprintf("<td><input type=text size=3 name=R value=%d>\n", r);
5856
5857 rr->rsprintf
5858 ("<td align=center colspan=3><input type=submit name=cmd value=\"C cycle\">\n");
5859 rr->rsprintf("<input type=submit name=cmd value=\"Z cycle\">\n");
5860
5861 rr->rsprintf("<tr><td colspan=2>Repeat delay [ms]");
5862 rr->rsprintf("<td><input type=text size=3 name=W value=%d>\n", w);
5863
5864 rr->rsprintf
5865 ("<td align=center colspan=3><input type=submit name=cmd value=\"Set inhibit\">\n");
5866 rr->rsprintf("<input type=submit name=cmd value=\"Clear inhibit\">\n");
5867
5868 rr->rsprintf("<tr><td colspan=2>Data increment");
5869 rr->rsprintf("<td><input type=text size=3 name=ID value=%d>\n", id);
5870
5871 rr->rsprintf
5872 ("<td colspan=3 align=center>Branch <input type=text size=3 name=B value=0>\n");
5873
5874 rr->rsprintf("<tr><td colspan=2>A increment");
5875 rr->rsprintf("<td><input type=text size=3 name=IA value=%d>\n", ia);
5876
5877 rr->rsprintf
5878 ("<td colspan=3 align=center>Crate <input type=text size=3 name=C value=%d>\n",
5879 c);
5880
5881 rr->rsprintf("</table></body>\r\n");
5882}
5883
5884/*------------------------------------------------------------------*/
5885
5886#ifdef HAVE_MSCB
5887
5888typedef struct {
5889 signed char id;
5890 char name[32];
5891} NAME_TABLE;
5892
5893static const NAME_TABLE prefix_table[] = {
5894 {PRFX_PICO, "pico",},
5895 {PRFX_NANO, "nano",},
5896 {PRFX_MICRO, "micro",},
5897 {PRFX_MILLI, "milli",},
5898 {PRFX_NONE, "",},
5899 {PRFX_KILO, "kilo",},
5900 {PRFX_MEGA, "mega",},
5901 {PRFX_GIGA, "giga",},
5902 {PRFX_TERA, "tera",},
5903 {99}
5904};
5905
5906static const NAME_TABLE unit_table[] = {
5907
5908 {UNIT_METER, "meter",},
5909 {UNIT_GRAM, "gram",},
5910 {UNIT_SECOND, "second",},
5911 {UNIT_MINUTE, "minute",},
5912 {UNIT_HOUR, "hour",},
5913 {UNIT_AMPERE, "ampere",},
5914 {UNIT_KELVIN, "kelvin",},
5915 {UNIT_CELSIUS, "deg. celsius",},
5916 {UNIT_FARENHEIT, "deg. farenheit",},
5917
5918 {UNIT_HERTZ, "hertz",},
5919 {UNIT_PASCAL, "pascal",},
5920 {UNIT_BAR, "bar",},
5921 {UNIT_WATT, "watt",},
5922 {UNIT_VOLT, "volt",},
5923 {UNIT_OHM, "ohm",},
5924 {UNIT_TESLA, "tesls",},
5925 {UNIT_LITERPERSEC, "liter/sec",},
5926 {UNIT_RPM, "RPM",},
5927 {UNIT_FARAD, "farad",},
5928
5929 {UNIT_BOOLEAN, "boolean",},
5930 {UNIT_BYTE, "byte",},
5931 {UNIT_WORD, "word",},
5932 {UNIT_DWORD, "dword",},
5933 {UNIT_ASCII, "ascii",},
5934 {UNIT_STRING, "string",},
5935 {UNIT_BAUD, "baud",},
5936
5937 {UNIT_PERCENT, "percent",},
5938 {UNIT_PPM, "RPM",},
5939 {UNIT_COUNT, "counts",},
5940 {UNIT_FACTOR, "factor",},
5941 {0}
5942};
5943
5944/*------------------------------------------------------------------*/
5945
5946void print_mscb_var(char *value, char *evalue, char *unit, MSCB_INFO_VAR *info_chn, void *pdata)
5947{
5948 char str[80];
5949 signed short sdata;
5950 unsigned short usdata;
5951 signed int idata;
5952 unsigned int uidata;
5953 float fdata;
5954 int i;
5955
5956 value[0] = 0;
5957 evalue[0] = 0;
5958
5959 if (info_chn->unit == UNIT_STRING) {
5960 memset(str, 0, sizeof(str));
5961 strncpy(str, (char *)pdata, info_chn->width);
5962 for (i = 0; i < (int) strlen(str); i++)
5963 switch (str[i]) {
5964 case 1:
5965 strcat(value, "\\001");
5966 break;
5967 case 2:
5968 strcat(value, "\\002");
5969 break;
5970 case 9:
5971 strcat(value, "\\t");
5972 break;
5973 case 10:
5974 strcat(value, "\\n");
5975 break;
5976 case 13:
5977 strcat(value, "\\r");
5978 break;
5979 default:
5980 value[strlen(value) + 1] = 0;
5981 value[strlen(value)] = str[i];
5982 break;
5983 }
5984 mstrlcpy(evalue, value, 256);
5985 } else {
5986 switch (info_chn->width) {
5987 case 0:
5988 strcpy(value, "0");
5989 strcpy(evalue, "0");
5990 break;
5991
5992 case 1:
5993 if (info_chn->flags & MSCBF_SIGNED) {
5994 sprintf(value, "%d (0x%02X/", *((signed char *)pdata), *((signed char *)pdata));
5995 sprintf(evalue, "%d", *((signed char *)pdata));
5996 } else {
5997 sprintf(value, "%u (0x%02X/", *((unsigned char *)pdata), *((unsigned char *)pdata));
5998 sprintf(evalue, "%u", *((unsigned char *)pdata));
5999 }
6000
6001 for (i = 0; i < 8; i++)
6002 if (*((unsigned char *)pdata) & (0x80 >> i))
6003 sprintf(value + strlen(value), "1");
6004 else
6005 sprintf(value + strlen(value), "0");
6006 sprintf(value + strlen(value), ")");
6007 break;
6008
6009 case 2:
6010 if (info_chn->flags & MSCBF_SIGNED) {
6011 sdata = *((signed short *)pdata);
6012 WORD_SWAP(&sdata);
6013 sprintf(value, "%d (0x%04X)", sdata, sdata);
6014 sprintf(evalue, "%d", sdata);
6015 } else {
6016 usdata = *((unsigned short *)pdata);
6017 WORD_SWAP(&usdata);
6018 sprintf(value, "%u (0x%04X)", usdata, usdata);
6019 sprintf(evalue, "%u", usdata);
6020 }
6021 break;
6022
6023 case 4:
6024 if (info_chn->flags & MSCBF_FLOAT) {
6025 fdata = *((float *)pdata);
6026 DWORD_SWAP(&fdata);
6027 sprintf(value, "%1.6lg", fdata);
6028 sprintf(evalue, "%1.6lg", fdata);
6029 } else {
6030 if (info_chn->flags & MSCBF_SIGNED) {
6031 idata = *((signed int *)pdata);
6032 DWORD_SWAP(&idata);
6033 sprintf(value, "%d (0x%08X)", idata, idata);
6034 sprintf(evalue, "%d", idata);
6035 } else {
6036 uidata = *((unsigned int *)pdata);
6037 DWORD_SWAP(&uidata);
6038 sprintf(value, "%u (0x%08X)", uidata, uidata);
6039 sprintf(evalue, "%u", uidata);
6040 }
6041 }
6042 break;
6043 }
6044 }
6045
6046 /* evaluate prefix */
6047 unit[0] = 0;
6048 if (info_chn->prefix) {
6049 for (i = 0; prefix_table[i].id != 99; i++)
6050 if ((unsigned char)prefix_table[i].id == info_chn->prefix)
6051 break;
6052 if (prefix_table[i].id)
6053 strcpy(unit, prefix_table[i].name);
6054 }
6055
6056 /* evaluate unit */
6057 if (info_chn->unit && info_chn->unit != UNIT_STRING) {
6058 for (i = 0; unit_table[i].id; i++)
6059 if ((unsigned char)unit_table[i].id == info_chn->unit)
6060 break;
6061 if (unit_table[i].id)
6062 strcat(unit, unit_table[i].name);
6063 }
6064}
6065
6066static int cmp_int(const void *a, const void *b)
6067{
6068 return *((int *)a) > *((int *)b);
6069}
6070
6071/*------------------------------------------------------------------*/
6072
6073void create_mscb_tree()
6074{
6075 HNDLE hDB, hKeySubm, hKeyEq, hKeyAdr, hKey, hKeyDev;
6076 KEY key;
6077 int i, j, k, l, size, address[1000], dev_badr[1000], dev_adr[1000], dev_chn[1000],
6078 n_address, n_dev_adr;
6079 char mscb_dev[256], mscb_pwd[32], eq_name[32];
6080
6082
6083 db_create_key(hDB, 0, "MSCB/Submaster", TID_KEY);
6084 db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
6085 assert(hKeySubm);
6086
6087 /*---- go through equipment list ----*/
6088 db_find_key(hDB, 0, "Equipment", &hKeyEq);
6089 if (hKeyEq) {
6090 for (i=0 ; ; i++) {
6091 db_enum_key(hDB, hKeyEq, i, &hKey);
6092 if (!hKey)
6093 break;
6094 db_get_key(hDB, hKey, &key);
6095 strcpy(eq_name, key.name);
6096 db_find_key(hDB, hKey, "Settings/Devices", &hKeyDev);
6097 if (hKeyDev) {
6098 for (j=0 ;; j++) {
6099 db_enum_key(hDB, hKeyDev, j, &hKey);
6100 if (!hKey)
6101 break;
6102
6103 if (db_find_key(hDB, hKey, "MSCB Address", &hKeyAdr) == DB_SUCCESS) {
6104 /* mscbdev type of device */
6105 size = sizeof(mscb_dev);
6106 if (db_get_value(hDB, hKey, "Device", mscb_dev, &size, TID_STRING, FALSE) != DB_SUCCESS)
6107 continue;
6108 size = sizeof(mscb_pwd);
6109 if (db_get_value(hDB, hKey, "Pwd", mscb_pwd, &size, TID_STRING, FALSE) != DB_SUCCESS)
6110 continue;
6111
6112 size = sizeof(dev_adr);
6113 db_get_data(hDB, hKeyAdr, dev_adr, &size, TID_INT);
6114 n_dev_adr = size / sizeof(int);
6115 } else if (db_find_key(hDB, hKey, "Block Address", &hKeyAdr) == DB_SUCCESS) {
6116 /* mscbhvr type of device */
6117 size = sizeof(mscb_dev);
6118 if (db_get_value(hDB, hKey, "MSCB Device", mscb_dev, &size, TID_STRING, FALSE) != DB_SUCCESS)
6119 continue;
6120 size = sizeof(mscb_pwd);
6121 if (db_get_value(hDB, hKey, "MSCB Pwd", mscb_pwd, &size, TID_STRING, FALSE) != DB_SUCCESS)
6122 continue;
6123
6124 n_dev_adr = 0;
6125 size = sizeof(dev_badr);
6126 db_get_data(hDB, hKeyAdr, dev_badr, &size, TID_INT);
6127 size = sizeof(dev_chn);
6128 if (db_get_value(hDB, hKey, "Block Channels", dev_chn, &size, TID_INT, FALSE) == DB_SUCCESS) {
6129 for (k=0 ; k<size/(int)sizeof(int) && n_dev_adr < (int)(sizeof(dev_adr)/sizeof(int)) ; k++) {
6130 for (l=0 ; l<dev_chn[k] ; l++)
6131 dev_adr[n_dev_adr++] = dev_badr[k]+l;
6132 }
6133 }
6134 } else
6135 continue;
6136
6137 /* create or open submaster entry */
6138 db_find_key(hDB, hKeySubm, mscb_dev, &hKey);
6139 if (!hKey) {
6140 db_create_key(hDB, hKeySubm, mscb_dev, TID_KEY);
6141 db_find_key(hDB, hKeySubm, mscb_dev, &hKey);
6142 assert(hKey);
6143 }
6144
6145 /* get old address list */
6146 size = sizeof(address);
6147 if (db_get_value(hDB, hKey, "Address", address, &size, TID_INT, FALSE) == DB_SUCCESS)
6148 n_address = size / sizeof(int);
6149 else
6150 n_address = 0;
6151
6152 /* merge with new address list */
6153 for (k=0 ; k<n_dev_adr ; k++) {
6154 for (l=0 ; l<n_address ; l++)
6155 if (address[l] == dev_adr[k])
6156 break;
6157
6158 if (l == n_address)
6159 address[n_address++] = dev_adr[k];
6160 }
6161
6162 /* sort address list */
6163 qsort(address, n_address, sizeof(int), cmp_int);
6164
6165 /* store new address list */
6166 db_set_value(hDB, hKey, "Pwd", mscb_pwd, 32, 1, TID_STRING);
6167 db_set_value(hDB, hKey, "Comment", eq_name, 32, 1, TID_STRING);
6168 db_set_value(hDB, hKey, "Address", address, n_address*sizeof(int), n_address, TID_INT);
6169 }
6170 }
6171 }
6172 }
6173}
6174
6175/*------------------------------------------------------------------*/
6176
6177void show_mscb_page(Param* p, Return* r, int refresh)
6178{
6179 int i, j, n, ind, fi, fd, status, size, n_addr, cur_node, adr, show_hidden;
6180 unsigned int uptime;
6181 BOOL comment_created;
6182 float fvalue;
6183 char *pd;
6184 char dbuf[256], evalue[256], unit[256], cur_subm_name[256];
6185 HNDLE hDB, hKeySubm, hKeyCurSubm, hKey, hKeyAddr, hKeyComm;
6186 KEY key;
6187 MSCB_INFO info;
6188 MSCB_INFO_VAR info_var;
6189 int ping_addr[0x10000];
6190
6192
6193 status = db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
6194 if (!hKeySubm)
6195 create_mscb_tree();
6196
6197 mstrlcpy(cur_subm_name, p->getparam("subm"), sizeof(cur_subm_name));
6198 if (cur_subm_name[0] == 0) {
6199 db_enum_key(hDB, hKeySubm, 0, &hKeyCurSubm);
6200 if (!hKeyCurSubm) {
6201 char errorstr[256];
6202 sprintf(errorstr, "No submaster defined under /MSCB/Submaster");
6203 show_error(r, errorstr);
6204 return;
6205 }
6206 db_get_key(hDB, hKeyCurSubm, &key);
6207 strcpy(cur_subm_name, key.name);
6208 } else
6209 db_find_key(hDB, hKeySubm, cur_subm_name, &hKeyCurSubm);
6210
6211 if (p->isparam("node"))
6212 cur_node = atoi(p->getparam("node"));
6213 else
6214 cur_node = -1;
6215
6216 /* perform MSCB rescan */
6217 if (p->isparam("mcmd") && equal_ustring(p->getparam("mcmd"), "Rescan") && p->isparam("subm")) {
6218 /* create Pwd and Comment if not there */
6219 char tmp[32];
6220 size = 32;
6221 tmp[0] = 0;
6222 db_get_value(hDB, hKeyCurSubm, "Pwd", (void *)tmp, &size, TID_STRING, true);
6223 tmp[0] = 0;
6224 db_get_value(hDB, hKeyCurSubm, "Comment", (void *)tmp, &size, TID_STRING, true);
6225
6226 db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
6227 std::vector<int> addr;
6228 std::vector<char> node_comment;
6229 if (hKeyAddr) {
6230 /* get current address array */
6231 db_get_key(hDB, hKeyAddr, &key);
6232 n_addr = key.num_values;
6233 addr.resize(n_addr);
6234 size = sizeof(int)*n_addr;
6235 db_get_data(hDB, hKeyAddr, addr.data(), &size, TID_INT);
6236 } else {
6237 /* create new address array */
6238 db_create_key(hDB, hKeyCurSubm, "Address", TID_INT);
6239 db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
6240 n_addr = 0;
6241 addr.resize(1);
6242 }
6243
6244 comment_created = FALSE;
6245 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6246 if (hKeyComm) {
6247 /* get current node comments */
6248 db_get_key(hDB, hKeyComm, &key);
6249 node_comment.resize(32*key.num_values);
6250 size = 32*key.num_values;
6251 db_get_data(hDB, hKeyComm, node_comment.data(), &size, TID_STRING);
6252 } else {
6253 /* create new comment array */
6254 db_create_key(hDB, hKeyCurSubm, "Node comment", TID_STRING);
6255 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6256 node_comment.resize(32);
6257 comment_created = TRUE;
6258 }
6259
6260 fd = mscb_init(cur_subm_name, 0, "", FALSE);
6261 if (fd >= 0) {
6262 /* fill table of possible addresses */
6263 for (i=0 ; i<0x10000 ; i++)
6264 ping_addr[i] = 0;
6265 for (i=0 ; i<1000 ; i++) // 0..999
6266 ping_addr[i] = 1;
6267 for (i=0 ; i<0x10000 ; i+=100) // 100, 200, ...
6268 ping_addr[i] = 1;
6269 for (i=0 ; i<0x10000 ; i+= 0x100)
6270 ping_addr[i] = 1; // 256, 512, ...
6271 for (i=0xFF00 ; i<0x10000 ; i++)
6272 ping_addr[i] = 1; // 0xFF00-0xFFFF
6273
6274 for (ind = n = 0; ind < 0x10000; ind++) {
6275 if (!ping_addr[ind])
6276 continue;
6277
6278 status = mscb_ping(fd, (unsigned short) ind, 1, 0);
6279 if (status == MSCB_SUCCESS) {
6280
6281 /* node found, search next 100 as well */
6282 for (j=ind; j<ind+100 && j<0x10000 ; j++)
6283 if (j >= 0)
6284 ping_addr[j] = 1;
6285
6286 status = mscb_info(fd, (unsigned short) ind, &info);
6287
6288 if (status == MSCB_SUCCESS) {
6289 /* check if node already in list */
6290 for (j=0 ; j<n_addr ; j++)
6291 if (addr[j] == ind)
6292 break;
6293 if (j == n_addr) {
6294 addr.resize(n_addr+1);
6295 addr[n_addr] = ind;
6296 node_comment.resize(32*(n_addr+1));
6297 /* use node name as default comment */
6298 strncpy(node_comment.data()+n_addr*32, info.node_name, 32);
6299 n_addr ++;
6300 } else if (comment_created) {
6301 node_comment.resize(32*n_addr);
6302 /* use node name as default comment */
6303 strncpy(node_comment.data()+j*32, info.node_name, 32);
6304 }
6305 }
6306 }
6307 }
6308
6309 db_set_data(hDB, hKeyAddr, addr.data(), n_addr*sizeof(int), n_addr, TID_INT);
6310 db_set_data(hDB, hKeyComm, node_comment.data(), n_addr*32, n_addr, TID_STRING);
6311
6312 char redirstr[512];
6313 sprintf(redirstr, "?cmd=mscb&subm=%s", cur_subm_name);
6314 redirect(r, redirstr);
6315 return;
6316
6317 } else {
6318 char errorstr[512];
6319 sprintf(errorstr, "Cannot talk to submaster \"%s\"", cur_subm_name);
6320 show_error(r, errorstr);
6321 return;
6322 }
6323 }
6324
6325 /* write data to node */
6326 if (p->isparam("subm") && p->isparam("node") &&
6327 p->isparam("idx") && p->isparam("value")) {
6328 i = atoi(p->getparam("idx"));
6329 char value[256];
6330 mstrlcpy(value, p->getparam("value"), sizeof(value));
6331
6332 fd = mscb_init(cur_subm_name, 0, "", FALSE);
6333 if (fd >= 0) {
6334 status = mscb_info_variable(fd,
6335 (unsigned short) cur_node, (unsigned char) i, &info_var);
6336 if (status == MSCB_SUCCESS) {
6337 if (info_var.unit == UNIT_STRING) {
6338 char valstr[256];
6339 mstrlcpy(valstr, value, sizeof(valstr));
6340 if (strlen(valstr) > 0 && valstr[strlen(valstr) - 1] == '\n')
6341 valstr[strlen(valstr) - 1] = 0;
6342
6343 status = mscb_write(fd, (unsigned short) cur_node,
6344 (unsigned char) i, valstr, strlen(valstr) + 1);
6345 } else {
6346 if (info_var.flags & MSCBF_FLOAT) {
6347 fvalue = (float) atof(value);
6348 memcpy(&dbuf, &fvalue, sizeof(float));
6349 } else {
6350 if (value[1] == 'x')
6351 sscanf(value + 2, "%x", (int *)&dbuf);
6352 else
6353 *((int *)dbuf) = atoi(value);
6354 }
6355
6356 status = mscb_write(fd, (unsigned short) cur_node,
6357 (unsigned char) i, dbuf, info_var.width);
6358 }
6359 }
6360 }
6361 char redirstr[512];
6362 sprintf(redirstr, "?cmd=mscb&subm=%s&node=%d", cur_subm_name, cur_node);
6363 redirect(r, redirstr);
6364 return;
6365 }
6366
6367 if (p->isparam("hidden"))
6368 show_hidden = atoi(p->getparam("hidden"));
6369 else
6370 show_hidden = FALSE;
6371
6372 show_header(r, "MSCB", "GET", "./", refresh);
6373 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
6374 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
6375 show_navigation_bar(r, "MSCB");
6376
6377 /* style sheet */
6378 r->rsprintf("<style type=\"text/css\">\r\n");
6379 r->rsprintf("select { width:150px; background-color:#FFFFE0; font-size:12px; }\r\n");
6380 r->rsprintf(".subm {\r\n");
6381 r->rsprintf(" background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
6382 r->rsprintf(" padding:5px;\r\n");
6383 r->rsprintf(" vertical-align:top;\r\n");
6384 r->rsprintf(" font-size:16px;\r\n");
6385 r->rsprintf(" border-right:1px solid #808080;\r\n");
6386 r->rsprintf("}\r\n");
6387 r->rsprintf(".node {\r\n");
6388 r->rsprintf(" background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
6389 r->rsprintf(" padding:5px;\r\n");
6390 r->rsprintf(" vertical-align:top;\r\n");
6391 r->rsprintf(" font-size:16px;\r\n");
6392 r->rsprintf(" border-right:1px solid #808080;\r\n");
6393 r->rsprintf("}\r\n");
6394 r->rsprintf(".vars {\r\n");
6395 r->rsprintf(" background-color:#E0E0E0; text-align:center; font-weight:bold;\r\n");
6396 r->rsprintf(" padding:5px;\r\n");
6397 r->rsprintf(" vertical-align:top;\r\n");
6398 r->rsprintf(" font-size:10px;\r\n");
6399 r->rsprintf("}\r\n");
6400 r->rsprintf(".v1 {\r\n");
6401 r->rsprintf(" padding:3px;\r\n");
6402 r->rsprintf(" font-weight:bold;\r\n");
6403 r->rsprintf(" font-size:12px;\r\n");
6404 r->rsprintf("}\r\n");
6405 r->rsprintf(".v2 {\r\n");
6406 r->rsprintf(" background-color:#F0F0F0;\r\n");
6407 r->rsprintf(" padding:3px;\r\n");
6408 r->rsprintf(" font-size:12px;\r\n");
6409 r->rsprintf(" border:1px solid #808080;\r\n");
6410 r->rsprintf(" border-right:1px solid #FFFFFF;\r\n");
6411 r->rsprintf(" border-bottom:1px solid #FFFFFF;\r\n");
6412 r->rsprintf("}\r\n");
6413 r->rsprintf(".v3 {\r\n");
6414 r->rsprintf(" padding:3px;\r\n");
6415 r->rsprintf(" font-size:12px;\r\n");
6416 r->rsprintf("}\r\n");
6417 r->rsprintf("</style>\r\n\r\n");
6418
6419 /* javascript */
6420 r->rsprintf("<script type=\"text/javascript\">\r\n");
6421 r->rsprintf("function mscb_edit(index, value)\r\n");
6422 r->rsprintf("{\r\n");
6423 r->rsprintf(" var new_value = prompt('Please enter new value', value);\r\n");
6424 r->rsprintf(" if (new_value != undefined) {\r\n");
6425 r->rsprintf(" window.location.search = '?cmd=mscb&subm=%s&node=%d&idx='+index+'&value='+new_value;\n", cur_subm_name, cur_node);
6426 r->rsprintf(" }\n");
6427 r->rsprintf("}\r\n");
6428 r->rsprintf("</script>\r\n\r\n");
6429
6430 /*---- main content ----*/
6431
6432 r->rsprintf("<table class=\"mtable\">"); //main table
6433 r->rsprintf("<tr><th class=\"mtableheader\" colspan=2>MSCB</th><tr>");
6434
6435 /*---- menu buttons ----*/
6436
6437 r->rsprintf("<tr><td colspan=2>\n");
6438 r->rsprintf("<table width=100%%><tr>\n");
6439 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());
6440
6441 r->rsprintf("<tr><td colspan=\"2\" cellpadding=\"0\" cellspacing=\"0\">\r\n");
6442
6443 status = db_find_key(hDB, 0, "MSCB/Submaster", &hKeySubm);
6444 if (status != DB_SUCCESS) {
6445 r->rsprintf("<h1>No MSCB Submasters defined in ODB</h1>\r\n");
6446 r->rsprintf("</td></tr>\r\n");
6447 r->rsprintf("</table>\r\n"); //submaster table
6448 r->rsprintf("</td></tr>\r\n");
6449 r->rsprintf("</table>\r\n"); //main table
6450 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6451 r->rsprintf("</form>\n");
6452 r->rsprintf("</body></html>\r\n");
6453 return;
6454 }
6455
6456 r->rsprintf("<table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\">");
6457
6458 /*---- submaster list ----*/
6459 r->rsprintf("<tr><td class=\"subm\">\r\n");
6460 r->rsprintf("Submaster<hr>\r\n");
6461
6462 /* count submasters */
6463 for (i = 0;;i++) {
6464 db_enum_key(hDB, hKeySubm, i, &hKey);
6465 if (!hKey)
6466 break;
6467 }
6468 if (i<2)
6469 i = 2;
6470
6471 r->rsprintf("<select name=\"subm\" id=\"subm\" size=%d ", i);
6472 r->rsprintf("onChange=\"window.location.search='?cmd=mscb&subm='+document.getElementById('subm').value;\">\r\n");
6473 hKeyCurSubm = 0;
6474 for (i = 0;;i++) {
6475 db_enum_key(hDB, hKeySubm, i, &hKey);
6476 if (!hKey)
6477 break;
6478 db_get_key(hDB, hKey, &key);
6479 char str[NAME_LENGTH+10+256];
6480 mstrlcpy(str, key.name, sizeof(str));
6481 char comment[256];
6482 size = sizeof(comment);
6483 if (db_get_value(hDB, hKey, "Comment", comment, &size, TID_STRING, FALSE) == DB_SUCCESS) {
6484 mstrlcat(str, ": ", sizeof(str));
6485 mstrlcat(str, comment, sizeof(str));
6486 }
6487
6488 if ((cur_subm_name[0] && equal_ustring(cur_subm_name, key.name)) ||
6489 (cur_subm_name[0] == 0 && i == 0)) {
6490 r->rsprintf("<option value=\"%s\" selected>%s</option>\r\n", key.name, str);
6491 hKeyCurSubm = hKey;
6492 } else
6493 r->rsprintf("<option value=\"%s\">%s</option>\r\n", key.name, str);
6494 }
6495 r->rsprintf("</select>\r\n");
6496
6497 /*---- node list ----*/
6498 r->rsprintf("<td class=\"node\">\r\n");
6499 r->rsprintf("Node ");
6500
6501 r->rsprintf("<script type=\"text/javascript\">\n");
6502 r->rsprintf("<!--\n");
6503 r->rsprintf("function rescan()\n");
6504 r->rsprintf("{\n");
6505 r->rsprintf(" flag = confirm('Rescan can take up to one minute.');\n");
6506 r->rsprintf(" if (flag == true)\n");
6507 r->rsprintf(" window.location.href = '?cmd=mscb&mcmd=Rescan&subm=%s';\n", cur_subm_name);
6508 r->rsprintf("}\n");
6509 r->rsprintf("//-->\n");
6510 r->rsprintf("</script>\n");
6511
6512 r->rsprintf("<input type=button name=cmd value=\"Rescan\" onClick=\"rescan();\">");
6513 r->rsprintf("<hr>\r\n");
6514
6515 if (!hKeyCurSubm) {
6516 r->rsprintf("No submaster found in ODB\r\n");
6517 r->rsprintf("</td></tr>\r\n");
6518 r->rsprintf("</table>\r\n"); //inner submaster table
6519 r->rsprintf("</td></tr>\r\n");
6520 r->rsprintf("</table>\r\n"); //submaster table
6521 r->rsprintf("</td></tr>\r\n");
6522 r->rsprintf("</table>\r\n"); //main table
6523 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6524 r->rsprintf("</form>\n");
6525 r->rsprintf("</body></html>\r\n");
6526 return;
6527 }
6528
6529 db_find_key(hDB, hKeyCurSubm, "Address", &hKeyAddr);
6530 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6531
6532 i = 10;
6533 if (hKeyAddr) {
6534 db_get_key(hDB, hKeyAddr, &key);
6535 i = key.num_values;
6536
6537 if (hKeyComm == 0) {
6538 db_create_key(hDB, hKeyCurSubm, "Node comment", TID_STRING);
6539 db_find_key(hDB, hKeyCurSubm, "Node comment", &hKeyComm);
6540 }
6541 db_get_key(hDB, hKeyComm, &key);
6542 if (key.num_values < i) {
6543 char str[32] = "";
6544 for (int j=key.num_values ; j<i ; j++)
6545 db_set_data_index(hDB, hKeyComm, str, 32, j, TID_STRING);
6546 }
6547 }
6548 if (i < 2)
6549 i = 2;
6550
6551 r->rsprintf("<select name=\"node\" id=\"node\" size=%d ", i);
6552 r->rsprintf("onChange=\"window.location.search='?cmd=mscb&subm=%s&node='+document.getElementById('node').value;\">\r\n", cur_subm_name);
6553
6554 if (hKeyAddr) {
6555 db_get_key(hDB, hKeyAddr, &key);
6556 size = sizeof(adr);
6557
6558 /* check if current node is in list */
6559 for (i = 0; i<key.num_values ;i++) {
6560 size = sizeof(adr);
6561 db_get_data_index(hDB, hKeyAddr, &adr, &size, i, TID_INT);
6562 if (adr == cur_node)
6563 break;
6564 }
6565 if (i == key.num_values) // if not found, use first one in list
6566 db_get_data_index(hDB, hKeyAddr, &cur_node, &size, 0, TID_INT);
6567
6568 for (i = 0; i<key.num_values ;i++) {
6569 char str[100+256];
6570 size = sizeof(adr);
6571 db_get_data_index(hDB, hKeyAddr, &adr, &size, i, TID_INT);
6572 if (hKeyComm) {
6573 char comment[256];
6574 size = sizeof(comment);
6575 db_get_data_index(hDB, hKeyComm, comment, &size, i, TID_STRING);
6576 sprintf(str, "%d: %s", adr, comment);
6577 } else {
6578 sprintf(str, "%d", adr);
6579 }
6580 if (cur_node == 0 && i == 0)
6581 cur_node = adr;
6582 if (adr == cur_node)
6583 r->rsprintf("<option selected>%s</option>\r\n", str);
6584 else
6585 r->rsprintf("<option>%s</option>\r\n", str);
6586 }
6587 }
6588 r->rsprintf("</select>\r\n");
6589
6590 /*---- node contents ----*/
6591 r->rsprintf("<td class=\"vars\">\r\n");
6592 r->rsprintf("<table>\r\n");
6593 db_get_key(hDB, hKeyCurSubm, &key);
6594 if (cur_node != -1)
6595 r->rsprintf("<tr><td colspan=3 align=center><b>%s:%d</b>", key.name, cur_node);
6596 else
6597 r->rsprintf("<tr><td colspan=3 align=center><b>%s</b>", key.name);
6598 r->rsprintf("<hr></td></tr>\r\n");
6599
6600 char passwd[32];
6601 passwd[0] = 0;
6602 size = 32;
6603 db_get_value(hDB, hKeyCurSubm, "Pwd", passwd, &size, TID_STRING, TRUE);
6604
6605 fd = mscb_init(key.name, 0, passwd, FALSE);
6606 if (fd < 0) {
6607 if (fd == EMSCB_WRONG_PASSWORD)
6608 r->rsprintf("<tr><td colspan=3><b>Invalid password</b></td>");
6609 else
6610 r->rsprintf("<tr><td colspan=3><b>Submaster does not respond</b></td>");
6611 goto mscb_error;
6612 }
6613 mscb_set_eth_max_retry(fd, 3);
6614 mscb_set_max_retry(1);
6615
6616 status = mscb_ping(fd, cur_node, 0, 1);
6617 if (status != MSCB_SUCCESS) {
6618 r->rsprintf("<tr><td colspan=3><b>No response from node</b></td>");
6619 goto mscb_error;
6620 }
6621 status = mscb_info(fd, (unsigned short) cur_node, &info);
6622 if (status != MSCB_SUCCESS) {
6623 r->rsprintf("<tr><td colspan=3><b>No response from node</b></td>");
6624 goto mscb_error;
6625 }
6626 char tr16[17];
6627 mstrlcpy(tr16, info.node_name, sizeof(tr16));
6628 r->rsprintf("<tr><td class=\"v1\">Node name<td colspan=2 class=\"v2\">%s</tr>\n", tr16);
6629 r->rsprintf("<tr><td class=\"v1\">GIT revision<td colspan=2 class=\"v2\">%d</tr>\n", info.revision);
6630
6631 if (info.rtc[0] && info.rtc[0] != 0xFF) {
6632 for (i=0 ; i<6 ; i++)
6633 info.rtc[i] = (info.rtc[i] / 0x10) * 10 + info.rtc[i] % 0x10;
6634 r->rsprintf("<tr><td class=\"v1\">Real Time Clock<td colspan=2 class=\"v2\">%02d-%02d-%02d %02d:%02d:%02d</td>\n",
6635 info.rtc[0], info.rtc[1], info.rtc[2],
6636 info.rtc[3], info.rtc[4], info.rtc[5]);
6637 }
6638
6639 status = mscb_uptime(fd, (unsigned short) cur_node, &uptime);
6640 if (status == MSCB_SUCCESS)
6641 r->rsprintf("<tr><td class=\"v1\">Uptime<td colspan=2 class=\"v2\">%dd %02dh %02dm %02ds</tr>\n",
6642 uptime / (3600 * 24),
6643 (uptime % (3600 * 24)) / 3600, (uptime % 3600) / 60,
6644 (uptime % 60));
6645
6646 r->rsprintf("<tr><td colspan=3><hr></td></tr>\r\n");
6647
6648 /* check for hidden variables */
6649 for (i=0 ; i < info.n_variables ; i++) {
6650 mscb_info_variable(fd, cur_node, i, &info_var);
6651 if (info_var.flags & MSCBF_HIDDEN)
6652 break;
6653 }
6654 if (i < info.n_variables) {
6655 char str[32];
6656 strcpy(str, show_hidden ? " checked" : "");
6657 r->rsprintf("<tr><td colspan=3><input type=checkbox%s name=\"hidden\" value=\"1\"", str);
6658 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);
6659 }
6660
6661 /* read variables in blocks of 100 bytes */
6662 for (fi=0 ; fi < info.n_variables ; ) {
6663 for (i=fi,size=0 ; i < info.n_variables && size < 100; i++) {
6664 mscb_info_variable(fd, cur_node, i, &info_var);
6665 size += info_var.width;
6666 }
6667
6668 size = sizeof(dbuf);
6669 status = mscb_read_range(fd, cur_node, fi, i-1, dbuf, &size);
6670 if (status != MSCB_SUCCESS) {
6671 r->rsprintf("<tr><td colspan=3><b>Error reading data from node</b></td>");
6672 goto mscb_error;
6673 }
6674 pd = dbuf;
6675
6676 for (j=fi ; j<i ; j++) {
6677 status = mscb_info_variable(fd, cur_node, j, &info_var);
6678 if ((info_var.flags & MSCBF_HIDDEN) == 0 || show_hidden) {
6679 char tr8[9];
6680 mstrlcpy(tr8, info_var.name, sizeof(tr8));
6681 r->rsprintf("<tr><td class=\"v1\">%s</td>\r\n", tr8);
6682 r->rsprintf("<td class=\"v2\">\r\n");
6683 char value[256];
6684 print_mscb_var(value, evalue, unit, &info_var, pd);
6685 r->rsprintf("<a href=\"#\" onClick=\"mscb_edit(%d,'%s')\">%s</a>",
6686 j, evalue, value);
6687 r->rsprintf("</td><td class=\"v3\">%s</td>", unit);
6688 r->rsprintf("</tr>\r\n");
6689 }
6690 pd += info_var.width;
6691 }
6692
6693 fi = i;
6694 }
6695
6696mscb_error:
6697 r->rsprintf("</tr></table>\r\n");
6698 r->rsprintf("</td></tr></table>\r\n");
6699 r->rsprintf("</td></tr></table>\r\n");
6700 r->rsprintf("</td></tr></table>\r\n");
6701 r->rsprintf("</div></body></html>\r\n");
6702}
6703
6704#endif // HAVE_MSCB
6705
6706/*------------------------------------------------------------------*/
6707
6708void show_password_page(Return* r, const char* dec_path, const char *password)
6709{
6710 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
6711 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
6712 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
6713
6714 r->rsprintf("<html><head>\n");
6715 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
6716 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
6717 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
6718 r->rsprintf("<title>Enter password</title></head><body>\n\n");
6719
6720 r->rsprintf("<form method=\"GET\" action=\".\">\n\n");
6721
6722 /*---- page header ----*/
6723 r->rsprintf("<table class=\"headerTable\"><tr><td></td><tr></table>\n");
6724
6725 r->rsprintf("<table class=\"dialogTable\">\n"); //main table
6726 if (password[0])
6727 r->rsprintf("<tr><th class=\"redLight\">Wrong password!</tr>\n");
6728
6729 r->rsprintf("<tr><th>Please enter password</tr>\n");
6730 r->rsprintf("<tr><td align=center><input type=password name=pwd></tr>\n");
6731 r->rsprintf("<tr><td align=center><input type=submit value=Submit></tr>");
6732
6733 r->rsprintf("</table>\n");
6734
6735 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6736 r->rsprintf("</form>\n");
6737 r->rsprintf("</body></html>\r\n");
6738}
6739
6740/*------------------------------------------------------------------*/
6741
6742BOOL check_web_password(Return* r, HNDLE hDB, const char* dec_path, const char *password, const char *redir)
6743{
6744 HNDLE hkey;
6745 INT size;
6746 char str[256];
6747
6748 /* check for password */
6749 db_find_key(hDB, 0, "/Experiment/Security/Web Password", &hkey);
6750 if (hkey) {
6751 size = sizeof(str);
6752 db_get_data(hDB, hkey, str, &size, TID_STRING);
6753 if (strcmp(password, str) == 0)
6754 return TRUE;
6755
6756 /* show web password page */
6757 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
6758 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
6759 r->rsprintf("Content-Type: text/html; charset=%s\r\n\r\n", HTTP_ENCODING);
6760
6761 r->rsprintf("<html><head>\n");
6762 r->rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
6763 r->rsprintf("<link rel=\"stylesheet\" href=\"midas.css\" type=\"text/css\" />\n");
6764 r->rsprintf("<link rel=\"stylesheet\" href=\"mhttpd.css\" type=\"text/css\" />\n");
6765 r->rsprintf("<title>Enter password</title></head><body>\n\n");
6766
6767 r->rsprintf("<form method=\"GET\" action=\".\">\n\n");
6768
6769 /* define hidden fields for current experiment and destination */
6770 if (redir[0])
6771 r->rsprintf("<input type=hidden name=redir value=\"%s\">\n", redir);
6772
6773 /*---- page header ----*/
6774 r->rsprintf("<table class=\"headerTable\"><tr><td></td><tr></table>\n");
6775
6776 r->rsprintf("<table class=\"dialogTable\">\n"); //main table
6777
6778 if (password[0])
6779 r->rsprintf("<tr><th class=\"redLight\">Wrong password!</tr>\n");
6780
6781 r->rsprintf
6782 ("<tr><th>Please enter password to obtain write access</tr>\n");
6783 r->rsprintf("<tr><td align=center><input type=password name=wpwd></tr>\n");
6784 r->rsprintf("<tr><td align=center><input type=submit value=Submit></tr>");
6785
6786 r->rsprintf("</table>\n");
6787
6788 r->rsprintf("</div>\n"); // closing for <div id="mmain">
6789 r->rsprintf("</form>\n");
6790 r->rsprintf("</body></html>\r\n");
6791
6792 return FALSE;
6793 } else
6794 return TRUE;
6795}
6796
6797/*------------------------------------------------------------------*/
6798
6799void show_odb_page(Param* pp, Return* r, const char* dec_path, int write_access)
6800{
6801 int keyPresent, size, status, line, link_index;
6802 char colspan;
6803 char style[32];
6804 HNDLE hDB, hkey, hkeyroot;
6805 KEY key;
6806 DWORD delta;
6807
6809
6810 //printf("path [%s]\n", dec_path);
6811
6812 if (strcmp(dec_path, "root") == 0) {
6813 dec_path = "";
6814 }
6815
6816 char xdecpath[256];
6817 mstrlcpy(xdecpath, dec_path, sizeof(xdecpath));
6818 if (strrchr(xdecpath, '/'))
6819 mstrlcpy(xdecpath, strrchr(xdecpath, '/')+1, sizeof(xdecpath));
6820 if (xdecpath[0] == 0)
6821 mstrlcpy(xdecpath, "root", sizeof(xdecpath));
6822 show_header(r, "MIDAS online database", "", xdecpath, 0);
6823
6824 /* use javascript file */
6825 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
6826 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
6827 r->rsprintf("<script type=\"text/javascript\" src=\"obsolete.js\"></script>\n");
6828 r->rsprintf("<script type=\"text/javascript\" src=\"controls.js\"></script>\n");
6829
6830 /* find key via path */
6831 status = db_find_key(hDB, 0, dec_path, &hkeyroot);
6832 if (status != DB_SUCCESS) {
6833 r->rsprintf("Error: cannot find key %s<P>\n", dec_path);
6834 r->rsprintf("</body></html>\r\n");
6835 return;
6836 }
6837
6838 char xdec_path[MAX_ODB_PATH];
6839
6840 /* if key is not of type TID_KEY, cut off key name */
6841 db_get_key(hDB, hkeyroot, &key);
6842 if (key.type != TID_KEY) {
6843 mstrlcpy(xdec_path, dec_path, sizeof(xdec_path));
6844
6845 /* strip variable name from path */
6846 char* p = xdec_path + strlen(xdec_path) - 1;
6847 while (*p && *p != '/')
6848 *p-- = 0;
6849 if (*p == '/')
6850 *p = 0;
6851
6852 status = db_find_key(hDB, 0, xdec_path, &hkeyroot);
6853 if (status != DB_SUCCESS) {
6854 r->rsprintf("Error: cannot find key %s<P>\n", xdec_path);
6855 r->rsprintf("</body></html>\r\n");
6856 return;
6857 }
6858
6859 dec_path = xdec_path;
6860 }
6861
6862 //mstrlcpy(enc_path, dec_path, enc_path_size);
6863 //urlEncode(enc_path, enc_path_size);
6864
6865 std::string odbpath = db_get_path(hDB, hkeyroot);
6866
6867 /*---- navigation bar ----*/
6868
6869 colspan = 7;
6870
6871 if (elog_mode) {
6872 r->rsprintf("<table class=\"mtableheader\">\n");
6873 r->rsprintf("<tr><td colspan=%d>\n", colspan);
6874 r->rsprintf("<input type=button value=ELog onclick=\"self.location=\'?cmd=Alarms\';\">\n");
6875 r->rsprintf("</td></tr></table>\n\n");
6876 } else
6877 show_navigation_bar(r, "ODB");
6878
6879 /*---- begin ODB directory table ----*/
6880
6881 r->rsprintf("<table class=\"mtable\" style=\"border-spacing:0px;\">\n");
6882 r->rsprintf("<tr><th colspan=%d class=\"mtableheader\">Online Database Browser</tr>\n", colspan);
6883 //buttons:
6884 if(!elog_mode){
6885 r->rsprintf("<tr><td colspan=%d>\n", colspan);
6886 r->rsprintf("<input type=button value=Find onclick=\"self.location=\'?cmd=Find\';\">\n");
6887 r->rsprintf("<input type=button value=Create onclick=\"dlgShow('dlgCreate')\">\n");
6888 r->rsprintf("<input type=button value=Link onclick=\"dlgShow('dlgLink')\">\n");
6889 r->rsprintf("<input type=button value=Delete onclick=\"dlgShow('dlgDelete')\">\n");
6890 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());
6891 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());
6892 r->rsprintf("<input type=button value=\"Show ODB clients\" onclick=\"self.location=\'?cmd=odb_scl\';\">\n");
6893 r->rsprintf("</td></tr>\n");
6894 }
6895
6896 /*---- Build the Delete dialog------------------------------------*/
6897
6898 std::string dd = "";
6899
6900 dd += "<!-- Demo dialog -->\n";
6901 dd += "<div id=\"dlgDelete\" class=\"dlgFrame\">\n";
6902 dd += "<div class=\"dlgTitlebar\">Delete ODB entry</div>\n";
6903 dd += "<div class=\"dlgPanel\">\n";
6904 dd += "<div id=odbpath>";
6905 dd += "\"";
6906 dd += MJsonNode::Encode(odbpath.c_str());
6907 dd += "\"";
6908 dd += "</div>\n";
6909 dd += "<div><br></div>\n";
6910
6911 dd += "<table class=\"dialogTable\">\n";
6912 dd += "<th colspan=2>Delete ODB entries:</th>\n";
6913
6914 std::vector<std::string> delete_list;
6915
6916 int count_delete = 0;
6917
6918 /*---- ODB display -----------------------------------------------*/
6919
6920 /* display root key */
6921 r->rsprintf("<tr><td colspan=%d class='ODBpath'><b>", colspan);
6922 r->rsprintf("<a href=\"?cmd=oldodb\">/</a> \n");
6923
6924 std::string enc_root_path;
6925
6926 /*---- display path ----*/
6927 {
6928 const char* p = dec_path;
6929 while (*p) {
6930 std::string pd;
6931 while (*p && *p != '/')
6932 pd += *p++;
6933
6934 enc_root_path += urlEncode(pd.c_str());
6935
6936 if (pd.length() > 0)
6937 r->rsprintf("<a href=\"?cmd=oldodb&odb_path=%s\">%s</a>\n / ", enc_root_path.c_str(), pd.c_str());
6938
6939 enc_root_path += "/";
6940 if (*p == '/')
6941 p++;
6942 }
6943 }
6944
6945 r->rsprintf("</b></tr>\n");
6946
6947 /* enumerate subkeys */
6948 keyPresent = 0;
6949 for(int scan=0; scan<2; scan++){
6950 if(scan==1 && keyPresent==1) {
6951 r->rsprintf("<tr class=\"titleRow\">\n");
6952 r->rsprintf("<th class=\"ODBkey\">Key</th>\n");
6953 r->rsprintf("<th class=\"ODBvalue\">Value&nbsp;");
6954 r->rsprintf("<script type=\"text/javascript\">\n");
6955 r->rsprintf("function expand()\n");
6956 r->rsprintf("{\n");
6957 r->rsprintf(" var n = document.getElementsByName('ext');\n");
6958 r->rsprintf(" for (i=0 ; i<n.length ; i++) {\n");
6959 r->rsprintf(" if (n[i].style.display == 'none')\n");
6960 r->rsprintf(" n[i].style.display = 'table-cell';\n");
6961 r->rsprintf(" else\n");
6962 r->rsprintf(" n[i].style.display = 'none';\n");
6963 r->rsprintf(" }\n");
6964 r->rsprintf(" if (document.getElementById('expp').expflag === true) {\n");
6965 r->rsprintf(" document.getElementById('expp').expflag = false;\n");
6966 r->rsprintf(" document.getElementById('expp').innerHTML = '&#x21E5;';\n");
6967 r->rsprintf(" } else {\n");
6968 r->rsprintf(" document.getElementById('expp').expflag = true;\n");
6969 r->rsprintf(" document.getElementById('expp').innerHTML = '&#x21E4;';\n");
6970 r->rsprintf(" }\n");
6971 r->rsprintf("}\n");
6972 r->rsprintf("</script>");
6973 r->rsprintf("<div style=\"display:inline;float:right\"><a id=\"expp\"href=\"#\" onClick=\"expand();return false;\">&#x21E5;</div>");
6974 r->rsprintf("</th>\n");
6975 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Type</th>\n");
6976 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">#Val</th>\n");
6977 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Size</th>\n");
6978 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Written</th>\n");
6979 r->rsprintf("<th class=\"ODBvalue\" name=\"ext\" style=\"display:none\">Mode</th>\n");
6980 r->rsprintf("</tr>\n");
6981 }
6982 line = 0;
6983 for (int i = 0;; i++) {
6984 db_enum_link(hDB, hkeyroot, i, &hkey);
6985 if (!hkey)
6986 break;
6987 db_get_link(hDB, hkey, &key);
6988
6989 if (scan == 0) {
6990 delete_list.push_back(key.name);
6991 }
6992
6993 if (line % 2 == 0)
6994 mstrlcpy(style, "ODBtableEven", sizeof(style));
6995 else
6996 mstrlcpy(style, "ODBtableOdd", sizeof(style));
6997
6998 std::string keyname = key.name;
6999 std::string enc_keyname = urlEncode(key.name);
7000
7001 std::string enc_full_path = enc_root_path + enc_keyname;
7002
7003 std::string odb_path = dec_path;
7004 if (odb_path.length() > 0 && odb_path[odb_path.length() - 1] != '/')
7005 odb_path += "/";
7006 odb_path += key.name;
7007
7008 /* resolve links */
7009 std::string enc_link_ref;
7010 char link_name[MAX_ODB_PATH];
7011 link_name[0] = 0;
7013 if (key.type == TID_LINK) {
7014 size = sizeof(link_name);
7015 db_get_link_data(hDB, hkey, link_name, &size, TID_LINK);
7016
7017 status = db_find_key(hDB, 0, link_name, &hkey);
7018
7019 if (status == DB_SUCCESS)
7020 db_get_key(hDB, hkey, &key);
7021
7022 //sprintf(link_ref, "?cmd=Set&odb_path=%s", full_path);
7023 enc_link_ref = "?cmd=Set&odb_path=";
7024 enc_link_ref += enc_full_path;
7025
7026 if (status == DB_SUCCESS && link_name[0] == 0) {
7027 // fake the case when an empty link somehow resolves
7028 sprintf(link_name, "%s", "(empty)");
7029 }
7030 }
7031
7032 std::string enc_ref;
7033
7034 if (link_name[0]) {
7035 if (enc_root_path.back() == '/' && link_name[0] == '/') {
7036 //sprintf(ref, "?cmd=Set&odb_path=%s%s", root_path, link_name+1);
7037 enc_ref = "";
7038 enc_ref += "?cmd=Set&odb_path=";
7039 enc_ref += enc_root_path;
7040 enc_ref += urlEncode(link_name + 1);
7041 } else {
7042 //sprintf(ref, "?cmd=Set&odb_path=%s%s", root_path, link_name);
7043 enc_ref = "";
7044 enc_ref += "?cmd=Set&odb_path=";
7045 enc_ref += enc_root_path;
7046 enc_ref += urlEncode(link_name);
7047 }
7048 } else {
7049 //sprintf(ref, "?cmd=Set&odb_path=%s", full_path);
7050 enc_ref = "";
7051 enc_ref += "?cmd=Set&odb_path=";
7052 enc_ref += enc_full_path;
7053 }
7054
7055 if (status != DB_SUCCESS) {
7056 if (scan == 1) {
7057 r->rsprintf("<tr><td class=\"yellowLight\">");
7058 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)");
7059 }
7060 } else {
7061
7062 if (key.type == TID_KEY && scan == 0) {
7063 /* for keys, don't display data value */
7064 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());
7065 if (link_name[0])
7066 r->rsprintf("<i>&rarr; <a href=\"%s\">%s</a></i>", enc_link_ref.c_str(), link_name);
7067 r->rsprintf("</tr>\n");
7068 } else if(key.type != TID_KEY && scan == 1) {
7069
7070 if (strchr(link_name, '['))
7071 link_index = atoi(strchr(link_name, '[')+1);
7072 else
7073 link_index = -1;
7074
7075 /* display single value */
7076 if (key.num_values == 1 || link_index != -1) {
7077 char data[TEXT_SIZE];
7078 size = sizeof(data);
7079 db_get_data(hDB, hkey, data, &size, key.type);
7080
7081 std::string data_str;
7082
7083 if (link_index != -1)
7084 data_str = db_sprintf(data, key.item_size, link_index, key.type);
7085 else
7086 data_str = db_sprintf(data, key.item_size, 0, key.type);
7087
7088 if (key.type == TID_STRING) {
7089 if (size == sizeof(data)) {
7090 data_str += "...(truncated)";
7091 }
7092 }
7093
7094 std::string hex_str;
7095
7096 if (key.type != TID_STRING) {
7097 if (link_index != -1)
7098 hex_str = db_sprintfh(data, key.item_size, link_index, key.type);
7099 else
7100 hex_str = db_sprintfh(data, key.item_size, 0, key.type);
7101 }
7102
7103 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
7104 data_str = "(empty)";
7105 hex_str = "";
7106 }
7107
7108 r->rsprintf("<tr>\n");
7109 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0]) {
7110 if (link_name[0]) {
7111 r->rsprintf("<td class=\"ODBkey\">\n");
7112 r->rsprintf("%s <i>&rarr; ", keyname.c_str());
7113 r->rsprintf("<a href=\"%s\">%s</a></i>\n", enc_link_ref.c_str(), link_name);
7114 r->rsprintf("<td class=\"%s\">\n", style);
7115 if (!write_access)
7116 r->rsprintf("%s (%s)", data_str.c_str(), hex_str.c_str());
7117 else {
7118 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7119 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">%s (%s)</a>\n", odb_path.c_str(), data_str.c_str(), hex_str.c_str());
7120 }
7121 } else {
7122 r->rsprintf("<td class=\"ODBkey\">\n");
7123 r->rsprintf("%s<td class=\"%s\">", keyname.c_str(), style);
7124 if (!write_access)
7125 r->rsprintf("%s (%s)", data_str.c_str(), hex_str.c_str());
7126 else {
7127 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7128 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">%s (%s)</a>\n", odb_path.c_str(), data_str.c_str(), hex_str.c_str());
7129 }
7130 }
7131 } else {
7132 if (strchr(data_str.c_str(), '\n')) {
7133 if (link_name[0]) {
7134 r->rsprintf("<td class=\"ODBkey\">");
7135 r->rsprintf("%s <i>&rarr; <a href=\"%s\">%s</a></i><td class=\"ODBvalue\">", keyname.c_str(), enc_link_ref.c_str(), link_name);
7136 } else
7137 r->rsprintf("<td class=\"ODBkey\">%s<td class=\"%s\">", keyname.c_str(), style);
7138 r->rsprintf("\n<pre>");
7139 strencode3(r, data_str.c_str());
7140 r->rsprintf("</pre>");
7141 if (strlen(data) > data_str.length())
7142 r->rsprintf("<i>... (%d bytes total)<p>\n", (int)strlen(data));
7143
7144 r->rsprintf("<a href=\"%s\">Edit</a>\n", enc_ref.c_str());
7145 } else {
7146 if (link_name[0]) {
7147 r->rsprintf("<td class=\"ODBkey\">\n");
7148 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);
7149 if (!write_access)
7150 strencode(r, data_str.c_str());
7151 else {
7152 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7153 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", odb_path.c_str());
7154 strencode(r, data_str.c_str());
7155 r->rsprintf("</a>\n");
7156 }
7157 } else {
7158 r->rsprintf("<td class=\"ODBkey\">%s<td class=\"%s\">", keyname.c_str(), style);
7159 if (!write_access) {
7160 strencode(r, data_str.c_str());
7161 } else {
7162 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), odb_path.c_str());
7163 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", odb_path.c_str());
7164 strencode(r, data_str.c_str());
7165 r->rsprintf("</a>\n");
7166 }
7167 }
7168 }
7169 }
7170
7171 /* extended key information */
7172 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7173 r->rsprintf("%s", rpc_tid_name(key.type));
7174 r->rsprintf("</td>\n");
7175
7176 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7177 r->rsprintf("%d", key.num_values);
7178 r->rsprintf("</td>\n");
7179
7180 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7181 r->rsprintf("%d", key.item_size);
7182 r->rsprintf("</td>\n");
7183
7184 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7185 db_get_key_time(hDB, hkey, &delta);
7186 if (delta < 60)
7187 r->rsprintf("%ds", delta);
7188 else if (delta < 3600)
7189 r->rsprintf("%1.0lfm", delta / 60.0);
7190 else if (delta < 86400)
7191 r->rsprintf("%1.0lfh", delta / 3600.0);
7192 else if (delta < 86400 * 99)
7193 r->rsprintf("%1.0lfd", delta / 86400.0);
7194 else
7195 r->rsprintf(">99d");
7196 r->rsprintf("</td>\n");
7197
7198 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\">");
7200 r->rsprintf("R");
7202 r->rsprintf("W");
7204 r->rsprintf("D");
7206 r->rsprintf("E");
7207 r->rsprintf("</td>\n");
7208
7209 line++;
7210 r->rsprintf("</tr>\n");
7211 } else { /* display array value */
7212 /* check for exceeding length */
7213 if (key.num_values > 1000 && !pp->isparam("all"))
7214 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);
7215 else {
7216 /* display first value */
7217 if (link_name[0])
7218 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);
7219 else
7220 r->rsprintf("<tr><td class=\"ODBkey\" rowspan=%d>%s\n", key.num_values, keyname.c_str());
7221
7222 for (int j = 0; j < key.num_values; j++) {
7223 if (line % 2 == 0)
7224 mstrlcpy(style, "ODBtableEven", sizeof(style));
7225 else
7226 mstrlcpy(style, "ODBtableOdd", sizeof(style));
7227
7228 char data[TEXT_SIZE];
7229 size = sizeof(data);
7230 db_get_data_index(hDB, hkey, data, &size, j, key.type);
7231 std::string data_str = db_sprintf(data, key.item_size, 0, key.type);
7232
7233 std::string hex_str;
7234 if (key.type == TID_STRING || key.type == TID_LINK) {
7235 hex_str = "";
7236 } else {
7237 hex_str = db_sprintfh(data, key.item_size, 0, key.type);
7238 }
7239
7240 if (key.type == TID_STRING) {
7241 if (size == sizeof(data)) {
7242 data_str += "...(truncated)";
7243 }
7244 }
7245
7246 if (data_str.empty() || equal_ustring(data_str.c_str(), "<NULL>")) {
7247 data_str = "(empty)";
7248 hex_str = "";
7249 }
7250
7251 //sprintf(ref, "?cmd=Set&odb_path=%s&index=%d", full_path, j);
7252 enc_ref = "";
7253 enc_ref += "?cmd=Set&odb_path=";
7254 enc_ref += enc_full_path;
7255 enc_ref += "&index=";
7256 enc_ref += toString(j);
7257
7258 std::string tmpstr;
7259 //sprintf(str, "%s[%d]", odb_path, j);
7260 tmpstr += odb_path;
7261 tmpstr += "[";
7262 tmpstr += toString(j);
7263 tmpstr += "]";
7264
7265 if (j > 0)
7266 r->rsprintf("<tr>");
7267
7268 r->rsprintf("<td class=\"%s\">[%d]&nbsp;", style, j);
7269 if (!write_access)
7270 r->rsprintf("<a href=\"%s\">", enc_ref.c_str());
7271 else {
7272 r->rsprintf("<a href=\"%s\" onClick=\"ODBInlineEdit(this.parentNode,\'%s\');return false;\" ", enc_ref.c_str(), tmpstr.c_str());
7273 r->rsprintf("onFocus=\"ODBInlineEdit(this.parentNode,\'%s\');\">", tmpstr.c_str());
7274 }
7275 if (strcmp(data_str.c_str(), hex_str.c_str()) != 0 && hex_str[0])
7276 r->rsprintf("%s (%s)</a>\n", data_str.c_str(), hex_str.c_str());
7277 else
7278 r->rsprintf("%s</a>\n", data_str.c_str());
7279
7280 if (j == 0) {
7281 /* extended key information */
7282 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7283 r->rsprintf("%s", rpc_tid_name(key.type));
7284 r->rsprintf("</td>\n");
7285
7286 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7287 r->rsprintf("%d", key.num_values);
7288 r->rsprintf("</td>\n");
7289
7290 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7291 r->rsprintf("%d", key.item_size);
7292 r->rsprintf("</td>\n");
7293
7294 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7295 db_get_key_time(hDB, hkey, &delta);
7296 if (delta < 60)
7297 r->rsprintf("%ds", delta);
7298 else if (delta < 3600)
7299 r->rsprintf("%1.0lfm", delta / 60.0);
7300 else if (delta < 86400)
7301 r->rsprintf("%1.0lfh", delta / 3600.0);
7302 else if (delta < 86400 * 99)
7303 r->rsprintf("%1.0lfh", delta / 86400.0);
7304 else
7305 r->rsprintf(">99d");
7306 r->rsprintf("</td>\n");
7307
7308 r->rsprintf("<td class=\"ODBkey\" name=\"ext\" style=\"display:none\" rowspan=%d>", key.num_values);
7310 r->rsprintf("R");
7312 r->rsprintf("W");
7314 r->rsprintf("D");
7316 r->rsprintf("E");
7317 r->rsprintf("</td>\n");
7318 }
7319 line++;
7320 }
7321
7322 r->rsprintf("</tr>\n");
7323 }
7324 }
7325 } else if(key.type != TID_KEY){
7326 keyPresent = 1; //flag that we've seen a key on the first pass, and should therefore write the Key / Value headline
7327 }
7328 }
7329 }
7330 }
7331 r->rsprintf("</table>\n");
7332 r->rsprintf("</div>\n"); // <div id="mmain">
7333
7334 /*---- Build the Delete dialog------------------------------------*/
7335
7336 std::sort(delete_list.begin(), delete_list.end());
7337
7338 for (unsigned i=0; i<delete_list.size(); i++) {
7339 std::string name = delete_list[i];
7340
7341 dd += "<tr><td style=\"text-align:left;\" align=left><input align=left type=checkbox id=delete";
7342 dd += toString(count_delete++);
7343 dd += " value=\'";
7344 dd += "\"";
7345 dd += MJsonNode::Encode(name.c_str());
7346 dd += "\"";
7347 dd += "\'>";
7348 dd += name;
7349 dd += "</input></td></tr>\n";
7350 }
7351
7352 dd += "</table>\n";
7353 dd += "<input type=button value=Delete onClick='mhttpd_delete_page_handle_delete(event);'>\n";
7354 dd += "<input type=button value=Cancel onClick='mhttpd_delete_page_handle_cancel(event);'>\n";
7355 dd += "</div>\n";
7356 dd += "</div>\n";
7357
7358 r->rsputs(dd.c_str());
7359
7360 /*---- Build the Create dialog------------------------------------*/
7361
7362 std::string cd = "";
7363
7364 cd += "<!-- Demo dialog -->\n";
7365 cd += "<div id=\"dlgCreate\" class=\"dlgFrame\">\n";
7366 cd += "<div class=\"dlgTitlebar\">Create ODB entry</div>\n";
7367 cd += "<div class=\"dlgPanel\">\n";
7368 cd += "<br />\n";
7369 cd += "<div id=odbpath>";
7370 cd += "\"";
7371 cd += MJsonNode::Encode(odbpath.c_str());
7372 cd += "\"";
7373 cd += "</div>\n";
7374 cd += "<div><br></div>\n";
7375
7376 cd += "<table class=\"dialogTable\">\n";
7377 cd += "<th colspan=2>Create ODB entry:</th>\n";
7378 cd += "<tr>";
7379 cd += "<td>Type";
7380 cd += "<td>";
7381 cd += "<select type=text size=1 id=create_tid name=type>";
7382 cd += "<option value=7>Integer (32-bit)";
7383 cd += "<option value=9>Float (4 Bytes)";
7384 cd += "<option value=12>String";
7385 cd += "<option selected value=15>Subdirectory";
7386 cd += "<option value=1>Byte";
7387 cd += "<option value=2>Signed byte";
7388 cd += "<option value=3>Character (8-bit)";
7389 cd += "<option value=4>Word (16-bit)";
7390 cd += "<option value=5>Short integer (16-bit)";
7391 cd += "<option value=6>Double Word (32-bit)";
7392 cd += "<option value=8>Boolean";
7393 cd += "<option value=10>Double float (8 Bytes)";
7394 //cd += "<option value=16>Symbolic link";
7395 cd += "</select>";
7396 cd += "</tr>\n";
7397 cd += "<tr><td>Name<td><input type=text size=31 maxlength=31 id=create_name name=value></tr>\n";
7398 cd += "<tr><td>Array size<td><input type=text size=31 maxlength=31 id=create_array_length name=index value=1></tr>\n";
7399 cd += "<tr><td>String length<td><input type=text size=31 maxlength=31 id=create_strlen name=strlen value=32></tr>\n";
7400 cd += "</table>\n";
7401 cd += "<input type=button value=Create onClick='mhttpd_create_page_handle_create(event);'>\n";
7402 cd += "<input type=button value=Cancel onClick='mhttpd_create_page_handle_cancel(event);'>\n";
7403 cd += "</div>\n";
7404 cd += "</div>\n";
7405
7406 r->rsputs(cd.c_str());
7407
7408 /*---- Build the Link dialog------------------------------------*/
7409
7410 std::string ld = "";
7411
7412 ld += "<!-- Demo dialog -->\n";
7413 ld += "<div id=\"dlgLink\" class=\"dlgFrame\">\n";
7414 ld += "<div class=\"dlgTitlebar\">Create a link to an ODB entry</div>\n";
7415 ld += "<div class=\"dlgPanel\">\n";
7416 ld += "<br />\n";
7417 ld += "<div id=link_odbpath>";
7418 ld += "\"";
7419 ld += MJsonNode::Encode(odbpath.c_str());
7420 ld += "\"";
7421 ld += "</div>\n";
7422 ld += "<div><br></div>\n";
7423
7424 ld += "<table class=\"dialogTable\">\n";
7425 ld += "<th colspan=2>Create a link to an ODB entry:</th>\n";
7426 ld += "<tr><td>Name<td><input type=text size=31 maxlength=31 id=link_name name=value></tr>\n";
7427 ld += "<tr><td>Link target<td><input type=text size=31 maxlength=256 id=link_target name=target></tr>\n";
7428 ld += "</table>\n";
7429 ld += "<input type=button value=Link onClick='mhttpd_link_page_handle_link(event);'>\n";
7430 ld += "<input type=button value=Cancel onClick='mhttpd_link_page_handle_cancel(event);'>\n";
7431 ld += "</div>\n";
7432 ld += "</div>\n";
7433
7434 r->rsputs(ld.c_str());
7435}
7436
7437/*------------------------------------------------------------------*/
7438
7440 const char *group,
7441 int index, const char *value)
7442{
7443 int status, size;
7444 HNDLE hDB, hkey;
7445 KEY key;
7446 char data[TEXT_SIZE];
7447
7448 std::string odb_path = pp->getparam("odb_path");
7449
7450 //printf("show_set_page: odb_path [%s] group [%s] index %d value [%s]\n", odb_path.c_str(), group, index, value);
7451
7453
7454 /* show set page if no value is given */
7455 if (!pp->isparam("value") && !*pp->getparam("text")) {
7456 status = db_find_link(hDB, 0, odb_path.c_str(), &hkey);
7457 if (status != DB_SUCCESS) {
7458 r->rsprintf("Error: cannot find key %s<P>\n", odb_path.c_str());
7459 return;
7460 }
7461 db_get_link(hDB, hkey, &key);
7462
7463 show_header(r, "Set value", "POST", "", 0);
7464 //close header:
7465 r->rsprintf("</table>");
7466
7467 //main table:
7468 r->rsprintf("<table class=\"dialogTable\">");
7469
7470 if (index > 0)
7471 r->rsprintf("<input type=hidden name=index value=\"%d\">\n", index);
7472 else
7473 index = 0;
7474
7475 if (group[0])
7476 r->rsprintf("<input type=hidden name=group value=\"%s\">\n", group);
7477
7478 r->rsprintf("<input type=hidden name=odb_path value=\"%s\">\n", odb_path.c_str());
7479
7480 std::string data_str1 = rpc_tid_name(key.type);
7481 std::string str1;
7482 if (key.num_values > 1) {
7483 data_str1 += msprintf("[%d]", key.num_values);
7484 str1 = msprintf("%s[%d]", odb_path.c_str(), index);
7485 } else
7486 str1 = odb_path.c_str();
7487
7488 r->rsprintf("<tr><th colspan=2>Set new value - type = %s</tr>\n", data_str1.c_str());
7489 r->rsprintf("<tr><td>%s<td>\n", str1.c_str());
7490
7491 /* set current value as default */
7492 size = sizeof(data);
7493 db_get_link_data(hDB, hkey, data, &size, key.type);
7494 std::string data_str = db_sprintf(data, key.item_size, index, key.type);
7495
7496 if (equal_ustring(data_str.c_str(), "<NULL>"))
7497 data_str = "";
7498
7499 if (strchr(data_str.c_str(), '\n') != NULL) {
7500 r->rsprintf("<textarea rows=20 cols=80 name=\"text\">\n");
7501 strencode3(r, data);
7502 r->rsprintf("</textarea>\n");
7503 } else {
7504 size = 20;
7505 if ((int) data_str.length() > size)
7506 size = data_str.length() + 3;
7507 if (size > 80)
7508 size = 80;
7509
7510 r->rsprintf("<input type=\"text\" size=%d maxlength=256 name=\"value\" value=\"", size);
7511 strencode(r, data_str.c_str());
7512 r->rsprintf("\">\n");
7513 }
7514
7515 r->rsprintf("</tr>\n");
7516
7517 r->rsprintf("<tr><td align=center colspan=2>");
7518 r->rsprintf("<input type=submit name=cmd value=Set>");
7519 r->rsprintf("<input type=submit name=cmd value=Cancel>");
7520 r->rsprintf("</tr>");
7521 r->rsprintf("</table>");
7522
7523 r->rsprintf("<input type=hidden name=cmd value=Set>\n");
7524
7525 r->rsprintf("</div>\n"); // closing for <div id="mmain">
7526 r->rsprintf("</form>\n");
7527 r->rsprintf("</body></html>\r\n");
7528 return;
7529 } else {
7530 /* set value */
7531
7532 status = db_find_link(hDB, 0, odb_path.c_str(), &hkey);
7533 if (status != DB_SUCCESS) {
7534 r->rsprintf("Error: cannot find key %s<P>\n", odb_path.c_str());
7535 return;
7536 }
7537 db_get_link(hDB, hkey, &key);
7538
7539 memset(data, 0, sizeof(data));
7540
7541 if (pp->getparam("text") && *pp->getparam("text"))
7542 mstrlcpy(data, pp->getparam("text"), sizeof(data));
7543 else
7544 db_sscanf(value, data, &size, 0, key.type);
7545
7546 if (index < 0)
7547 index = 0;
7548
7549 /* extend data size for single string if necessary */
7550 if ((key.type == TID_STRING || key.type == TID_LINK)
7551 && (int) strlen(data) + 1 > key.item_size && key.num_values == 1)
7552 key.item_size = strlen(data) + 1;
7553
7554 if (key.item_size == 0)
7556
7557 if (key.num_values > 1)
7559 else
7561
7562 if (status == DB_NO_ACCESS)
7563 r->rsprintf("<h2>Write access not allowed</h2>\n");
7564
7565 redirect(r, "");
7566
7567 return;
7568 }
7569}
7570
7571/*------------------------------------------------------------------*/
7572
7573void show_find_page(Return* r, const char *value)
7574{
7575 HNDLE hDB, hkey;
7576
7578
7579 if (value[0] == 0) {
7580 /* without value, show find dialog */
7581 show_header(r, "Find value", "GET", "", 0);
7582
7583 //end header:
7584 r->rsprintf("</table>");
7585
7586 //find dialog:
7587 r->rsprintf("<table class=\"dialogTable\">");
7588
7589 r->rsprintf("<tr><th colspan=2>Find string in Online Database</tr>\n");
7590 r->rsprintf("<tr><td>Enter substring (case insensitive)\n");
7591
7592 r->rsprintf("<td><input type=\"text\" size=\"20\" maxlength=\"80\" name=\"value\">\n");
7593 r->rsprintf("</tr>");
7594
7595 r->rsprintf("<tr><td align=center colspan=2>");
7596 r->rsprintf("<input type=submit name=cmd value=Find>");
7597 r->rsprintf("<input type=submit name=cmd value=Cancel>");
7598 r->rsprintf("</tr>");
7599 r->rsprintf("</table>");
7600
7601 r->rsprintf("<input type=hidden name=cmd value=Find>");
7602
7603 r->rsprintf("</div>\n"); // closing for <div id="mmain">
7604 r->rsprintf("</form>\n");
7605 r->rsprintf("</body></html>\r\n");
7606 } else {
7607 show_header(r, "Search results", "GET", "", 0);
7608
7609 r->rsprintf("<table class=\"mtable\">\n");
7610 r->rsprintf("<tr><th colspan=2 class=\"mtableheader\">");
7611 r->rsprintf("Results of search for substring \"%s\"</tr>\n", value);
7612 r->rsprintf("<tr><th class=\"titlerow\">Key<th>Value</tr>\n");
7613
7614 /* start from root */
7615 db_find_key(hDB, 0, "", &hkey);
7616 assert(hkey);
7617
7618 /* scan tree, call "search_callback" for each key */
7620 data.r = r;
7621 data.search_name = value;
7622
7623 db_scan_tree(hDB, hkey, 0, search_callback, (void *)&data);
7624
7625 r->rsprintf("</table>");
7626 r->rsprintf("</div>\n"); // closing for <div id="mmain">
7627 r->rsprintf("</form>\n");
7628 r->rsprintf("</body></html>\r\n");
7629 }
7630}
7631
7632/*------------------------------------------------------------------*/
7633
7634#define LN10 2.302585094
7635#define LOG2 0.301029996
7636#define LOG5 0.698970005
7637
7638void haxis(gdImagePtr im, gdFont * font, int col, int gcol,
7639 int x1, int y1, int width,
7640 int minor, int major, int text, int label, int grid, double xmin, double xmax)
7641{
7642 double dx, int_dx, frac_dx, x_act, label_dx, major_dx, x_screen, maxwidth;
7643 int tick_base, major_base, label_base, n_sig1, n_sig2, xs;
7644 char str[80];
7645 double base[] = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
7646
7647 if (xmax <= xmin || width <= 0)
7648 return;
7649
7650 /* use 5 as min tick distance */
7651 dx = (xmax - xmin) / (double) (width / 5);
7652
7653 frac_dx = modf(log(dx) / LN10, &int_dx);
7654 if (frac_dx < 0) {
7655 frac_dx += 1;
7656 int_dx -= 1;
7657 }
7658
7659 tick_base = frac_dx < LOG2 ? 1 : frac_dx < LOG5 ? 2 : 3;
7660 major_base = label_base = tick_base + 1;
7661
7662 /* rounding up of dx, label_dx */
7663 dx = pow(10, int_dx) * base[tick_base];
7664 major_dx = pow(10, int_dx) * base[major_base];
7665 label_dx = major_dx;
7666
7667 /* number of significant digits */
7668 if (xmin == 0)
7669 n_sig1 = 0;
7670 else
7671 n_sig1 = (int) floor(log(fabs(xmin)) / LN10) - (int) floor(log(fabs(label_dx)) / LN10) + 1;
7672
7673 if (xmax == 0)
7674 n_sig2 = 0;
7675 else
7676 n_sig2 =
7677 (int) floor(log(fabs(xmax)) / LN10) - (int) floor(log(fabs(label_dx)) / LN10) + 1;
7678
7679 n_sig1 = MAX(n_sig1, n_sig2);
7680 n_sig1 = MAX(n_sig1, 4);
7681
7682 /* determination of maximal width of labels */
7683 sprintf(str, "%1.*lG", n_sig1, floor(xmin / dx) * dx);
7684 maxwidth = font->h / 2 * strlen(str);
7685 sprintf(str, "%1.*lG", n_sig1, floor(xmax / dx) * dx);
7686 maxwidth = MAX(maxwidth, font->h / 2 * strlen(str));
7687 sprintf(str, "%1.*lG", n_sig1, floor(xmax / dx) * dx + label_dx);
7688 maxwidth = MAX(maxwidth, font->h / 2 * strlen(str));
7689
7690 /* increasing label_dx, if labels would overlap */
7691 while (maxwidth > 0.7 * label_dx / (xmax - xmin) * width) {
7692 label_base++;
7693 label_dx = pow(10, int_dx) * base[label_base];
7694 if (label_base % 3 == 2 && major_base % 3 == 1) {
7695 major_base++;
7696 major_dx = pow(10, int_dx) * base[major_base];
7697 }
7698 }
7699
7700 x_act = floor(xmin / dx) * dx;
7701
7702 gdImageLine(im, x1, y1, x1 + width, y1, col);
7703
7704 do {
7705 x_screen = (x_act - xmin) / (xmax - xmin) * width + x1;
7706 xs = (int) (x_screen + 0.5);
7707
7708 if (x_screen > x1 + width + 0.001)
7709 break;
7710
7711 if (x_screen >= x1) {
7712 if (fabs(floor(x_act / major_dx + 0.5) - x_act / major_dx) <
7713 dx / major_dx / 10.0) {
7714
7715 if (fabs(floor(x_act / label_dx + 0.5) - x_act / label_dx) <
7716 dx / label_dx / 10.0) {
7717 /* label tick mark */
7718 gdImageLine(im, xs, y1, xs, y1 + text, col);
7719
7720 /* grid line */
7721 if (grid != 0 && xs > x1 && xs < x1 + width)
7722 gdImageLine(im, xs, y1, xs, y1 + grid, col);
7723
7724 /* label */
7725 if (label != 0) {
7726 sprintf(str, "%1.*lG", n_sig1, x_act);
7727 gdImageString(im, font, (int) xs - font->w * strlen(str) / 2,
7728 y1 + label, str, col);
7729 }
7730 } else {
7731 /* major tick mark */
7732 gdImageLine(im, xs, y1, xs, y1 + major, col);
7733
7734 /* grid line */
7735 if (grid != 0 && xs > x1 && xs < x1 + width)
7736 gdImageLine(im, xs, y1 - 1, xs, y1 + grid, gcol);
7737 }
7738
7739 } else
7740 /* minor tick mark */
7741 gdImageLine(im, xs, y1, xs, y1 + minor, col);
7742
7743 }
7744
7745 x_act += dx;
7746
7747 /* supress 1.23E-17 ... */
7748 if (fabs(x_act) < dx / 100)
7749 x_act = 0;
7750
7751 } while (1);
7752}
7753
7754/*------------------------------------------------------------------*/
7755
7756void sec_to_label(char *result, int sec, int base, int force_date)
7757{
7758 char mon[80];
7759 time_t t_sec;
7760
7761 t_sec = (time_t) sec;
7762
7763 struct tm tms;
7764 localtime_r(&t_sec, &tms);
7765 strcpy(mon, mname[tms.tm_mon]);
7766 mon[3] = 0;
7767
7768 if (force_date) {
7769 if (base < 600)
7770 sprintf(result, "%02d %s %02d %02d:%02d:%02d",
7771 tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min,
7772 tms.tm_sec);
7773 else if (base < 3600 * 24)
7774 sprintf(result, "%02d %s %02d %02d:%02d",
7775 tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min);
7776 else
7777 sprintf(result, "%02d %s %02d", tms.tm_mday, mon, tms.tm_year % 100);
7778 } else {
7779 if (base < 600)
7780 sprintf(result, "%02d:%02d:%02d", tms.tm_hour, tms.tm_min, tms.tm_sec);
7781 else if (base < 3600 * 3)
7782 sprintf(result, "%02d:%02d", tms.tm_hour, tms.tm_min);
7783 else if (base < 3600 * 24)
7784 sprintf(result, "%02d %s %02d %02d:%02d",
7785 tms.tm_mday, mon, tms.tm_year % 100, tms.tm_hour, tms.tm_min);
7786 else
7787 sprintf(result, "%02d %s %02d", tms.tm_mday, mon, tms.tm_year % 100);
7788 }
7789}
7790
7791void taxis(gdImagePtr im, gdFont * font, int col, int gcol,
7792 int x1, int y1, int width, int xr,
7793 int minor, int major, int text, int label, int grid, double xmin, double xmax)
7794{
7795 int dx, x_act, label_dx, major_dx, x_screen, maxwidth;
7796 int tick_base, major_base, label_base, xs, xl;
7797 char str[80];
7798 const int base[] = { 1, 5, 10, 60, 300, 600, 1800, 3600, 3600 * 6, 3600 * 12, 3600 * 24, 0 };
7799 time_t ltime;
7800 int force_date, d1, d2;
7801 struct tm tms;
7802
7803 if (xmax <= xmin || width <= 0)
7804 return;
7805
7806 /* force date display if xmax not today */
7807 ltime = ss_time();
7808 localtime_r(&ltime, &tms);
7809 d1 = tms.tm_mday;
7810 ltime = (time_t) xmax;
7811 localtime_r(&ltime, &tms);
7812 d2 = tms.tm_mday;
7813 force_date = (d1 != d2);
7814
7815 /* use 5 pixel as min tick distance */
7816 dx = (int) ((xmax - xmin) / (double) (width / 5) + 0.5);
7817
7818 for (tick_base = 0; base[tick_base]; tick_base++) {
7819 if (base[tick_base] > dx)
7820 break;
7821 }
7822 if (!base[tick_base])
7823 tick_base--;
7824 dx = base[tick_base];
7825
7826 if (base[tick_base + 1])
7827 major_base = tick_base + 1;
7828 else
7829 major_base = tick_base;
7830 major_dx = base[major_base];
7831
7832 if (base[major_base + 1])
7833 label_base = major_base + 1;
7834 else
7835 label_base = major_base;
7836 label_dx = base[label_base];
7837
7838 do {
7839 sec_to_label(str, (int) (xmin + 0.5), label_dx, force_date);
7840 maxwidth = font->h / 2 * strlen(str);
7841
7842 /* increasing label_dx, if labels would overlap */
7843 if (maxwidth > 0.7 * label_dx / (xmax - xmin) * width) {
7844 if (base[label_base + 1])
7845 label_dx = base[++label_base];
7846 else
7847 label_dx += 3600 * 24;
7848 } else
7849 break;
7850 } while (1);
7851
7852 x_act =
7853 (int) floor((double) (xmin - ss_timezone()) / label_dx) * label_dx + ss_timezone();
7854
7855 gdImageLine(im, x1, y1, x1 + width, y1, col);
7856
7857 do {
7858 x_screen = (int) ((x_act - xmin) / (xmax - xmin) * width + x1 + 0.5);
7859 xs = (int) (x_screen + 0.5);
7860
7861 if (x_screen > x1 + width + 0.001)
7862 break;
7863
7864 if (x_screen >= x1) {
7865 if ((x_act - ss_timezone()) % major_dx == 0) {
7866 if ((x_act - ss_timezone()) % label_dx == 0) {
7867 /* label tick mark */
7868 gdImageLine(im, xs, y1, xs, y1 + text, col);
7869
7870 /* grid line */
7871 if (grid != 0 && xs > x1 && xs < x1 + width)
7872 gdImageLine(im, xs, y1, xs, y1 + grid, col);
7873
7874 /* label */
7875 if (label != 0) {
7876 sec_to_label(str, x_act, label_dx, force_date);
7877
7878 /* if labels at edge, shift them in */
7879 xl = (int) xs - font->w * strlen(str) / 2;
7880 if (xl < 0)
7881 xl = 0;
7882 if (xl + font->w * (int) strlen(str) > xr)
7883 xl = xr - font->w * strlen(str);
7884 gdImageString(im, font, xl, y1 + label, str, col);
7885 }
7886 } else {
7887 /* major tick mark */
7888 gdImageLine(im, xs, y1, xs, y1 + major, col);
7889
7890 /* grid line */
7891 if (grid != 0 && xs > x1 && xs < x1 + width)
7892 gdImageLine(im, xs, y1 - 1, xs, y1 + grid, gcol);
7893 }
7894
7895 } else
7896 /* minor tick mark */
7897 gdImageLine(im, xs, y1, xs, y1 + minor, col);
7898
7899 }
7900
7901 x_act += dx;
7902
7903 /* supress 1.23E-17 ... */
7904 if (fabs((double)x_act) < dx / 100)
7905 x_act = 0;
7906
7907 } while (1);
7908}
7909
7910/*------------------------------------------------------------------*/
7911
7912int vaxis(gdImagePtr im, gdFont * font, int col, int gcol,
7913 int x1, int y1, int width,
7914 int minor, int major, int text, int label, int grid, double ymin, double ymax,
7915 BOOL logaxis)
7916{
7917 double dy, int_dy, frac_dy, y_act, label_dy, major_dy, y_screen, y_next;
7918 int tick_base, major_base, label_base, n_sig1, n_sig2, ys, max_width;
7919 int last_label_y;
7920 char str[80];
7921 const double base[] = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
7922
7923 if (ymax <= ymin || width <= 0)
7924 return 0;
7925
7926 // 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
7927 if (fabs(ymax - ymin) <= 1e-10)
7928 return 0;
7929
7930 if (logaxis) {
7931 dy = pow(10, floor(log(ymin) / LN10));
7932 label_dy = dy;
7933 major_dy = dy * 10;
7934 n_sig1 = 4;
7935 } else {
7936 dy = (ymax - ymin) / (double) (width / 5);
7937
7938 frac_dy = modf(log(dy) / LN10, &int_dy);
7939 if (frac_dy < 0) {
7940 frac_dy += 1;
7941 int_dy -= 1;
7942 }
7943
7944 tick_base = frac_dy < LOG2 ? 1 : frac_dy < LOG5 ? 2 : 3;
7945 major_base = label_base = tick_base + 1;
7946
7947 /* rounding up of dy, label_dy */
7948 dy = pow(10, int_dy) * base[tick_base];
7949 major_dy = pow(10, int_dy) * base[major_base];
7950 label_dy = major_dy;
7951
7952 /* number of significant digits */
7953 if (ymin == 0)
7954 n_sig1 = 0;
7955 else
7956 n_sig1 =
7957 (int) floor(log(fabs(ymin)) / LN10) -
7958 (int) floor(log(fabs(label_dy)) / LN10) + 1;
7959
7960 if (ymax == 0)
7961 n_sig2 = 0;
7962 else
7963 n_sig2 =
7964 (int) floor(log(fabs(ymax)) / LN10) -
7965 (int) floor(log(fabs(label_dy)) / LN10) + 1;
7966
7967 n_sig1 = MAX(n_sig1, n_sig2);
7968 n_sig1 = MAX(n_sig1, 4);
7969
7970 /* increasing label_dy, if labels would overlap */
7971 while (label_dy / (ymax - ymin) * width < 1.5 * font->h) {
7972 label_base++;
7973 label_dy = pow(10, int_dy) * base[label_base];
7974 if (label_base % 3 == 2 && major_base % 3 == 1) {
7975 major_base++;
7976 major_dy = pow(10, int_dy) * base[major_base];
7977 }
7978 }
7979 }
7980
7981 max_width = 0;
7982 y_act = floor(ymin / dy) * dy;
7983
7984 if (x1 != 0 || y1 != 0)
7985 gdImageLine(im, x1, y1, x1, y1 - width, col);
7986
7987 last_label_y = y1 + 2 * font->h;
7988
7989 do {
7990 if (logaxis)
7991 y_screen = y1 - (log(y_act) - log(ymin)) / (log(ymax) - log(ymin)) * width;
7992 else
7993 y_screen = y1 - (y_act - ymin) / (ymax - ymin) * width;
7994 ys = (int) (y_screen + 0.5);
7995
7996 if (y_screen < y1 - width - 0.001)
7997 break;
7998
7999 if (y_screen <= y1 + 0.001) {
8000 if (fabs(floor(y_act / major_dy + 0.5) - y_act / major_dy) <
8001 dy / major_dy / 10.0) {
8002 if (fabs(floor(y_act / label_dy + 0.5) - y_act / label_dy) <
8003 dy / label_dy / 10.0) {
8004 if (x1 != 0 || y1 != 0) {
8005 /* label tick mark */
8006 gdImageLine(im, x1, ys, x1 + text, ys, col);
8007
8008 /* grid line */
8009 if (grid != 0 && y_screen < y1 && y_screen > y1 - width) {
8010 if (grid > 0)
8011 gdImageLine(im, x1 + 1, ys, x1 + grid, ys, gcol);
8012 else
8013 gdImageLine(im, x1 - 1, ys, x1 + grid, ys, gcol);
8014 }
8015
8016 /* label */
8017 if (label != 0) {
8018 sprintf(str, "%1.*lG", n_sig1, y_act);
8019 if (label < 0)
8020 gdImageString(im, font, x1 + label - font->w * strlen(str),
8021 ys - font->h / 2, str, col);
8022 else
8023 gdImageString(im, font, x1 + label, ys - font->h / 2, str, col);
8024
8025 last_label_y = ys - font->h / 2;
8026 }
8027 } else {
8028 sprintf(str, "%1.*lG", n_sig1, y_act);
8029 max_width = MAX(max_width, (int) (font->w * strlen(str)));
8030 }
8031 } else {
8032 if (x1 != 0 || y1 != 0) {
8033 /* major tick mark */
8034 gdImageLine(im, x1, ys, x1 + major, ys, col);
8035
8036 /* grid line */
8037 if (grid != 0 && y_screen < y1 && y_screen > y1 - width)
8038 gdImageLine(im, x1, ys, x1 + grid, ys, col);
8039 }
8040 }
8041 if (logaxis) {
8042 dy *= 10;
8043 major_dy *= 10;
8044 label_dy *= 10;
8045 }
8046
8047 } else {
8048 if (x1 != 0 || y1 != 0) {
8049 /* minor tick mark */
8050 gdImageLine(im, x1, ys, x1 + minor, ys, col);
8051 }
8052
8053 /* for logaxis, also put labes on minor tick marks */
8054 if (logaxis) {
8055 if (label != 0) {
8056 if (x1 != 0 || y1 != 0) {
8057 /* calculate position of next major label */
8058 y_next = pow(10, floor(log(y_act) / LN10) + 1);
8059 y_screen =
8060 (int) (y1 -
8061 (log(y_next) - log(ymin)) / (log(ymax) -
8062 log(ymin)) * width + 0.5);
8063
8064 if (ys + font->h / 2 < last_label_y
8065 && ys - font->h / 2 > y_screen + font->h / 2) {
8066 sprintf(str, "%1.*lG", n_sig1, y_act);
8067 if (label < 0)
8068 gdImageString(im, font, x1 + label - font->w * strlen(str),
8069 ys - font->h / 2, str, col);
8070 else
8071 gdImageString(im, font, x1 + label, ys - font->h / 2, str,
8072 col);
8073 }
8074
8075 last_label_y = ys - font->h / 2;
8076 } else {
8077 sprintf(str, "%1.*lG", n_sig1, y_act);
8078 max_width = MAX(max_width, (int) (font->w * strlen(str)));
8079 }
8080 }
8081 }
8082 }
8083 }
8084
8085 y_act += dy;
8086
8087 /* supress 1.23E-17 ... */
8088 if (fabs(y_act) < dy / 100)
8089 y_act = 0;
8090
8091 } while (1);
8092
8093 return max_width + abs(label);
8094}
8095
8096/*------------------------------------------------------------------*/
8097
8098int time_to_sec(const char *str)
8099{
8100 double s;
8101
8102 s = atof(str);
8103 switch (str[strlen(str) - 1]) {
8104 case 'm':
8105 case 'M':
8106 s *= 60;
8107 break;
8108 case 'h':
8109 case 'H':
8110 s *= 3600;
8111 break;
8112 case 'd':
8113 case 'D':
8114 s *= 3600 * 24;
8115 break;
8116 }
8117
8118 return (int) s;
8119}
8120
8121/*------------------------------------------------------------------*/
8122
8123time_t string_to_time(const char *str)
8124{
8125 time_t t = 0;
8126 for (; *str != 0; str++) {
8127 if (*str < '0')
8128 break;
8129 if (*str > '9')
8130 break;
8131 t *= 10;
8132 t += *str - '0';
8133 }
8134 return t;
8135}
8136
8137/*------------------------------------------------------------------*/
8138
8139std::string time_to_string(time_t t)
8140{
8141 char buf[256];
8142 sprintf(buf, "%.0f", (double)t);
8143 return buf;
8144}
8145
8146/*------------------------------------------------------------------*/
8147
8148static bool gDoSetupHistoryWatch = true;
8149static bool gDoReloadHistory = false;
8150
8152{
8153 //printf("history_watch_callback %d %d %d\n", hDB, hKey, index);
8154 gDoReloadHistory = true;
8155 cm_msg(MINFO, "history_watch_callback", "History configuration may have changed, will reconnect");
8156}
8157
8159static HNDLE gMhkey = 0;
8160
8161/*------------------------------------------------------------------*/
8162
8163static MidasHistoryInterface* get_history(bool reset = false)
8164{
8165 int status;
8166 HNDLE hDB;
8167
8168 // history reconnect requested by watch callback?
8169
8170 if (gDoReloadHistory) {
8171 gDoReloadHistory = false;
8172 reset = true;
8173 }
8174
8175 // disconnect from previous history
8176
8177 if (reset && gMh) {
8178 gMh->hs_disconnect();
8179 delete gMh;
8180 gMh = NULL;
8181 gMhkey = 0;
8182 }
8183
8185 assert(status == CM_SUCCESS);
8186
8187 // setup a watch on history configuration
8188
8190 HNDLE hKey;
8191 gDoSetupHistoryWatch = false;
8192
8193 status = db_find_key(hDB, 0, "/Logger/History", &hKey);
8194 if (status == DB_SUCCESS)
8196
8197 status = db_find_key(hDB, 0, "/History/LoggerHistoryChannel", &hKey);
8198 if (status == DB_SUCCESS)
8200 }
8201
8202 // find out if ODB settings have changed and we need to connect to a different history channel
8203
8204 HNDLE hKey = 0;
8206 if (status != HS_SUCCESS)
8207 return gMh;
8208
8209 //printf("mh %p, hKey %d, mhkey %d\n", mh, hKey, mhkey);
8210
8211 if (gMh && hKey == gMhkey) // same channel as before
8212 return gMh;
8213
8214 if (gMh) {
8215 delete gMh;
8216 gMh = NULL;
8217 gMhkey = 0;
8218 }
8219
8221 if (status != HS_SUCCESS || gMh==NULL) {
8222 cm_msg(MERROR, "get_history", "Cannot configure history, hs_get_history() status %d", status);
8223 gMh = NULL;
8224 return NULL;
8225 }
8226
8227 gMhkey = hKey;
8228
8229 // cm_msg(MINFO, "get_history", "Reading history from channel \'%s\' type \'%s\'", mh->name, mh->type);
8230
8231 return gMh;
8232}
8233
8234/*------------------------------------------------------------------*/
8235
8236#ifdef OS_WINNT
8237#undef DELETE
8238#endif
8239
8240#define ALLOC(t,n) (t*)calloc(sizeof(t),(n))
8241#define DELETE(x) if (x) { free(x); (x)=NULL; }
8242#define DELETEA(x, n) if (x) { for (int i=0; i<(n); i++) { free((x)[i]); (x)[i]=NULL; }; DELETE(x); }
8243#define STRDUP(x) strdup(x)
8244
8246{
8255 time_t** t;
8256 double** v;
8257
8260
8261 time_t tstart;
8262 time_t tend;
8263 time_t scale;
8264
8265 void Allocate(int xnvars) {
8266 if (alloc_nvars > 0)
8267 Free();
8268 nvars = 0;
8269 alloc_nvars = xnvars;
8270 event_names = ALLOC(char*, alloc_nvars);
8271 var_names = ALLOC(char*, alloc_nvars);
8272 var_index = ALLOC(int, alloc_nvars);
8273 odb_index = ALLOC(int, alloc_nvars);
8274 status = ALLOC(int, alloc_nvars);
8276 t = ALLOC(time_t*, alloc_nvars);
8277 v = ALLOC(double*, alloc_nvars);
8278
8279 have_last_written = false;
8280 last_written = ALLOC(time_t, alloc_nvars);
8281 }
8282
8283 void Free() {
8288 DELETE(status);
8293 nvars = 0;
8294 alloc_nvars = 0;
8295 have_last_written = false;
8296 }
8297
8298 void Print() const {
8299 printf("this %p, nvars %d. tstart %d, tend %d, scale %d\n", this, nvars, (int)tstart, (int)tend, (int)scale);
8300 for (int i=0; i<nvars; i++) {
8301 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]);
8302 if (status[i]==HS_SUCCESS && num_entries[i]>0 && t[i] && v[i])
8303 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]);
8304 printf(" last_written %d", (int)last_written[i]);
8305 printf("\n");
8306 }
8307 }
8308
8309 HistoryData() // ctor
8310 {
8311 nvars = 0;
8312 alloc_nvars = 0;
8313 have_last_written = false;
8314 tstart = 0;
8315 tend = 0;
8316 scale = 0;
8317 }
8318
8320 {
8321 if (alloc_nvars > 0)
8322 Free();
8323 }
8324};
8325
8326#define READ_HISTORY_DATA 0x1
8327#define READ_HISTORY_RUNMARKER 0x2
8328#define READ_HISTORY_LAST_WRITTEN 0x4
8329
8331{
8332 std::string event_name;
8333 std::string tag_name;
8334 std::string formula;
8335 std::string colour;
8336 std::string label;
8337 bool show_raw_value = false;
8338 int order = -1;
8339 double factor = 1.0;
8340 double offset = 0;
8341 double voffset = 0;
8342};
8343
8345{
8346 std::string timescale = "1h";
8347 double minimum = 0;
8348 double maximum = 0;
8349 bool zero_ylow = false;
8350 bool log_axis = false;
8351 bool show_run_markers = true;
8352 bool show_values = true;
8353 bool show_fill = true;
8354 bool show_factor = false;
8355 bool enable_factor = true;
8356
8357 std::vector<HistVar> vars;
8358};
8359
8360static void LoadHistPlotFromOdb(MVOdb* odb, HistPlot* hp, const char* group, const char* panel);
8361
8362int 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)
8363{
8364 //HNDLE hkeypanel, hkeydvar, hkey;
8365 //KEY key;
8366 //char path[256];
8367 //int n_vars;
8368 int status;
8369 int debug = 1;
8370
8371 //mstrlcpy(path, group, sizeof(path));
8372 //mstrlcat(path, "/", sizeof(path));
8373 //mstrlcat(path, panel, sizeof(path));
8374
8375 //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);
8376
8377 /* connect to history */
8379 if (mh == NULL) {
8380 //r->rsprintf(str, "History is not configured\n");
8381 return HS_FILE_ERROR;
8382 }
8383
8384#if 0
8385 /* check panel name in ODB */
8386 status = db_find_key(hDB, 0, "/History/Display", &hkey);
8387 if (!hkey) {
8388 cm_msg(MERROR, "read_history", "Cannot find \'/History/Display\' in ODB, status %d", status);
8389 return HS_FILE_ERROR;
8390 }
8391
8392 /* check panel name in ODB */
8393 status = db_find_key(hDB, hkey, path, &hkeypanel);
8394 if (!hkeypanel) {
8395 cm_msg(MERROR, "read_history", "Cannot find \'%s\' in ODB, status %d", path, status);
8396 return HS_FILE_ERROR;
8397 }
8398
8399 status = db_find_key(hDB, hkeypanel, "Variables", &hkeydvar);
8400 if (!hkeydvar) {
8401 cm_msg(MERROR, "read_history", "Cannot find \'%s/Variables\' in ODB, status %d", path, status);
8402 return HS_FILE_ERROR;
8403 }
8404
8405 db_get_key(hDB, hkeydvar, &key);
8406 n_vars = key.num_values;
8407#endif
8408
8409 data->Allocate(hp.vars.size()+2);
8410
8411 data->tstart = tstart;
8412 data->tend = tend;
8413 data->scale = scale;
8414
8415 for (size_t i=0; i<hp.vars.size(); i++) {
8416 if (index != -1 && (size_t)index != i)
8417 continue;
8418
8419 //char str[256];
8420 //int size = sizeof(str);
8421 //status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
8422 //if (status != DB_SUCCESS) {
8423 // cm_msg(MERROR, "read_history", "Cannot read tag %d in panel %s, status %d", i, path, status);
8424 // continue;
8425 //}
8426
8427 /* split varname in event, variable and index: "event/tag[index]" */
8428
8429 //char *p = strchr(str, ':');
8430 //if (!p)
8431 // p = strchr(str, '/');
8432 //
8433 //if (!p) {
8434 // cm_msg(MERROR, "read_history", "Tag \"%s\" has wrong format in panel \"%s\"", str, path);
8435 // continue;
8436 //}
8437
8438 //*p = 0;
8439
8440 data->odb_index[data->nvars] = i;
8441 data->event_names[data->nvars] = STRDUP(hp.vars[i].event_name.c_str());
8442 data->var_names[data->nvars] = STRDUP(hp.vars[i].tag_name.c_str());
8443 data->var_index[data->nvars] = 0;
8444
8445 char *q = strchr(data->var_names[data->nvars], '[');
8446 if (q) {
8447 data->var_index[data->nvars] = atoi(q+1);
8448 *q = 0;
8449 }
8450
8451 data->nvars++;
8452 } // loop over variables
8453
8454 /* write run markes if selected */
8455 if (flags & READ_HISTORY_RUNMARKER) {
8456
8457 data->event_names[data->nvars+0] = STRDUP("Run transitions");
8458 data->event_names[data->nvars+1] = STRDUP("Run transitions");
8459
8460 data->var_names[data->nvars+0] = STRDUP("State");
8461 data->var_names[data->nvars+1] = STRDUP("Run number");
8462
8463 data->var_index[data->nvars+0] = 0;
8464 data->var_index[data->nvars+1] = 0;
8465
8466 data->odb_index[data->nvars+0] = -1;
8467 data->odb_index[data->nvars+1] = -2;
8468
8469 data->nvars += 2;
8470 }
8471
8472 bool get_last_written = false;
8473
8474 if (flags & READ_HISTORY_DATA) {
8475 status = mh->hs_read(tstart, tend, scale,
8476 data->nvars,
8477 data->event_names,
8478 data->var_names,
8479 data->var_index,
8480 data->num_entries,
8481 data->t,
8482 data->v,
8483 data->status);
8484
8485 if (debug) {
8486 printf("read_history: nvars %d, hs_read() status %d\n", data->nvars, status);
8487 for (int i=0; i<data->nvars; i++) {
8488 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]);
8489 }
8490 }
8491
8492 if (status != HS_SUCCESS) {
8493 cm_msg(MERROR, "read_history", "Complete history failure, hs_read() status %d, see messages", status);
8494 return HS_FILE_ERROR;
8495 }
8496
8497 for (int i=0; i<data->nvars; i++) {
8498 if (data->status[i] != HS_SUCCESS || data->num_entries[i] < 1) {
8499 get_last_written = true;
8500 break;
8501 }
8502 }
8503 }
8504
8505 if (flags & READ_HISTORY_LAST_WRITTEN)
8506 get_last_written = true;
8507
8508 if (get_last_written) {
8509 data->have_last_written = true;
8510
8512 tstart,
8513 data->nvars,
8514 data->event_names,
8515 data->var_names,
8516 data->var_index,
8517 data->last_written);
8518
8519 if (status != HS_SUCCESS) {
8520 data->have_last_written = false;
8521 }
8522 }
8523
8524 return SUCCESS;
8525}
8526
8527int get_hist_last_written(MVOdb* odb, const char *group, const char *panel, time_t endtime, int index, int want_all, time_t *plastwritten)
8528{
8529 //HNDLE hDB;
8530 int status;
8531
8532 time_t now = ss_time();
8533
8534 if (endtime == 0)
8535 endtime = now;
8536
8537 HistoryData hsxxx;
8538 HistoryData* hsdata = &hsxxx;
8539
8540 //cm_get_experiment_database(&hDB, NULL);
8541
8542 HistPlot hp;
8543 LoadHistPlotFromOdb(odb, &hp, group, panel);
8544
8545 double tstart = ss_millitime();
8546
8547 int flags = READ_HISTORY_LAST_WRITTEN;
8548
8549 status = read_history(hp, /*hDB, group, panel,*/ index, flags, endtime, endtime, 0, hsdata);
8550
8551 if (status != HS_SUCCESS) {
8552 //sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
8553 return status;
8554 }
8555
8556 if (!hsdata->have_last_written) {
8557 //sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
8558 return HS_FILE_ERROR;
8559 }
8560
8561 int count = 0;
8562 time_t tmin = endtime;
8563 time_t tmax = 0;
8564
8565 for (int k=0; k<hsdata->nvars; k++) {
8566 int i = hsdata->odb_index[k];
8567
8568 if (i<0)
8569 continue;
8570 if (index != -1 && index != i)
8571 continue;
8572
8573 time_t lw = hsdata->last_written[k];
8574
8575 if (lw==0) // no last_written for this variable, skip it.
8576 continue;
8577
8578 if (lw > endtime)
8579 lw = endtime; // just in case hs_get_last_written() returns dates in the "future" for this plot
8580
8581 if (lw > tmax)
8582 tmax = lw;
8583
8584 if (lw < tmin)
8585 tmin = lw;
8586
8587 count++;
8588
8589 //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);
8590 }
8591
8592 if (count == 0) // all variables have no last_written
8593 return HS_FILE_ERROR;
8594
8595 if (want_all)
8596 *plastwritten = tmin; // all variables have data
8597 else
8598 *plastwritten = tmax; // at least one variable has data
8599
8600 //printf("tmin %.0f, tmax %.0f, endtime %.0f, last written %.0f\n", (double)tmin, (double)tmax, (double)endtime, (double)*plastwritten);
8601
8602 double tend = ss_millitime();
8603
8604 if (/* DISABLES CODE */ (0))
8605 printf("get_hist_last_written: elapsed time %f ms\n", tend-tstart);
8606
8607 return HS_SUCCESS;
8608}
8609
8610void generate_hist_graph(MVOdb* odb, Return* rr, const char *hgroup, const char *hpanel, char *buffer, int *buffer_size,
8611 int width, int height,
8612 time_t xendtime,
8613 int scale,
8614 int index,
8615 int labels, const char *bgcolor, const char *fgcolor, const char *gridcolor)
8616{
8617 HNDLE hDB;
8618 //KEY key;
8619 gdImagePtr im;
8620 gdGifBuffer gb;
8621 int i, j, k, l;
8622 //int n_vars;
8623 int size, status, r, g, b;
8624 //int x_marker;
8625 int length;
8626 int white, grey, red;
8627 //int black, ltgrey, green, blue;
8628 int fgcol, bgcol, gridcol;
8629 int curve_col[MAX_VARS];
8630 int state_col[3];
8631 char str[256], *p;
8632 //INT var_index[MAX_VARS];
8633 //char event_name[MAX_VARS][NAME_LENGTH];
8634 //char tag_name[MAX_VARS][64];
8635 //char var_name[MAX_VARS][NAME_LENGTH];
8636 //char varname[64];
8637 //char key_name[256];
8638 //float factor[MAX_VARS], offset[MAX_VARS];
8639 //BOOL logaxis, runmarker;
8640 //double xmin, xrange;
8641 double ymin, ymax;
8642 double upper_limit[MAX_VARS], lower_limit[MAX_VARS];
8643 //float minvalue = (float) -HUGE_VAL;
8644 //float maxvalue = (float) +HUGE_VAL;
8645 //int show_values = 0;
8646 //int sort_vars = 0;
8647 //int old_vars = 0;
8648 time_t starttime, endtime;
8649 int flags;
8650
8651 time_t now = ss_time();
8652
8653 if (xendtime == 0)
8654 xendtime = now;
8655
8656 HistPlot hp;
8657 LoadHistPlotFromOdb(odb, &hp, hgroup, hpanel);
8658
8659 std::vector<int> var_index; var_index.resize(hp.vars.size());
8660
8661 for (size_t i=0; i<hp.vars.size(); i++) {
8662 var_index[i] = 0;
8663 const char *vp = strchr(hp.vars[i].tag_name.c_str(), '[');
8664 if (vp) {
8665 var_index[i] = atoi(vp + 1);
8666 }
8667 }
8668
8669 int logaxis = hp.log_axis;
8670 double minvalue = hp.minimum;
8671 double maxvalue = hp.maximum;
8672
8673 if ((minvalue == 0) && (maxvalue == 0)) {
8674 minvalue = -HUGE_VAL;
8675 maxvalue = +HUGE_VAL;
8676 }
8677
8678 std::vector<int> x[MAX_VARS];
8679 std::vector<double> y[MAX_VARS];
8680
8681 HistoryData hsxxx;
8682 HistoryData* hsdata = &hsxxx;
8683
8685
8686 /* generate image */
8687 im = gdImageCreate(width, height);
8688
8689 /* allocate standard colors */
8690 sscanf(bgcolor, "%02x%02x%02x", &r, &g, &b);
8691 bgcol = gdImageColorAllocate(im, r, g, b);
8692 sscanf(fgcolor, "%02x%02x%02x", &r, &g, &b);
8693 fgcol = gdImageColorAllocate(im, r, g, b);
8694 sscanf(gridcolor, "%02x%02x%02x", &r, &g, &b);
8695 gridcol = gdImageColorAllocate(im, r, g, b);
8696
8697 grey = gdImageColorAllocate(im, 192, 192, 192);
8698 //ltgrey = gdImageColorAllocate(im, 208, 208, 208);
8699 white = gdImageColorAllocate(im, 255, 255, 255);
8700 //black = gdImageColorAllocate(im, 0, 0, 0);
8701 red = gdImageColorAllocate(im, 255, 0, 0);
8702 //green = gdImageColorAllocate(im, 0, 255, 0);
8703 //blue = gdImageColorAllocate(im, 0, 0, 255);
8704
8705 curve_col[0] = gdImageColorAllocate(im, 0, 0, 255);
8706 curve_col[1] = gdImageColorAllocate(im, 0, 192, 0);
8707 curve_col[2] = gdImageColorAllocate(im, 255, 0, 0);
8708 curve_col[3] = gdImageColorAllocate(im, 0, 192, 192);
8709 curve_col[4] = gdImageColorAllocate(im, 255, 0, 255);
8710 curve_col[5] = gdImageColorAllocate(im, 192, 192, 0);
8711 curve_col[6] = gdImageColorAllocate(im, 128, 128, 128);
8712 curve_col[7] = gdImageColorAllocate(im, 128, 255, 128);
8713 curve_col[8] = gdImageColorAllocate(im, 255, 128, 128);
8714 curve_col[9] = gdImageColorAllocate(im, 128, 128, 255);
8715 for (i=10; i<MAX_VARS; i++)
8716 curve_col[i] = gdImageColorAllocate(im, 128, 128, 128);
8717
8718 state_col[0] = gdImageColorAllocate(im, 255, 0, 0);
8719 state_col[1] = gdImageColorAllocate(im, 255, 255, 0);
8720 state_col[2] = gdImageColorAllocate(im, 0, 255, 0);
8721
8722 /* Set transparent color. */
8723 gdImageColorTransparent(im, grey);
8724
8725 /* Title */
8726 gdImageString(im, gdFontGiant, width / 2 - (strlen(hpanel) * gdFontGiant->w) / 2, 2, (char*)hpanel, fgcol);
8727
8728 /* connect to history */
8730 if (mh == NULL) {
8731 sprintf(str, "History is not configured, see messages");
8732 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8733 goto error;
8734 }
8735
8737 //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
8738 //db_find_key(hDB, 0, str, &hkeypanel);
8739 //if (!hkeypanel) {
8740 // sprintf(str, "Cannot find /History/Display/%s/%s in ODB", hgroup, hpanel);
8741 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8742 // goto error;
8743 //}
8744
8745 //db_find_key(hDB, hkeypanel, "Variables", &hkeydvar);
8746 //if (!hkeydvar) {
8747 // sprintf(str, "Cannot find /History/Display/%s/%s/Variables in ODB", hgroup, hpanel);
8748 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8749 // goto error;
8750 //}
8751
8752 //db_get_key(hDB, hkeydvar, &key);
8753 //n_vars = key.num_values;
8754
8755 if (hp.vars.empty()) {
8756 sprintf(str, "No variables in panel %s/%s", hgroup, hpanel);
8757 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8758 goto error;
8759 }
8760
8761 if (hp.vars.size() > MAX_VARS) {
8762 sprintf(str, "Too many variables in panel %s/%s", hgroup, hpanel);
8763 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8764 goto error;
8765 }
8766
8767 ymin = ymax = 0;
8768 //logaxis = runmarker = 0;
8769
8770 for (i = 0; i < (int)hp.vars.size(); i++) {
8771 if (index != -1 && index != i)
8772 continue;
8773
8774 //size = sizeof(str);
8775 //status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
8776 //if (status != DB_SUCCESS) {
8777 // sprintf(str, "Cannot read tag %d in panel %s/%s, status %d", i, hgroup, hpanel, status);
8778 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8779 // goto error;
8780 //}
8781 //
8782 //mstrlcpy(tag_name[i], str, sizeof(tag_name[0]));
8783
8785 //char *tp = strchr(tag_name[i], ':');
8786 //if (tp) {
8787 // mstrlcpy(event_name[i], tag_name[i], sizeof(event_name[0]));
8788 // char *ep = strchr(event_name[i], ':');
8789 // if (ep)
8790 // *ep = 0;
8791 // mstrlcpy(var_name[i], tp+1, sizeof(var_name[0]));
8792 // var_index[i] = 0;
8793 // char *vp = strchr(var_name[i], '[');
8794 // if (vp) {
8795 // var_index[i] = atoi(vp + 1);
8796 // *vp = 0;
8797 // }
8798 //} else {
8799 // sprintf(str, "Tag \"%s\" has wrong format in panel \"%s/%s\"", tag_name[i], hgroup, hpanel);
8800 // gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
8801 // goto error;
8802 //}
8803 //
8804 //db_find_key(hDB, hkeypanel, "Colour", &hkey);
8805 //if (hkey) {
8806 // size = sizeof(str);
8807 // status = db_get_data_index(hDB, hkey, str, &size, i, TID_STRING);
8808 // if (status == DB_SUCCESS) {
8809 // if (str[0] == '#') {
8810 // char sss[3];
8811 // int r, g, b;
8812 //
8813 // sss[0] = str[1];
8814 // sss[1] = str[2];
8815 // sss[2] = 0;
8816 // r = strtoul(sss, NULL, 16);
8817 // sss[0] = str[3];
8818 // sss[1] = str[4];
8819 // sss[2] = 0;
8820 // g = strtoul(sss, NULL, 16);
8821 // sss[0] = str[5];
8822 // sss[1] = str[6];
8823 // sss[2] = 0;
8824 // b = strtoul(sss, NULL, 16);
8825 //
8826 // curve_col[i] = gdImageColorAllocate(im, r, g, b);
8827 // }
8828 // }
8829 //}
8830
8831 if (hp.vars[i].colour[0] == '#') {
8832 const char* str = hp.vars[i].colour.c_str();
8833 char sss[3];
8834 int r, g, b;
8835
8836 sss[0] = str[1];
8837 sss[1] = str[2];
8838 sss[2] = 0;
8839 r = strtoul(sss, NULL, 16);
8840 sss[0] = str[3];
8841 sss[1] = str[4];
8842 sss[2] = 0;
8843 g = strtoul(sss, NULL, 16);
8844 sss[0] = str[5];
8845 sss[1] = str[6];
8846 sss[2] = 0;
8847 b = strtoul(sss, NULL, 16);
8848
8849 curve_col[i] = gdImageColorAllocate(im, r, g, b);
8850 }
8851
8852 /* get timescale */
8853 if (scale == 0) {
8854 //std::string ts = "1h";
8855 //status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
8856 //if (status != DB_SUCCESS) {
8857 // /* delete old integer key */
8858 // db_find_key(hDB, hkeypanel, "Timescale", &hkey);
8859 // if (hkey)
8860 // db_delete_key(hDB, hkey, FALSE);
8861 //
8862 // ts = "1h";
8863 // status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
8864 //}
8865
8866 scale = time_to_sec(hp.timescale.c_str());
8867 }
8868
8869 //for (j = 0; j < MAX_VARS; j++) {
8870 // factor[j] = 1;
8871 // offset[j] = 0;
8872 //}
8873
8875 //size = sizeof(float) * n_vars;
8876 //db_get_value(hDB, hkeypanel, "Factor", factor, &size, TID_FLOAT, TRUE);
8877
8879 //size = sizeof(float) * n_vars;
8880 //db_get_value(hDB, hkeypanel, "Offset", offset, &size, TID_FLOAT, TRUE);
8881
8883 //size = sizeof(logaxis);
8884 //logaxis = 0;
8885 //db_get_value(hDB, hkeypanel, "Log axis", &logaxis, &size, TID_BOOL, TRUE);
8886
8888 //size = sizeof(show_values);
8889 //show_values = 0;
8890 //db_get_value(hDB, hkeypanel, "Show values", &show_values, &size, TID_BOOL, TRUE);
8891
8893 //size = sizeof(sort_vars);
8894 //sort_vars = 0;
8895 //db_get_value(hDB, hkeypanel, "Sort vars", &sort_vars, &size, TID_BOOL, TRUE);
8896
8898 //size = sizeof(old_vars);
8899 //old_vars = 0;
8900 //db_get_value(hDB, hkeypanel, "Show old vars", &old_vars, &size, TID_BOOL, TRUE);
8901
8903 //size = sizeof(minvalue);
8904 //minvalue = (float) -HUGE_VAL;
8905 //db_get_value(hDB, hkeypanel, "Minimum", &minvalue, &size, TID_FLOAT, TRUE);
8906
8908 //size = sizeof(maxvalue);
8909 //maxvalue = (float) +HUGE_VAL;
8910 //db_get_value(hDB, hkeypanel, "Maximum", &maxvalue, &size, TID_FLOAT, TRUE);
8911
8912 //if ((minvalue == 0) && (maxvalue == 0)) {
8913 // minvalue = (float) -HUGE_VAL;
8914 // maxvalue = (float) +HUGE_VAL;
8915 //}
8916
8918 //size = sizeof(runmarker);
8919 //runmarker = 1;
8920 //db_get_value(hDB, hkeypanel, "Show run markers", &runmarker, &size, TID_BOOL, TRUE);
8921
8922 /* make ODB path from tag name */
8923 std::string odbpath;
8924 HNDLE hkeyeq = 0;
8925 HNDLE hkeyroot;
8926 db_find_key(hDB, 0, "/Equipment", &hkeyroot);
8927 if (hkeyroot) {
8928 for (j = 0;; j++) {
8929 HNDLE hkeyeq;
8930 db_enum_key(hDB, hkeyroot, j, &hkeyeq);
8931
8932 if (!hkeyeq)
8933 break;
8934
8935 KEY key;
8936 db_get_key(hDB, hkeyeq, &key);
8937 if (equal_ustring(key.name, hp.vars[i].event_name.c_str())) {
8938 /* check if variable is individual key under variables/ */
8939 sprintf(str, "Variables/%s", hp.vars[i].tag_name.c_str());
8940 HNDLE hkey;
8941 db_find_key(hDB, hkeyeq, str, &hkey);
8942 if (hkey) {
8943 //sprintf(odbpath, "/Equipment/%s/Variables/%s", event_name[i], var_name[i]);
8944 odbpath = "";
8945 odbpath += "/Equipment/";
8946 odbpath += hp.vars[i].event_name;
8947 odbpath += "/Variables/";
8948 odbpath += hp.vars[i].tag_name;
8949 break;
8950 }
8951
8952 /* check if variable is in setttins/names array */
8953 HNDLE hkeynames;
8954 db_find_key(hDB, hkeyeq, "Settings/Names", &hkeynames);
8955 if (hkeynames) {
8956 /* extract variable name and Variables/<key> */
8957 mstrlcpy(str, hp.vars[i].tag_name.c_str(), sizeof(str));
8958 p = str + strlen(str) - 1;
8959 while (p > str && *p != ' ')
8960 p--;
8961 std::string key_name = p + 1;
8962 *p = 0;
8963
8964 std::string varname = str;
8965
8966 /* find key in single name array */
8967 db_get_key(hDB, hkeynames, &key);
8968 for (k = 0; k < key.num_values; k++) {
8969 size = sizeof(str);
8970 db_get_data_index(hDB, hkeynames, str, &size, k, TID_STRING);
8971 if (equal_ustring(str, varname.c_str())) {
8972 //sprintf(odbpath, "/Equipment/%s/Variables/%s[%d]", event_name[i], key_name, k);
8973 odbpath = "";
8974 odbpath += "/Equipment/";
8975 odbpath += hp.vars[i].event_name;
8976 odbpath += "/Variables/";
8977 odbpath += key_name;
8978 odbpath += "[";
8979 odbpath += toString(k);
8980 odbpath += "]";
8981 break;
8982 }
8983 }
8984 } else {
8985 /* go through /variables/<name> entries */
8986 HNDLE hkeyvars;
8987 db_find_key(hDB, hkeyeq, "Variables", &hkeyvars);
8988 if (hkeyvars) {
8989 for (k = 0;; k++) {
8990 db_enum_key(hDB, hkeyvars, k, &hkey);
8991
8992 if (!hkey)
8993 break;
8994
8995 /* find "settins/names <key>" for this key */
8996 db_get_key(hDB, hkey, &key);
8997
8998 /* find key in key_name array */
8999 std::string key_name = key.name;
9000
9001 std::string path;
9002 //sprintf(str, "Settings/Names %s", key_name);
9003 path += "Settings/Names ";
9004 path += key_name;
9005
9006 HNDLE hkeynames;
9007 db_find_key(hDB, hkeyeq, path.c_str(), &hkeynames);
9008 if (hkeynames) {
9009 db_get_key(hDB, hkeynames, &key);
9010 for (l = 0; l < key.num_values; l++) {
9011 size = sizeof(str);
9012 db_get_data_index(hDB, hkeynames, str, &size, l, TID_STRING);
9013 if (equal_ustring(str, hp.vars[i].tag_name.c_str())) {
9014 //sprintf(odbpath, "/Equipment/%s/Variables/%s[%d]", event_name[i], key_name, l);
9015 odbpath = "";
9016 odbpath += "/Equipment/";
9017 odbpath += hp.vars[i].event_name;
9018 odbpath += "/Variables/";
9019 odbpath += key_name;
9020 odbpath += "[";
9021 odbpath += toString(l);
9022 odbpath += "]";
9023 break;
9024 }
9025 }
9026 }
9027 }
9028 }
9029 }
9030
9031 break;
9032 }
9033 }
9034
9035 if (!hkeyeq) {
9036 db_find_key(hDB, 0, "/History/Links", &hkeyroot);
9037 if (hkeyroot) {
9038 for (j = 0;; j++) {
9039 HNDLE hkey;
9040 db_enum_link(hDB, hkeyroot, j, &hkey);
9041
9042 if (!hkey)
9043 break;
9044
9045 KEY key;
9046 db_get_key(hDB, hkey, &key);
9047 if (equal_ustring(key.name, hp.vars[i].event_name.c_str())) {
9048 db_enum_key(hDB, hkeyroot, j, &hkey);
9049 db_find_key(hDB, hkey, hp.vars[i].tag_name.c_str(), &hkey);
9050 if (hkey) {
9051 db_get_key(hDB, hkey, &key);
9052 odbpath = db_get_path(hDB, hkey);
9053 if (key.num_values > 1) {
9054 odbpath += "[";
9055 odbpath += toString(var_index[i]);
9056 odbpath += "]";
9057 }
9058 break;
9059 }
9060 }
9061 }
9062 }
9063 }
9064 }
9065
9066 /* search alarm limits */
9067 upper_limit[i] = lower_limit[i] = -12345;
9068 db_find_key(hDB, 0, "Alarms/Alarms", &hkeyroot);
9069 if (odbpath.length() > 0 && hkeyroot) {
9070 for (j = 0;; j++) {
9071 HNDLE hkey;
9072 db_enum_key(hDB, hkeyroot, j, &hkey);
9073
9074 if (!hkey)
9075 break;
9076
9077 size = sizeof(str);
9078 db_get_value(hDB, hkey, "Condition", str, &size, TID_STRING, TRUE);
9079
9080 if (strstr(str, odbpath.c_str())) {
9081 if (strchr(str, '<')) {
9082 p = strchr(str, '<') + 1;
9083 if (*p == '=')
9084 p++;
9085 if (hp.enable_factor) {
9086 lower_limit[i] = (hp.vars[i].factor * (atof(p) - hp.vars[i].voffset) + hp.vars[i].offset);
9087 } else {
9088 lower_limit[i] = atof(p);
9089 }
9090 }
9091 if (strchr(str, '>')) {
9092 p = strchr(str, '>') + 1;
9093 if (*p == '=')
9094 p++;
9095 if (hp.enable_factor) {
9096 upper_limit[i] = (hp.vars[i].factor * (atof(p) - hp.vars[i].voffset) + hp.vars[i].offset);
9097 } else {
9098 upper_limit[i] = atof(p);
9099 }
9100 }
9101 }
9102 }
9103 }
9104 } // loop over variables
9105
9106 //starttime = now - scale + toffset;
9107 //endtime = now + toffset;
9108
9109 starttime = xendtime - scale;
9110 endtime = xendtime;
9111
9112 //printf("now %d, scale %d, xendtime %d, starttime %d, endtime %d\n", now, scale, xendtime, starttime, endtime);
9113
9114 flags = READ_HISTORY_DATA;
9115 if (hp.show_run_markers)
9116 flags |= READ_HISTORY_RUNMARKER;
9117
9118 status = read_history(hp, /*hDB, hgroup, hpanel,*/ index, flags, starttime, endtime, scale/1000+1, hsdata);
9119
9120 if (status != HS_SUCCESS) {
9121 sprintf(str, "Complete history failure, read_history() status %d, see messages", status);
9122 gdImageString(im, gdFontSmall, width / 2 - (strlen(str) * gdFontSmall->w) / 2, height / 2, str, red);
9123 goto error;
9124 }
9125
9126 DWORD n_point[MAX_VARS];
9127 char var_status[MAX_VARS][256];
9128
9129 for (int k=0; k<hsdata->nvars; k++) {
9130 int i = hsdata->odb_index[k];
9131
9132 if (i<0)
9133 continue;
9134
9135 if (index != -1 && index != i)
9136 continue;
9137
9138 n_point[i] = 0;
9139
9140 var_status[i][0] = 0;
9141 if (hsdata->status[k] == HS_UNDEFINED_VAR) {
9142 sprintf(var_status[i], "not found in history");
9143 continue;
9144 } else if (hsdata->status[k] != HS_SUCCESS) {
9145 sprintf(var_status[i], "hs_read() error %d, see messages", hsdata->status[k]);
9146 continue;
9147 }
9148
9149 int n_vp = 0;
9150 for (int j=0; j<hsdata->num_entries[k]; j++) {
9151 int xx = (int)(hsdata->t[k][j]);
9152 double yy = hsdata->v[k][j];
9153
9154 /* skip NaNs */
9155 if (ss_isnan(yy))
9156 continue;
9157
9158 /* skip INFs */
9159 if (!ss_isfin(yy))
9160 continue;
9161
9162 /* avoid overflow */
9163 if (yy > 1E30)
9164 yy = 1E30f;
9165
9166 /* apply factor and offset */
9167 if (hp.enable_factor) {
9168 yy = hp.vars[i].factor * (yy - hp.vars[i].voffset) + hp.vars[i].offset;
9169 }
9170
9171 /* calculate ymin and ymax */
9172 if ((i == 0 || index != -1) && n_vp == 0)
9173 ymin = ymax = yy;
9174 else {
9175 if (yy > ymax)
9176 ymax = yy;
9177 if (yy < ymin)
9178 ymin = yy;
9179 }
9180
9181 /* increment number of valid points */
9182
9183 x[i].push_back(xx);
9184 y[i].push_back(yy);
9185
9186 n_vp++;
9187
9188 } // loop over data
9189
9190 n_point[i] = n_vp;
9191
9192 assert(x[i].size() == y[i].size());
9193 }
9194
9195 //int flag;
9196 int xmaxm;
9197 int row;
9198 int xold;
9199 int yold;
9200 int aoffset;
9201 double yb1, yb2, yf1, yf2;
9202 int xs, ys;
9203 int x1, x2;
9204 int y1, y2;
9205 int xs_old;
9206 double ybase;
9207
9208 gdPoint poly[3];
9209
9210 if (ymin < minvalue)
9211 ymin = minvalue;
9212
9213 if (ymax > maxvalue)
9214 ymax = maxvalue;
9215
9216 /* check if ylow = 0 */
9217 if (index == -1) {
9218 //flag = 0;
9219 //size = sizeof(flag);
9220 //db_get_value(hDB, hkeypanel, "Zero ylow", &flag, &size, TID_BOOL, TRUE);
9221 if (hp.zero_ylow && ymin > 0)
9222 ymin = 0;
9223 }
9224
9225 /* if min and max too close together, switch to linear axis */
9226 if (logaxis && ymin > 0 && ymax > 0) {
9227 yb1 = pow(10, floor(log(ymin) / LN10));
9228 yf1 = floor(ymin / yb1);
9229 yb2 = pow(10, floor(log(ymax) / LN10));
9230 yf2 = floor(ymax / yb2);
9231
9232 if (yb1 == yb2 && yf1 == yf2)
9233 logaxis = 0;
9234 else {
9235 /* round down and up ymin and ymax */
9236 ybase = pow(10, floor(log(ymin) / LN10));
9237 ymin = (floor(ymin / ybase) * ybase);
9238 ybase = pow(10, floor(log(ymax) / LN10));
9239 ymax = ((floor(ymax / ybase) + 1) * ybase);
9240 }
9241 }
9242
9243 /* avoid negative limits for log axis */
9244 if (logaxis) {
9245 if (ymax <= 0)
9246 ymax = 1;
9247 if (ymin <= 0)
9248 ymin = 1E-12f;
9249 }
9250
9251 /* increase limits by 5% */
9252 if (ymin == 0 && ymax == 0) {
9253 ymin = -1;
9254 ymax = 1;
9255 } else {
9256 if (!logaxis) {
9257 ymax += (ymax - ymin) / 20.f;
9258
9259 if (ymin != 0)
9260 ymin -= (ymax - ymin) / 20.f;
9261 }
9262 }
9263
9264 /* avoid ymin == ymax */
9265 if (ymax == ymin) {
9266 if (logaxis) {
9267 ymax *= 2;
9268 ymin /= 2;
9269 } else {
9270 ymax += 10;
9271 ymin -= 10;
9272 }
9273 }
9274
9275 /* calculate X limits */
9276 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9277 //xmax = (double) (toffset / 3600.0);
9278 //xrange = xmax - xmin;
9279 //xrange = scale/3600.0;
9280
9281 /* caluclate required space for Y-axis */
9282 aoffset = vaxis(im, gdFontSmall, fgcol, gridcol, 0, 0, height, -3, -5, -7, -8, 0, ymin, ymax, logaxis);
9283 aoffset += 2;
9284
9285 x1 = aoffset;
9286 y1 = height - 20;
9287 x2 = width - 20;
9288 y2 = 20;
9289
9290 gdImageFilledRectangle(im, x1, y2, x2, y1, bgcol);
9291
9292 /* draw axis frame */
9293 taxis(im, gdFontSmall, fgcol, gridcol, x1, y1, x2 - x1, width, 3, 5, 9, 10, 0, (double)starttime, (double)endtime);
9294
9295 vaxis(im, gdFontSmall, fgcol, gridcol, x1, y1, y1 - y2, -3, -5, -7, -8, x2 - x1, ymin, ymax, logaxis);
9296 gdImageLine(im, x1, y2, x2, y2, fgcol);
9297 gdImageLine(im, x2, y2, x2, y1, fgcol);
9298
9299 xs = ys = xold = yold = 0;
9300
9301 /* old code for run markers, new code is below */
9302
9303 /* write run markes if selected */
9304 if (/* DISABLES CODE */ (0) && hp.show_run_markers) {
9305
9306 const char* event_names[] = {
9307 "Run transitions",
9308 "Run transitions",
9309 0 };
9310
9311 const char* tag_names[] = {
9312 "State",
9313 "Run number",
9314 0 };
9315
9316 const int tag_indexes[] = {
9317 0,
9318 0,
9319 0 };
9320
9321 int num_entries[3];
9322 time_t *tbuf[3];
9323 double *dbuf[3];
9324 int st[3];
9325
9326 num_entries[0] = 0;
9327 num_entries[1] = 0;
9328
9329 status = mh->hs_read(starttime - scale, endtime, 0,
9330 2, event_names, tag_names, tag_indexes,
9331 num_entries, tbuf, dbuf, st);
9332
9333 //printf("read run info: status %d, entries %d %d\n", status, num_entries[0], num_entries[1]);
9334
9335 int n_marker = num_entries[0];
9336
9337 if (status == HS_SUCCESS && n_marker > 0 && n_marker < 100) {
9338 xs_old = -1;
9339 xmaxm = x1;
9340 for (j = 0; j < (int) n_marker; j++) {
9341 int col;
9342
9343 // explicit algebra manipulation to clarify computations:
9344
9345 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9346 //xrange = scale/3600.0;
9347 //time_t starttime = now - scale + toffset;
9348
9349 //x_marker = (int)(tbuf[1][j] - now);
9350 //xs = (int) ((x_marker / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9351 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9352 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - (-scale / 3600.0 + toffset / 3600.0)) / (scale/3600.0) * (x2 - x1) + x1 + 0.5);
9353 //xs = (int) (((tbuf[1][j] - now) - (-scale + toffset)) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9354 xs = (int) ((tbuf[1][j] - starttime) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9355
9356 if (xs < x1)
9357 continue;
9358 if (xs >= x2)
9359 continue;
9360
9361 double run_number = dbuf[1][j];
9362
9363 if (xs <= xs_old)
9364 xs = xs_old + 1;
9365 xs_old = xs;
9366
9367 if (dbuf[0][j] == 1)
9368 col = state_col[0];
9369 else if (dbuf[0][j] == 2)
9370 col = state_col[1];
9371 else if (dbuf[0][j] == 3)
9372 col = state_col[2];
9373 else
9374 col = state_col[0];
9375
9376 gdImageDashedLine(im, xs, y1, xs, y2, col);
9377
9378 sprintf(str, "%.0f", run_number);
9379
9380 if (dbuf[0][j] == STATE_RUNNING) {
9381 if (xs > xmaxm) {
9382 gdImageStringUp(im, gdFontSmall, xs + 0, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9383 xmaxm = xs - 2 + gdFontSmall->h;
9384 }
9385 } else if (dbuf[0][j] == STATE_STOPPED) {
9386 if (xs + 2 - gdFontSmall->h > xmaxm) {
9387 gdImageStringUp(im, gdFontSmall, xs + 2 - gdFontSmall->h, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9388 xmaxm = xs - 1;
9389 }
9390 }
9391 }
9392 }
9393
9394 if (num_entries[0]) {
9395 free(tbuf[0]);
9396 free(dbuf[0]);
9397 tbuf[0] = NULL;
9398 dbuf[0] = NULL;
9399 }
9400
9401 if (num_entries[1]) {
9402 free(tbuf[1]);
9403 free(dbuf[1]);
9404 tbuf[1] = NULL;
9405 dbuf[1] = NULL;
9406 }
9407 }
9408
9409 /* write run markes if selected */
9410 if (hp.show_run_markers) {
9411
9412 int index_state = -1;
9413 int index_run_number = -1;
9414
9415 for (int k=0; k<hsdata->nvars; k++) {
9416 if (hsdata->odb_index[k] == -1)
9417 index_state = k;
9418
9419 if (hsdata->odb_index[k] == -2)
9420 index_run_number = k;
9421 }
9422
9423 bool ok = true;
9424
9425 if (ok)
9426 ok = (index_state >= 0) && (index_run_number >= 0);
9427
9428 if (ok)
9429 ok = (hsdata->status[index_state] == HS_SUCCESS);
9430
9431 if (ok)
9432 ok = (hsdata->status[index_run_number] == HS_SUCCESS);
9433
9434 if (/* DISABLES CODE */ (0) && ok)
9435 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]);
9436
9437 if (ok)
9438 ok = (hsdata->num_entries[index_state] == hsdata->num_entries[index_run_number]);
9439
9440 int n_marker = hsdata->num_entries[index_state];
9441
9442 if (ok && n_marker > 0 && n_marker < 100) {
9443 xs_old = -1;
9444 xmaxm = x1;
9445 for (j = 0; j < (int) n_marker; j++) {
9446 int col;
9447
9448 // explicit algebra manipulation to clarify computations:
9449
9450 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9451 //xrange = scale/3600.0;
9452 //time_t starttime = now - scale + toffset;
9453
9454 //x_marker = (int)(tbuf[1][j] - now);
9455 //xs = (int) ((x_marker / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9456 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9457 //xs = (int) (((tbuf[1][j] - now) / 3600.0 - (-scale / 3600.0 + toffset / 3600.0)) / (scale/3600.0) * (x2 - x1) + x1 + 0.5);
9458 //xs = (int) (((tbuf[1][j] - now) - (-scale + toffset)) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9459 xs = (int) ((hsdata->t[index_state][j] - starttime) / (scale/1.0) * (x2 - x1) + x1 + 0.5);
9460
9461 if (xs < x1)
9462 continue;
9463 if (xs >= x2)
9464 continue;
9465
9466 double run_number = hsdata->v[index_run_number][j];
9467
9468 if (xs <= xs_old)
9469 xs = xs_old + 1;
9470 xs_old = xs;
9471
9472 int state = (int)hsdata->v[index_state][j];
9473
9474 if (state == 1)
9475 col = state_col[0];
9476 else if (state == 2)
9477 col = state_col[1];
9478 else if (state == 3)
9479 col = state_col[2];
9480 else
9481 col = state_col[0];
9482
9483 gdImageDashedLine(im, xs, y1, xs, y2, col);
9484
9485 sprintf(str, "%.0f", run_number);
9486
9487 if (state == STATE_RUNNING) {
9488 if (xs > xmaxm) {
9489 gdImageStringUp(im, gdFontSmall, xs + 0, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9490 xmaxm = xs - 2 + gdFontSmall->h;
9491 }
9492 } else if (state == STATE_STOPPED) {
9493 if (xs + 2 - gdFontSmall->h > xmaxm) {
9494 gdImageStringUp(im, gdFontSmall, xs + 2 - gdFontSmall->h, y2 + 2 + gdFontSmall->w * strlen(str), str, fgcol);
9495 xmaxm = xs - 1;
9496 }
9497 }
9498 }
9499 }
9500 }
9501
9502 for (i = 0; i < (int)hp.vars.size(); i++) {
9503 if (index != -1 && index != i)
9504 continue;
9505
9506 /* draw alarm limits */
9507 if (lower_limit[i] != -12345) {
9508 if (logaxis) {
9509 if (lower_limit[i] <= 0)
9510 ys = y1;
9511 else
9512 ys = (int) (y1 - (log(lower_limit[i]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
9513 } else {
9514 ys = (int) (y1 - (lower_limit[i] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
9515 }
9516
9517 if (xs < 0)
9518 xs = 0;
9519 if (xs >= width)
9520 xs = width-1;
9521 if (ys < 0)
9522 ys = 0;
9523 if (ys >= height)
9524 ys = height-1;
9525
9526 if (ys > y2 && ys < y1) {
9527 gdImageDashedLine(im, x1, ys, x2, ys, curve_col[i]);
9528
9529 poly[0].x = x1;
9530 poly[0].y = ys;
9531 poly[1].x = x1 + 5;
9532 poly[1].y = ys;
9533 poly[2].x = x1;
9534 poly[2].y = ys - 5;
9535
9536 gdImageFilledPolygon(im, poly, 3, curve_col[i]);
9537 }
9538 }
9539 if (upper_limit[i] != -12345) {
9540 if (logaxis) {
9541 if (upper_limit[i] <= 0)
9542 ys = y1;
9543 else
9544 ys = (int) (y1 - (log(upper_limit[i]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
9545 } else {
9546 ys = (int) (y1 - (upper_limit[i] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
9547 }
9548
9549 if (xs < 0)
9550 xs = 0;
9551 if (xs >= width)
9552 xs = width-1;
9553 if (ys < 0)
9554 ys = 0;
9555 if (ys >= height)
9556 ys = height-1;
9557
9558 if (ys > y2 && ys < y1) {
9559 gdImageDashedLine(im, x1, ys, x2, ys, curve_col[i]);
9560
9561 poly[0].x = x1;
9562 poly[0].y = ys;
9563 poly[1].x = x1 + 5;
9564 poly[1].y = ys;
9565 poly[2].x = x1;
9566 poly[2].y = ys + 5;
9567
9568 gdImageFilledPolygon(im, poly, 3, curve_col[i]);
9569 }
9570 }
9571
9572 for (j = 0; j < (int) n_point[i]; j++) {
9573 //xmin = (double) (-scale / 3600.0 + toffset / 3600.0);
9574 //xrange = scale/3600.0;
9575 //xs = (int) (((x[i][j]-now) / 3600.0 - xmin) / xrange * (x2 - x1) + x1 + 0.5);
9576 //xs = (int) (((x[i][j] - now + scale - toffset) / 3600.0) / xrange * (x2 - x1) + x1 + 0.5);
9577 //xs = (int) (((x[i][j] - starttime) / 3600.0) / xrange * (x2 - x1) + x1 + 0.5);
9578 xs = (int) (((x[i][j] - starttime)/1.0) / (1.0*scale) * (x2 - x1) + x1 + 0.5);
9579
9580 if (logaxis) {
9581 if (y[i][j] <= 0)
9582 ys = y1;
9583 else
9584 ys = (int) (y1 - (log(y[i][j]) - log(ymin)) / (log(ymax) - log(ymin)) * (y1 - y2) + 0.5);
9585 } else {
9586 ys = (int) (y1 - (y[i][j] - ymin) / (ymax - ymin) * (y1 - y2) + 0.5);
9587 }
9588
9589 if (xs < 0)
9590 xs = 0;
9591 if (xs >= width)
9592 xs = width-1;
9593 if (ys < 0)
9594 ys = 0;
9595 if (ys >= height)
9596 ys = height-1;
9597
9598 if (j > 0)
9599 gdImageLine(im, xold, yold, xs, ys, curve_col[i]);
9600 xold = xs;
9601 yold = ys;
9602 }
9603
9604 if (n_point[i] > 0) {
9605 poly[0].x = xs;
9606 poly[0].y = ys;
9607 poly[1].x = xs + 12;
9608 poly[1].y = ys - 6;
9609 poly[2].x = xs + 12;
9610 poly[2].y = ys + 6;
9611
9612 gdImageFilledPolygon(im, poly, 3, curve_col[i]);
9613 }
9614 }
9615
9616 if (labels) {
9617 for (i = 0; i < (int)hp.vars.size(); i++) {
9618 if (index != -1 && index != i)
9619 continue;
9620
9621 //str[0] = 0;
9622 //status = db_find_key(hDB, hkeypanel, "Label", &hkeydvar);
9623 //if (status == DB_SUCCESS) {
9624 // size = sizeof(str);
9625 // status = db_get_data_index(hDB, hkeydvar, str, &size, i, TID_STRING);
9626 //}
9627
9628 std::string str = hp.vars[i].label.c_str();
9629
9630 if (str.empty()) {
9631 if (hp.enable_factor) {
9632 str = hp.vars[i].tag_name;
9633
9634 if (hp.vars[i].voffset > 0)
9635 str += msprintf(" - %G", hp.vars[i].voffset);
9636 else if (hp.vars[i].voffset < 0)
9637 str += msprintf(" + %G", -hp.vars[i].voffset);
9638
9639 if (hp.vars[i].factor != 1) {
9640 if (hp.vars[i].voffset == 0)
9641 str += msprintf(" * %+G", hp.vars[i].factor);
9642 else {
9643 str = msprintf("(%s) * %+G", str.c_str(), hp.vars[i].factor);
9644 }
9645 }
9646
9647 if (hp.vars[i].offset > 0)
9648 str += msprintf(" + %G", hp.vars[i].offset);
9649 else if (hp.vars[i].offset < 0)
9650 str += msprintf(" - %G", -hp.vars[i].offset);
9651
9652 } else {
9653 str = hp.vars[i].tag_name;
9654 }
9655 }
9656
9657 int k=0;
9658 for (int j=0; j<hsdata->nvars; j++)
9659 if (hsdata->odb_index[j] == i) {
9660 k = j;
9661 break;
9662 }
9663
9664 if (/* DISABLES CODE */ (0)) {
9665 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]);
9666 }
9667
9668 if (hp.show_values) {
9669 char xstr[256];
9670 if (n_point[i] > 0) {
9671 sprintf(xstr," = %g", y[i][n_point[i]-1]);
9672 } else if (hsdata->num_entries[k] > 0) {
9673 sprintf(xstr," = all data is NaN or INF");
9674 } else if (hsdata->have_last_written) {
9675 if (hsdata->last_written[k]) {
9676 char ctimebuf[32];
9677 ctime_r(&hsdata->last_written[k], ctimebuf);
9678 sprintf(xstr," = last data %s", ctimebuf);
9679 // kill trailing '\n'
9680 char*s = strchr(xstr, '\n');
9681 if (s) *s=0;
9682 // clear the unnecessary error status report
9683 if (hsdata->status[k] == HS_UNDEFINED_VAR)
9684 var_status[i][0] = 0;
9685 } else {
9686 sprintf(xstr," = no data ever");
9687 }
9688 } else {
9689 sprintf(xstr," = no data");
9690 }
9691 str += xstr;
9692 }
9693
9694 if (strlen(var_status[i]) > 1) {
9695 str += msprintf(" (%s)", var_status[i]);
9696 }
9697
9698 row = index == -1 ? i : 0;
9699
9701 x1 + 10,
9702 y2 + 10 + row * (gdFontMediumBold->h + 10),
9703 x1 + 10 + str.length() * gdFontMediumBold->w + 10,
9704 y2 + 10 + row * (gdFontMediumBold->h + 10) +
9705 gdFontMediumBold->h + 2 + 2, white);
9706 gdImageRectangle(im, x1 + 10, y2 + 10 + row * (gdFontMediumBold->h + 10),
9707 x1 + 10 + str.length() * gdFontMediumBold->w + 10,
9708 y2 + 10 + row * (gdFontMediumBold->h + 10) +
9709 gdFontMediumBold->h + 2 + 2, curve_col[i]);
9710
9712 x1 + 10 + 5, y2 + 10 + 2 + row * (gdFontMediumBold->h + 10),
9713 (char*)str.c_str(),
9714 curve_col[i]);
9715 }
9716 }
9717
9718 gdImageRectangle(im, x1, y2, x2, y1, fgcol);
9719
9720 error:
9721
9722 /* generate GIF */
9723 gdImageInterlace(im, 1);
9724 gdImageGif(im, &gb);
9725 gdImageDestroy(im);
9726 length = gb.size;
9727
9728 if (buffer == NULL) {
9729 rr->rsprintf("HTTP/1.1 200 Document follows\r\n");
9730 rr->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
9731
9732 rr->rsprintf("Content-Type: image/gif\r\n");
9733 rr->rsprintf("Content-Length: %d\r\n", length);
9734 rr->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
9735 rr->rsprintf("Expires: Fri, 01-Jan-1983 00:00:00 GMT\r\n\r\n");
9736
9737 rr->rmemcpy(gb.data, length);
9738 } else {
9739 if (length > *buffer_size) {
9740 printf("return buffer too small\n");
9741 return;
9742 }
9743
9744 memcpy(buffer, gb.data, length);
9745 *buffer_size = length;
9746 }
9747}
9748
9749/*------------------------------------------------------------------*/
9750
9751time_t mktime_with_dst(const struct tm* ptms)
9752{
9753 // this silly stuff is required to correctly handle daylight savings time (Summer time/Winter time)
9754 // when we fill "struct tm" from user input, we cannot know if daylight savings time is in effect
9755 // and we do not know how to initialize the value of tms.tm_isdst.
9756 // This can cause the output of mktime() to be off by one hour.
9757 // (Rules for daylight savings time are set by national and local govt and in some locations, changes yearly)
9758 // (There are no locations with 2 hour or half-hour daylight savings that I know of)
9759 // (Yes, "man mktime" talks about using "tms.tm_isdst = -1")
9760 //
9761 // We assume the user is using local time and we convert in two steps:
9762 //
9763 // first we convert "struct tm" to "time_t" using mktime() with unknown tm_isdst
9764 // second we convert "time_t" back to "struct tm" using localtime_r()
9765 // this fills "tm_isdst" with correct value from the system time zone database
9766 // then we reset all the time fields (except for sub-minute fields not affected by daylight savings)
9767 // and call mktime() again, now with the correct value of "tm_isdst".
9768 // K.O. 2013-09-14
9769
9770 struct tm tms = *ptms;
9771 struct tm tms2;
9772 time_t t1 = ss_mktime(&tms);
9773 localtime_r(&t1, &tms2);
9774 tms2.tm_year = ptms->tm_year;
9775 tms2.tm_mon = ptms->tm_mon;
9776 tms2.tm_mday = ptms->tm_mday;
9777 tms2.tm_hour = ptms->tm_hour;
9778 tms2.tm_min = ptms->tm_min;
9779 time_t t2 = ss_mktime(&tms2);
9780 //printf("t1 %.0f, t2 %.0f, diff %d\n", (double)t1, (double)t2, (int)(t1-t2));
9781 return t2;
9782}
9783
9784/*------------------------------------------------------------------*/
9785
9786static std::string add_param_to_url(const char* name, const char* value)
9787{
9788 std::string s;
9789 s += name; // FIXME: should be URI-encoded
9790 s += "=";
9791 s += value; // FIXME: should be URI-encoded
9792 return s;
9793}
9794
9795/*------------------------------------------------------------------*/
9796
9798{
9799 int i;
9800 HNDLE hDB;
9801
9802 if (p->getparam("m1") && *p->getparam("m1")) {
9803 struct tm tms;
9804 memset(&tms, 0, sizeof(struct tm));
9805
9806 tms.tm_year = atoi(p->getparam("y1")) % 100;
9807
9808 std::string m1 = p->getparam("m1");
9809 for (i = 0; i < 12; i++)
9810 if (equal_ustring(m1.c_str(), mname[i]))
9811 break;
9812 if (i == 12)
9813 i = 0;
9814
9815 tms.tm_mon = i;
9816 tms.tm_mday = atoi(p->getparam("d1"));
9817 tms.tm_hour = atoi(p->getparam("h1"));
9818
9819 if (tms.tm_year < 90)
9820 tms.tm_year += 100;
9821
9822 time_t ltime_start = mktime_with_dst(&tms);
9823
9824 memset(&tms, 0, sizeof(struct tm));
9825 tms.tm_year = atoi(p->getparam("y2")) % 100;
9826
9827 std::string m2 = p->getparam("m2");
9828 for (i = 0; i < 12; i++)
9829 if (equal_ustring(m2.c_str(), mname[i]))
9830 break;
9831 if (i == 12)
9832 i = 0;
9833
9834 tms.tm_mon = i;
9835 tms.tm_mday = atoi(p->getparam("d2"));
9836 tms.tm_hour = atoi(p->getparam("h2"));
9837
9838 if (tms.tm_year < 90)
9839 tms.tm_year += 100;
9840
9841 time_t ltime_end = mktime_with_dst(&tms);
9842
9843 if (ltime_end == ltime_start)
9844 ltime_end += 3600 * 24;
9845
9846 std::string redir;
9847 redir += "?cmd=oldhistory&";
9848 redir += add_param_to_url("group", p->getparam("group"));
9849 redir += "&";
9850 redir += add_param_to_url("panel", p->getparam("panel"));
9851 redir += "&";
9852 redir += add_param_to_url("scale", toString((int)(ltime_end - ltime_start)).c_str());
9853 redir += "&";
9854 redir += add_param_to_url("time", time_to_string(ltime_end).c_str());
9855 if (p->isparam("hindex")) {
9856 redir += "&";
9857 redir += add_param_to_url("index", p->getparam("hindex"));
9858 }
9859 redirect(r, redir.c_str());
9860 return;
9861 }
9862
9864 show_header(r, "History", "GET", "", 0);
9865
9866 /* set the times */
9867
9868 time_t now = time(NULL);
9869
9870 time_t starttime = now - 3600 * 24;
9871 time_t endtime = now;
9872 bool full_day = true;
9873
9874 if (p->isparam("htime")) {
9875 endtime = string_to_time(p->getparam("htime"));
9876
9877 if (p->isparam("hscale")) {
9878 starttime = endtime - atoi(p->getparam("hscale"));
9879 full_day = false;
9880 } else {
9881 starttime = endtime - 3600 * 24;
9882 full_day = false;
9883 }
9884 }
9885
9886 /* menu buttons */
9887 r->rsprintf("<tr><td colspan=2>\n");
9888 r->rsprintf("<input type=hidden name=cmd value=OldHistory>\n");
9889 r->rsprintf("<input type=submit name=hcmd value=Query>\n");
9890 r->rsprintf("<input type=submit name=hcmd value=Cancel>\n");
9891 if (p->isparam("group"))
9892 r->rsprintf("<input type=hidden name=group value=\"%s\">\n", p->getparam("group"));
9893 if (p->isparam("panel"))
9894 r->rsprintf("<input type=hidden name=panel value=\"%s\">\n", p->getparam("panel"));
9895 if (p->isparam("htime"))
9896 r->rsprintf("<input type=hidden name=htime value=\"%s\">\n", p->getparam("htime"));
9897 if (p->isparam("hscale"))
9898 r->rsprintf("<input type=hidden name=hscale value=\"%s\">\n", p->getparam("hscale"));
9899 if (p->isparam("hindex"))
9900 r->rsprintf("<input type=hidden name=hindex value=\"%s\">\n", p->getparam("hindex"));
9901 r->rsprintf("</tr>\n\n");
9902 r->rsprintf("</table>"); //end header
9903
9904 r->rsprintf("<table class=\"dialogTable\">"); //main table
9905
9906 struct tm tms;
9907 localtime_r(&starttime, &tms);
9908 tms.tm_year += 1900;
9909
9910 r->rsprintf("<tr><td nowrap>Start date:</td>");
9911
9912 r->rsprintf("<td>Month: <select name=\"m1\">\n");
9913 r->rsprintf("<option value=\"\">\n");
9914 for (i = 0; i < 12; i++)
9915 if (i == tms.tm_mon)
9916 r->rsprintf("<option selected value=\"%s\">%s\n", mname[i], mname[i]);
9917 else
9918 r->rsprintf("<option value=\"%s\">%s\n", mname[i], mname[i]);
9919 r->rsprintf("</select>\n");
9920
9921 r->rsprintf("&nbsp;Day: <select name=\"d1\">");
9922 r->rsprintf("<option selected value=\"\">\n");
9923 for (i = 0; i < 31; i++)
9924 if (i + 1 == tms.tm_mday)
9925 r->rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
9926 else
9927 r->rsprintf("<option value=%d>%d\n", i + 1, i + 1);
9928 r->rsprintf("</select>\n");
9929
9930 int start_hour = tms.tm_hour;
9931 if (full_day)
9932 start_hour = 0;
9933
9934 r->rsprintf("&nbsp;Hour: <input type=\"text\" size=5 maxlength=5 name=\"h1\" value=\"%d\">", start_hour);
9935
9936 r->rsprintf("&nbsp;Year: <input type=\"text\" size=5 maxlength=5 name=\"y1\" value=\"%d\">", tms.tm_year);
9937 r->rsprintf("</td></tr>\n");
9938
9939 r->rsprintf("<tr><td nowrap>End date:</td>");
9940
9941 localtime_r(&endtime, &tms);
9942 tms.tm_year += 1900;
9943
9944 r->rsprintf("<td>Month: <select name=\"m2\">\n");
9945 r->rsprintf("<option value=\"\">\n");
9946 for (i = 0; i < 12; i++)
9947 if (i == tms.tm_mon)
9948 r->rsprintf("<option selected value=\"%s\">%s\n", mname[i], mname[i]);
9949 else
9950 r->rsprintf("<option value=\"%s\">%s\n", mname[i], mname[i]);
9951 r->rsprintf("</select>\n");
9952
9953 r->rsprintf("&nbsp;Day: <select name=\"d2\">");
9954 r->rsprintf("<option selected value=\"\">\n");
9955 for (i = 0; i < 31; i++)
9956 if (i + 1 == tms.tm_mday)
9957 r->rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
9958 else
9959 r->rsprintf("<option value=%d>%d\n", i + 1, i + 1);
9960 r->rsprintf("</select>\n");
9961
9962 int end_hour = tms.tm_hour;
9963 if (full_day)
9964 end_hour = 24;
9965
9966 r->rsprintf("&nbsp;Hour: <input type=\"text\" size=5 maxlength=5 name=\"h2\" value=\"%d\">", end_hour);
9967
9968 r->rsprintf("&nbsp;Year: <input type=\"text\" size=5 maxlength=5 name=\"y2\" value=\"%d\">", tms.tm_year);
9969 r->rsprintf("</td></tr>\n");
9970
9971 r->rsprintf("</table>\n");
9972 r->rsprintf("</div>\n"); // closing for <div id="mmain">
9973 r->rsprintf("</form>\n");
9974 r->rsprintf("</body></html>\r\n");
9975}
9976
9977/*------------------------------------------------------------------*/
9978/* history plot code starts here */
9979/*------------------------------------------------------------------*/
9980
9981static int cmp_names(const void *a, const void *b)
9982{
9983 int i;
9984 const char*sa = (const char*)a;
9985 const char*sb = (const char*)b;
9986
9987 int debug = 0;
9988
9989 // Cannot use strcmp() because it does not know how to compare numerical values, e.g.
9990 // it thinks "111" is smaller than "9"
9991 //return strcmp(sa, sb);
9992
9993 if (debug)
9994 printf("compare [%s] and [%s]\n", sa, sb);
9995
9996 for (i=0; ; i++) {
9997 if (sa[i]==0 && sb[i]==0)
9998 return 0; // both strings have the same length and the same characters
9999
10000 //printf("index %d, char [%c] [%c], isdigit %d %d\n", i, sa[i], sb[i], isdigit(sa[i]), isdigit(sb[i]));
10001
10002 if (isdigit(sa[i]) && isdigit(sb[i])) {
10003 int va = atoi(sa+i);
10004 int vb = atoi(sb+i);
10005
10006 if (debug)
10007 printf("index %d, values %d %d\n", i, va, vb);
10008
10009 if (va < vb)
10010 return -1;
10011 else if (va > vb)
10012 return 1;
10013
10014 // values are equal, skip the the end of the digits, compare any trailing text
10015 continue;
10016 }
10017
10018 if (sa[i]==sb[i]) {
10019 continue;
10020 }
10021
10022 if (debug)
10023 printf("index %d, char [%c] [%c]\n", i, sa[i], sb[i]);
10024
10025 if (sa[i] == 0) // string sa is shorter
10026 return -1;
10027 else if (sb[i] == 0) // string sb is shorter
10028 return 1;
10029
10030 if (sa[i]<sb[i])
10031 return -1;
10032 else
10033 return 1;
10034 }
10035
10036 // NOT REACHED
10037}
10038
10039const bool cmp_events(const std::string& a, const std::string& b)
10040{
10041 return cmp_names(a.c_str(), b.c_str()) < 0;
10042}
10043
10044const bool cmp_events1(const std::string& a, const std::string& b)
10045{
10046 return a < b;
10047}
10048
10049const bool cmp_tags(const TAG& a, const TAG& b)
10050{
10051 return cmp_names(a.name, b.name) < 0;
10052}
10053
10054#if 0
10055static int cmp_tags(const void *a, const void *b)
10056{
10057 const TAG*sa = (const TAG*)a;
10058 const TAG*sb = (const TAG*)b;
10059 return cmp_names(sa->name, sb->name);
10060}
10061
10062static void sort_tags(int ntags, TAG* tags)
10063{
10064 qsort(tags, ntags, sizeof(TAG), cmp_tags);
10065}
10066#endif
10067
10068int xdb_get_data_index(HNDLE hDB, const char* str, void *value, int size, int index, int tid)
10069{
10070 HNDLE hKey;
10071 int status = db_find_key(hDB, 0, str, &hKey);
10072 if (status != DB_SUCCESS)
10073 return status;
10074
10075 KEY key;
10076 db_get_key(hDB, hKey, &key);
10077 if (index >= key.num_values)
10078 return DB_OUT_OF_RANGE;
10079
10080 status = db_get_data_index(hDB, hKey, value, &size, index, tid);
10081 return status;
10082}
10083
10084static int xdb_find_key(HNDLE hDB, HNDLE dir, const char* str, HNDLE* hKey, int tid, int size)
10085{
10086 int status = db_find_key(hDB, dir, str, hKey);
10087 if (status == DB_SUCCESS)
10088 return status;
10089
10090 db_create_key(hDB, dir, str, tid);
10091 status = db_find_key(hDB, dir, str, hKey);
10092 if (status != DB_SUCCESS || !*hKey) {
10093 cm_msg(MERROR, "xdb_find_key", "Invalid ODB path \"%s\"", str);
10094 str = "bad_xdb_find_key";
10095 db_create_key(hDB, dir, str, tid);
10096 db_find_key(hDB, dir, str, hKey);
10097 }
10098 assert(*hKey);
10099
10100 if (tid == TID_STRING) {
10101 db_set_data_index(hDB, *hKey, "", size, 0, TID_STRING);
10102 }
10103
10104 return status;
10105}
10106
10107static bool cmp_vars(const HistVar &a, const HistVar &b)
10108{
10109 return a.order < b.order;
10110}
10111
10112static void PrintHistPlot(const HistPlot& hp)
10113{
10114 printf("hist plot: %d variables\n", (int)hp.vars.size());
10115 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);
10116
10117 for (size_t i=0; i<hp.vars.size(); i++) {
10118 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);
10119 }
10120}
10121
10122static std::string NextHistPlotColour(const HistPlot& hp)
10123{
10124 const char* const colour[] =
10125 {
10126 "#00AAFF", "#FF9000", "#FF00A0", "#00C030",
10127 "#A0C0D0", "#D0A060", "#C04010", "#807060",
10128 "#F0C000", "#2090A0", "#D040D0", "#90B000",
10129 "#B0B040", "#B0B0FF", "#FFA0A0", "#A0FFA0",
10130 NULL };
10131
10132 for (int i=0; colour[i]; i++) {
10133 bool in_use = false;
10134
10135 for (size_t j=0; j<hp.vars.size(); j++)
10136 if (hp.vars[j].colour == colour[i]) {
10137 in_use = true;
10138 break;
10139 }
10140
10141 if (!in_use)
10142 return colour[i];
10143 }
10144
10145 return "#808080";
10146}
10147
10148static int NextHistPlotOrder(const HistPlot& hp)
10149{
10150 int order = 0;
10151 for (size_t i=0; i<hp.vars.size(); i++)
10152 if (hp.vars[i].order > order)
10153 order = hp.vars[i].order;
10154 return order + 10;
10155}
10156
10157static void SplitEventAndTagNames(std::string var_name, std::string& event_name, std::string& tag_name) {
10158 event_name = "";
10159 tag_name = "";
10160
10161 std::vector<size_t> colons;
10162
10163 for (size_t i = 0; i < var_name.size(); i++) {
10164 if (var_name[i] == ':') {
10165 colons.push_back(i);
10166 }
10167 }
10168
10169 if (colons.size() == 0) {
10170 // No colons - leave the tag name empty
10171 event_name = var_name;
10172 } else {
10173 size_t split_pos;
10174 size_t slash_pos = var_name.find("/");
10175 bool uses_per_variable_naming = (slash_pos != std::string::npos);
10176
10177 if (uses_per_variable_naming && colons.size() % 2 == 1) {
10178 size_t middle_colon_pos = colons[colons.size() / 2];
10179 std::string slash_to_mid = var_name.substr(slash_pos + 1, middle_colon_pos - slash_pos - 1);
10180 std::string mid_to_end = var_name.substr(middle_colon_pos + 1);
10181
10182 if (slash_to_mid == mid_to_end) {
10183 // Special case - we have a string of the form Beamlime/GS2:FC1:GS2:FC1.
10184 // Logger has already warned people that having colons in the equipment/event
10185 // names is a bad idea, so we only need to worry about them in the tag name.
10186 split_pos = middle_colon_pos;
10187 } else {
10188 // We have a string of the form Beamlime/Demand:GS2:FC1. Split at the first colon.
10189 split_pos = colons[0];
10190 }
10191 } else {
10192 // Normal case - split at the fist colon.
10193 split_pos = colons[0];
10194 }
10195
10196 event_name = var_name.substr(0, split_pos);
10197 tag_name = var_name.substr(split_pos + 1);
10198 }
10199}
10200
10201static void LoadHistPlotFromOdb(MVOdb* odb, HistPlot* hp, const char* group, const char* panel)
10202{
10203 std::string path = "History/Display/";
10204 path += group;
10205 path += "/";
10206 path += panel;
10207
10208 MVOdb* o = odb->Chdir(path.c_str());
10209 if (!o) {
10210 return;
10211 }
10212
10213 o->RS("Timescale", &hp->timescale);
10214 o->RD("Minimum", &hp->minimum);
10215 o->RD("Maximum", &hp->maximum);
10216 o->RB("Zero ylow", &hp->zero_ylow);
10217 o->RB("Log axis", &hp->log_axis);
10218 o->RB("Zero ylow", &hp->zero_ylow);
10219 o->RB("Show run markers", &hp->show_run_markers);
10220 o->RB("Show values", &hp->show_values);
10221 o->RB("Show fill", &hp->show_fill);
10222 o->RB("Show factor", &hp->show_factor);
10223 //o->RB("Enable factor and offset", &hp->enable_factor);
10224
10225 std::vector<std::string> hist_vars;
10226 std::vector<std::string> hist_formula;
10227 std::vector<std::string> hist_colour;
10228 std::vector<std::string> hist_label;
10229 std::vector<bool> hist_show_raw_value;
10230 std::vector<double> hist_factor;
10231 std::vector<double> hist_offset;
10232 std::vector<double> hist_voffset;
10233
10234 o->RSA("Variables", &hist_vars);
10235 o->RSA("Formula", &hist_formula);
10236 o->RSA("Colour", &hist_colour);
10237 o->RSA("Label", &hist_label);
10238 o->RBA("Show raw value", &hist_show_raw_value);
10239 o->RDA("Factor", &hist_factor);
10240 o->RDA("Offset", &hist_offset);
10241 o->RDA("VOffset", &hist_voffset);
10242
10243 // fix broken plots with "factor" all zero. for reasons
10244 // unknown the new history code has corrupted many
10245 // history plot definitions like this. K.O.
10246 {
10247 bool all_zero = true;
10248 for (size_t i=0; i<hist_factor.size(); i++) {
10249 if (hist_factor[i] != 0)
10250 all_zero = false;
10251 }
10252 if (all_zero) {
10253 for (size_t i=0; i<hist_factor.size(); i++) {
10254 hist_factor[i] = 1.0;
10255 }
10256 }
10257 }
10258
10259 size_t num = std::max(hist_vars.size(), hist_formula.size());
10260 num = std::max(num, hist_colour.size());
10261 num = std::max(num, hist_label.size());
10262 num = std::max(num, hist_show_raw_value.size());
10263 num = std::max(num, hist_factor.size());
10264 num = std::max(num, hist_offset.size());
10265 num = std::max(num, hist_voffset.size());
10266
10267 hist_vars.resize(num);
10268 hist_formula.resize(num);
10269 hist_colour.resize(num);
10270 hist_label.resize(num);
10271 hist_show_raw_value.resize(num);
10272 hist_factor.resize(num, 1.0);
10273 hist_offset.resize(num, 0.0);
10274 hist_voffset.resize(num, 0.0);
10275
10276 for (size_t i=0; i<num; i++) {
10277 HistVar v;
10278
10279 SplitEventAndTagNames(hist_vars[i], v.event_name, v.tag_name);
10280
10281 v.formula = hist_formula[i];
10282 v.colour = hist_colour[i];
10283 v.label = hist_label[i];
10284 v.show_raw_value = hist_show_raw_value[i];
10285 v.factor = hist_factor[i];
10286 v.offset = hist_offset[i];
10287 v.voffset = hist_voffset[i];
10288 v.order = NextHistPlotOrder(*hp);
10289
10290 // one-time migration of factor and offset to formula
10291 if (hp->enable_factor && v.formula.empty()) {
10292 if (v.factor!=1 || v.offset!=0 || v.voffset!=0) {
10293 v.formula = msprintf("%g%+g*(x%+g)", v.offset, v.factor, -v.voffset);
10294 }
10295 }
10296
10297 hp->vars.push_back(v);
10298 }
10299
10300// printf("Load from ODB %s: ", path.c_str());
10301// PrintHistPlot(*hp);
10302
10303 delete o;
10304}
10305
10307{
10308 hp->timescale = p->getparam("timescale");
10309 hp->minimum = strtod(p->getparam("minimum"), NULL);
10310 hp->maximum = strtod(p->getparam("maximum"), NULL);
10311 hp->zero_ylow = *p->getparam("zero_ylow");
10312 hp->log_axis = *p->getparam("log_axis");
10313 hp->show_run_markers = *p->getparam("run_markers");
10314 hp->show_values = *p->getparam("show_values");
10315 hp->show_fill = *p->getparam("show_fill");
10316 hp->show_factor = *p->getparam("show_factor");
10317 //hp->enable_factor = *p->getparam("enable_factor");
10318
10319 for (int index=0; ; index++) {
10320 char str[256];
10321 sprintf(str, "event%d", index);
10322
10323 //printf("param event %d: [%s] [%s] [%d]\n", index, str, p->getparam(str), *p->getparam(str));
10324
10325 if (!p->isparam(str))
10326 break;
10327
10328 if (*p->getparam(str) == '/') // "/empty"
10329 continue;
10330
10331 HistVar v;
10332
10333 v.event_name = p->xgetparam(str);
10334
10335 sprintf(str, "var%d", index);
10336 v.tag_name = p->xgetparam(str);
10337
10338 sprintf(str, "form%d", index);
10339 v.formula = p->xgetparam(str);
10340
10341 sprintf(str, "col%d", index);
10342 v.colour = p->xgetparam(str);
10343
10344 sprintf(str, "lab%d", index);
10345 v.label = p->xgetparam(str);
10346
10347 sprintf(str, "raw%d", index);
10348 v.show_raw_value = atoi(p->xgetparam(str).c_str());
10349
10350 sprintf(str, "factor%d", index);
10351 if (p->isparam(str)) {
10352 v.factor = atof(p->xgetparam(str).c_str());
10353 } else {
10354 v.factor = 1.0;
10355 }
10356
10357 sprintf(str, "offset%d", index);
10358 v.offset = atof(p->xgetparam(str).c_str());
10359
10360 sprintf(str, "voffset%d", index);
10361 v.voffset = atof(p->xgetparam(str).c_str());
10362
10363 sprintf(str, "ord%d", index);
10364 if (p->isparam(str)) {
10365 v.order = atoi(p->xgetparam(str).c_str());
10366 } else {
10367 v.order = NextHistPlotOrder(*hp);
10368 }
10369
10370 hp->vars.push_back(v);
10371 }
10372
10373 /* correctly number newly added variables */
10374 for (size_t index=0; index<hp->vars.size(); index++) {
10375 if (hp->vars[index].order < 0)
10376 hp->vars[index].order = NextHistPlotOrder(*hp);
10377 }
10378
10379// printf("Load from param:\n");
10380// PrintHistPlot(*hp);
10381}
10382
10384{
10385 int seln = atoi(p->getparam("seln"));
10386 for (int i=0; i<seln; i++) {
10387 char str[256];
10388 sprintf(str, "sel%d", i);
10389
10390 std::string par = p->getparam(str);
10391 if (par.length() < 1)
10392 continue;
10393
10394 std::string event_name, tag_name;
10395 SplitEventAndTagNames(par, event_name, tag_name);
10396
10397 if (tag_name == "")
10398 continue;
10399
10400 HistVar v;
10401
10402 v.event_name = event_name;
10403 v.tag_name = tag_name;
10404 v.colour = NextHistPlotColour(hp);
10405 v.order = NextHistPlotOrder(hp);
10406
10407 hp.vars.push_back(v);
10408 }
10409}
10410
10411static void SaveHistPlotToOdb(MVOdb* odb, const HistPlot& hp, const char* group, const char* panel)
10412{
10413 if (strlen(group) < 1) {
10414 cm_msg(MERROR, "SaveHistPlotToOdb", "Error: Cannot write history plot to ODB, group \"%s\", panel \"%s\", invalid group name", group, panel);
10415 return;
10416 }
10417
10418 if (strlen(panel) < 1) {
10419 cm_msg(MERROR, "SaveHistPlotToOdb", "Error: Cannot write history plot to ODB, group \"%s\", panel \"%s\", invalid panel name", group, panel);
10420 return;
10421 }
10422
10423 std::string path = "History/Display/";
10424 path += group;
10425 path += "/";
10426 path += panel;
10427
10428// printf("Save to ODB %s: ", path.c_str());
10429// PrintHistPlot(hp);
10430
10431 MVOdb* o = odb->Chdir(path.c_str(), true);
10432
10433 o->WS("Timescale", hp.timescale.c_str());
10434 o->WD("Minimum", hp.minimum);
10435 o->WD("Maximum", hp.maximum);
10436 o->WB("Zero ylow", hp.zero_ylow);
10437 o->WB("Log axis", hp.log_axis);
10438 o->WB("Show run markers", hp.show_run_markers);
10439 o->WB("Show values", hp.show_values);
10440 o->WB("Show fill", hp.show_fill);
10441 o->WB("Show factor and offset", hp.show_factor);
10442 //o->WB("Enable factor and offset", hp.enable_factor);
10443
10444 std::vector<std::string> hist_vars;
10445 std::vector<std::string> hist_formula;
10446 std::vector<std::string> hist_colour;
10447 std::vector<std::string> hist_label;
10448 std::vector<bool> hist_show_raw_value;
10449 std::vector<double> hist_factor;
10450 std::vector<double> hist_offset;
10451 std::vector<double> hist_voffset;
10452
10453 for (size_t i=0; i<hp.vars.size(); i++) {
10454 hist_vars.push_back(hp.vars[i].event_name + ":" + hp.vars[i].tag_name);
10455 hist_formula.push_back(hp.vars[i].formula);
10456 hist_colour.push_back(hp.vars[i].colour);
10457 hist_label.push_back(hp.vars[i].label);
10458 hist_show_raw_value.push_back(hp.vars[i].show_raw_value);
10459 hist_factor.push_back(hp.vars[i].factor);
10460 hist_offset.push_back(hp.vars[i].offset);
10461 hist_voffset.push_back(hp.vars[i].voffset);
10462 }
10463
10464 if (hp.vars.size() > 0) {
10465 o->WSA("Variables", hist_vars, 64);
10466 o->WSA("Formula", hist_formula, 64);
10467 o->WSA("Colour", hist_colour, NAME_LENGTH);
10468 o->WSA("Label", hist_label, NAME_LENGTH);
10469 o->WBA("Show raw value", hist_show_raw_value);
10470 o->WDA("Factor", hist_factor);
10471 o->WDA("Offset", hist_offset);
10472 o->WDA("VOffset", hist_voffset);
10473 } else {
10474 o->Delete("Variables");
10475 o->Delete("Formula");
10476 o->Delete("Colour");
10477 o->Delete("Label");
10478 o->Delete("Show raw value");
10479 o->Delete("Factor");
10480 o->Delete("Offset");
10481 o->Delete("VOffset");
10482 }
10483
10484 delete o;
10485}
10486
10488{
10489 /* delete variables according to "hist_order" */
10490
10491 while (1) {
10492 bool something_deleted = false;
10493 for (unsigned i=0; i<hp.vars.size(); i++) {
10494 if (hp.vars[i].order <= 0) {
10495 hp.vars.erase(hp.vars.begin() + i);
10496 something_deleted = true;
10497 }
10498 }
10499 if (!something_deleted)
10500 break;
10501 }
10502}
10503
10505{
10506 /* sort variables according to "hist_order" */
10507
10508 bool need_sort = false;
10509 for (size_t i=1; i<hp.vars.size(); i++) {
10510 if (hp.vars[i-1].order >= hp.vars[i].order) {
10511 need_sort = true;
10512 }
10513 }
10514
10515 if (need_sort) {
10516 /* sort variables by order */
10517 std::sort(hp.vars.begin(), hp.vars.end(), cmp_vars);
10518
10519 /* renumber the variables according to the new sorted order */
10520 for (size_t index=0; index<hp.vars.size(); index++)
10521 hp.vars[index].order = (index+1)*10;
10522 }
10523}
10524
10525void show_hist_config_page(MVOdb* odb, Param* p, Return* r, const char *hgroup, const char *hpanel)
10526{
10527 int status;
10528 int max_display_events = 20;
10529 int max_display_tags = 200;
10530 char str[256], hcmd[256];
10531
10532 odb->RI("History/MaxDisplayEvents", &max_display_events, true);
10533 odb->RI("History/MaxDisplayTags", &max_display_tags, true);
10534
10535 mstrlcpy(hcmd, p->getparam("hcmd"), sizeof(hcmd));
10536
10537 if (equal_ustring(hcmd, "Clear history cache")) {
10538 //printf("clear history cache!\n");
10539 strcpy(hcmd, "Refresh");
10541 if (mh)
10542 mh->hs_clear_cache();
10543 }
10544
10545 //printf("cmd [%s]\n", cmd);
10546 //printf("cmdx [%s]\n", p->getparam("cmdx"));
10547
10548 HistPlot hp;
10549
10550 if (equal_ustring(hcmd, "refresh") || equal_ustring(hcmd, "save")) {
10551 LoadHistPlotFromParam(&hp, p);
10553 } else {
10554 LoadHistPlotFromOdb(odb, &hp, hgroup, hpanel);
10555 }
10556
10557 SortHistPlotVars(hp);
10558
10559 if (strlen(p->getparam("seln")) > 0)
10561
10562 //hp->Print();
10563
10564 if (hcmd[0] && equal_ustring(hcmd, "save")) {
10565 SaveHistPlotToOdb(odb, hp, hgroup, hpanel);
10566
10567 if (p->getparam("redir") && *p->getparam("redir"))
10568 redirect(r, p->getparam("redir"));
10569 else {
10570 sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
10571 redirect(r, str);
10572 }
10573 return;
10574 }
10575
10576 show_header(r, "History Config", "GET", "", 0);
10577 r->rsprintf("</table>"); //close header table
10578
10579 r->rsprintf("<table class=\"mtable\">"); //open main table
10580
10581 r->rsprintf("<tr><th colspan=11 class=\"subStatusTitle\">History Panel \"%s\" / \"%s\"</th></tr>\n", hgroup, hpanel);
10582
10583 /* menu buttons */
10584 r->rsprintf("<tr><td colspan=11>\n");
10585
10586 r->rsprintf("<input type=button value=Refresh ");
10587 r->rsprintf("onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">\n");
10588
10589 r->rsprintf("<input type=button value=Save ");
10590 r->rsprintf("onclick=\"document.form1.hcmd.value='Save';document.form1.submit()\">\n");
10591
10592 {
10593 r->rsprintf("<input type=button value=Cancel ");
10594 std::string url = "?cmd=oldhistory&group=";
10595 url += hgroup;
10596 url += "&panel=";
10597 url += hpanel;
10598 url += "&hcmd=Cancel";
10599 if (p->getparam("redir")) {
10600 url += "&redir=";
10601 url += urlEncode(p->getparam("redir"));
10602 }
10603 r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
10604 }
10605 {
10606 r->rsprintf("<input type=button value=\"Edit in ODB\"");
10607 std::string url = "?cmd=odb&odb_path=";
10608 url += "/History/Display/";
10609 url += urlEncode(hgroup);
10610 url += "/";
10611 url += urlEncode(hpanel);
10612 r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
10613 }
10614 {
10615 r->rsprintf("<input type=button value=\"Edit in new editor\"");
10616 std::string url = "?cmd=hs_edit";
10617 url += "&group=";
10618 url += urlEncode(hgroup);
10619 url += "&panel=";
10620 url += urlEncode(hpanel);
10621 if (p->getparam("redir")) {
10622 url += "&redir=";
10623 url += urlEncode(p->getparam("redir"));
10624 }
10625 r->rsprintf("onclick=\"window.location.search='%s'\">\n", url.c_str());
10626 }
10627 r->rsprintf("<input type=button value=\"Clear history cache\"");
10628 r->rsprintf("onclick=\"document.form1.hcmd.value='Clear history cache';document.form1.submit()\">\n");
10629 r->rsprintf("<input type=button value=\"Delete panel\"");
10630 r->rsprintf("onclick=\"window.location.search='?cmd=oldhistory&group=%s&panel=%s&hcmd=Delete%%20panel'\">\n", hgroup, hpanel);
10631 r->rsprintf("</td></tr>\n");
10632
10633 r->rsprintf("<tr><td colspan=11>\n");
10634
10635 /* sort_vars */
10636 int sort_vars = *p->getparam("sort_vars");
10637 r->rsprintf("<input type=checkbox %s name=sort_vars value=1 onclick=\"this.form.submit();\">Sort variable names", sort_vars?"checked":"");
10638
10639 /* old_vars */
10640 int old_vars = *p->getparam("old_vars");
10641 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":"");
10642
10643 if (hp.show_factor)
10644 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_factor value=1 onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">");
10645 else
10646 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_factor value=1 onclick=\"document.form1.hcmd.value='Refresh';document.form1.submit()\">");
10647 r->rsprintf("Show&nbsp;factor&nbsp;and&nbsp;offset\n");
10648
10649 /* hidden command for refresh */
10650 r->rsprintf("<input type=hidden name=cmd value=Oldhistory>\n");
10651 r->rsprintf("<input type=hidden name=hcmd value=Refresh>\n");
10652 r->rsprintf("<input type=hidden name=panel value=\"%s\">\n", hpanel);
10653 r->rsprintf("<input type=hidden name=group value=\"%s\">\n", hgroup);
10654
10655 if (p->getparam("redir") && *p->getparam("redir"))
10656 r->rsprintf("<input type=hidden name=redir value=\"%s\">\n", p->getparam("redir"));
10657
10658 r->rsprintf("</td></tr>\n");
10659
10660 r->rsprintf("<tr><td colspan=4 style='text-align:right'>Time scale (in units 'm', 'h', 'd'):</td>\n");
10661 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());
10662
10663 r->rsprintf("<tr><td colspan=4 style='text-align:right'>Minimum (set to '-inf' for autoscale):</td>\n");
10664 r->rsprintf("<td colspan=3><input type=text size=12 name=minimum value=%f></td><td colspan=4></td></tr>\n", hp.minimum);
10665
10666 r->rsprintf("<tr><td colspan=4 style='text-align:right'>Maximum (set to 'inf' for autoscale):</td>\n");
10667 r->rsprintf("<td colspan=3><input type=text size=12 name=maximum value=%f></td><td colspan=4></td></tr>\n", hp.maximum);
10668
10669 r->rsprintf("<tr><td colspan=11>");
10670
10671 if (hp.zero_ylow)
10672 r->rsprintf("<input type=checkbox checked name=zero_ylow value=1>");
10673 else
10674 r->rsprintf("<input type=checkbox name=zero_ylow value=1>");
10675 r->rsprintf("Zero&nbsp;Y;&nbsp;axis\n");
10676
10677 if (hp.log_axis)
10678 r->rsprintf("<input type=checkbox checked name=log_axis value=1>");
10679 else
10680 r->rsprintf("<input type=checkbox name=log_axis value=1>");
10681 r->rsprintf("Logarithmic&nbsp;Y&nbsp;axis\n");
10682
10683 if (hp.show_run_markers)
10684 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=run_markers value=1>");
10685 else
10686 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=run_markers value=1>");
10687 r->rsprintf("Show&nbsp;run&nbsp;markers\n");
10688
10689 if (hp.show_values)
10690 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_values value=1>");
10691 else
10692 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_values value=1>");
10693 r->rsprintf("Show&nbsp;values&nbsp;of&nbsp;variables\n");
10694
10695 if (hp.show_fill)
10696 r->rsprintf("&nbsp;&nbsp;<input type=checkbox checked name=show_fill value=1>");
10697 else
10698 r->rsprintf("&nbsp;&nbsp;<input type=checkbox name=show_fill value=1>");
10699 r->rsprintf("Show&nbsp;graph&nbsp;fill\n");
10700
10701 r->rsprintf("</td></tr>\n");
10702
10703 /*---- events and variables ----*/
10704
10705 /* get display event name */
10706
10708 if (mh == NULL) {
10709 r->rsprintf(str, "History is not configured\n");
10710 return;
10711 }
10712
10713 time_t t = time(NULL);
10714
10715 if (old_vars)
10716 t = 0;
10717
10718 std::vector<std::string> events;
10719
10720 if (!old_vars)
10721 hs_read_event_list(&events);
10722
10723 if (events.size() == 0)
10724 mh->hs_get_events(t, &events);
10725
10726#if 0
10727 for (unsigned i=0; i<events.size(); i++)
10728 printf("event %d: \"%s\"\n", i, events[i].c_str());
10729#endif
10730
10731 // has to be sorted or equipment name code below would not work
10732 //std::sort(events.begin(), events.end(), cmp_events);
10733 std::sort(events.begin(), events.end(), cmp_events1);
10734
10735 if (strlen(p->getparam("cmdx")) > 0) {
10736 r->rsprintf("<tr><th colspan=8 class=\"subStatusTitle\">List of available history variables</th></tr>\n");
10737 r->rsprintf("<tr><th colspan=1>Sel<th colspan=1>Equipment<th colspan=1>Event<th colspan=1>Variable</tr>\n");
10738
10739 std::string cmdx = p->xgetparam("cmdx");
10740 std::string xeqname;
10741
10742 int i=0;
10743 for (unsigned e=0; e<events.size(); e++) {
10744 std::string eqname;
10745 eqname = events[e].substr(0, events[e].find("/"));
10746
10747 if (eqname.length() < 1)
10748 eqname = events[e];
10749
10750 bool once = false;
10751 if (eqname != xeqname)
10752 once = true;
10753
10754 std::string qcmd = "Expand " + eqname;
10755
10756 //printf("param [%s] is [%s]\n", qcmd.c_str(), p->getparam(qcmd.c_str()));
10757
10758 bool collapsed = true;
10759
10760 if (cmdx == qcmd)
10761 collapsed = false;
10762
10763 if (strlen(p->getparam(qcmd.c_str())) > 0)
10764 collapsed = false;
10765
10766 if (collapsed) {
10767 if (eqname == xeqname)
10768 continue;
10769
10770 r->rsprintf("<tr align=left>\n");
10771 r->rsprintf("<td></td>\n");
10772 r->rsprintf("<td>%s</td>\n", eqname.c_str());
10773 r->rsprintf("<td><input type=submit name=cmdx value=\"%s\"></td>\n", qcmd.c_str());
10774 r->rsprintf("<td>%s</td>\n", "");
10775 r->rsprintf("</tr>\n");
10776 xeqname = eqname;
10777 continue;
10778 }
10779
10780 if (once)
10781 r->rsprintf("<tr><input type=hidden name=\"%s\" value=%d></tr>\n", qcmd.c_str(), 1);
10782
10783 std::string rcmd = "Expand " + events[e];
10784
10785 //printf("param [%s] is [%s]\n", rcmd.c_str(), p->getparam(rcmd.c_str()));
10786
10787 collapsed = true;
10788
10789 if (cmdx == rcmd)
10790 collapsed = false;
10791
10792 if (strlen(p->getparam(rcmd.c_str())) > 0)
10793 collapsed = false;
10794
10795 if (collapsed) {
10796 r->rsprintf("<tr align=left>\n");
10797 r->rsprintf("<td></td>\n");
10798 r->rsprintf("<td>%s</td>\n", eqname.c_str());
10799 r->rsprintf("<td>%s</td>\n", events[e].c_str());
10800 r->rsprintf("<td><input type=submit name=cmdx value=\"%s\"></td>\n", rcmd.c_str());
10801 r->rsprintf("</tr>\n");
10802 continue;
10803 }
10804
10805 r->rsprintf("<tr><input type=hidden name=\"%s\" value=%d></tr>\n", rcmd.c_str(), 1);
10806
10807 xeqname = eqname;
10808
10809 std::vector<TAG> tags;
10810
10811 status = mh->hs_get_tags(events[e].c_str(), t, &tags);
10812
10813 if (status == HS_SUCCESS && tags.size() > 0) {
10814
10815 if (sort_vars)
10816 std::sort(tags.begin(), tags.end(), cmp_tags);
10817
10818 for (unsigned v=0; v<tags.size(); v++) {
10819
10820 for (unsigned j=0; j<tags[v].n_data; j++) {
10821 char tagname[256];
10822
10823 if (tags[v].n_data == 1)
10824 sprintf(tagname, "%s", tags[v].name);
10825 else
10826 sprintf(tagname, "%s[%d]", tags[v].name, j);
10827
10828 bool checked = false;
10829#if 0
10830 for (int index=0; index<MAX_VARS; index++) {
10831 if (equal_ustring(vars[index].event_name, events[e].c_str()) && equal_ustring(vars[index].var_name, tagname)) {
10832 checked = true;
10833 break;
10834 }
10835 }
10836#endif
10837
10838 r->rsprintf("<tr align=left>\n");
10839 r->rsprintf("<td><input type=checkbox %s name=\"sel%d\" value=\"%s:%s\"></td>\n", checked?"checked":"", i++, events[e].c_str(), tagname);
10840 r->rsprintf("<td>%s</td>\n", eqname.c_str());
10841 r->rsprintf("<td>%s</td>\n", events[e].c_str());
10842 r->rsprintf("<td>%s</td>\n", tagname);
10843 r->rsprintf("</tr>\n");
10844 }
10845 }
10846 }
10847 }
10848
10849 r->rsprintf("<tr>\n");
10850 r->rsprintf("<td></td>\n");
10851 r->rsprintf("<td>\n");
10852 r->rsprintf("<input type=hidden name=seln value=%d>\n", i);
10853 r->rsprintf("<input type=submit value=\"Add Selected\">\n");
10854 r->rsprintf("</td>\n");
10855 r->rsprintf("</tr>\n");
10856 }
10857
10858 r->rsprintf("<tr><td colspan=11 style='text-align:left'>New history: displayed_value = formula(history_value)</td></tr>\n");
10859 r->rsprintf("<tr><td colspan=11 style='text-align:left'>Old history: displayed_value = offset + factor*(history_value - voffset)</td></tr>\n");
10860 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");
10861 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");
10862 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");
10863 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");
10864
10865 r->rsprintf("<tr>\n");
10866 r->rsprintf("<th>Col<th>Event<th>Variable<th>Formula<th>Colour<th>Label<th>Raw<th>Order");
10867 if (hp.show_factor) {
10868 r->rsprintf("<th>Factor<th>Offset<th>VOffset");
10869 }
10870 r->rsprintf("</tr>\n");
10871
10872 //print_vars(vars);
10873
10874 size_t nvars = hp.vars.size();
10875 for (size_t index = 0; index <= nvars; index++) {
10876
10877 r->rsprintf("<tr>");
10878
10879 if (index < nvars) {
10880 if (hp.vars[index].colour.empty())
10881 hp.vars[index].colour = NextHistPlotColour(hp);
10882 r->rsprintf("<td style=\"background-color:%s\">&nbsp;<td>\n", hp.vars[index].colour.c_str());
10883 } else {
10884 r->rsprintf("<td>&nbsp;<td>\n");
10885 }
10886
10887 /* event and variable selection */
10888
10889 r->rsprintf("<select name=\"event%d\" size=1 onChange=\"document.form1.submit()\">\n", (int)index);
10890
10891 /* enumerate events */
10892
10893 /* empty option */
10894 r->rsprintf("<option value=\"/empty\">&lt;empty&gt;\n");
10895
10896 if (index==nvars) { // last "empty" entry
10897 for (unsigned e=0; e<events.size(); e++) {
10898 const char *p = events[e].c_str();
10899 r->rsprintf("<option value=\"%s\">%s\n", p, p);
10900 }
10901 } else if ((int)events.size() > max_display_events) { // too many events
10902 r->rsprintf("<option selected value=\"%s\">%s\n", hp.vars[index].event_name.c_str(), hp.vars[index].event_name.c_str());
10903 r->rsprintf("<option>(%d events omitted)\n", (int)events.size());
10904 } else { // show all events
10905 bool found = false;
10906 for (unsigned e=0; e<events.size(); e++) {
10907 const char *s = "";
10908 const char *p = events[e].c_str();
10909 if (equal_ustring(hp.vars[index].event_name.c_str(), p)) {
10910 s = "selected";
10911 found = true;
10912 }
10913 r->rsprintf("<option %s value=\"%s\">%s\n", s, p, p);
10914 }
10915 if (!found) {
10916 const char *p = hp.vars[index].event_name.c_str();
10917 r->rsprintf("<option selected value=\"%s\">%s\n", p, p);
10918 }
10919 }
10920
10921 r->rsprintf("</select></td>\n");
10922
10923 //if (hp.vars[index].order <= 0)
10924 // hp.vars[index].order = (index+1)*10;
10925
10926 if (index < nvars) {
10927 bool found_tag = false;
10928 std::string selected_tag = hp.vars[index].tag_name;
10929
10930 r->rsprintf("<td><select name=\"var%d\">\n", (int)index);
10931
10932 std::vector<TAG> tags;
10933
10934 status = mh->hs_get_tags(hp.vars[index].event_name.c_str(), t, &tags);
10935
10936 if (status == HS_SUCCESS && tags.size() > 0) {
10937
10938 if (/* DISABLES CODE */ (0)) {
10939 printf("Compare %d\n", cmp_names("AAA", "BBB"));
10940 printf("Compare %d\n", cmp_names("BBB", "AAA"));
10941 printf("Compare %d\n", cmp_names("AAA", "AAA"));
10942 printf("Compare %d\n", cmp_names("A", "AAA"));
10943 printf("Compare %d\n", cmp_names("A111", "A1"));
10944 printf("Compare %d\n", cmp_names("A111", "A2"));
10945 printf("Compare %d\n", cmp_names("A111", "A222"));
10946 printf("Compare %d\n", cmp_names("A111a", "A111b"));
10947 }
10948
10949 if (sort_vars)
10950 std::sort(tags.begin(), tags.end(), cmp_tags);
10951
10952 if (/* DISABLES CODE */ (0)) {
10953 printf("Event [%s] %d tags\n", hp.vars[index].event_name.c_str(), (int)tags.size());
10954
10955 for (unsigned v=0; v<tags.size(); v++) {
10956 printf("tag[%d] [%s]\n", v, tags[v].name);
10957 }
10958 }
10959
10960 unsigned count_tags = 0;
10961 for (unsigned v=0; v<tags.size(); v++)
10962 count_tags += tags[v].n_data;
10963
10964 //printf("output %d option tags\n", count_tags);
10965
10966 if ((int)count_tags < max_display_tags) {
10967 for (unsigned v=0; v<tags.size(); v++) {
10968
10969 for (unsigned j=0; j<tags[v].n_data; j++) {
10970 std::string tagname;
10971
10972 if (tags[v].n_data == 1)
10973 tagname = tags[v].name;
10974 else {
10975 char buf[256];
10976 sprintf(buf, "[%d]", j);
10977 tagname = std::string(tags[v].name) + buf;
10978 }
10979
10980 if (equal_ustring(selected_tag.c_str(), tagname.c_str())) {
10981 r->rsprintf("<option selected value=\"%s\">%s\n", tagname.c_str(), tagname.c_str());
10982 found_tag = true;
10983 }
10984 else
10985 r->rsprintf("<option value=\"%s\">%s\n", tagname.c_str(), tagname.c_str());
10986
10987 //printf("%d [%s] [%s] [%s][%s] %d\n", (int)index, vars[index].event_name, tagname, vars[index].var_name, selected_var, found_var);
10988 }
10989 }
10990 }
10991 }
10992
10993 if (!found_tag)
10994 if (hp.vars[index].tag_name.length() > 0)
10995 r->rsprintf("<option selected value=\"%s\">%s\n", hp.vars[index].tag_name.c_str(), hp.vars[index].tag_name.c_str());
10996
10997 r->rsprintf("</select></td>\n");
10998 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());
10999 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());
11000 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());
11001 if (hp.vars[index].show_raw_value)
11002 r->rsprintf("<td><input type=checkbox checked name=\"raw%d\" value=1></td>", (int)index);
11003 else
11004 r->rsprintf("<td><input type=checkbox name=\"raw%d\" value=1></td>", (int)index);
11005 r->rsprintf("<td><input type=text size=3 maxlength=32 name=\"ord%d\" value=\"%d\"></td>\n", (int)index, hp.vars[index].order);
11006 if (hp.show_factor) {
11007 r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"factor%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].factor);
11008 r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"offset%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].offset);
11009 r->rsprintf("<td><input type=text size=6 maxlength=32 name=\"voffset%d\" value=\"%g\"></td>\n", (int)index, hp.vars[index].voffset);
11010 } else {
11011 r->rsprintf("<input type=hidden name=\"factor%d\" value=\"%f\">\n", (int)index, hp.vars[index].factor);
11012 r->rsprintf("<input type=hidden name=\"offset%d\" value=\"%f\">\n", (int)index, hp.vars[index].offset);
11013 r->rsprintf("<input type=hidden name=\"voffset%d\" value=\"%f\">\n", (int)index, hp.vars[index].voffset);
11014 }
11015 } else {
11016 r->rsprintf("<td colspan=2><input type=submit name=cmdx value=\"List all variables\"></td>\n");
11017 }
11018
11019 r->rsprintf("</tr>\n");
11020 }
11021
11022 r->rsprintf("</table>\n");
11023 //r->rsprintf("</form>\n");
11024 r->rsprintf("</div>\n"); // closing for <div id="mmain">
11025 r->rsprintf("</form>\n");
11026 r->rsprintf("</body></html>\r\n");
11027}
11028
11029/*------------------------------------------------------------------*/
11030
11031void export_hist(MVOdb* odb, Return* r, const char *group, const char *panel, time_t endtime, int scale, int index, int labels)
11032{
11033 //HNDLE hDB, hkey, hkeypanel;
11034 //int size;
11035 int status;
11036 //char str[256];
11037
11038 int debug = 0;
11039
11040 ss_tzset(); // required for localtime_r()
11041
11042#if 0
11044
11045 /* check panel name in ODB */
11046 sprintf(str, "/History/Display/%s/%s", group, panel);
11047 db_find_key(hDB, 0, str, &hkeypanel);
11048 if (!hkeypanel) {
11049 sprintf(str, "Cannot find /History/Display/%s/%s in ODB\n", group, panel);
11050 show_error(r, str);
11051 return;
11052 }
11053
11054 /* get runmarker flag */
11055 BOOL runmarker = 1;
11056 size = sizeof(runmarker);
11057 db_get_value(hDB, hkeypanel, "Show run markers", &runmarker, &size, TID_BOOL, TRUE);
11058
11059 if (scale == 0) {
11060 /* get timescale */
11061 std::string ts = "1h";
11062 status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
11063 if (status != DB_SUCCESS) {
11064 /* delete old integer key */
11065 db_delete(hDB, hkeypanel, "Timescale");
11066
11067 ts = "1h";
11068 status = db_get_value_string(hDB, hkeypanel, "Timescale", 0, &ts, TRUE);
11069 }
11070
11071 scale = time_to_sec(ts.c_str());
11072 }
11073#endif
11074
11075 time_t now = ss_time();
11076
11077 if (endtime == 0)
11078 endtime = now;
11079
11080 HistoryData hsxxx;
11081 HistoryData* hsdata = &hsxxx;
11082
11083 HistPlot hp;
11084 LoadHistPlotFromOdb(odb, &hp, group, panel);
11085
11086 time_t starttime = endtime - scale;
11087
11088 //printf("start %.0f, end %.0f, scale %.0f\n", (double)starttime, (double)endtime, (double)scale);
11089
11090 status = read_history(hp, /*hDB, group, panel,*/ index, hp.show_run_markers, starttime, endtime, 0, hsdata);
11091 if (status != HS_SUCCESS) {
11092 char str[256];
11093 sprintf(str, "History error, status %d\n", status);
11094 show_error(r, str);
11095 return;
11096 }
11097
11098 if (debug)
11099 hsdata->Print();
11100
11101 int *i_var = (int *)malloc(sizeof(int)*hsdata->nvars);
11102
11103 assert(i_var != NULL);
11104
11105 for (int i = 0; i < hsdata->nvars; i++)
11106 i_var[i] = -1;
11107
11108 time_t t = 0;
11109
11110 /* find first time where all variables are available */
11111 for (int i = 0; i < hsdata->nvars; i++)
11112 if (hsdata->odb_index[i] >= 0)
11113 if (hsdata->num_entries[i] > 0)
11114 if ((t == 0) || (hsdata->t[i][0] > t))
11115 t = hsdata->t[i][0];
11116
11117 if (t == 0 && hsdata->nvars > 1) {
11118 show_error(r, "No history available for choosen period");
11119 free(i_var);
11120 return;
11121 }
11122
11123 int run_index = -1;
11124 int state_index = -1;
11125 int n_run_number = 0;
11126 time_t* t_run_number = NULL;
11127 if (hp.show_run_markers)
11128 for (int i = 0; i < hsdata->nvars; i++) {
11129 if (hsdata->odb_index[i] == -2) {
11130 n_run_number = hsdata->num_entries[i];
11131 t_run_number = hsdata->t[i];
11132 run_index = i;
11133 } else if (hsdata->odb_index[i] == -1) {
11134 state_index = i;
11135 }
11136 }
11137
11138 //printf("runmarker %d, state %d, run %d\n", runmarker, state_index, run_index);
11139
11140 /* header */
11141 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
11142 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
11143 r->rsprintf("Accept-Ranges: bytes\r\n");
11144 r->rsprintf("Cache-control: private, max-age=0, no-cache\r\n");
11145 r->rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n");
11146 r->rsprintf("Content-Type: text/plain\r\n");
11147 r->rsprintf("Content-disposition: attachment; filename=\"export.csv\"\r\n");
11148 r->rsprintf("\r\n");
11149
11150 /* output header line with variable names */
11151 if (hp.show_run_markers && t_run_number)
11152 r->rsprintf("Time, Timestamp, Run, Run State, ");
11153 else
11154 r->rsprintf("Time, Timestamp, ");
11155
11156 for (int i = 0, first = 1; i < hsdata->nvars; i++) {
11157 if (hsdata->odb_index[i] < 0)
11158 continue;
11159 if (hsdata->num_entries[i] <= 0)
11160 continue;
11161 if (!first)
11162 r->rsprintf(", ");
11163 first = 0;
11164 r->rsprintf("%s", hsdata->var_names[i]);
11165 }
11166 r->rsprintf("\n");
11167
11168 int i_run = 0;
11169
11170 do {
11171
11172 if (debug)
11173 printf("hsdata %p, t %d, irun %d\n", hsdata, (int)t, i_run);
11174
11175 /* find run number/state which is valid for t */
11176 if (hp.show_run_markers && t_run_number)
11177 while (i_run < n_run_number-1 && t_run_number[i_run+1] <= t)
11178 i_run++;
11179
11180 //printf("irun %d\n", i_run);
11181
11182 /* find index for all variables which is valid for t */
11183 for (int i = 0; i < hsdata->nvars; i++)
11184 while (hsdata->num_entries[i] > 0 && i_var[i] < hsdata->num_entries[i] - 1 && hsdata->t[i][i_var[i]+1] <= t)
11185 i_var[i]++;
11186
11187 /* finish if last point for all variables reached */
11188 bool done = true;
11189 for (int i = 0 ; i < hsdata->nvars ; i++)
11190 if (hsdata->num_entries[i] > 0 && i_var[i] < hsdata->num_entries[i]) {
11191 done = false;
11192 break;
11193 }
11194
11195 if (debug) {
11196 printf("step to time %d: ", (int)t);
11197 for (int i = 0; i < hsdata->nvars; i++) {
11198 printf(" [%d] %d, ", hsdata->num_entries[i], i_var[i]);
11199 }
11200 printf(" done: %d\n", done);
11201 }
11202
11203 if (done)
11204 break;
11205
11206 struct tm tms;
11207 localtime_r(&t, &tms);
11208
11209 char fmt[256];
11210 //strcpy(fmt, "%c");
11211 strcpy(fmt, "%Y.%m.%d %H:%M:%S");
11212 char str[256];
11213 strftime(str, sizeof(str), fmt, &tms);
11214
11215 if (t_run_number && run_index>=0 && state_index>=0) {
11216 if (t_run_number[i_run] <= t)
11217 r->rsprintf("%s, %d, %.0f, %.0f, ", str, (int)t, hsdata->v[run_index][i_run], hsdata->v[state_index][i_run]);
11218 else
11219 r->rsprintf("%s, %d, N/A, N/A, ", str, (int)t);
11220 } else
11221 r->rsprintf("%s, %d, ", str, (int)t);
11222
11223 if (debug) {
11224 for (int i= 0 ; i < hsdata->nvars ; i++)
11225 printf(" %d (%g)", i_var[i], hsdata->v[i][i_var[i]]);
11226 printf("\n");
11227 }
11228
11229 for (int i=0, first=1 ; i<hsdata->nvars ; i++) {
11230 if (i_var[i] < 0)
11231 continue;
11232 if (hsdata->odb_index[i] < 0)
11233 continue;
11234 if (!first)
11235 r->rsprintf(", ");
11236 first = 0;
11237 //r->rsprintf("(%d %g)", i_var[i], hsdata->v[i][i_var[i]]);
11238 r->rsprintf("%g", hsdata->v[i][i_var[i]]);
11239 }
11240 r->rsprintf("\n");
11241
11242 /* find next t as smallest delta t */
11243 int dt = -1;
11244 for (int i = 0 ; i < hsdata->nvars ; i++)
11245 if (i_var[i]>=0 && hsdata->odb_index[i]>=0 && hsdata->num_entries[i]>0 && i_var[i]<hsdata->num_entries[i]-1) {
11246 int xdt = hsdata->t[i][i_var[i]+1] - t;
11247 if (debug)
11248 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);
11249 if (dt <= 0 || xdt < dt)
11250 dt = xdt;
11251 }
11252
11253 if (debug)
11254 printf("dt %d\n", dt);
11255
11256 if (dt <= 0)
11257 break;
11258
11259 t += dt;
11260
11261 } while (1);
11262
11263 free(i_var);
11264}
11265
11266/*------------------------------------------------------------------*/
11267
11268void show_hist_page(MVOdb* odb, Param* p, Return* r, const char *dec_path, char *buffer, int *buffer_size, int refresh)
11269{
11270 HNDLE hDB, hkey, hikeyp, hkeyp, hkeybutton;
11271 KEY key, ikey;
11272 int i, j, k, scale, index, width, size, status, labels;
11273 char hgroup[256], hpanel[256], hcmd[256];
11274 const char def_button[][NAME_LENGTH] = { "10m", "1h", "3h", "12h", "24h", "3d", "7d" };
11275
11277
11278 hcmd[0] = hgroup[0] = hpanel[0] = 0;
11279
11280 if (p->getparam("group") && *p->getparam("group"))
11281 mstrlcpy(hgroup, p->getparam("group"), sizeof(hgroup));
11282 if (p->getparam("panel") && *p->getparam("panel"))
11283 mstrlcpy(hpanel, p->getparam("panel"), sizeof(hpanel));
11284 if (p->getparam("hcmd") && *p->getparam("hcmd"))
11285 mstrlcpy(hcmd, p->getparam("hcmd"), sizeof(hcmd));
11286
11287 if (equal_ustring(hcmd, "Reset")) {
11288 std::string redir;
11289 //sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
11290 redir += "?cmd=oldhistory&group=";
11291 redir += hgroup;
11292 redir += "&panel=";
11293 redir += hpanel;
11294 redirect(r, redir.c_str());
11295 return;
11296 }
11297
11298 if (equal_ustring(hcmd, "Query")) {
11299 show_query_page(p, r);
11300 return;
11301 }
11302
11303 if (equal_ustring(hcmd, "Cancel")) {
11304 //sprintf(str, "?cmd=oldhistory&group=%s&panel=%s", hgroup, hpanel);
11305 if (p->getparam("redir") && *p->getparam("redir"))
11306 redirect(r, p->getparam("redir"));
11307 else {
11308 std::string redir;
11309 redir += "?cmd=oldhistory&group=";
11310 redir += hgroup;
11311 redir += "&panel=";
11312 redir += hpanel;
11313 redirect(r, redir.c_str());
11314 }
11315 return;
11316 }
11317
11318 if (equal_ustring(hcmd, "Config") ||
11319 equal_ustring(hcmd, "Save")
11320 || equal_ustring(hcmd, "Clear history cache")
11321 || equal_ustring(hcmd, "Refresh")) {
11322
11323 show_hist_config_page(odb, p, r, hgroup, hpanel);
11324 return;
11325 }
11326
11327 if (equal_ustring(hcmd, "New")) {
11328 show_header(r, "History", "GET", "", 0);
11329
11330 r->rsprintf("<table class=\"dialogTable\">");
11331 r->rsprintf("<tr><th class=\"subStatusTitle\" colspan=2>New History Item</th><tr>");
11332 r->rsprintf("<tr><td align=center colspan=2>\n");
11333 r->rsprintf("Select group: &nbsp;&nbsp;");
11334 r->rsprintf("<select id=\"group\" name=\"group\">\n");
11335
11336 /* list existing groups */
11337 db_find_key(hDB, 0, "/History/Display", &hkey);
11338 i = 0;
11339 if (hkey) {
11340 for (i = 0;; i++) {
11341 db_enum_link(hDB, hkey, i, &hkeyp);
11342
11343 if (!hkeyp)
11344 break;
11345
11346 db_get_key(hDB, hkeyp, &key);
11347 if (equal_ustring(hgroup, key.name))
11348 r->rsprintf("<option selected>%s</option>\n", key.name);
11349 else
11350 r->rsprintf("<option>%s</option>\n", key.name);
11351 }
11352 }
11353 if (!hkey || i == 0)
11354 r->rsprintf("<option>Default</option>\n");
11355 r->rsprintf("</select><p>\n");
11356
11357 r->rsprintf("Or enter new group name: &nbsp;&nbsp;");
11358 r->rsprintf("<input type=text size=15 maxlength=31 id=new_group name=new_group>\n");
11359
11360 r->rsprintf("<tr><td align=center colspan=2>\n");
11361 r->rsprintf("<br>Panel name: &nbsp;&nbsp;");
11362 r->rsprintf("<input type=text size=15 maxlength=31 id=panel name=panel><br><br>\n");
11363 r->rsprintf("</td></tr>\n");
11364
11365 r->rsprintf("<tr><td align=center colspan=2>");
11366 std::string str = "?cmd=oldhistory&hcmd=createnew";
11367 str += "&new_group='+document.getElementById('new_group').value+'";
11368 str += "&group='+document.getElementById('group').value+'";
11369 str += "&panel='+document.getElementById('panel').value+'";
11370 r->rsprintf("<input type=button value=Submit onclick=\"window.location.search='%s'\">\n", str.c_str());
11371 r->rsprintf("</td></tr>\n");
11372
11373 r->rsprintf("</table>\r\n");
11374 r->rsprintf("</div>\n"); // closing for <div id="mmain">
11375 r->rsprintf("</form>\n");
11376 r->rsprintf("</body></html>\r\n");
11377 return;
11378 }
11379
11380 if (equal_ustring(hcmd, "Delete Panel")) {
11381 std::string path;
11382 //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
11383 path += "/History/Display/";
11384 path += hgroup;
11385 path += "/";
11386 path += hpanel;
11387 db_delete(hDB, 0, path.c_str());
11388 redirect(r, "?cmd=oldhistory");
11389 return;
11390 }
11391
11392 if (equal_ustring(hcmd, "createnew")) {
11393
11394 /* strip leading/trailing spaces */
11395 while (hpanel[0] == ' ') {
11396 char str[256];
11397 mstrlcpy(str, hpanel+1, sizeof(str));
11398 mstrlcpy(hpanel, str, sizeof(hpanel));
11399 }
11400 while (strlen(hpanel)> 1 && hpanel[strlen(hpanel)-1] == ' ')
11401 hpanel[strlen(hpanel)-1] = 0;
11402
11403 /* use new group if present */
11404 if (p->isparam("new_group") && *p->getparam("new_group"))
11405 mstrlcpy(hgroup, p->getparam("new_group"), sizeof(hgroup));
11406
11407 /* configure that panel */
11408 show_hist_config_page(odb, p, r, hgroup, hpanel);
11409 return;
11410 }
11411
11412 const char* pscale = p->getparam("scale");
11413 if (pscale == NULL || *pscale == 0)
11414 pscale = p->getparam("hscale");
11415 const char* pwidth = p->getparam("width");
11416 if (pwidth == NULL || *pwidth == 0)
11417 pwidth = p->getparam("hwidth");
11418 const char* pheight = p->getparam("height");
11419 if (pheight == NULL || *pheight == 0)
11420 pheight = p->getparam("hheight");
11421 const char* pindex = p->getparam("index");
11422 if (pindex == NULL || *pindex == 0)
11423 pindex = p->getparam("hindex");
11424
11425 labels = 1;
11426 if (*p->getparam("labels") && atoi(p->getparam("labels")) == 0)
11427 labels = 0;
11428
11429 std::string bgcolor = "FFFFFF";
11430 if (*p->getparam("bgcolor"))
11431 bgcolor = p->xgetparam("bgcolor");
11432
11433 std::string fgcolor = "000000";
11434 if (*p->getparam("fgcolor"))
11435 fgcolor = p->xgetparam("fgcolor");
11436
11437 std::string gridcolor = "A0A0A0";
11438 if (*p->getparam("gcolor"))
11439 gridcolor = p->xgetparam("gcolor");
11440
11441 /* evaluate scale and offset */
11442
11443 time_t endtime = 0;
11444 if (p->isparam("time"))
11445 endtime = string_to_time(p->getparam("time"));
11446 else if (p->isparam("htime"))
11447 endtime = string_to_time(p->getparam("htime"));
11448
11449 if (pscale && *pscale)
11450 scale = time_to_sec(pscale);
11451 else
11452 scale = 0;
11453
11454 index = -1;
11455 if (pindex && *pindex)
11456 index = atoi(pindex);
11457
11458#ifdef BROKEN
11459 if (equal_ustring(hcmd, "Create ELog")) {
11460 std::string xurl;
11461 status = db_get_value_string(hDB, 0, "/Elog/URL", 0, &xurl, FALSE);
11462 if (status == DB_SUCCESS) {
11463 char url[256];
11464 get_elog_url(url, sizeof(url));
11465
11466 /*---- use external ELOG ----*/
11467 fsize = 100000;
11468 char* fbuffer = (char*)M_MALLOC(fsize);
11469 assert(fbuffer != NULL);
11470
11471 int width = 640;
11472 int height = 400;
11473
11474 if (equal_ustring(pmag, "Large")) {
11475 width = 1024;
11476 height = 768;
11477 } else if (equal_ustring(pmag, "Small")) {
11478 width = 320;
11479 height = 200;
11480 } else if (atoi(pmag) > 0) {
11481 width = atoi(pmag);
11482 height = 200;
11483 }
11484
11485 printf("hereA\n");
11486 generate_hist_graph(odb, r, hgroup, hpanel, fbuffer, &fsize, width, height, endtime, scale, index, labels, bgcolor.c_str(), fgcolor.c_str(), gridcolor.c_str());
11487
11488 /* save temporary file */
11489 std::string dir;
11490 db_get_value_string(hDB, 0, "/Elog/Logbook Dir", 0, &dir, TRUE);
11491 if (dir.length() > 0 && dir[dir.length()-1] != DIR_SEPARATOR)
11492 dir += DIR_SEPARATOR_STR;
11493
11494 time_t now = time(NULL);
11495 localtime_r(&now, &tms);
11496
11497 std::string file_name = msprintf("%02d%02d%02d_%02d%02d%02d_%s.gif",
11498 tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday,
11499 tms.tm_hour, tms.tm_min, tms.tm_sec, hpanel);
11500 std::string fname = dir + file_name;
11501
11502 /* save attachment */
11503 fh = open(fname.c_str(), O_CREAT | O_RDWR | O_BINARY, 0644);
11504 if (fh < 0) {
11505 cm_msg(MERROR, "show_hist_page", "Cannot write attachment file \"%s\", open() errno %d (%s)", fname.c_str(), errno, strerror(errno));
11506 } else {
11507 int wr = write(fh, fbuffer, fsize);
11508 if (wr != fsize) {
11509 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));
11510 }
11511 close(fh);
11512 }
11513
11514 /* redirect to ELOG */
11515 if (strlen(url) > 1 && url[strlen(url)-1] != '/')
11516 mstrlcat(url, "/", sizeof(url));
11517 mstrlcat(url, "?cmd=New&fa=", sizeof(url));
11518 mstrlcat(url, file_name, sizeof(url));
11519 redirect(r, url);
11520
11521 M_FREE(fbuffer);
11522 return;
11523
11524 } else {
11525 /*---- use internal ELOG ----*/
11526 std::string str = msprintf("\\HS\\%s.gif", hpanel);
11527 if (p->getparam("hscale") && *p->getparam("hscale"))
11528 str += msprintf("?scale=%s", p->getparam("hscale"));
11529 if (p->getparam("htime") && *p->getparam("htime")) {
11530 if (strchr(str.c_str(), '?'))
11531 str += "&";
11532 else
11533 str += "?";
11534 str += msprintf("time=%s", p->getparam("htime"));
11535 }
11536 //if (p->getparam("hoffset") && *p->getparam("hoffset")) {
11537 // if (strchr(str, '?'))
11538 // mstrlcat(str, "&", sizeof(str));
11539 // else
11540 // mstrlcat(str, "?", sizeof(str));
11541 // sprintf(str + strlen(str), "offset=%s", p->getparam("hoffset"));
11542 //}
11543 if (p->getparam("hwidth") && *p->getparam("hwidth")) {
11544 if (strchr(str.c_str(), '?'))
11545 str += "&";
11546 else
11547 str += "?";
11548 str += msprintf("width=%s", p->getparam("hwidth"));
11549 }
11550 if (p->getparam("hindex") && *p->getparam("hindex")) {
11551 if (strchr(str.c_str(), '?'))
11552 str += "&";
11553 else
11554 str += "?";
11555 str += msprintf("index=%s", p->getparam("hindex"));
11556 }
11557
11558 show_elog_new(r, hpanel, NULL, FALSE, str.c_str(), "../../EL/");
11559 return;
11560 }
11561 }
11562#endif
11563
11564 if (equal_ustring(hcmd, "Export")) {
11565 export_hist(odb, r, hgroup, hpanel, endtime, scale, index, labels);
11566 return;
11567 }
11568
11569 if (strstr(dec_path, ".gif")) {
11570 int width = 640;
11571 int height = 400;
11572 if (equal_ustring(pwidth, "Large")) {
11573 width = 1024;
11574 height = 768;
11575 } else if (equal_ustring(pwidth, "Small")) {
11576 width = 320;
11577 height = 200;
11578 } else if (atoi(pwidth) > 0) {
11579 width = atoi(pwidth);
11580 if (atoi(pheight) > 0)
11581 height = atoi(pheight);
11582 else
11583 height = (int)(0.625 * width);
11584 }
11585
11586 //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);
11587
11588 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());
11589
11590 return;
11591 }
11592
11593 if (history_mode && index < 0)
11594 return;
11595
11596 time_t now = time(NULL);
11597
11598 /* evaluate offset shift */
11599 if (equal_ustring(p->getparam("shift"), "leftmaxall")) {
11600 if (endtime == 0)
11601 endtime = now;
11602 time_t last_written = 0;
11603 status = get_hist_last_written(odb, hgroup, hpanel, endtime, index, 1, &last_written);
11604 if (status == HS_SUCCESS)
11605 endtime = last_written + scale/2;
11606 }
11607
11608 if (equal_ustring(p->getparam("shift"), "leftmax")) {
11609 if (endtime == 0)
11610 endtime = now;
11611 time_t last_written = 0;
11612 status = get_hist_last_written(odb, hgroup, hpanel, endtime, index, 0, &last_written);
11613 if (status == HS_SUCCESS)
11614 if (last_written != endtime)
11615 endtime = last_written + scale/2;
11616 }
11617
11618 if (equal_ustring(p->getparam("shift"), "left")) {
11619 if (endtime == 0)
11620 endtime = now;
11621 endtime -= scale/2;
11622 //offset -= scale / 2;
11623 }
11624
11625 if (equal_ustring(p->getparam("shift"), "right")) {
11626 if (endtime == 0)
11627 endtime = now;
11628 endtime += scale/2;
11629 if (endtime > now)
11630 endtime = now;
11631 }
11632
11633 if (equal_ustring(p->getparam("shift"), "rightmax")) {
11634 endtime = 0;
11635 }
11636
11637 if (equal_ustring(p->getparam("shift"), "zoomin")) {
11638 if (endtime == 0)
11639 endtime = now;
11640 endtime -= scale / 4;
11641 scale /= 2;
11642 }
11643
11644 if (equal_ustring(p->getparam("shift"), "zoomout")) {
11645 if (endtime == 0)
11646 endtime = now;
11647 endtime += scale / 2;
11648 if (endtime > now)
11649 endtime = now;
11650 scale *= 2;
11651 }
11652
11653 int xrefresh = refresh;
11654 if (endtime != 0)
11655 xrefresh = 0;
11656 show_header(r, hpanel, "GET", "", xrefresh);
11657
11658 r->rsprintf("<script type=\"text/javascript\" src=\"midas.js\"></script>\n");
11659 r->rsprintf("<script type=\"text/javascript\" src=\"mhttpd.js\"></script>\n");
11660 show_navigation_bar(r, "History");
11661
11662 r->rsprintf("<table class=\"mtable\">");
11663 r->rsprintf("<tr><th class=\"mtableheader\" colspan=2>History</th></tr>");
11664
11665 {
11666 /* check if panel exists */
11667 std::string path;
11668 //sprintf(str, "/History/Display/%s/%s", hgroup, hpanel);
11669 path += "/History/Display/";
11670 path += hgroup;
11671 path += "/";
11672 path += hpanel;
11673 status = db_find_key(hDB, 0, path.c_str(), &hkey);
11674 if (status != DB_SUCCESS && !equal_ustring(hpanel, "All") && !equal_ustring(hpanel,"")) {
11675 r->rsprintf("<h1>Error: History panel \"%s\" in group \"%s\" does not exist</h1>\n", hpanel, hgroup);
11676 r->rsprintf("</table>\r\n");
11677 r->rsprintf("</div>\n"); // closing for <div id="mmain">
11678 r->rsprintf("</form>\n");
11679 r->rsprintf("</body></html>\r\n");
11680 return;
11681 }
11682 }
11683
11684 /* define hidden field for parameters */
11685 if (pscale && *pscale)
11686 r->rsprintf("<input type=hidden name=hscale id=hscale value=%d>\n", scale);
11687 else {
11688 /* if no scale and offset given, get it from default */
11689 if (hpanel[0] && !equal_ustring(hpanel, "All") && hgroup[0]) {
11690 std::string path;
11691 path += "/History/Display/";
11692 path += hgroup;
11693 path += "/";
11694 path += hpanel;
11695 path += "/Timescale";
11696
11697 std::string scalestr = "1h";
11698 status = db_get_value_string(hDB, 0, path.c_str(), 0, &scalestr, TRUE);
11699 if (status != DB_SUCCESS) {
11700 /* delete old integer key */
11701 db_delete(hDB, 0, path.c_str());
11702 scalestr = "1h";
11703 db_get_value_string(hDB, 0, path.c_str(), 0, &scalestr, TRUE);
11704 }
11705
11706 r->rsprintf("<input type=hidden name=hscale id=hscale value=%s>\n", scalestr.c_str());
11707 scale = time_to_sec(scalestr.c_str());
11708 }
11709 }
11710
11711 if (endtime != 0)
11712 r->rsprintf("<input type=hidden name=htime id=htime value=%s>\n", time_to_string(endtime).c_str());
11713 if (pwidth && *pwidth)
11714 r->rsprintf("<input type=hidden name=hwidth id=hwidth value=%s>\n", pwidth);
11715 if (pheight && *pheight)
11716 r->rsprintf("<input type=hidden name=hheight id=hheight value=%s>\n", pheight);
11717 if (pindex && *pindex)
11718 r->rsprintf("<input type=hidden name=hindex id=hindex value=%s>\n", pindex);
11719
11720 r->rsprintf("</td></tr>\n");
11721
11722 if (hgroup[0] == 0) {
11723 /* "New" button */
11724 r->rsprintf("<tr><td colspan=2><input type=\"button\" name=\"New\" value=\"New\" ");
11725 r->rsprintf("onClick=\"window.location.href='?cmd=oldhistory&hcmd=New'\"></td></tr>\n");
11726
11727 /* links for history panels */
11728 r->rsprintf("<tr><td colspan=2 style=\"text-align:left;\">\n");
11729 if (!hpanel[0])
11730 r->rsprintf("<b>Please select panel:</b><br>\n");
11731
11732 /* table for panel selection */
11733 r->rsprintf("<table class=\"historyTable\">");
11734
11735 /* "All" link */
11736 r->rsprintf("<tr><td colspan=2 class=\"titleCell\">\n");
11737 if (equal_ustring(hgroup, "All"))
11738 r->rsprintf("All &nbsp;&nbsp;");
11739 else
11740 r->rsprintf("<a href=\"?cmd=oldhistory&group=All\">ALL</a>\n");
11741 r->rsprintf("</td></tr>\n");
11742
11743 /* Setup History table links */
11744 db_find_key(hDB, 0, "/History/Display", &hkey);
11745 if (!hkey) {
11746 /* create default panel */
11747 char str[256];
11748 strcpy(str, "System:Trigger per sec.");
11749 strcpy(str + 2 * NAME_LENGTH, "System:Trigger kB per sec.");
11750 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Variables", str, 64, 2, TID_STRING);
11751 strcpy(str, "1h");
11752 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Time Scale", str, NAME_LENGTH, 1, TID_STRING);
11753
11754 strcpy(str, "1h");
11755 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Timescale", str, NAME_LENGTH, 1, TID_STRING);
11756 i = 1;
11757 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Zero ylow", &i, sizeof(BOOL), 1, TID_BOOL);
11758 i = 1;
11759 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Show run markers", &i, sizeof(BOOL), 1, TID_BOOL);
11760
11761 strcpy(str, "");
11762 db_set_value(hDB, 0, "/History/Display/Default/Trigger rate/Formula", str, 64, 1, TID_STRING);
11763 db_set_value_index(hDB, 0, "/History/Display/Default/Trigger rate/Formula", str, 64, 1, TID_STRING, FALSE);
11764 }
11765
11766 db_find_key(hDB, 0, "/History/Display", &hkey);
11767 if (hkey) {
11768 for (i = 0;; i++) {
11769 db_enum_link(hDB, hkey, i, &hkeyp);
11770
11771 if (!hkeyp)
11772 break;
11773
11774 // Group key
11775 db_get_key(hDB, hkeyp, &key);
11776
11777 char enc_name[256];
11778 mstrlcpy(enc_name, key.name, sizeof(enc_name));
11779 urlEncode(enc_name, sizeof(enc_name));
11780
11781 if (equal_ustring(hpanel, key.name))
11782 r->rsprintf("<tr><td class=\"titleCell\">%s</td>\n<td>", key.name);
11783 else
11784 r->rsprintf("<tr><td class=\"titleCell\"><a href=\"?cmd=oldhistory&group=%s\">%s</a></td>\n<td>", enc_name, key.name);
11785
11786 for (j = 0;; j++) {
11787 // scan items
11788 db_enum_link(hDB, hkeyp, j, &hikeyp);
11789
11790 if (!hikeyp) {
11791 r->rsprintf("</tr>");
11792 break;
11793 }
11794 // Item key
11795 db_get_key(hDB, hikeyp, &ikey);
11796
11797 char enc_iname[256];
11798 mstrlcpy(enc_iname, ikey.name, sizeof(enc_iname));
11799 urlEncode(enc_iname, sizeof(enc_iname));
11800
11801 if (equal_ustring(hpanel, ikey.name))
11802 r->rsprintf("<small><b>%s</b></small> &nbsp;", ikey.name);
11803 else
11804 r->rsprintf("<small><a href=\"?cmd=oldhistory&group=%s&panel=%s\">%s</a></small> &nbsp;\n", enc_name, enc_iname, ikey.name);
11805 }
11806 }
11807 }
11808
11809 r->rsprintf("</table></tr>\n");
11810
11811 } else {
11812 int found = 0;
11813
11814 /* show drop-down selectors */
11815 r->rsprintf("<tr><td colspan=2>\n");
11816
11817 r->rsprintf("Group:\n");
11818
11819 r->rsprintf("<select title=\"Select group\" id=\"fgroup\" onChange=\"window.location.search='?cmd=oldhistory&group='+document.getElementById('fgroup').value;\">\n");
11820
11821 db_find_key(hDB, 0, "/History/Display", &hkey);
11822 if (hkey) {
11823 hkeyp = 0;
11824 for (i = 0;; i++) {
11825 db_enum_link(hDB, hkey, i, &hikeyp);
11826
11827 if (!hikeyp)
11828 break;
11829
11830 if (i == 0)
11831 hkeyp = hikeyp;
11832
11833 // Group key
11834 db_get_key(hDB, hikeyp, &key);
11835
11836 if (equal_ustring(key.name, hgroup)) {
11837 r->rsprintf("<option selected value=\"%s\">%s\n", key.name, key.name);
11838 hkeyp = hikeyp;
11839 } else
11840 r->rsprintf("<option value=\"%s\">%s\n", key.name, key.name);
11841 }
11842
11843 if (equal_ustring("ALL", hgroup)) {
11844 r->rsprintf("<option selected value=\"%s\">%s\n", "ALL", "ALL");
11845 } else {
11846 r->rsprintf("<option value=\"%s\">%s\n", "ALL", "ALL");
11847 }
11848
11849 r->rsprintf("</select>\n");
11850 r->rsprintf("&nbsp;&nbsp;Panel:\n");
11851 r->rsprintf("<select title=\"Select panel\" id=\"fpanel\" ");
11852 r->rsprintf("onChange=\"window.location.search='?cmd=oldhistory&group='+document.getElementById('fgroup').value+");
11853 r->rsprintf("'&panel='+document.getElementById('fpanel').value;\">\n");
11854
11855 found = 0;
11856 if (hkeyp) {
11857 for (i = 0;; i++) {
11858 // scan panels
11859 db_enum_link(hDB, hkeyp, i, &hikeyp);
11860
11861 if (!hikeyp)
11862 break;
11863
11864 // Item key
11865 db_get_key(hDB, hikeyp, &key);
11866
11867 if (equal_ustring(hpanel, key.name)) {
11868 r->rsprintf("<option selected value=\"%s\">%s\n", key.name, key.name);
11869 found = 1;
11870 } else
11871 r->rsprintf("<option value=\"%s\">%s\n", key.name, key.name);
11872 }
11873 }
11874
11875 if (found)
11876 r->rsprintf("<option value=\"\">- all -\n");
11877 else
11878 r->rsprintf("<option selected value=\"\">- all -\n");
11879
11880 r->rsprintf("</select>\n");
11881 }
11882
11883 r->rsprintf("<noscript>\n");
11884 r->rsprintf("<input type=submit value=\"Go\">\n");
11885 r->rsprintf("</noscript>\n");
11886
11887 r->rsprintf("&nbsp;&nbsp;<input type=\"button\" name=\"New\" value=\"New\" ");
11888 r->rsprintf("onClick=\"window.location.href='?cmd=oldhistory&hcmd=New&group=%s'\">\n", hgroup);
11889
11890 r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"Reset\" onClick=\"window.location.href='?cmd=oldhistory&hcmd=Reset&group=%s&panel=%s'\">\n", hgroup, hpanel);
11891
11892 r->rsprintf("<input type=\"button\" name=\"Cmd\" value=\"Query\" onClick=\"window.location.href='?cmd=oldhistory&hcmd=Query&group=%s&panel=%s'\">\n", hgroup, hpanel);
11893
11894 double xendtime = endtime;
11895 if (xendtime == 0)
11896 xendtime = now;
11897 double xstarttime = xendtime - scale;
11898
11899 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);
11900
11901 r->rsprintf("</td></tr>\n");
11902 }
11903
11904 //printf("hgroup [%s] hpanel [%s]\n", hgroup, hpanel);
11905
11906 /* check if whole group should be displayed */
11907 if (hgroup[0] && !equal_ustring(hgroup, "ALL") && hpanel[0] == 0) {
11908 std::string strwidth = "Small";
11909 db_get_value_string(hDB, 0, "/History/Display Settings/Width Group", 0, &strwidth, TRUE);
11910
11911 std::string path;
11912 path += "/History/Display/";
11913 path += hgroup;
11914 db_find_key(hDB, 0, path.c_str(), &hkey);
11915 if (hkey) {
11916 for (i = 0 ;; i++) { // scan group
11917 db_enum_link(hDB, hkey, i, &hikeyp);
11918
11919 if (!hikeyp)
11920 break;
11921
11922 db_get_key(hDB, hikeyp, &key);
11923
11924 char enc_name[256];
11925 mstrlcpy(enc_name, key.name, sizeof(enc_name));
11926 urlEncode(enc_name, sizeof(enc_name));
11927
11928 std::string ref;
11929 ref += "graph.gif?width=";
11930 ref += strwidth;
11931 ref += "&cmd=oldhistory&group=";
11932 ref += hgroup;
11933 ref += "&panel=";
11934 ref += enc_name;
11935
11936 std::string ref2;
11937 ref2 += "?cmd=oldhistory&group=";
11938 ref2 += hgroup;
11939 ref2 += "&panel=";
11940 ref2 += enc_name;
11941
11942 if (endtime != 0) {
11943 char tmp[256];
11944 sprintf(tmp, "time=%s&scale=%d", time_to_string(endtime).c_str(), scale);
11945 ref += "&";
11946 ref += tmp;
11947 ref2 += "?";
11948 ref2 += tmp;
11949 }
11950
11951 if (i % 2 == 0)
11952 r->rsprintf("<tr><td><a href=\"%s\"><img src=\"%s\"></a>\n", ref2.c_str(), ref.c_str());
11953 else
11954 r->rsprintf("<td><a href=\"%s\"><img src=\"%s\"></a></tr>\n", ref2.c_str(), ref.c_str());
11955 }
11956
11957 } else {
11958 r->rsprintf("Group \"%s\" not found", hgroup);
11959 }
11960 }
11961
11962 /* image panel */
11963 else if (hpanel[0] && !equal_ustring(hpanel, "All")) {
11964 /* navigation links */
11965 r->rsprintf("<tr><td>\n");
11966
11967 std::string path;
11968 path += "/History/Display/";
11969 path += hgroup;
11970 path += "/";
11971 path += hpanel;
11972 path += "/Buttons";
11973 db_find_key(hDB, 0, path.c_str(), &hkeybutton);
11974 if (hkeybutton == 0) {
11975 /* create default buttons */
11976 db_create_key(hDB, 0, path.c_str(), TID_STRING);
11977 status = db_find_key(hDB, 0, path.c_str(), &hkeybutton);
11978 if (status != DB_SUCCESS || !hkey) {
11979 cm_msg(MERROR, "show_hist_page", "Cannot create history panel with invalid ODB path \"%s\"", path.c_str());
11980 return;
11981 }
11982 db_set_data(hDB, hkeybutton, def_button, sizeof(def_button), 7, TID_STRING);
11983 }
11984
11985 r->rsprintf("<script>\n");
11986 r->rsprintf("function histDisp(p) {\n");
11987 r->rsprintf(" var params = '?cmd=oldhistory&group=%s&panel=%s';\n", hgroup, hpanel);
11988 r->rsprintf(" params += '&'+p;\n");
11989 r->rsprintf(" if (document.getElementById(\'hscale\') !== null)\n");
11990 r->rsprintf(" params += '&hscale='+document.getElementById(\'hscale\').value;\n");
11991 r->rsprintf(" if (document.getElementById(\'htime\') !== null)\n");
11992 r->rsprintf(" params += '&htime='+document.getElementById(\'htime\').value;\n");
11993 r->rsprintf(" if (document.getElementById(\'hwdith\') !== null)\n");
11994 r->rsprintf(" params += '&hwidth='+document.getElementById(\'hwidth\').value;\n");
11995 r->rsprintf(" if (document.getElementById(\'hindex\') !== null)\n");
11996 r->rsprintf(" params += '&hindex='+document.getElementById(\'hindex\').value;\n");
11997 r->rsprintf(" window.location.search = params;\n");
11998 r->rsprintf("}\n\n");
11999 r->rsprintf("</script>\n");
12000
12001 db_get_key(hDB, hkeybutton, &key);
12002
12003 for (i = 0; i < key.num_values; i++) {
12004 char str[256];
12005 size = sizeof(str);
12006 db_get_data_index(hDB, hkeybutton, str, &size, i, TID_STRING);
12007 r->rsprintf("<input type=\"button\" title=\"display last %s\" value=%s onclick=\"histDisp('scale=%s')\">\n", str, str, str);
12008 }
12009
12010 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')\">");
12011 r->rsprintf("<input type=\"button\" value=\"<<\" title=\"go back in time to last available data\" onclick=\"histDisp('shift=leftmax')\">");
12012 r->rsprintf("<input type=\"button\" value=\"<\" title=\"go back in time\" onclick=\"histDisp('shift=left')\">");
12013
12014 r->rsprintf("<input type=\"button\" value=\" + \" title=\"zoom in\" onclick=\"histDisp('shift=zoomin')\">");
12015 r->rsprintf("<input type=\"button\" value=\" - \" title=\"zoom out\" onclick=\"histDisp('shift=zoomout')\">");
12016
12017 if (endtime != 0) {
12018 r->rsprintf("<input type=\"button\" value=\">\" title=\"go forward in time\" onclick=\"histDisp('shift=right')\">");
12019 r->rsprintf("<input type=\"button\" value=\">>\" title=\"go to currently updated fresh data\" onclick=\"histDisp('shift=rightmax')\">");
12020 }
12021
12022 r->rsprintf("<td>\n");
12023 r->rsprintf("<input type=\"button\" value=\"Large\" title=\"large display\" onclick=\"histDisp('width=Large')\">\n");
12024 r->rsprintf("<input type=\"button\" value=\"Small\" title=\"large display\" onclick=\"histDisp('width=Small')\">\n");
12025 r->rsprintf("<input type=\"button\" value=\"Create Elog\" title=\"large display\" onclick=\"histDisp('hcmd=Create Elog')\">\n");
12026 r->rsprintf("<input type=\"button\" value=\"Config\" title=\"large display\" onclick=\"histDisp('hcmd=Config')\">\n");
12027 r->rsprintf("<input type=\"button\" value=\"Export\" title=\"large display\" onclick=\"histDisp('hcmd=Export')\">\n");
12028 r->rsprintf("</tr>\n");
12029
12030 char paramstr[256];
12031
12032 paramstr[0] = 0;
12033 sprintf(paramstr + strlen(paramstr), "&scale=%d", scale);
12034 if (endtime != 0)
12035 sprintf(paramstr + strlen(paramstr), "&time=%s", time_to_string(endtime).c_str());
12036 if (pwidth && *pwidth)
12037 sprintf(paramstr + strlen(paramstr), "&width=%s", pwidth);
12038 else {
12039 std::string wi = "640";
12040 db_get_value_string(hDB, 0, "/History/Display Settings/Width Individual", 0, &wi, TRUE);
12041 sprintf(paramstr + strlen(paramstr), "&width=%s", wi.c_str());
12042 }
12043 if (pheight && *pheight)
12044 sprintf(paramstr + strlen(paramstr), "&height=%s", pheight);
12045
12046 /* define image map */
12047 r->rsprintf("<map name=\"%s\">\r\n", hpanel);
12048
12049 if (!(pindex && *pindex)) {
12050 std::string path;
12051 path += "/History/Display/";
12052 path += hgroup;
12053 path += "/";
12054 path += hpanel;
12055 path += "/Variables";
12056 db_find_key(hDB, 0, path.c_str(), &hkey);
12057 if (hkey) {
12058 db_get_key(hDB, hkey, &key);
12059
12060 for (i = 0; i < key.num_values; i++) {
12061 std::string ref;
12062 //if (paramstr[0]) {
12063 // sprintf(ref, "?cmd=oldhistory&group=%s&panel=%s&%s&index=%d", hgroup, hpanel, paramstr, i);
12064 //} else {
12065 // sprintf(ref, "?cmd=oldhistory&group=%s&panel=%s&index=%d", hgroup, hpanel, i);
12066 //}
12067
12068 ref += "?cmd=oldhistory&group=";
12069 ref += hgroup;
12070 ref += "&panel=";
12071 ref += hpanel;
12072 if (paramstr[0]) {
12073 ref += "&";
12074 ref += paramstr;
12075 }
12076 ref += "&index=";
12077 ref += toString(i);
12078
12079 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());
12080 }
12081 }
12082 } else {
12083 std::string ref = "?cmd=oldhistory&group=";
12084 ref += hgroup;
12085 ref += "&panel=";
12086 ref += hpanel;
12087
12088 if (paramstr[0]) {
12089 ref += "&";
12090 ref += paramstr;
12091 }
12092
12093 if (equal_ustring(pwidth, "Large"))
12094 width = 1024;
12095 else if (equal_ustring(pwidth, "Small"))
12096 width = 320;
12097 else if (atoi(pwidth) > 0)
12098 width = atoi(pwidth);
12099 else
12100 width = 640;
12101
12102 r->rsprintf(" <area shape=rect coords=\"%d,%d,%d,%d\" href=\"%s\">\r\n", 0, 0, width, 20, ref.c_str());
12103 }
12104
12105 r->rsprintf("</map>\r\n");
12106
12107 /* Display individual panels */
12108 if (pindex && *pindex)
12109 sprintf(paramstr + strlen(paramstr), "&index=%s", pindex);
12110
12111 std::string ref;
12112 //sprintf(ref, "graph.gif?cmd=oldhistory&group=%s&panel=%s%s", hgroup, hpanel, paramstr);
12113 ref += "graph.gif?cmd=oldhistory&group=";
12114 ref += hgroup;
12115 ref += "&panel=";
12116 ref += hpanel;
12117 ref += paramstr;
12118
12119 /* put reference to graph */
12120 r->rsprintf("<tr><td colspan=2><img src=\"%s\" usemap=\"#%s\"></tr>\n", ref.c_str(), hpanel);
12121 }
12122
12123 else if (equal_ustring(hgroup, "All")) {
12124 /* Display all panels */
12125 db_find_key(hDB, 0, "/History/Display", &hkey);
12126 if (hkey)
12127 for (i = 0, k = 0;; i++) { // scan Groups
12128 db_enum_link(hDB, hkey, i, &hkeyp);
12129
12130 if (!hkeyp)
12131 break;
12132
12133 db_get_key(hDB, hkeyp, &key);
12134
12135 char enc_group_name[256];
12136 mstrlcpy(enc_group_name, key.name, sizeof(enc_group_name));
12137 urlEncode(enc_group_name, sizeof(enc_group_name));
12138
12139 for (j = 0;; j++, k++) {
12140 // scan items
12141 db_enum_link(hDB, hkeyp, j, &hikeyp);
12142
12143 if (!hikeyp)
12144 break;
12145
12146 db_get_key(hDB, hikeyp, &ikey);
12147
12148 char enc_panel_name[256];
12149 mstrlcpy(enc_panel_name, ikey.name, sizeof(enc_panel_name));
12150 urlEncode(enc_panel_name, sizeof(enc_panel_name));
12151
12152 std::string ref;
12153 ref += "graph.gif?width=Small";
12154 ref += "&cmd=oldhistory&group=";
12155 ref += enc_group_name;
12156 ref += "&panel=";
12157 ref += enc_panel_name;
12158
12159 std::string ref2;
12160 ref2 += "?cmd=oldhistory&group=";
12161 ref2 += enc_group_name;
12162 ref2 += "&panel=";
12163 ref2 += enc_panel_name;
12164
12165 if (endtime != 0) {
12166 char tmp[256];
12167 sprintf(tmp, "time=%s&scale=%d", time_to_string(endtime).c_str(), scale);
12168 ref += "&";
12169 ref += tmp;
12170 ref2 += "&";
12171 ref2 += tmp;
12172 }
12173
12174 if (k % 2 == 0)
12175 r->rsprintf("<tr><td><a href=\"%s\"><img src=\"%s\"></a>\n", ref2.c_str(), ref.c_str());
12176 else
12177 r->rsprintf("<td><a href=\"%s\"><img src=\"%s\"></a></tr>\n", ref2.c_str(), ref.c_str());
12178 } // items loop
12179 } // Groups loop
12180 } // All
12181 r->rsprintf("</table>\r\n");
12182 r->rsprintf("</div>\n"); // closing for <div id="mmain">
12183 r->rsprintf("</form>\n");
12184 r->rsprintf("</body></html>\r\n");
12185}
12186
12187
12188/*------------------------------------------------------------------*/
12189
12190void send_icon(Return* r, const char *icon)
12191{
12192 int length;
12193 const unsigned char *picon;
12194 char str[256], format[256];
12195 time_t now;
12196
12197 if (strstr(icon, "favicon.ico") != 0) {
12198 length = sizeof(favicon_ico);
12199 picon = favicon_ico;
12200 } else if (strstr(icon, "favicon.png") != 0) {
12201 length = sizeof(favicon_png);
12202 picon = favicon_png;
12203 } else
12204 return;
12205
12206 r->rsprintf("HTTP/1.1 200 Document follows\r\n");
12207 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12208 r->rsprintf("Accept-Ranges: bytes\r\n");
12209
12210 /* set expiration time to one day */
12211 time(&now);
12212 now += (int) (3600 * 24);
12213 struct tm gmt_tms;
12214 gmtime_r(&now, &gmt_tms);
12215 strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT");
12216 strftime(str, sizeof(str), format, &gmt_tms);
12217 r->rsprintf("Expires: %s\r\n", str);
12218
12219 if (equal_ustring(icon, "favicon.ico"))
12220 r->rsprintf("Content-Type: image/x-icon\r\n");
12221 else
12222 r->rsprintf("Content-Type: image/png\r\n");
12223
12224 r->rsprintf("Content-Length: %d\r\n\r\n", length);
12225
12226 r->rmemcpy(picon, length);
12227}
12228
12229/*------------------------------------------------------------------*/
12230
12232{
12233 std::string cookie_pwd;
12234 std::string cookie_wpwd;
12235 std::string cookie_cpwd;
12236 int refresh = 0;
12237 //int expand_equipment = 0;
12238};
12239
12240/*------------------------------------------------------------------*/
12241
12243{
12244 gMutex.lock();
12245 t->fTimeLocked = GetTimeSec();
12246}
12247
12249{
12251 gMutex.unlock();
12252}
12253
12254/*------------------------------------------------------------------*/
12255
12256void interprete(Param* p, Return* r, Attachment* a, const Cookies* c, const char *dec_path, RequestTrace* t)
12257/********************************************************************\
12258
12259 Routine: interprete
12260
12261 Purpose: Main interpreter of web commands
12262
12263 \********************************************************************/
12264{
12265 int status;
12266 HNDLE hkey, hDB;
12267
12268 //printf("dec_path [%s]\n", dec_path);
12269
12270 if (strstr(dec_path, "favicon.ico") != 0 ||
12271 strstr(dec_path, "favicon.png")) {
12272 send_icon(r, dec_path);
12273 return;
12274 }
12275
12276 const char* password = p->getparam("pwd");
12277 const char* wpassword = p->getparam("wpwd");
12278 const char* command = p->getparam("cmd");
12279
12280 //printf("interprete: dec_path [%s], command [%s]\n", dec_path, command);
12281
12283 MVOdb* odb = gOdb;
12284
12285 if (history_mode) {
12286 if (equal_ustring(command, "history")) {
12287 if (equal_ustring(command, "config")) {
12288 return;
12289 }
12290
12291 Lock(t);
12292 show_hist_page(odb, p, r, dec_path, NULL, NULL, c->refresh);
12293 Unlock(t);
12294 return;
12295 }
12296 return;
12297 }
12298
12299 /* check for password */
12300 db_find_key(hDB, 0, "/Experiment/Security/Password", &hkey);
12301 if (!password[0] && hkey) {
12302 char str[256];
12303 int size = sizeof(str);
12304 db_get_data(hDB, hkey, str, &size, TID_STRING);
12305
12306 /* check for excemption */
12307 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs/mhttpd", &hkey);
12308 if (hkey == 0 && strcmp(c->cookie_pwd.c_str(), str) != 0) {
12309 Lock(t);
12310 show_password_page(r, dec_path, "");
12311 Unlock(t);
12312 return;
12313 }
12314 }
12315
12316 /*---- redirect with cookie if password given --------------------*/
12317
12318 if (password[0]) {
12319 r->rsprintf("HTTP/1.1 302 Found\r\n");
12320 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12321
12322 time_t now;
12323 time(&now);
12324
12325 now += 3600 * 24;
12326
12327 struct tm gmt_tms;
12328 gmtime_r(&now, &gmt_tms);
12329
12330 char str[256];
12331 strftime(str, sizeof(str), "%A, %d-%b-%Y %H:00:00 GMT", &gmt_tms);
12332
12333 r->rsprintf("Set-Cookie: midas_pwd=%s; path=/; expires=%s\r\n",
12334 ss_crypt(password, "mi"), str);
12335
12336 r->rsprintf("Location: ./\n\n<html>redir</html>\r\n");
12337 return;
12338 }
12339
12340 if (wpassword[0]) {
12341 /* check if password correct */
12342 if (!check_web_password(r, hDB, dec_path, ss_crypt(wpassword, "mi"), p->getparam("redir")))
12343 return;
12344
12345 r->rsprintf("HTTP/1.1 302 Found\r\n");
12346 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12347
12348 time_t now;
12349 time(&now);
12350
12351 now += 3600 * 24;
12352
12353 struct tm gmt_tms;
12354 gmtime_r(&now, &gmt_tms);
12355
12356 char str[256];
12357 strftime(str, sizeof(str), "%A, %d-%b-%Y %H:%M:%S GMT", &gmt_tms);
12358
12359 r->rsprintf("Set-Cookie: midas_wpwd=%s; path=/; expires=%s\r\n", ss_crypt(wpassword, "mi"), str);
12360
12361 sprintf(str, "./%s", p->getparam("redir"));
12362 r->rsprintf("Location: %s\n\n<html>redir</html>\r\n", str);
12363 return;
12364 }
12365
12366 /*---- send sound file -------------------------------------------*/
12367
12368 if (strlen(dec_path) > 3 &&
12369 dec_path[strlen(dec_path)-3] == 'm' &&
12370 dec_path[strlen(dec_path)-2] == 'p' &&
12371 dec_path[strlen(dec_path)-1] == '3') {
12372 if (strrchr(dec_path, '/'))
12373 send_resource(r, strrchr(dec_path, '/')+1);
12374 else
12375 send_resource(r, dec_path);
12376 return;
12377 }
12378
12379 /*---- send midas.js and midas.css -------------------------------*/
12380
12381 if (strstr(dec_path, "midas.js")) {
12382 send_resource(r, "midas.js");
12383 return;
12384 }
12385
12386 if (strstr(dec_path, "midas.css")) {
12387 send_resource(r, "midas.css");
12388 return;
12389 }
12390
12391 /*---- send mhttpd.js --------------------------------------------*/
12392
12393 if (strstr(dec_path, "mhttpd.js")) {
12394 send_resource(r, "mhttpd.js");
12395 return;
12396 }
12397
12398 /*---- send obsolete.js ------------------------------------------*/
12399
12400 if (strstr(dec_path, "obsolete.js")) {
12401 send_resource(r, "obsolete.js");
12402 return;
12403 }
12404
12405 /*---- send the obsolete mhttpd.css ------------------------------*/
12406
12407 if (strstr(dec_path, "mhttpd.css")) {
12408 send_resource(r, "mhttpd.css");
12409 return;
12410 }
12411
12412 /*---- send controls.js ------------------------------------------*/
12413
12414 if (strstr(dec_path, "controls.js")) {
12415 send_resource(r, "controls.js");
12416 return;
12417 }
12418
12419 /*---- send example web page -------------------------------------*/
12420
12421 if (equal_ustring(command, "example")) {
12422 send_resource(r, "example.html");
12423 return;
12424 }
12425
12426 /*---- send example custom page -------------------------------------*/
12427
12428 if (equal_ustring(command, "custom_example")) {
12429 send_resource(r, "custom_example.html");
12430 return;
12431 }
12432
12433 if (equal_ustring(command, "plot_example")) {
12434 send_resource(r, "plot_example.html");
12435 return;
12436 }
12437
12438 /*---- script command --------------------------------------------*/
12439
12440 if (p->getparam("script") && *p->getparam("script")) {
12441
12442 std::string str = msprintf("%s?script=%s", dec_path, p->getparam("script"));
12443 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
12444 return;
12445
12446 std::string path;
12447 path += "/Script/";
12448 path += p->getparam("script");
12449
12450 Lock(t);
12451
12452 cm_exec_script(path.c_str());
12453
12454 Unlock(t);
12455
12456 if (p->isparam("redir"))
12457 redirect2(r, p->getparam("redir"));
12458 else
12459 redirect2(r, "");
12460
12461 return;
12462 }
12463
12464 /*---- customscript command --------------------------------------*/
12465
12466 if (p->getparam("customscript") && *p->getparam("customscript")) {
12467
12468 std::string str = msprintf("%s?customscript=%s", dec_path, p->getparam("customscript"));
12469 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
12470 return;
12471
12472 std::string path;
12473 path += "/CustomScript/";
12474 path += p->getparam("customscript");
12475
12476 Lock(t);
12477
12478 cm_exec_script(path.c_str());
12479
12480 Unlock(t);
12481
12482 if (p->isparam("redir"))
12483 redirect2(r, p->getparam("redir"));
12484 else
12485 redirect2(r, str.c_str());
12486
12487 return;
12488 }
12489
12490 /*---- send the new html pages -----------------------------------*/
12491
12492 if (equal_ustring(command, "start")) {
12493 send_resource(r, "start.html");
12494 return;
12495 }
12496
12497 if ((equal_ustring(command, "") || equal_ustring(command, "status")) && strlen(dec_path) == 0) {
12498 if (midas::odb::exists("/Custom/Status")) {
12499 midas::odb custom("/Custom");
12500
12501 std::string filename = custom["Status"];
12502 filename = add_custom_path(filename);
12503
12504 // if custom file exists, send it (like normal web server)
12505 if (ss_file_exist(filename.c_str())) {
12506 send_file(r, filename);
12507 return;
12508 }
12509 } else
12510 send_resource(r, "status.html");
12511 return;
12512 }
12513
12514 if (equal_ustring(command, "eqtable")) {
12515 send_resource(r, "eqtable.html");
12516 return;
12517 }
12518
12519 if (equal_ustring(command, "newODB")) {
12520 send_resource(r, "odb.html");
12521 return;
12522 }
12523
12524 if (equal_ustring(command, "programs")) {
12525 send_resource(r, "programs.html");
12526 return;
12527 }
12528
12529 if (equal_ustring(command, "alarms")) {
12530 send_resource(r, "alarms.html");
12531 return;
12532 }
12533
12534 if (equal_ustring(command, "transition")) {
12535 send_resource(r, "transition.html");
12536 return;
12537 }
12538
12539 if (equal_ustring(command, "messages")) {
12540 send_resource(r, "messages.html");
12541 return;
12542 }
12543
12544 if (equal_ustring(command, "config") &&
12545 !(dec_path[0] == 'H' && dec_path[1] == 'S' && dec_path[2] == '/')) {
12546 send_resource(r, "config.html");
12547 return;
12548 }
12549
12550 if (equal_ustring(command, "chat")) {
12551 send_resource(r, "chat.html");
12552 return;
12553 }
12554
12555 if (equal_ustring(command, "buffers")) {
12556 send_resource(r, "buffers.html");
12557 return;
12558 }
12559
12560 if (equal_ustring(command, "Show elog")) {
12561 send_resource(r, "elog_show.html");
12562 return;
12563 }
12564
12565 if (equal_ustring(command, "Query elog")) {
12566 send_resource(r, "elog_query_form.html");
12567 return;
12568 }
12569
12570 if (equal_ustring(command, "New elog")) {
12571 send_resource(r, "elog_edit.html");
12572 return;
12573 }
12574
12575 if (equal_ustring(command, "Edit elog")) {
12576 send_resource(r, "elog_edit.html");
12577 return;
12578 }
12579
12580 if (equal_ustring(command, "Reply Elog")) {
12581 send_resource(r, "elog_edit.html");
12582 return;
12583 }
12584
12585 if (equal_ustring(command, "Last elog")) {
12586 send_resource(r, "elog_show.html");
12587 return;
12588 }
12589
12590 if (equal_ustring(command, "Submit Query")) {
12591 send_resource(r, "elog_query.html");
12592 return;
12593 }
12594
12595 if (equal_ustring(dec_path, "spinning-wheel.gif")) {
12596 send_resource(r, "spinning-wheel.gif");
12597 return;
12598 }
12599
12600 /*---- java script commands --------------------------------------*/
12601
12602 if (equal_ustring(command, "jset") ||
12603 equal_ustring(command, "jget") ||
12604 equal_ustring(command, "jcopy") ||
12605 equal_ustring(command, "jpaste") ||
12606 equal_ustring(command, "jkey") ||
12607 equal_ustring(command, "jcreate") ||
12608 equal_ustring(command, "jresize") ||
12609 equal_ustring(command, "jlink") ||
12610 equal_ustring(command, "jrename") ||
12611 equal_ustring(command, "jreorder") ||
12612 equal_ustring(command, "jdelete") ||
12613 equal_ustring(command, "jmsg") ||
12614 equal_ustring(command, "jalm") ||
12615 equal_ustring(command, "jgenmsg") ||
12616 equal_ustring(command, "jrpc_rev0") ||
12617 equal_ustring(command, "jrpc_rev1") ||
12618 equal_ustring(command, "jrpc")) {
12619 Lock(t);
12620 javascript_commands(p, r, c->cookie_cpwd.c_str());
12621 Unlock(t);
12622 return;
12623 }
12624
12625 /*---- history editord -------------------------------------------*/
12626
12627 if (equal_ustring(command, "hs_edit")) {
12628 send_resource(r, "hs_edit.html");
12629 return;
12630 }
12631
12632 /*---- history command -------------------------------------------*/
12633
12634 if (equal_ustring(command, "oldhistory")) {
12635 Lock(t);
12636 show_hist_page(odb, p, r, dec_path, NULL, NULL, c->refresh);
12637 Unlock(t);
12638 return;
12639 }
12640
12641 if (equal_ustring(command, "history")) {
12642 send_resource(r, "history.html");
12643 return;
12644 }
12645
12646 /*---- MSCB command ----------------------------------------------*/
12647
12648 if (equal_ustring(command, "MSCB")) {
12649 if (equal_ustring(command, "set")) {
12650 std::string str;
12651 str += dec_path;
12652 str += "?";
12653 str += add_param_to_url("cmd", command);
12654 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str.c_str()))
12655 return;
12656 }
12657
12658 Lock(t);
12659
12660#ifdef HAVE_MSCB
12661 show_mscb_page(p, r, c->refresh);
12662#else
12663 show_error(r, "MSCB support not compiled into this version of mhttpd");
12664#endif
12665
12666 Unlock(t);
12667 return;
12668 }
12669
12670 /*---- help command ----------------------------------------------*/
12671
12672 if (equal_ustring(command, "help")) {
12673 Lock(t);
12674 show_help_page(r, dec_path);
12675 Unlock(t);
12676 return;
12677 }
12678
12679 /*---- trigger equipment readout ---------------------------*/
12680
12681 if (strncmp(command, "Trigger", 7) == 0) {
12682 std::string cmd;
12683 cmd += "?cmd=";
12684 cmd += command;
12685 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), cmd.c_str())) {
12686 return;
12687 }
12688
12689 Lock(t);
12690
12691 /* extract equipment name */
12692 char eq_name[NAME_LENGTH];
12693
12694 mstrlcpy(eq_name, command + 8, sizeof(eq_name));
12695 if (strchr(eq_name, ' '))
12696 *strchr(eq_name, ' ') = 0;
12697
12698 /* get frontend name */
12699 std::string path;
12700 path += "/Equipment/";
12701 path += eq_name;
12702 path += "/Common/Frontend name";
12703 char fe_name[NAME_LENGTH];
12704 int size = NAME_LENGTH;
12705 db_get_value(hDB, 0, path.c_str(), fe_name, &size, TID_STRING, TRUE);
12706
12707 /* and ID */
12708 path = "";
12709 path += "/Equipment/";
12710 path += eq_name;
12711 path += "/Common/Event ID";
12712 WORD event_id = 0;
12713 size = sizeof(event_id);
12714 db_get_value(hDB, 0, path.c_str(), &event_id, &size, TID_WORD, TRUE);
12715
12716 if (cm_exist(fe_name, FALSE) != CM_SUCCESS) {
12717 std::string str;
12718 str += "Frontend \"";
12719 str += fe_name;
12720 str += "\" not running!";
12721 show_error(r, str.c_str());
12722 } else {
12723 HNDLE hconn;
12724 status = cm_connect_client(fe_name, &hconn);
12725 if (status != RPC_SUCCESS) {
12726 std::string str;
12727 str += "Cannot connect to frontend \"";
12728 str += fe_name;
12729 str +="\" !";
12730 show_error(r, str.c_str());
12731 } else {
12733 if (status != CM_SUCCESS)
12734 show_error(r, "Error triggering event");
12735 else
12736 redirect(r, "");
12737
12738 //cm_disconnect_client(hconn, FALSE);
12739 }
12740 }
12741
12742 Unlock(t);
12743
12744 return;
12745 }
12746
12747 /*---- switch to next subrun -------------------------------------*/
12748
12749 if (strncmp(command, "Next Subrun", 11) == 0) {
12750 int i = TRUE;
12751 db_set_value(hDB, 0, "/Logger/Next subrun", &i, sizeof(i), 1, TID_BOOL);
12752 redirect(r, "");
12753 return;
12754 }
12755
12756 /*---- cancel command --------------------------------------------*/
12757
12758 if (equal_ustring(command, "cancel")) {
12759 if (p->isparam("redir"))
12760 redirect(r, p->getparam("redir"));
12761 else
12762 redirect(r, "");
12763 return;
12764 }
12765
12766 /*---- set command -----------------------------------------------*/
12767
12768 if (equal_ustring(command, "set")) {
12769 char str[256];
12770 mstrlcpy(str, "?cmd=set", sizeof(str));
12771 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), str))
12772 return;
12773
12774 const char* group = p->getparam("group");
12775 int index = atoi(p->getparam("index"));
12776 const char* value = p->getparam("value");
12777
12778 Lock(t);
12779 show_set_page(p, r, group, index, value);
12780 Unlock(t);
12781 return;
12782 }
12783
12784 /*---- find command ----------------------------------------------*/
12785
12786 if (equal_ustring(command, "find")) {
12787 const char* value = p->getparam("value");
12788 Lock(t);
12790 Unlock(t);
12791 return;
12792 }
12793
12794 /*---- CAMAC CNAF command ----------------------------------------*/
12795
12796 if (equal_ustring(command, "CNAF") || strncmp(dec_path, "CNAF", 4) == 0) {
12797 if (!check_web_password(r, hDB, dec_path, c->cookie_wpwd.c_str(), "?cmd=CNAF"))
12798 return;
12799
12800 Lock(t);
12801 show_cnaf_page(p, r);
12802 Unlock(t);
12803 return;
12804 }
12805
12806 /*---- ELog command ----------------------------------------------*/
12807
12808 if (equal_ustring(command, "elog")) {
12809 /* redirect to external ELOG if URL present */
12811 BOOL external_elog = FALSE;
12812 std::string external_elog_url;
12813 int size = sizeof(external_elog);
12814 status = db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, TRUE);
12815 status = db_get_value_string(hDB, 0, "/Elog/URL", 0, &external_elog_url, TRUE);
12816 if (external_elog && (external_elog_url.length() > 0)) {
12817 redirect(r, external_elog_url.c_str());
12818 return;
12819 }
12820 send_resource(r, "elog_show.html");
12821 return;
12822 }
12823
12824 // special processing for "Elog last 7d", etc
12825
12826 char cmdx[32];
12827 mstrlcpy(cmdx, command, sizeof(cmdx));
12828 cmdx[9] = 0;
12829
12830 if (equal_ustring(cmdx, "Elog last")) {
12831 // "Elog last 7d", etc
12832 send_resource(r, "elog_query.html");
12833 return;
12834 }
12835
12836 if (equal_ustring(command, "Create ELog from this page")) {
12837 std::string redir;
12838 redir += "?cmd=New+elog";
12839 redir += "&odb_path=";
12840 redir += p->getparam("odb_path");
12841 redirect(r, redir.c_str());
12842 return;
12843 }
12844
12845 if (equal_ustring(command, "Submit elog")) {
12846 Lock(t);
12847 submit_elog(odb, p, r, a);
12848 Unlock(t);
12849 return;
12850 }
12851
12852 if (equal_ustring(command, "elog_att")) {
12853 Lock(t);
12854 show_elog_attachment(p, r, dec_path);
12855 Unlock(t);
12856 return;
12857 }
12858
12859 /*---- accept command --------------------------------------------*/
12860
12861 if (equal_ustring(command, "accept")) {
12862 int refresh = atoi(p->getparam("refr"));
12863
12864 /* redirect with cookie */
12865 r->rsprintf("HTTP/1.1 302 Found\r\n");
12866 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
12867 r->rsprintf("Content-Type: text/html; charset=%s\r\n", HTTP_ENCODING);
12868
12869 time_t now;
12870 time(&now);
12871
12872 now += 3600 * 24 * 365;
12873
12874 struct tm gmt_tms;
12875 gmtime_r(&now, &gmt_tms);
12876
12877 char str[256];
12878 strftime(str, sizeof(str), "%A, %d-%b-%Y %H:00:00 GMT", &gmt_tms);
12879
12880 r->rsprintf("Set-Cookie: midas_refr=%d; path=/; expires=%s\r\n", refresh, str);
12881 r->rsprintf("Location: ./\r\n\r\n<html>redir</html>\r\n");
12882
12883 return;
12884 }
12885
12886#ifdef OBSOLETE
12887 /*---- slow control display --------------------------------------*/
12888
12889 if (equal_ustring(command, "eqtable")) {
12890 Lock(t);
12891 show_eqtable_page(p, r, c->refresh);
12892 Unlock(t);
12893 return;
12894 }
12895#endif
12896
12897 /*---- sequencer page --------------------------------------------*/
12898
12899 if (equal_ustring(command, "Sequencer")) {
12900 send_resource(r, "sequencer.html");
12901 return;
12902 }
12903
12904 // obsolete
12905 if (equal_ustring(command, "seq")) {
12906 send_resource(r, "sequencer.html");
12907 return;
12908 }
12909
12910 if (equal_ustring(command, "start_script")) {
12911 send_resource(r, "start_script.html");
12912 return;
12913 }
12914
12915 if (equal_ustring(command, "load_script")) {
12916 send_resource(r, "load_script.html");
12917 return;
12918 }
12919
12920 if (equal_ustring(command, "edit_script")) {
12921 send_resource(r, "edit_script.html");
12922 return;
12923 }
12924
12925 /*---- show ODB --------------------------------------------------*/
12926
12927 if (equal_ustring(command, "oldOdb")) {
12928 int write_access = TRUE;
12929 db_find_key(hDB, 0, "/Experiment/Security/Web Password", &hkey);
12930 if (hkey) {
12931 char str[256];
12932 int size = sizeof(str);
12933 db_get_data(hDB, hkey, str, &size, TID_STRING);
12934 if (strcmp(c->cookie_wpwd.c_str(), str) == 0)
12935 write_access = TRUE;
12936 else
12937 write_access = FALSE;
12938 }
12939
12940 std::string odb_path;
12941 if (p->getparam("odb_path") && *p->getparam("odb_path"))
12942 odb_path = p->getparam("odb_path");
12943
12944 Lock(t);
12945 show_odb_page(p, r, odb_path.c_str(), write_access);
12946 Unlock(t);
12947 return;
12948 }
12949
12950 /*---- New ODB browser --------------------------------------------*/
12951
12952 if (equal_ustring(command, "odb")) {
12953 send_resource(r, "odb.html");
12954 return;
12955 }
12956
12957 /*---- ODB show open records --------------------------------------*/
12958
12959 if (equal_ustring(command, "odb_sor")) {
12960 send_resource(r, "odb_sor.html");
12961 return;
12962 }
12963
12964 /*---- ODB show clients -------------------------------------------*/
12965
12966 if (equal_ustring(command, "odb_scl")) {
12967 send_resource(r, "odb_scl.html");
12968 return;
12969 }
12970
12971 /*---- old ODB path ----------------------------------------------*/
12972
12973 if ((command[0]==0) && dec_path[0]) {
12974 if (equal_ustring(dec_path, "root")) {
12975 std::string new_url = "./?cmd=odb";
12976 //printf("redirect old odb path url [%s] to [%s]\n", dec_path, new_url.c_str());
12977 redirect_307(r, new_url.c_str());
12978 return;
12979 }
12980 }
12981
12982 if ((command[0]==0) && dec_path[0]) {
12983 HNDLE hkey;
12984 status = db_find_key(hDB, 0, dec_path, &hkey);
12985 //printf("try odb path [%s], status %d\n", dec_path, status);
12986 if (status == DB_SUCCESS) {
12987 int level = 0;
12988 for (const char* s = dec_path; *s; s++) {
12989 if (*s == '/')
12990 level++;
12991 }
12992 std::string new_url;
12993 if (level == 0) {
12994 // Top-level directory like /Logger, (which appears in dec_path as "Logger")
12995 new_url += "./";
12996 } else {
12997 for (int i=0; i<level; i++) {
12998 if (i>0)
12999 new_url += "/";
13000 new_url += "..";
13001 }
13002 }
13003 new_url += "?cmd=odb";
13004 new_url += "&odb_path=";
13005 new_url += urlEncode(dec_path);
13006 //printf("redirect old odb path url [%s] to [%s]\n", dec_path, new_url.c_str());
13007 redirect_307(r, new_url.c_str());
13008 return;
13009 }
13010 }
13011
13012 /*---- event dump ------------------------------------------------*/
13013
13014 if (equal_ustring(command, "event dump")) {
13015 send_resource(r, "event_dump.html");
13016 return;
13017 }
13018
13019 /*---- custom page -----------------------------------------------*/
13020
13021 if (equal_ustring(command, "custom")) {
13022 Lock(t);
13023 show_custom_page(p, r, c->cookie_cpwd.c_str());
13024 Unlock(t);
13025 return;
13026 }
13027
13028 /*---- custom page accessed by direct URL that used to be under /CS/... ----*/
13029
13030 if (db_find_key(hDB, 0, "/Custom", &hkey) == DB_SUCCESS && dec_path[0]) {
13031 std::string odb_path;
13032 std::string value;
13033 int status;
13034
13035 odb_path = "";
13036 odb_path += "/Custom/Images/";
13037 odb_path += dec_path;
13038 odb_path += "/Background";
13039
13040 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13041
13042 //printf("Try custom gif [%s] status %d\n", odb_path.c_str(), status);
13043
13044 if (status == DB_SUCCESS) {
13045 if (strstr(dec_path, "..")) {
13046 std::string str;
13047 str += "Invalid custom gif name \'";
13048 str += dec_path;
13049 str += "\' contains \'..\'";
13050 show_error_404(r, str.c_str());
13051 return;
13052 }
13053
13054 Lock(t);
13055 show_custom_gif(r, dec_path);
13056 Unlock(t);
13057 return;
13058 }
13059
13060 bool found_custom = false;
13061
13062 odb_path = "";
13063 odb_path += "/Custom/";
13064 odb_path += dec_path;
13065
13066 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13067
13068 //printf("Try [%s] status %d\n", odb_path.c_str(), status);
13069
13070 if (status == DB_SUCCESS) {
13071 found_custom = true;
13072 } else {
13073 odb_path = "";
13074 odb_path += "/Custom/";
13075 odb_path += dec_path;
13076 odb_path += "&";
13077
13078 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13079
13080 //printf("Try [%s] status %d\n", odb_path.c_str(), status);
13081
13082 if (status == DB_SUCCESS) {
13083 found_custom = true;
13084 } else {
13085 odb_path = "";
13086 odb_path += "/Custom/";
13087 odb_path += dec_path;
13088 odb_path += "!";
13089
13090 status = db_get_value_string(hDB, 0, odb_path.c_str(), 0, &value, FALSE);
13091
13092 //printf("Try [%s] status %d\n", odb_path.c_str(), status);
13093
13094 if (status == DB_SUCCESS) {
13095 found_custom = true;
13096 }
13097 }
13098 }
13099
13100 if (found_custom) {
13101 //printf("custom file: serving [%s] value [%s]\n", dec_path, value.c_str());
13102 if (strstr(dec_path, "..")) {
13103 std::string str;
13104 str += "Invalid custom page name \'";
13105 str += dec_path;
13106 str += "\' contains \'..\'";
13107 show_error_404(r, str.c_str());
13108 return;
13109 }
13110
13111 p->setparam("page", dec_path);
13112 Lock(t);
13113 show_custom_page(p, r, c->cookie_cpwd.c_str());
13114 Unlock(t);
13115 return;
13116 }
13117 }
13118
13119 /* new custom pages */
13120 if (db_find_key(hDB, 0, "/Custom", &hkey) == DB_SUCCESS && dec_path[0]) {
13121 std::string custom_path;
13122 status = db_get_value_string(hDB, 0, "/Custom/Path", 0, &custom_path, TRUE);
13123 if ((status == DB_SUCCESS) && (custom_path.length() > 0)) {
13124 if (strstr(dec_path, "..")) {
13125 std::string str;
13126 str += "Invalid custom file name \'";
13127 str += dec_path;
13128 str += "\' contains \'..\'";
13129 show_error_404(r, str.c_str());
13130 return;
13131 }
13132
13133 std::string full_filename = add_custom_path(dec_path);
13134
13135 // if custom file exists, send it (like normal web server)
13136 if (ss_file_exist(full_filename.c_str())) {
13137 send_file(r, full_filename);
13138 return;
13139 }
13140 }
13141 }
13142
13143 /*---- redirect if web page --------------------------------------*/
13144
13145 //if (strlen(command) > 0) {
13146 // if (send_resource(r, std::string(command) + ".html", false))
13147 // return;
13148 //}
13149
13150 /*---- serve url as a resource file ------------------------------*/
13151
13152 if (strlen(p->getparam("path")) > 0) {
13153 if (send_resource(r, p->getparam("path"), false)) {
13154 return;
13155 }
13156 }
13157
13158 /*---- show status -----------------------------------------------*/
13159
13160 if (elog_mode) {
13161 redirect(r, "EL/");
13162 return;
13163 }
13164
13165 /* header */
13166 r->rsprintf("HTTP/1.1 400 Bad Request\r\n");
13167 r->rsprintf("Server: MIDAS HTTP %s\r\n", mhttpd_revision());
13168 r->rsprintf("Content-Type: text/plain; charset=%s\r\n", HTTP_ENCODING);
13169 r->rsprintf("\r\n");
13170 r->rsprintf("Error: Invalid URL \"%s\" or query \"%s\" or command \"%s\"\n", p->getparam("path"), p->getparam("query"), command);
13171}
13172
13173/*------------------------------------------------------------------*/
13174
13175void decode_query(Param* pp, const char *query_string)
13176{
13177 int len = strlen(query_string);
13178 char *buf = (char *)malloc(len+1);
13179 assert(buf != NULL);
13180 memcpy(buf, query_string, len+1);
13181 char* p = buf;
13182 p = strtok(p, "&");
13183 while (p != NULL) {
13184 char *pitem = p;
13185 p = strchr(p, '=');
13186 if (p != NULL) {
13187 *p++ = 0;
13188 urlDecode(pitem); // parameter name
13189 if (!equal_ustring(pitem, "format"))
13190 urlDecode(p); // parameter value
13191
13192 pp->setparam(pitem, p); // decoded query parameters
13193
13194 p = strtok(NULL, "&");
13195 }
13196 }
13197 free(buf);
13198}
13199
13200void decode_get(Return* rr, char *string, const Cookies* c, const char* url, const char* query_string, RequestTrace* t)
13201{
13202 char path[256];
13203
13204 //printf("decode_get: string [%s], decode_url %d, url [%s], query_string [%s]\n", string, decode_url, url, query_string);
13205
13206 Param* param = new Param();
13207
13208 param->initparam();
13209
13210 if (url)
13211 mstrlcpy(path, url + 1, sizeof(path)); /* strip leading '/' */
13212 else {
13213 mstrlcpy(path, string + 1, sizeof(path)); /* strip leading '/' */
13214
13215 if (strchr(path, '?'))
13216 *strchr(path, '?') = 0;
13217 }
13218
13219 param->setparam("path", path);
13220
13221 assert(query_string != NULL);
13222
13223 decode_query(param, query_string);
13224
13225 param->setparam("query", query_string);
13226
13227 char dec_path[256];
13228 mstrlcpy(dec_path, path, sizeof(dec_path));
13229
13230 interprete(param, rr, NULL, c, dec_path, t);
13231
13232 param->freeparam();
13233 delete param;
13234}
13235
13236/*------------------------------------------------------------------*/
13237
13238void decode_post(Return* rr, const char *header, const char *string, const char *boundary, int length, const Cookies* c, const char* url, RequestTrace* t)
13239{
13240 bool debug_decode_post = false;
13241
13242 Param* param = new Param;
13243
13244 param->initparam();
13245
13246 char path[256];
13247
13248 if (url)
13249 mstrlcpy(path, url + 1, sizeof(path)); /* strip leading '/' */
13250 else {
13251 mstrlcpy(path, header + 1, sizeof(path)); /* strip leading '/' */
13252 if (strchr(path, '?'))
13253 *strchr(path, '?') = 0;
13254 if (strchr(path, ' '))
13255 *strchr(path, ' ') = 0;
13256 }
13257 param->setparam("path", path); // undecoded path
13258
13259 Attachment* a = new Attachment;
13260
13261 const char* pinit = string;
13262
13263 /* return if no boundary defined */
13264 if (!boundary[0])
13265 return;
13266
13267 if (strstr(string, boundary))
13268 string = strstr(string, boundary) + strlen(boundary);
13269
13270 if (debug_decode_post)
13271 printf("decode_post: -->[%s]<--\n", string);
13272
13273 do {
13274 //printf("decode_post: [%s]\n", string);
13275 if (strstr(string, "name=")) {
13276 const char* pitem = strstr(string, "name=") + 5;
13277 if (*pitem == '\"')
13278 pitem++;
13279
13280 //printf("decode_post: pitem [%s]\n", pitem);
13281
13282 if (strncmp(pitem, "attfile", 7) == 0) {
13283 int n = pitem[7] - '1';
13284
13285 char file_name[256];
13286 file_name[0] = 0;
13287
13288 /* evaluate file attachment */
13289 if (strstr(pitem, "filename=")) {
13290 const char* p = strstr(pitem, "filename=") + 9;
13291 if (*p == '\"')
13292 p++;
13293 if (strstr(p, "\r\n\r\n"))
13294 string = strstr(p, "\r\n\r\n") + 4;
13295 else if (strstr(p, "\r\r\n\r\r\n"))
13296 string = strstr(p, "\r\r\n\r\r\n") + 6;
13297
13298 mstrlcpy(file_name, p, sizeof(file_name));
13299
13300 char* pp = file_name;
13301 if (strchr(pp, '\"'))
13302 *strchr(pp, '\"') = 0;
13303
13304 /* set attachment filename */
13305 char str[256];
13306 sprintf(str, "attachment%d", n);
13307 if (debug_decode_post)
13308 printf("decode_post: [%s] = [%s]\n", str, file_name);
13309 param->setparam(str, file_name); // file_name should be decoded?
13310 }
13311
13312 /* find next boundary */
13313 const char* ptmp = string;
13314 const char* p = NULL;
13315 do {
13316 while (*ptmp != '-')
13317 ptmp++;
13318
13319 p = strstr(ptmp, boundary);
13320 if (p != NULL) {
13321 while (*p == '-')
13322 p--;
13323 if (*p == 10)
13324 p--;
13325 if (*p == 13)
13326 p--;
13327 p++;
13328 break;
13329 } else
13330 ptmp += strlen(ptmp);
13331
13332 } while (TRUE);
13333
13334 /* save pointer to file */
13335 if (file_name[0]) {
13336 size_t size = (POINTER_T) p - (POINTER_T) string;
13337 char* buf = (char*)malloc(size+1);
13338 if (!buf) {
13339 return;
13340 }
13341 memcpy(buf, string, size);
13342 buf[size] = 0; // make sure string is NUL terminated
13343 a->attachment_buffer[n] = buf;
13344 a->attachment_size[n] = size;
13345 if (debug_decode_post)
13346 printf("decode_post: attachment[%d] size %d data --->[%s]<---\n", n, (int)a->attachment_size[n], a->attachment_buffer[n]);
13347 }
13348
13349 string = strstr(p, boundary) + strlen(boundary);
13350 } else {
13351 const char* p = pitem;
13352 if (strstr(p, "\r\n\r\n"))
13353 p = strstr(p, "\r\n\r\n") + 4;
13354 else if (strstr(p, "\r\r\n\r\r\n"))
13355 p = strstr(p, "\r\r\n\r\r\n") + 6;
13356
13357 char* ppitem = (char*)strchr(pitem, '\"'); // NB: defeat "const char* string"
13358 if (ppitem)
13359 *ppitem = 0;
13360
13361 char* pb = (char*)(strstr(p, boundary)); // NB: defeat "const char* string"
13362 if (pb) {
13363 string = pb + strlen(boundary);
13364 *pb = 0;
13365 char* ptmp = (char*)(p + (strlen(p) - 1)); // NB: defeat "const char* string"
13366 while (*ptmp == '-' || *ptmp == '\n' || *ptmp == '\r')
13367 *ptmp-- = 0;
13368 } else {
13369 show_error(rr, "Invalid POST request");
13370 return;
13371 }
13372 if (debug_decode_post)
13373 printf("decode_post: [%s] = [%s]\n", pitem, p);
13374 param->setparam(pitem, p); // in decode_post()
13375 }
13376
13377 while (*string == '-' || *string == '\n' || *string == '\r')
13378 string++;
13379 }
13380
13381 } while ((POINTER_T) string - (POINTER_T) pinit < length);
13382
13383 char dec_path[256];
13384 mstrlcpy(dec_path, path, sizeof(dec_path));
13385
13386 interprete(param, rr, a, c, dec_path, t);
13387
13388 delete a;
13389 delete param;
13390}
13391
13392/*------------------------------------------------------------------*/
13393
13395{
13396 HNDLE hDB, hKeyEq, hKey;
13397 RUNINFO_STR(runinfo_str);
13398 int i, status;
13399 KEY key;
13400
13401 /* check /Runinfo structure */
13403 assert(status == DB_SUCCESS);
13404
13405 status = db_check_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str(), FALSE);
13406 if (status == DB_STRUCT_MISMATCH) {
13407 status = db_check_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str(), TRUE);
13408 if (status == DB_SUCCESS) {
13409 cm_msg(MINFO, "check_odb_records", "ODB subtree /Runinfo corrected successfully");
13410 } else {
13411 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Runinfo, db_check_record() status %d", status);
13412 return 0;
13413 }
13414 } else if (status == DB_NO_KEY) {
13415 cm_msg(MERROR, "check_odb_records", "ODB subtree /Runinfo does not exist");
13416 status = db_create_record(hDB, 0, "/Runinfo", strcomb1(runinfo_str).c_str());
13417 if (status == DB_SUCCESS) {
13418 cm_msg(MINFO, "check_odb_records", "ODB subtree /Runinfo created successfully");
13419 } else {
13420 cm_msg(MERROR, "check_odb_records", "Cannot create ODB subtree /Runinfo, db_create_record() status %d", status);
13421 return 0;
13422 }
13423 } else if (status != DB_SUCCESS) {
13424 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Runinfo, db_check_record() status %d", status);
13425 return 0;
13426 }
13427
13428 /* check /Equipment/<name>/Common structures */
13429 if (db_find_key(hDB, 0, "/equipment", &hKeyEq) == DB_SUCCESS) {
13430 for (i = 0 ;; i++) {
13431 db_enum_key(hDB, hKeyEq, i, &hKey);
13432 if (!hKey)
13433 break;
13434 db_get_key(hDB, hKey, &key);
13435
13437 if (status == DB_STRUCT_MISMATCH) {
13439 if (status == DB_SUCCESS) {
13440 cm_msg(MINFO, "check_odb_records", "ODB subtree /Equipment/%s/Common corrected successfully", key.name);
13441 } else {
13442 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Equipment/%s/Common, db_check_record() status %d", key.name, status);
13443 }
13444 } else if (status != DB_SUCCESS) {
13445 cm_msg(MERROR, "check_odb_records", "Cannot correct ODB subtree /Equipment/%s/Common, db_check_record() status %d", key.name, status);
13446 }
13447 }
13448 }
13449
13450 return CM_SUCCESS;
13451}
13452
13453
13454/*------------------------------------------------------------------*/
13455
13456std::atomic_bool _abort{false};
13457
13458void ctrlc_handler(int sig)
13459{
13460 _abort = true;
13461}
13462
13463/*------------------------------------------------------------------*/
13464
13465#ifdef HAVE_MONGOOSE6
13466static std::vector<std::string> gUserAllowedHosts;
13467#endif
13468static std::vector<std::string> gAllowedHosts;
13469#ifdef HAVE_MONGOOSE6
13470static const std::string gOdbAllowedHosts = "/Experiment/Security/mhttpd hosts/Allowed hosts";
13471#endif
13472
13473#ifdef HAVE_MONGOOSE6
13474static void load_allowed_hosts(HNDLE hDB, HNDLE hKey, int index, void* info)
13475{
13476 if (hKey != 0)
13477 cm_msg(MINFO, "load_allowed_hosts", "Reloading mhttpd hosts access control list via hotlink callback");
13478
13479 gAllowedHosts.clear();
13480
13481 // copy the user allowed hosts
13482 for (unsigned int i=0; i<gUserAllowedHosts.size(); i++)
13483 gAllowedHosts.push_back(gUserAllowedHosts[i]);
13484
13485 int total = 0;
13486 int last = 0;
13487 for (int i=0; ; i++) {
13488 std::string s;
13489 int status = db_get_value_string(hDB, 0, gOdbAllowedHosts.c_str(), i, &s, FALSE);
13490 //printf("get %d, status %d, string [%s]\n", i, status, s.c_str());
13491 if (status != DB_SUCCESS) {
13492 total = i;
13493 break;
13494 }
13495
13496 if (s.length() < 1) // skip emties
13497 continue;
13498
13499 if (s[0] == '#') // skip commented-out entries
13500 continue;
13501
13502 //printf("add allowed hosts %d [%s]\n", i, s.c_str());
13503 gAllowedHosts.push_back(s);
13504 last = i;
13505 }
13506
13507 //printf("total %d, last %d\n", total, last);
13508
13509 if (total - last < 5) {
13510 int new_size = last + 10;
13511 //printf("new size %d\n", new_size);
13512 int status = db_resize_string(hDB, 0, gOdbAllowedHosts.c_str(), new_size, 256);
13513 if (status != DB_SUCCESS) {
13514 cm_msg(MERROR, "load_allowed_hosts", "Cannot resize the allowed hosts access control list, db_resize_string(%d) status %d", new_size, status);
13515 }
13516 }
13517}
13518
13519static int init_allowed_hosts()
13520{
13521 HNDLE hDB;
13522 HNDLE hKey;
13523 int status;
13524
13526
13527 // create "allowed hosts" so we can watch it
13528
13529 std::string s;
13530 status = db_get_value_string(hDB, 0, gOdbAllowedHosts.c_str(), 0, &s, TRUE);
13531
13532 if (status != DB_SUCCESS) {
13533 cm_msg(MERROR, "init_allowed_hosts", "Cannot create the mhttpd hosts access control list, db_get_value_string() status %d", status);
13534 return status;
13535 }
13536
13537 status = db_find_key(hDB, 0, gOdbAllowedHosts.c_str(), &hKey);
13538
13539 if (status != DB_SUCCESS || hKey == 0) {
13540 cm_msg(MERROR, "init_allowed_hosts", "Cannot find the mhttpd hosts access control list, db_find_key() status %d", status);
13541 return status;
13542 }
13543
13544 load_allowed_hosts(hDB, 0, 0, NULL);
13545
13546 status = db_watch(hDB, hKey, load_allowed_hosts, NULL);
13547
13548 if (status != DB_SUCCESS) {
13549 cm_msg(MERROR, "init_allowed_hosts", "Cannot watch the mhttpd hosts access control list, db_watch() status %d", status);
13550 return status;
13551 }
13552
13553 return SUCCESS;
13554}
13555
13556 int check_midas_acl(const struct sockaddr *sa, int len) {
13557 // access control list is empty?
13558 if (gAllowedHosts.size() == 0)
13559 return 1;
13560
13561 char hname[NI_MAXHOST];
13562 hname[0] = 0;
13563
13564 int status;
13565 const char* status_string = "success";
13566
13567 status = getnameinfo(sa, len, hname, sizeof(hname), NULL, 0, 0);
13568
13569 if (status)
13570 status_string = gai_strerror(status);
13571
13572 //printf("connection from [%s], status %d (%s)\n", hname, status, status_string);
13573
13574 if (status != 0) {
13575 printf("Rejecting connection from \'%s\', getnameinfo() status %d (%s)\n", hname, status, status_string);
13576 return 0;
13577 }
13578
13579 /* always permit localhost */
13580 if (strcmp(hname, "localhost.localdomain") == 0)
13581 return 1;
13582 if (strcmp(hname, "localhost") == 0)
13583 return 1;
13584
13585 for (unsigned int i=0 ; i<gAllowedHosts.size() ; i++)
13586 if (gAllowedHosts[i] == hname) {
13587 return 1;
13588 }
13589
13590 printf("Rejecting connection from \'%s\'\n", hname);
13591 return 0;
13592 }
13593
13594int open_listening_socket(int port)
13595{
13596 int status;
13597 struct sockaddr_in bind_addr;
13598
13599 /* create a new socket */
13600 int lsock = socket(AF_INET, SOCK_STREAM, 0);
13601
13602 if (lsock == -1) {
13603 printf("Cannot create socket, socket() errno %d (%s)\n", errno, strerror(errno));
13604 return -1;
13605 }
13606
13607 /* bind local node name and port to socket */
13608 memset(&bind_addr, 0, sizeof(bind_addr));
13609 bind_addr.sin_family = AF_INET;
13610 bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
13611 bind_addr.sin_port = htons((short) port);
13612
13613 /* try reusing address */
13614 int flag = 1;
13615 status = setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
13616
13617 if (status < 0) {
13618 printf("Cannot setsockopt(SOL_SOCKET, SO_REUSEADDR), errno %d (%s)\n", errno, strerror(errno));
13619 return -1;
13620 }
13621
13622 status = bind(lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
13623
13624 if (status < 0) {
13625 printf("Cannot bind() to port %d, bind() errno %d (%s)\n", port, errno, strerror(errno));
13626 return -1;
13627 }
13628
13629 /* listen for connection */
13630 status = listen(lsock, SOMAXCONN);
13631 if (status < 0) {
13632 printf("Cannot listen() on port %d, errno %d (%s), bye!\n", port, errno, strerror(errno));
13633 return -1;
13634 }
13635
13636 printf("mhttpd is listening on port %d\n", port);
13637
13638 return lsock;
13639}
13640#endif
13641
13642/*------------------------------------------------------------------*/
13643
13644int try_file_mg(const char* try_dir, const char* filename, std::string& path, FILE** fpp, bool trace)
13645{
13646 if (fpp)
13647 *fpp = NULL;
13648 if (!try_dir)
13649 return SS_FILE_ERROR;
13650 if (strlen(try_dir) < 1)
13651 return SS_FILE_ERROR;
13652
13653 path = try_dir;
13654 if (path[path.length()-1] != DIR_SEPARATOR)
13655 path += DIR_SEPARATOR_STR;
13656 path += filename;
13657
13658 FILE* fp = fopen(path.c_str(), "r");
13659
13660 if (trace) {
13661 if (fp)
13662 printf("file \"%s\": OK!\n", path.c_str());
13663 else
13664 printf("file \"%s\": not found.\n", path.c_str());
13665 }
13666
13667 if (!fp)
13668 return SS_FILE_ERROR;
13669 else if (fpp)
13670 *fpp = fp;
13671 else
13672 fclose(fp);
13673
13674 return SUCCESS;
13675}
13676
13677int find_file_mg(const char* filename, std::string& path, FILE** fpp, bool trace)
13678{
13679 std::string exptdir = cm_get_path();
13680
13681 if (try_file_mg(".", filename, path, fpp, trace) == SUCCESS)
13682 return SUCCESS;
13683
13684 if (try_file_mg(getenv("MIDAS_DIR"), filename, path, fpp, trace) == SUCCESS)
13685 return SUCCESS;
13686
13687 if (try_file_mg(exptdir.c_str(), filename, path, fpp, trace) == SUCCESS)
13688 return SUCCESS;
13689
13690 if (try_file_mg(getenv("MIDASSYS"), filename, path, fpp, trace) == SUCCESS)
13691 return SUCCESS;
13692
13693 // setup default filename
13694 try_file_mg(exptdir.c_str(), filename, path, NULL, false);
13695 return SS_FILE_ERROR;
13696}
13697
13698#ifdef HAVE_MONGOOSE6
13699#include "mongoose6.h"
13700#endif
13701
13702#ifdef HAVE_MONGOOSE616
13703#undef closesocket
13704#include "mongoose616.h"
13705// cs_md5() in not in mongoose.h
13706extern void cs_md5(char buf[33], ...);
13707#endif
13708
13709static bool verbose_mg = false;
13710static bool trace_mg = false;
13711static bool trace_mg_recv = false;
13712static bool trace_mg_send = false;
13713static bool trace_mg_verbose = false;
13714#ifdef HAVE_MONGOOSE616
13715static bool multithread_mg = true;
13716#endif
13717
13718#ifdef HAVE_MONGOOSE6
13719static struct mg_mgr mgr_mg;
13720#endif
13721
13723 std::string username;
13724 std::string realm;
13725 std::string password;
13726};
13727
13728class Auth {
13729public:
13730 std::string realm;
13731 std::string passwd_filename;
13732 std::vector<AuthEntry> passwords;
13733public:
13734 int Init();
13735};
13736
13737static bool read_passwords(Auth* auth);
13738
13740{
13741 std::string exptname = cm_get_experiment_name();
13742
13743 if (!exptname.empty())
13744 realm = exptname;
13745 else
13746 realm = "midas";
13747
13748 bool ok = read_passwords(this);
13749 if (!ok) {
13750 cm_msg(MERROR, "mongoose", "mongoose web server password file \"%s\" has no passwords for realm \"%s\"", passwd_filename.c_str(), realm.c_str());
13751 cm_msg(MERROR, "mongoose", "please add passwords by running: htdigest %s %s midas", passwd_filename.c_str(), realm.c_str());
13752 return SS_FILE_ERROR;
13753 }
13754
13755 return SUCCESS;
13756}
13757
13758static Auth *gAuthMg = NULL;
13759
13760static void xmg_mkmd5resp(const char *method, size_t method_len, const char *uri,
13761 size_t uri_len, const char *ha1, size_t ha1_len,
13762 const char *nonce, size_t nonce_len, const char *nc,
13763 size_t nc_len, const char *cnonce, size_t cnonce_len,
13764 const char *qop, size_t qop_len, char *resp) {
13765 static const char colon[] = ":";
13766 static const size_t one = 1;
13767 char ha2[33];
13768
13769 cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
13770 cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
13771 nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
13772 colon, one, ha2, sizeof(ha2) - 1, NULL);
13773}
13774
13775/*
13776 * Check for authentication timeout.
13777 * Clients send time stamp encoded in nonce. Make sure it is not too old,
13778 * to prevent replay attacks.
13779 * Assumption: nonce is a hexadecimal number of seconds since 1970.
13780 */
13781static int xmg_check_nonce(const char *nonce) {
13782 unsigned long now = (unsigned long) time(NULL);
13783 unsigned long val = (unsigned long) strtoul(nonce, NULL, 16);
13784 return now < val || now - val < 3600;
13785}
13786
13787/*
13788 * Authenticate HTTP request against opened passwords file.
13789 * Returns 1 if authenticated, 0 otherwise.
13790 */
13791
13793 const char *domain) {
13794 mg_printf(c,
13795 "HTTP/1.1 401 Unauthorized\r\n"
13796 "WWW-Authenticate: Digest qop=\"auth\", "
13797 "realm=\"%s\", nonce=\"%lu\"\r\n"
13798 "Content-Length: 0\r\n\r\n",
13799 domain, (unsigned long) time(NULL));
13800}
13801
13802static bool read_passwords(Auth* auth)
13803{
13804 std::string path;
13805 FILE *fp;
13806 int status = find_file_mg("htpasswd.txt", path, &fp, trace_mg||verbose_mg);
13807
13808 auth->passwd_filename = path;
13809 auth->passwords.clear();
13810
13811 if (status != SUCCESS || fp == NULL) {
13812 cm_msg(MERROR, "mongoose", "mongoose web server cannot find password file \"%s\"", path.c_str());
13813 cm_msg(MERROR, "mongoose", "please create password file: touch %s", path.c_str());
13814 return false;
13815 }
13816
13817 bool have_realm = false;
13818 char buf[256];
13819
13820 /*
13821 * Read passwords file line by line. If should have htdigest format,
13822 * i.e. each line should be a colon-separated sequence:
13823 * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD
13824 */
13825 while (fgets(buf, sizeof(buf), fp) != NULL) {
13826 char f_user[256];
13827 char f_domain[256];
13828 char f_ha1[256];
13829
13830 if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3) {
13831 AuthEntry e;
13832 e.realm = f_domain;
13833 e.username = f_user;
13834 e.password = f_ha1;
13835
13836 if (e.realm == auth->realm) {
13837 have_realm = true;
13838 auth->passwords.push_back(e);
13839 }
13840 }
13841 }
13842
13843 fclose(fp);
13844
13845 return have_realm;
13846}
13847
13848#ifdef HAVE_MONGOOSE6
13849std::string find_var_mg(struct mg_str *hdr, const char* var_name)
13850{
13851 assert(!"this code is untested!");
13852
13853 char* buf = NULL;
13854 int buf_size = 0;
13855
13856 while (1) {
13857 if (buf_size == 0) {
13858 buf_size = 256;
13859 buf = (char*)malloc(buf_size);
13860 assert(buf != NULL);
13861 }
13862
13863 int size = mg_http_parse_header(hdr, var_name, buf, buf_size);
13864
13865 if (size <= 0) {
13866 free(buf);
13867 return "";
13868 }
13869
13870 if (size < buf_size) {
13871 std::string s = buf;
13872 free(buf);
13873 return s;
13874 }
13875
13876 buf_size = buf_size*2 + 16;
13877 buf = (char*)realloc(buf, buf_size);
13878 assert(buf != NULL);
13879 }
13880}
13881#endif
13882
13883#ifdef HAVE_MONGOOSE616
13884std::string find_var_mg(struct mg_str *hdr, const char* var_name)
13885{
13886 char* buf = NULL;
13887 int buf_size = 0;
13888 int size = mg_http_parse_header2(hdr, var_name, &buf, buf_size);
13889 if (size <= 0)
13890 return "";
13891 assert(buf != NULL);
13892 std::string s = buf;
13893 free(buf);
13894 return s;
13895}
13896#endif
13897
13898static std::string check_digest_auth(struct http_message *hm, Auth* auth)
13899{
13900 char expected_response[33];
13901
13902 //printf("HereA!\n");
13903
13904 /* Parse "Authorization:" header, fail fast on parse error */
13905 struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
13906
13907 if (!hdr)
13908 return "";
13909
13910 //printf("HereB!\n");
13911
13912 std::string user = find_var_mg(hdr, "username");
13913 std::string cnonce = find_var_mg(hdr, "cnonce");
13914 std::string response = find_var_mg(hdr, "response");
13915 std::string uri = find_var_mg(hdr, "uri");
13916 std::string qop = find_var_mg(hdr, "qop");
13917 std::string nc = find_var_mg(hdr, "nc");
13918 std::string nonce = find_var_mg(hdr, "nonce");
13919
13920 if (user.length()<1) return "";
13921 if (cnonce.length()<1) return "";
13922 if (response.length()<1) return "";
13923 if (uri.length()<1) return "";
13924 if (qop.length()<1) return "";
13925 if (nc.length()<1) return "";
13926 if (nonce.length()<1) return "";
13927
13928 if (xmg_check_nonce(nonce.c_str()) == 0) return "";
13929 //printf("HereB8!\n");
13930
13931 //printf("HereC!\n");
13932
13933 const char* uri_end = strchr(hm->uri.p, ' ');
13934 if (!uri_end) return "";
13935
13936 size_t uri_length = uri_end - hm->uri.p;
13937
13938 if (uri_length != uri.length())
13939 return "";
13940
13941 int cmp = strncmp(hm->uri.p, uri.c_str(), uri_length);
13942
13943 //printf("check URI: message %d %d [%d] authorization [%s]\n", (int)hm->uri.len, uri_length, cmp, uri);
13944
13945 if (cmp != 0)
13946 return "";
13947
13948 for (unsigned i=0; i<auth->passwords.size(); i++) {
13949 AuthEntry* e = &auth->passwords[i];
13950 if (e->username != user)
13951 continue;
13952 if (e->realm != auth->realm)
13953 continue;
13954 const char* f_ha1 = e->password.c_str();
13955 int uri_len = hm->uri.len;
13956 if (hm->uri.p[uri_len] == '?')
13957 uri_len += hm->query_string.len + 1; // "+1" accounts for the "?" character
13958 xmg_mkmd5resp(hm->method.p, hm->method.len,
13959 hm->uri.p, uri_len,
13960 f_ha1, strlen(f_ha1),
13961 nonce.c_str(), nonce.length(),
13962 nc.c_str(), nc.length(),
13963 cnonce.c_str(), cnonce.length(),
13964 qop.c_str(), qop.length(),
13965 expected_response);
13966 int cmp = strcasecmp(response.c_str(), expected_response);
13967 //printf("digest_auth: expected %s, got %s, cmp %d\n", expected_response, response.c_str(), cmp);
13968 if (cmp == 0) {
13969 return e->username;
13970 }
13971 }
13972
13973 return "";
13974}
13975
13976#ifdef HAVE_MONGOOSE616
13977
13978struct HostlistCacheEntry
13979{
13980 time_t time_created = 0;
13981 time_t time_last_used = 0;
13982 int count_used = 0;
13983 bool ipv4 = false;
13984 bool ipv6 = false;
13985 uint32_t ipv4addr = 0;
13986 struct in6_addr ipv6addr;
13987 std::string hostname;
13988 int gai_status = 0;
13989 std::string gai_strerror;
13990 bool ok = false;
13991};
13992
13993static std::vector<HostlistCacheEntry*> gHostlistCache;
13994
13995static void print_hostlist_cache()
13996{
13997 time_t now = time(NULL);
13998
13999 for (unsigned i=0; i<gHostlistCache.size(); i++) {
14000 HostlistCacheEntry* e = gHostlistCache[i];
14001 if (!e) {
14002 // empty slot
14003 continue;
14004 }
14005
14006 printf("%3d: %s \"%s\", ok %d, count_used %d, age created: %d, last_used %d",
14007 i,
14008 e->ipv4?"IPv4":(e->ipv6?"IPv6":"????"),
14009 e->hostname.c_str(),
14010 e->ok,
14011 e->count_used,
14012 (int)(now - e->time_created),
14013 (int)(now - e->time_last_used));
14014
14015 if (e->gai_status) {
14016 printf(", getnameinfo() status %d (%s)", e->gai_status, e->gai_strerror.c_str());
14017 }
14018
14019 printf("\n");
14020 }
14021}
14022
14023static bool mongoose_check_hostlist(const union socket_address *sa)
14024{
14025 time_t now = time(NULL);
14026 bool ipv4 = false;
14027 bool ipv6 = false;
14028 uint32_t ipv4addr = 0;
14029 struct in6_addr ipv6addr;
14030
14031 if (sa->sa.sa_family == AF_INET) {
14032 ipv4 = true;
14033 ipv4addr = sa->sin.sin_addr.s_addr;
14034 } else if (sa->sa.sa_family == AF_INET6) {
14035 ipv6 = true;
14036 memcpy(&ipv6addr, &sa->sin6.sin6_addr, sizeof(ipv6addr));
14037 } else {
14038 printf("Rejecting connection from unknown address family %d (AF_xxx)\n", sa->sa.sa_family);
14039 return false;
14040 }
14041
14042 for (unsigned i=0; i<gHostlistCache.size(); i++) {
14043 HostlistCacheEntry* e = gHostlistCache[i];
14044 if (!e) {
14045 // empty slot
14046 continue;
14047 }
14048
14049 if ((ipv4 == e->ipv4) && (ipv4addr == e->ipv4addr)) {
14050 // IPv4 address match
14051 e->time_last_used = now;
14052 e->count_used++;
14053 return e->ok;
14054 }
14055
14056 if ((ipv6 == e->ipv6) && (memcmp(&ipv6addr, &e->ipv6addr, sizeof(ipv6addr)) == 0)) {
14057 // IPv6 address match
14058 e->time_last_used = now;
14059 e->count_used++;
14060 return e->ok;
14061 }
14062
14063 // not this one. maybe expire old entries?
14064
14065 if (e->time_last_used < now - 24*60*60) {
14066 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);
14067 gHostlistCache[i] = NULL;
14068 delete e;
14069 }
14070 }
14071
14072 // not found in cache
14073
14074 assert(ipv4 || ipv6);
14075
14076 HostlistCacheEntry* e = new HostlistCacheEntry;
14077
14078 bool found = false;
14079 for (unsigned i=0; i<gHostlistCache.size(); i++) {
14080 if (gHostlistCache[i] == NULL) {
14081 gHostlistCache[i] = e;
14082 found = true;
14083 }
14084 }
14085 if (!found) {
14086 gHostlistCache.push_back(e);
14087 }
14088
14089 e->time_created = now;
14090 e->time_last_used = now;
14091 e->count_used = 1;
14092 e->ipv4 = ipv4;
14093 e->ipv6 = ipv6;
14094 if (ipv4)
14095 e->ipv4addr = ipv4addr;
14096 if (ipv6)
14097 memcpy(&e->ipv6addr, &ipv6addr, sizeof(ipv6addr));
14098 e->ok = false;
14099
14100 char hname[NI_MAXHOST];
14101 hname[0] = 0;
14102
14103 e->gai_status = getnameinfo(&sa->sa, sizeof(*sa), hname, sizeof(hname), NULL, 0, 0);
14104
14105 if (e->gai_status) {
14106 e->gai_strerror = gai_strerror(e->gai_status);
14107
14108 printf("Rejecting connection from \'%s\', getnameinfo() status %d (%s)\n", hname, e->gai_status, e->gai_strerror.c_str());
14109
14110 e->ok = false;
14111 return e->ok;
14112 }
14113
14114 printf("connection from \"%s\"\n", hname);
14115
14116 e->hostname = hname;
14117
14118 /* always permit localhost */
14119 if (e->hostname == "localhost.localdomain")
14120 e->ok = true;
14121 else if (e->hostname == "localhost")
14122 e->ok = true;
14123 else {
14124 for (unsigned int i=0 ; i<gAllowedHosts.size() ; i++) {
14125 if (e->hostname == gAllowedHosts[i]) {
14126 e->ok = true;
14127 }
14128 }
14129 }
14130
14131 if (!e->ok) {
14132 printf("Rejecting connection from \'%s\'\n", hname);
14133 }
14134
14135 print_hostlist_cache();
14136
14137 return e->ok;
14138}
14139
14140#endif
14141
14142static std::string mgstr(const mg_str* s)
14143{
14144 return std::string(s->p, s->len);
14145}
14146
14147static const std::string find_header_mg(const struct http_message *msg, const char* name)
14148{
14149 size_t nlen = strlen(name);
14150 for (int i=0; i<MG_MAX_HTTP_HEADERS; i++) {
14151 if (msg->header_names[i].len != nlen)
14152 continue;
14153 if (strncmp(msg->header_names[i].p, name, nlen) != 0)
14154 continue;
14155 return mgstr(&msg->header_values[i]);
14156 }
14157 return "";
14158}
14159
14160static const std::string find_cookie_mg(const struct http_message *msg, const char* cookie_name)
14161{
14162 const std::string cookies = find_header_mg(msg, "Cookie");
14163 if (cookies.length() < 1)
14164 return "";
14165 const char* p = strstr(cookies.c_str(), cookie_name);
14166 if (!p)
14167 return "";
14168 const char* v = p+strlen(cookie_name);
14169 if (*v != '=')
14170 return "";
14171 v++;
14172 //printf("cookie [%s] value [%s]\n", cookie_name, v);
14173 return v;
14174}
14175
14176// Generic event handler
14177
14178static void handle_event_mg(struct mg_connection *nc, int ev, void *ev_data)
14179{
14180 struct mbuf *io = &nc->recv_mbuf;
14181 switch (ev) {
14182 case MG_EV_POLL: // periodic call from loop_mg() via mg_mgr_poll()
14183 break;
14184 case MG_EV_ACCEPT:
14185 if (trace_mg)
14186 printf("handle_event_mg: nc %p, ev %d, ev_data %p -> accept\n", nc, ev, ev_data);
14187 break;
14188 case MG_EV_RECV:
14189 if (trace_mg)
14190 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);
14191#if 0
14192 // This event handler implements simple TCP echo server
14193 mg_send(nc, io->buf, io->len); // Echo received data back
14194 mbuf_remove(io, io->len); // Discard data from recv buffer
14195#endif
14196 break;
14197 case MG_EV_SEND:
14198 if (trace_mg)
14199 printf("handle_event_mg: nc %p, ev %d, ev_data %p -> send %d bytes\n", nc, ev, ev_data, *(int*)ev_data);
14200 break;
14201 case MG_EV_CLOSE:
14202 if (trace_mg)
14203 printf("handle_event_mg: nc %p, ev %d, ev_data %p -> close\n", nc, ev, ev_data);
14204 break;
14205 default:
14206 if (trace_mg)
14207 printf("handle_event_mg: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
14208 break;
14209 }
14210}
14211
14213{
14214 // extract password cookies
14215
14216 char cookie_pwd[256]; // general access password
14217 char cookie_wpwd[256]; // "write mode" password
14218 char cookie_cpwd[256]; // custom page and javascript password
14219
14220 cookie_pwd[0] = 0;
14221 cookie_wpwd[0] = 0;
14222 cookie_cpwd[0] = 0;
14223
14224 std::string s = find_cookie_mg(msg, "midas_pwd");
14225 if (s.length() > 0) {
14226 mstrlcpy(cookie_pwd, s.c_str(), sizeof(cookie_pwd));
14227 cookie_pwd[strcspn(cookie_pwd, " ;\r\n")] = 0;
14228 }
14229
14230 s = find_cookie_mg(msg, "midas_wpwd");
14231 if (s.length()) {
14232 mstrlcpy(cookie_wpwd, s.c_str(), sizeof(cookie_pwd));
14233 cookie_wpwd[strcspn(cookie_wpwd, " ;\r\n")] = 0;
14234 }
14235
14236 s = find_cookie_mg(msg, "cpwd");
14237 if (s.length()) {
14238 mstrlcpy(cookie_cpwd, s.c_str(), sizeof(cookie_pwd));
14239 cookie_cpwd[strcspn(cookie_cpwd, " ;\r\n")] = 0;
14240 }
14241
14242 // extract refresh rate
14243 c->refresh = DEFAULT_REFRESH;
14244 s = find_cookie_mg(msg, "midas_refr");
14245 if (s.length() > 0)
14246 c->refresh = atoi(s.c_str());
14247
14248 // extract equipment expand flag
14249 //c->expand_equipment = 0;
14250 //s = find_cookie_mg(msg, "midas_expeq");
14251 //if (s.length() > 0)
14252 // c->expand_equipment = atoi(s.c_str());
14253
14254 c->cookie_pwd = cookie_pwd;
14255 c->cookie_wpwd = cookie_wpwd;
14256 c->cookie_cpwd = cookie_cpwd;
14257}
14258
14259#define RESPONSE_SENT 1
14260#define RESPONSE_QUEUED 2
14261#define RESPONSE_501 3
14262
14263static int handle_decode_get(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
14264{
14265 Cookies cookies;
14266
14267 decode_cookies(&cookies, msg);
14268
14269 // lock shared structures
14270
14271#ifdef HAVE_MONGOOSE6
14272 int status = ss_mutex_wait_for(request_mutex, 0);
14273 assert(status == SS_SUCCESS);
14274#endif
14275
14276 //t->fTimeLocked = GetTimeSec();
14277
14278 // prepare return buffer
14279
14280 Return *rr = new Return();
14281
14282 rr->zero();
14283
14284 // call midas
14285
14286 decode_get(rr, NULL, &cookies, uri, query_string, t);
14287
14288 if (trace_mg)
14289 printf("handle_decode_get: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14290
14292
14293 if (rr->return_length == -1) {
14294 delete rr;
14295#ifdef HAVE_MONGOOSE6
14296 //t->fTimeUnlocked = GetTimeSec();
14297 ss_mutex_release(request_mutex);
14298#endif
14299 return RESPONSE_501;
14300 }
14301
14302 if (rr->return_length == 0)
14303 rr->return_length = strlen(rr->return_buffer);
14304
14305 //t->fTimeUnlocked = GetTimeSec();
14306
14307#ifdef HAVE_MONGOOSE6
14308 ss_mutex_release(request_mutex);
14309#endif
14310
14311 mg_send(nc, rr->return_buffer, rr->return_length);
14312
14313 if (!strstr(rr->return_buffer, "Content-Length")) {
14314 // cannot do pipelined http if response generated by mhttpd
14315 // decode_get() has no Content-Length header.
14316 // must close the connection.
14318 }
14319
14320 t->fTimeSent = GetTimeSec();
14321
14322 delete rr;
14323
14324 return RESPONSE_SENT;
14325}
14326
14327#ifdef HAVE_MONGOOSE616
14328
14329static uint32_t s_ncseqno = 1;
14330
14331struct MongooseNcUserData
14332{
14333 uint32_t ncseqno = 0;
14334
14335 MongooseNcUserData() // ctor
14336 {
14337 ncseqno = s_ncseqno++;
14338 //printf("MongooseNcUserData::ctor! ncseqno %d\n", ncseqno);
14339 }
14340
14341 ~MongooseNcUserData() // ctor
14342 {
14343 //printf("MongooseNcUserData::dtor! ncseqno %d\n", ncseqno);
14344 }
14345};
14346
14347static uint32_t GetNcSeqno(const mg_connection* nc)
14348{
14349 if (nc == NULL)
14350 return 0;
14351 if (nc->user_data == NULL)
14352 return 0;
14353 const MongooseNcUserData* ncud = (const MongooseNcUserData*)nc->user_data;
14354 return ncud->ncseqno;
14355}
14356
14357static uint32_t s_wseqno = 1;
14358
14359struct MongooseWorkObject
14360{
14361 uint32_t wseqno = 0;
14362 mg_connection* nc = NULL;
14363 uint32_t wncseqno = 0;
14364 bool http_get = false;
14365 bool http_post = false;
14366 bool mjsonrpc = false;
14367 Cookies cookies;
14368 std::string origin;
14369 std::string uri;
14370 std::string query_string;
14371 std::string post_body;
14372 std::string post_boundary;
14373 RequestTrace* t = NULL;
14374 bool send_done = false;
14375
14376 MongooseWorkObject(mg_connection* xnc) // ctor
14377 {
14378 wseqno = s_wseqno++;
14379 nc = xnc;
14380 wncseqno = GetNcSeqno(nc);
14381
14382 //printf("MongooseWorkObject::ctor! wseqno %d, nc %p, wncseqno %d\n", wseqno, nc, wncseqno);
14383 }
14384
14385 ~MongooseWorkObject() // dtor
14386 {
14387 //printf("MongooseWorkObject::dtor! wseqno %d, nc %p, wncseqno %d\n", wseqno, nc, wncseqno);
14388
14389 // poison pointers
14390 nc = NULL;
14391 t = NULL;
14392 }
14393};
14394
14395struct MongooseThreadObject
14396{
14397 std::atomic_bool fIsRunning{false};
14398 std::thread* fThread = NULL; // thread
14399 void* fNc = NULL; // thread is attached to this network connection
14400 std::mutex fMutex;
14401 std::deque<MongooseWorkObject*> fQueue;
14402 std::condition_variable fNotify;
14403
14404 //MongooseThreadObject() // ctor
14405 //{
14406 // printf("MongooseThreadObject %p created!\n", this);
14407 //}
14408
14409 //~MongooseThreadObject() // dtor
14410 //{
14411 // printf("MongooseThreadObject %p destroyed!\n", this);
14412 //}
14413};
14414
14415static std::vector<MongooseThreadObject*> gMongooseThreads;
14416
14417static void mongoose_thread(MongooseThreadObject*);
14418
14419MongooseThreadObject* FindThread(void* nc)
14420{
14421 //printf("FindThread: nc %p, thread %s\n", nc, ss_tid_to_string(ss_gettid()).c_str());
14422
14423 MongooseThreadObject* last_not_connected = NULL;
14424
14425 for (auto it : gMongooseThreads) {
14426 MongooseThreadObject* to = it;
14427 if (to->fNc == nc) {
14428 //printf("to %p, nc %p: found thread\n", to, nc);
14429 return to;
14430 }
14431 if (to->fNc == NULL) {
14432 last_not_connected = to;
14433 }
14434 }
14435
14436 if (last_not_connected) {
14437 MongooseThreadObject* to = last_not_connected;
14438 to->fNc = nc;
14439 //printf("to %p, nc %p: reusing thread\n", to, nc);
14440 return to;
14441 }
14442
14443 MongooseThreadObject* to = new MongooseThreadObject();
14444
14445 to->fNc = nc;
14446
14447 //printf("to %p, nc %p: new thread\n", to, nc);
14448
14449 gMongooseThreads.push_back(to);
14450
14451 printf("Mongoose web server is using %d threads \r", (int)gMongooseThreads.size());
14452 fflush(stdout);
14453
14454 to->fThread = new std::thread(mongoose_thread, to);
14455
14456 return to;
14457}
14458
14459void FreeThread(void* nc)
14460{
14461 //printf("FreeThread, nc %p\n", nc);
14462
14463 for (auto it : gMongooseThreads) {
14464 MongooseThreadObject* to = it;
14465 if (to->fNc == nc) {
14466 //printf("to %p, nc %p: connection closed\n", to, nc);
14467 to->fNc = NULL;
14468 return;
14469 }
14470 }
14471
14472 //printf("to %p, nc %p: connection closed, but no thread\n", nullptr, nc);
14473}
14474
14475static void mongoose_queue(mg_connection* nc, MongooseWorkObject* w)
14476{
14477 w->nc = nc;
14478 MongooseThreadObject* to = FindThread(nc);
14479 assert(to->fNc == nc);
14480 to->fMutex.lock();
14481 to->fQueue.push_back(w);
14482 to->fMutex.unlock();
14483 to->fNotify.notify_one();
14484}
14485
14486static void mongoose_send(mg_connection* nc, MongooseWorkObject* w, const char* p1, size_t s1, const char* p2, size_t s2, bool close_flag = false);
14487
14488static int queue_decode_get(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
14489{
14490 MongooseWorkObject* w = new MongooseWorkObject(nc);
14491 w->http_get = true;
14492 decode_cookies(&w->cookies, msg);
14493 w->uri = uri;
14494 w->query_string = query_string;
14495 w->t = t;
14496
14497 mongoose_queue(nc, w);
14498
14499 return RESPONSE_QUEUED;
14500}
14501
14502static int queue_decode_post(struct mg_connection *nc, const http_message* msg, const char* boundary, const char* uri, const char* query_string, RequestTrace* t)
14503{
14504 MongooseWorkObject* w = new MongooseWorkObject(nc);
14505 w->http_post = true;
14506 decode_cookies(&w->cookies, msg);
14507 w->uri = uri;
14508 w->query_string = query_string;
14509 w->post_body = mgstr(&msg->body);
14510 w->post_boundary = boundary;
14511 w->t = t;
14512
14513 mongoose_queue(nc, w);
14514
14515 return RESPONSE_QUEUED;
14516}
14517
14518static int queue_mjsonrpc(struct mg_connection *nc, const std::string& origin, const std::string& post_body, RequestTrace* t)
14519{
14520 MongooseWorkObject* w = new MongooseWorkObject(nc);
14521 w->mjsonrpc = true;
14522 w->origin = origin;
14523 w->post_body = post_body;
14524 w->t = t;
14525
14526 mongoose_queue(nc, w);
14527
14528 return RESPONSE_QUEUED;
14529}
14530
14531static int thread_http_get(mg_connection *nc, MongooseWorkObject *w)
14532{
14533 // lock shared structures
14534
14535 //int status = ss_mutex_wait_for(request_mutex, 0);
14536 //assert(status == SS_SUCCESS);
14537
14538 //w->t->fTimeLocked = GetTimeSec();
14539
14540 // prepare return buffer
14541
14542 Return *rr = new Return();
14543
14544 rr->zero();
14545
14546 // call midas
14547
14548 decode_get(rr, NULL, &w->cookies, w->uri.c_str(), w->query_string.c_str(), w->t);
14549
14550 if (trace_mg)
14551 printf("handle_decode_get: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14552
14553 w->t->fTimeProcessed = GetTimeSec();
14554
14555 if (rr->return_length == -1) {
14556 delete rr;
14557 //w->t->fTimeUnlocked = GetTimeSec();
14558 //ss_mutex_release(request_mutex);
14559 return RESPONSE_501;
14560 }
14561
14562 if (rr->return_length == 0)
14563 rr->return_length = strlen(rr->return_buffer);
14564
14565 //w->t->fTimeUnlocked = GetTimeSec();
14566
14567 //ss_mutex_release(request_mutex);
14568
14569 bool close_flag = false;
14570
14571 if (!strstr(rr->return_buffer, "Content-Length")) {
14572 // cannot do pipelined http if response generated by mhttpd
14573 // decode_get() has no Content-Length header.
14574 // must close the connection.
14575 close_flag = true;
14576 }
14577
14578 mongoose_send(nc, w, rr->return_buffer, rr->return_length, NULL, 0, close_flag);
14579
14580 w->t->fTimeSent = GetTimeSec();
14581
14582 delete rr;
14583
14584 return RESPONSE_SENT;
14585}
14586
14587static int thread_http_post(mg_connection *nc, MongooseWorkObject *w)
14588{
14589 const char* post_data = w->post_body.c_str();
14590 int post_data_len = w->post_body.length();
14591
14592 // lock shared strctures
14593
14594 //int status = ss_mutex_wait_for(request_mutex, 0);
14595 //assert(status == SS_SUCCESS);
14596
14597 // prepare return buffer
14598
14599 Return* rr = new Return;
14600
14601 rr->zero();
14602
14603 //printf("post_data_len %d, data [%s], boundary [%s]\n", post_data_len, post_data, boundary);
14604
14605 decode_post(rr, NULL, (char*)post_data, w->post_boundary.c_str(), post_data_len, &w->cookies, w->uri.c_str(), w->t);
14606
14607 if (trace_mg)
14608 printf("handle_decode_post: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14609
14610 if (rr->return_length == -1) {
14611 //ss_mutex_release(request_mutex);
14612 delete rr;
14613 return RESPONSE_501;
14614 }
14615
14616 if (rr->return_length == 0)
14617 rr->return_length = strlen(rr->return_buffer);
14618
14619 //ss_mutex_release(request_mutex);
14620
14621 bool close_flag = false;
14622 if (!strstr(rr->return_buffer, "Content-Length")) {
14623 // cannot do pipelined http if response generated by mhttpd
14624 // decode_get() has no Content-Length header.
14625 // must close the connection.
14626 close_flag = true;
14627 }
14628
14629 mongoose_send(nc, w, rr->return_buffer, rr->return_length, NULL, 0, close_flag);
14630
14631 delete rr;
14632
14633 return RESPONSE_SENT;
14634
14635}
14636
14637static int thread_mjsonrpc(mg_connection *nc, MongooseWorkObject *w)
14638{
14639 w->t->fRPC = w->post_body;
14640
14641 //int status = ss_mutex_wait_for(request_mutex, 0);
14642 //assert(status == SS_SUCCESS);
14643
14644 //gMutex.lock();
14645 //w->t->fTimeLocked = GetTimeSec();
14646
14647 MJsonNode* reply = mjsonrpc_decode_post_data(w->post_body.c_str());
14648
14649 //w->t->fTimeUnlocked = GetTimeSec();
14650 //gMutex.unlock();
14651
14652 //ss_mutex_release(request_mutex);
14653
14654 if (reply->GetType() == MJSON_ARRAYBUFFER) {
14655 const char* ptr;
14656 size_t size;
14657 reply->GetArrayBuffer(&ptr, &size);
14658
14659 std::string headers;
14660 headers += "HTTP/1.1 200 OK\n";
14661 if (w->origin.length() > 0)
14662 headers += "Access-Control-Allow-Origin: " + w->origin + "\n";
14663 else
14664 headers += "Access-Control-Allow-Origin: *\n";
14665 headers += "Access-Control-Allow-Credentials: true\n";
14666 headers += "Content-Length: " + toString(size) + "\n";
14667 headers += "Content-Type: application/octet-stream\n";
14668 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14669
14670 //printf("sending headers: %s\n", headers.c_str());
14671 //printf("sending reply: %s\n", reply_string.c_str());
14672
14673 std::string send = headers + "\n";
14674
14675 w->t->fTimeProcessed = GetTimeSec();
14676
14677 mongoose_send(nc, w, send.c_str(), send.length(), ptr, size);
14678
14679 w->t->fTimeSent = GetTimeSec();
14680
14681 delete reply;
14682
14683 return RESPONSE_SENT;
14684 }
14685
14686 std::string reply_string = reply->Stringify();
14687 int reply_length = reply_string.length();
14688
14689 std::string headers;
14690 headers += "HTTP/1.1 200 OK\n";
14691 if (w->origin.length() > 0)
14692 headers += "Access-Control-Allow-Origin: " + w->origin + "\n";
14693 else
14694 headers += "Access-Control-Allow-Origin: *\n";
14695 headers += "Access-Control-Allow-Credentials: true\n";
14696 headers += "Content-Length: " + toString(reply_length) + "\n";
14697 headers += "Content-Type: application/json\n";
14698 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14699
14700 if (trace_mg_verbose) {
14701 printf("-----------------------\nSending headers: %s", headers.c_str());
14702 std::string r = reply_string.substr(0, 128);
14703 printf("-----------------------\nSending reply (%d bytes): %s\n\n\n", (int)reply_string.size(), r.c_str());
14704 }
14705
14706 std::string send = headers + "\n" + reply_string;
14707
14708 w->t->fTimeProcessed = GetTimeSec();
14709
14710 mongoose_send(nc, w, send.c_str(), send.length(), NULL, 0);
14711
14712 w->t->fTimeSent = GetTimeSec();
14713
14714 delete reply;
14715
14716 return RESPONSE_SENT;
14717}
14718
14719static int thread_work_function(mg_connection *nc, MongooseWorkObject *w)
14720{
14721 if (w->http_get)
14722 return thread_http_get(nc, w);
14723 else if (w->http_post)
14724 return thread_http_post(nc, w);
14725 else if (w->mjsonrpc)
14726 return thread_mjsonrpc(nc, w);
14727 else
14728 return RESPONSE_501;
14729}
14730
14731#endif
14732
14733static int handle_decode_post(struct mg_connection *nc, const http_message* msg, const char* uri, const char* query_string, RequestTrace* t)
14734{
14735
14736 char boundary[256];
14737 boundary[0] = 0;
14738 const std::string ct = find_header_mg(msg, "Content-Type");
14739 if (ct.length() > 0) {
14740 const char* s = strstr(ct.c_str(), "boundary=");
14741 if (s)
14742 mstrlcpy(boundary, s+9, sizeof(boundary));
14743 }
14744
14745#ifdef HAVE_MONGOOSE616
14746 if (multithread_mg)
14747 return queue_decode_post(nc, msg, boundary, uri, query_string, t);
14748#endif
14749
14750 Cookies cookies;
14751
14752 decode_cookies(&cookies, msg);
14753
14754 const char* post_data = msg->body.p;
14755 int post_data_len = msg->body.len;
14756
14757 // lock shared strctures
14758
14759#ifdef HAVE_MONGOOSE6
14760 int status = ss_mutex_wait_for(request_mutex, 0);
14761 assert(status == SS_SUCCESS);
14762#endif
14763
14764 // prepare return buffer
14765
14766 Return* rr = new Return;
14767
14768 rr->zero();
14769
14770 //printf("post_data_len %d, data [%s], boundary [%s]\n", post_data_len, post_data, boundary);
14771
14772 decode_post(rr, NULL, (char*)post_data, boundary, post_data_len, &cookies, uri, t);
14773
14774 if (trace_mg)
14775 printf("handle_decode_post: return buffer length %d bytes, strlen %d\n", rr->return_length, (int)strlen(rr->return_buffer));
14776
14777 if (rr->return_length == -1) {
14778#ifdef HAVE_MONGOOSE6
14779 ss_mutex_release(request_mutex);
14780#endif
14781 delete rr;
14782 return RESPONSE_501;
14783 }
14784
14785 if (rr->return_length == 0)
14786 rr->return_length = strlen(rr->return_buffer);
14787
14788#ifdef HAVE_MONGOOSE6
14789 ss_mutex_release(request_mutex);
14790#endif
14791
14792 mg_send(nc, rr->return_buffer, rr->return_length);
14793
14794 if (!strstr(rr->return_buffer, "Content-Length")) {
14795 // cannot do pipelined http if response generated by mhttpd
14796 // decode_get() has no Content-Length header.
14797 // must close the connection.
14799 }
14800
14801 delete rr;
14802
14803 return RESPONSE_SENT;
14804}
14805
14806static int handle_http_get(struct mg_connection *nc, const http_message* msg, const char* uri, RequestTrace* t)
14807{
14808 std::string query_string = mgstr(&msg->query_string);
14809
14810 if (trace_mg||verbose_mg)
14811 printf("handle_http_get: uri [%s], query [%s]\n", uri, query_string.c_str());
14812
14813 if (query_string == "mjsonrpc_schema") {
14814 MJsonNode* s = mjsonrpc_get_schema();
14815 std::string reply = s->Stringify();
14816 delete s;
14817
14818 int reply_length = reply.length();
14819
14820 const std::string origin_header = find_header_mg(msg, "Origin");
14821
14822 std::string headers;
14823 headers += "HTTP/1.1 200 OK\n";
14824 if (origin_header.length() > 0)
14825 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14826 else
14827 headers += "Access-Control-Allow-Origin: *\n";
14828 headers += "Access-Control-Allow-Credentials: true\n";
14829 headers += "Content-Length: " + toString(reply_length) + "\n";
14830 headers += "Content-Type: application/json\n";
14831 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14832
14833 //printf("sending headers: %s\n", headers.c_str());
14834 //printf("sending reply: %s\n", reply.c_str());
14835
14836 std::string send = headers + "\n" + reply;
14837
14839
14840 mg_send(nc, send.c_str(), send.length());
14841
14842 t->fTimeSent = GetTimeSec();
14843
14844 return RESPONSE_SENT;
14845 }
14846
14847 if (query_string == "mjsonrpc_schema_text") {
14848 MJsonNode* s = mjsonrpc_get_schema();
14849 std::string reply = mjsonrpc_schema_to_text(s);
14850 delete s;
14851
14852 int reply_length = reply.length();
14853
14854 const std::string origin_header = find_header_mg(msg, "Origin");
14855
14856 std::string headers;
14857 headers += "HTTP/1.1 200 OK\n";
14858 if (origin_header.length() > 0)
14859 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14860 else
14861 headers += "Access-Control-Allow-Origin: *\n";
14862 headers += "Access-Control-Allow-Credentials: true\n";
14863 headers += "Content-Length: " + toString(reply_length) + "\n";
14864 headers += "Content-Type: text/plain\n";
14865 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14866
14867 //printf("sending headers: %s\n", headers.c_str());
14868 //printf("sending reply: %s\n", reply.c_str());
14869
14870 std::string send = headers + "\n" + reply;
14871
14873
14874 mg_send(nc, send.c_str(), send.length());
14875
14876 t->fTimeSent = GetTimeSec();
14877
14878 return RESPONSE_SENT;
14879 }
14880
14881#ifdef HAVE_MONGOOSE616
14882 if (multithread_mg)
14883 return queue_decode_get(nc, msg, uri, query_string.c_str(), t);
14884#endif
14885
14886 return handle_decode_get(nc, msg, uri, query_string.c_str(), t);
14887}
14888
14889static int handle_http_post(struct mg_connection *nc, const http_message* msg, const char* uri, RequestTrace* t)
14890{
14891 std::string query_string = mgstr(&msg->query_string);
14892 std::string post_data = mgstr(&msg->body);
14893
14894 if (trace_mg||verbose_mg)
14895 printf("handle_http_post: uri [%s], query [%s], post data %d bytes\n", uri, query_string.c_str(), (int)post_data.length());
14896 if (trace_mg_verbose)
14897 printf("handle_http_post: post data = \n%s\n", post_data.c_str());
14898
14899 if (query_string.substr(0, 8) == "mjsonrpc") { // ignore any parameter after "mjsonrpc"
14900 const std::string origin_header = find_header_mg(msg, "Origin");
14901 const std::string ctype_header = find_header_mg(msg, "Content-Type");
14902
14903 if (strstr(ctype_header.c_str(), "application/json") == NULL) {
14904 std::string headers;
14905 headers += "HTTP/1.1 415 Unsupported Media Type\n";
14906 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14907
14908 //printf("sending headers: %s\n", headers.c_str());
14909 //printf("sending reply: %s\n", reply.c_str());
14910
14911 if (trace_mg_verbose)
14912 printf("handle_http_post: unsupported media type \"%s\"\n", ctype_header.c_str());
14913
14914 std::string send = headers + "\n";
14915
14917
14918 mg_send(nc, send.c_str(), send.length());
14919
14920 t->fTimeSent = GetTimeSec();
14921
14922 return RESPONSE_SENT;
14923 }
14924
14925#ifdef HAVE_MONGOOSE616
14926 if (multithread_mg)
14927 return queue_mjsonrpc(nc, origin_header, post_data, t);
14928#endif
14929
14930 //printf("post body: %s\n", post_data.c_str());
14931
14932 t->fRPC = post_data;
14933
14934#ifdef HAVE_MONGOOSE6
14935 int status = ss_mutex_wait_for(request_mutex, 0);
14936 assert(status == SS_SUCCESS);
14937#endif
14938
14939 //t->fTimeLocked = GetTimeSec();
14940
14941 MJsonNode* reply = mjsonrpc_decode_post_data(post_data.c_str());
14942
14943 //t->fTimeUnlocked = GetTimeSec();
14944
14945#ifdef HAVE_MONGOOSE6
14946 ss_mutex_release(request_mutex);
14947#endif
14948
14949 if (reply->GetType() == MJSON_ARRAYBUFFER) {
14950 const char* ptr;
14951 size_t size;
14952 reply->GetArrayBuffer(&ptr, &size);
14953
14954 std::string headers;
14955 headers += "HTTP/1.1 200 OK\n";
14956 if (origin_header.length() > 0)
14957 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14958 else
14959 headers += "Access-Control-Allow-Origin: *\n";
14960 headers += "Access-Control-Allow-Credentials: true\n";
14961 headers += "Content-Length: " + toString(size) + "\n";
14962 headers += "Content-Type: application/octet-stream\n";
14963 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14964
14965 //printf("sending headers: %s\n", headers.c_str());
14966 //printf("sending reply: %s\n", reply_string.c_str());
14967
14968 std::string send = headers + "\n";
14969
14971
14972 mg_send(nc, send.c_str(), send.length());
14973 mg_send(nc, ptr, size);
14974
14975 t->fTimeSent = GetTimeSec();
14976
14977 delete reply;
14978
14979 return RESPONSE_SENT;
14980 }
14981
14982 std::string reply_string = reply->Stringify();
14983 int reply_length = reply_string.length();
14984
14985 std::string headers;
14986 headers += "HTTP/1.1 200 OK\n";
14987 if (origin_header.length() > 0)
14988 headers += "Access-Control-Allow-Origin: " + std::string(origin_header) + "\n";
14989 else
14990 headers += "Access-Control-Allow-Origin: *\n";
14991 headers += "Access-Control-Allow-Credentials: true\n";
14992 headers += "Content-Length: " + toString(reply_length) + "\n";
14993 headers += "Content-Type: application/json\n";
14994 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
14995
14996 //printf("sending headers: %s\n", headers.c_str());
14997 //printf("sending reply: %s\n", reply_string.c_str());
14998
14999 std::string send = headers + "\n" + reply_string;
15000
15002
15003 mg_send(nc, send.c_str(), send.length());
15004
15005 t->fTimeSent = GetTimeSec();
15006
15007 delete reply;
15008
15009 return RESPONSE_SENT;
15010 }
15011
15012 return handle_decode_post(nc, msg, uri, query_string.c_str(), t);
15013}
15014
15016{
15017 //
15018 // JSON-RPC CORS pre-flight request, see
15019 // https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
15020 //
15021 // OPTIONS /resources/post-here/ HTTP/1.1
15022 // Host: bar.other
15023 // User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
15024 // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
15025 // Accept-Language: en-us,en;q=0.5
15026 // Accept-Encoding: gzip,deflate
15027 // Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
15028 // Connection: keep-alive
15029 // Origin: http://foo.example
15030 // Access-Control-Request-Method: POST
15031 // Access-Control-Request-Headers: X-PINGOTHER
15032 //
15033 // HTTP/1.1 200 OK
15034 // Date: Mon, 01 Dec 2008 01:15:39 GMT
15035 // Server: Apache/2.0.61 (Unix)
15036 // Access-Control-Allow-Origin: http://foo.example
15037 // Access-Control-Allow-Methods: POST, GET, OPTIONS
15038 // Access-Control-Allow-Headers: X-PINGOTHER
15039 // Access-Control-Max-Age: 1728000
15040 // Vary: Accept-Encoding, Origin
15041 // Content-Encoding: gzip
15042 // Content-Length: 0
15043 // Keep-Alive: timeout=2, max=100
15044 // Connection: Keep-Alive
15045 // Content-Type: text/plain
15046 //
15047
15048 const std::string origin_header = find_header_mg(msg, "Origin");
15049
15050 if (trace_mg||verbose_mg)
15051 printf("handle_http_options_cors: origin [%s]\n", origin_header.c_str());
15052
15053 std::string headers;
15054 headers += "HTTP/1.1 200 OK\n";
15055 //headers += "Date: Sat, 08 Jul 2006 12:04:08 GMT\n";
15056 if (origin_header.length() > 0)
15057 headers += "Access-Control-Allow-Origin: " + origin_header + "\n";
15058 else
15059 headers += "Access-Control-Allow-Origin: *\n";
15060 headers += "Access-Control-Allow-Headers: Content-Type\n";
15061 headers += "Access-Control-Allow-Credentials: true\n";
15062 headers += "Access-Control-Max-Age: 120\n";
15063 headers += "Content-Length: 0\n";
15064 headers += "Content-Type: text/plain\n";
15065 //printf("sending headers: %s\n", headers.c_str());
15066 //printf("sending reply: %s\n", reply.c_str());
15067
15068 std::string send = headers + "\n";
15069
15071
15072 mg_send(nc, send.c_str(), send.length());
15073
15074 t->fTimeSent = GetTimeSec();
15075}
15076
15077// HTTP event handler
15078
15079static bool mongoose_passwords_enabled(const struct mg_connection *nc);
15080
15081#ifdef HAVE_MONGOOSE616
15082static MVOdb* gProxyOdb = NULL;
15083#endif
15084
15086{
15087 std::string method = mgstr(&msg->method);
15088 std::string query_string = mgstr(&msg->query_string);
15089 std::string uri_encoded = mgstr(&msg->uri);
15090 std::string uri = UrlDecode(uri_encoded.c_str());
15091
15092 if (trace_mg)
15093 printf("handle_http_message: method [%s] uri [%s] proto [%s]\n", method.c_str(), uri.c_str(), mgstr(&msg->proto).c_str());
15094
15095 RequestTrace* t = new RequestTrace;
15097 t->fMethod = method;
15098 t->fUri = uri;
15099 t->fQuery = query_string;
15100
15101 // process OPTIONS for Cross-origin (CORS) preflight request
15102 // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
15103 if (method == "OPTIONS" && query_string == "mjsonrpc" && mg_get_http_header(msg, "Access-Control-Request-Method") != NULL) {
15104 handle_http_options_cors(nc, msg, t);
15105 t->fCompleted = true;
15107 return;
15108 }
15109
15111 std::string username = check_digest_auth(msg, gAuthMg);
15112
15113 // Cannot re-read the password file - it is not thread safe to do so
15114 // unless I lock gAuthMg for each call check_digest_auth() and if I do so,
15115 // I will serialize (single-thread) all the http requests and defeat
15116 // the whole point of multithreading the web server. K.O.
15117 //
15119 //if (username.length() < 1) {
15120 // bool ok = read_passwords(&gAuthMg);
15121 // if (ok)
15122 // username = check_digest_auth(msg, &gAuthMg);
15123 //}
15124
15125 if (trace_mg)
15126 printf("handle_http_message: auth user: \"%s\"\n", username.c_str());
15127
15128 if (username.length() == 0) {
15129 if (trace_mg||verbose_mg)
15130 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());
15131
15133 t->fCompleted = true;
15135 return;
15136 }
15137 t->fAuthOk = true;
15138 } else {
15139 t->fAuthOk = true;
15140 }
15141
15142#ifdef HAVE_MONGOOSE616
15143 if (gProxyOdb && starts_with(uri, "/proxy/")) {
15144 std::string::size_type p1 = uri.find("/", 1);
15145 if (p1 == uri.length()-1) {
15146 std::string response = "404 Not Found (Proxy name is missing)";
15147 mg_send_head(nc, 404, response.length(), NULL);
15148 mg_send(nc, response.c_str(), response.length());
15149 delete t;
15150 return;
15151 }
15152 std::string::size_type p2 = uri.find("/", p1+1);
15153 if (p2 == std::string::npos) {
15154 std::string response = "404 Not Found (Proxy URL should end with a slash)";
15155 mg_send_head(nc, 404, response.length(), NULL);
15156 mg_send(nc, response.c_str(), response.length());
15157 delete t;
15158 return;
15159 }
15160 std::string p = uri.substr(p1+1, p2-p1-1);
15161 //printf("uri [%s], p1: %d, p2: %d, substr: [%s]\n", uri.c_str(), (int)p1, (int)p2, p.c_str());
15162 if (p.length() < 1) {
15163 std::string response = "404 Not Found (Double-slash or Proxy name is too short)";
15164 mg_send_head(nc, 404, response.length(), NULL);
15165 mg_send(nc, response.c_str(), response.length());
15166 delete t;
15167 return;
15168 }
15169 std::string destination;
15170 gProxyOdb->RS(p.c_str(), &destination);
15171 if (destination.length() < 1) {
15172 std::string response = "404 Not Found (Proxy not found in ODB)";
15173 mg_send_head(nc, 404, response.length(), NULL);
15174 mg_send(nc, response.c_str(), response.length());
15175 delete t;
15176 return;
15177 } else if (destination[0] == '#') {
15178 std::string response = "404 Not Found (Proxy commented-out in ODB)";
15179 mg_send_head(nc, 404, response.length(), NULL);
15180 mg_send(nc, response.c_str(), response.length());
15181 delete t;
15182 return;
15183 } else if (ends_with_char(destination, '/')) {
15184 std::string response = "404 Not Found (Proxy address should not end with a slash)";
15185 mg_send_head(nc, 404, response.length(), NULL);
15186 mg_send(nc, response.c_str(), response.length());
15187 delete t;
15188 return;
15189 } else if (!starts_with(destination, "http")) {
15190 std::string response = "404 Not Found (Proxy address does not start with http";
15191 mg_send_head(nc, 404, response.length(), NULL);
15192 mg_send(nc, response.c_str(), response.length());
15193 delete t;
15194 return;
15195 } else {
15196 std::string m;
15197 m += "/proxy";
15198 m += "/";
15199 m += p;
15200 mg_str mount = mg_mk_str(m.c_str());
15201 mg_str upstream = mg_mk_str(destination.c_str());
15202 if (verbose_mg||trace_mg) {
15203 printf("proxy: uri [%s] mount [%s] upstream [%s]\n", uri.c_str(), mgstr(&mount).c_str(), mgstr(&upstream).c_str());
15204 }
15205 mg_http_reverse_proxy(nc, msg, mount, upstream);
15206 delete t;
15207 return;
15208 }
15209 }
15210#endif
15211
15212 int response = RESPONSE_501;
15213
15214 if (method == "GET")
15215 response = handle_http_get(nc, msg, uri.c_str(), t);
15216 else if (method == "POST")
15217 response = handle_http_post(nc, msg, uri.c_str(), t);
15218
15219 if (response == RESPONSE_501) {
15220 if (trace_mg||verbose_mg)
15221 printf("handle_http_message: sending 501 Not Implemented error\n");
15222
15223 std::string response = "501 Not Implemented";
15224 mg_send_head(nc, 501, response.length(), NULL); // 501 Not Implemented
15225 mg_send(nc, response.c_str(), response.length());
15226 }
15227
15228 if (response != RESPONSE_QUEUED) {
15229 t->fCompleted = true;
15231 }
15232}
15233
15234#ifdef HAVE_MONGOOSE6
15235
15236static void handle_http_event_mg(struct mg_connection *nc, int ev, void *ev_data)
15237{
15238 switch (ev) {
15239 case MG_EV_HTTP_REQUEST:
15240 if (trace_mg)
15241 printf("handle_http_event_mg: nc %p, ev %d, ev_data %p -> http request\n", nc, ev, ev_data);
15242 handle_http_message(nc, (http_message*)ev_data);
15243 break;
15244 default:
15245 if (trace_mg)
15246 printf("handle_http_event_mg: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
15247 break;
15248 }
15249}
15250
15251static void handle_http_redirect(struct mg_connection *nc, int ev, void *ev_data)
15252{
15253 switch (ev) {
15254 case MG_EV_HTTP_REQUEST:
15255 {
15256 http_message* msg = (http_message*)ev_data;
15257 if (trace_mg)
15258 printf("handle_http_redirect: nc %p, ev %d, ev_data %p -> http request\n", nc, ev, ev_data);
15259
15260 mg_printf(nc, "HTTP/1.1 302 Found\r\nLocation: https://%s%s\r\n\r\n",
15261 ((std::string*)(nc->user_data))->c_str(),
15262 mgstr(&msg->uri).c_str());
15264 }
15265 break;
15266 default:
15267 if (trace_mg)
15268 printf("handle_http_redirect: nc %p, ev %d, ev_data %p\n", nc, ev, ev_data);
15269 }
15270}
15271
15272#endif
15273
15274#ifdef HAVE_MONGOOSE616
15275
15276// from mongoose examples/multithreaded/multithreaded.c
15277
15278//static sock_t s_sock[2];
15279static std::atomic_bool s_shutdown{false};
15280static struct mg_mgr s_mgr;
15281static std::atomic_int s_rseqno{1};
15282static std::mutex s_mg_broadcast_mutex;
15283
15284#if 0
15285// This info is passed to the worker thread
15286struct work_request {
15287 void* nc;
15288 MongooseWorkObject* w;
15289};
15290#endif
15291
15292// This info is passed by the worker thread to mg_broadcast
15293struct work_result {
15294 mg_connection* nc = NULL;
15295 uint32_t check = 0x12345678;
15296 int rseqno = 0;
15297 MongooseWorkObject* w = NULL;
15298 const char* p1 = NULL;
15299 size_t s1 = 0;
15300 const char* p2 = NULL;
15301 size_t s2 = 0;
15302 bool close_flag = false;
15303 bool send_501 = false;
15304};
15305
15306#if 0
15307static void mongoose_queue(void *nc, MongooseWorkObject *w)
15308{
15309 struct work_request req = {nc, w};
15310
15311 //printf("nc: %p: wseqno: %d, queue work object!\n", nc, w->wseqno);
15312
15313 if (write(s_sock[0], &req, sizeof(req)) < 0) {
15314 fprintf(stderr, "mongoose_queue: Error: write(s_sock(0)) error %d (%s)\n", errno, strerror(errno));
15315 abort();
15316 }
15317}
15318#endif
15319
15320static void on_work_complete(struct mg_connection *nc, int ev, void *ev_data)
15321{
15322 (void) ev;
15323 struct work_result *res = (struct work_result *)ev_data;
15324
15325 assert(res != NULL);
15326 assert(res->w != NULL);
15327
15328 //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);
15329
15330 // check for correct connection, note that nc objects are reused and after a socket is closed
15331 // and a new one is opened, the same nc address and the same nc->sock can now refer to a completely
15332 // different tcp connection. So we check ncseqno instead of nc. K.O.
15333
15334 //if (res->nc != nc)
15335 //return;
15336
15337 if (GetNcSeqno(nc) != res->w->wncseqno)
15338 return;
15339
15340 //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);
15341
15342 if (res->send_501) {
15343 std::string response = "501 Not Implemented";
15344 mg_send_head(nc, 501, response.length(), NULL); // 501 Not Implemented
15345 mg_send(nc, response.c_str(), response.length());
15346 }
15347
15348 if (res->s1 > 0)
15349 mg_send(nc, res->p1, res->s1);
15350
15351 if (res->s2 > 0)
15352 mg_send(nc, res->p2, res->s2);
15353
15354 if (res->close_flag) {
15355 // cannot do pipelined http if response generated by mhttpd
15356 // decode_get() has no Content-Length header.
15357 // must close the connection.
15359 }
15360
15361 res->w->send_done = true;
15362}
15363
15364static void mongoose_send(mg_connection* nc, MongooseWorkObject* w, const char* p1, size_t s1, const char* p2, size_t s2, bool close_flag)
15365{
15366 //printf("nc: %p: send %d and %d\n", nc, (int)s1, (int)s2);
15367 struct work_result res;
15368 res.nc = nc;
15369 res.w = w;
15370 res.rseqno = s_rseqno++; // thread-asfe, s_rseqno is std::atomic_int
15371 res.p1 = p1;
15372 res.s1 = s1;
15373 res.p2 = p2;
15374 res.s2 = s2;
15375 res.close_flag = close_flag;
15376 res.send_501 = false;
15377 //printf("nc: %p: call mg_broadcast()\n", nc);
15378
15379 // NB: mg_broadcast() is advertised as thread-safe, but it is not.
15380 //
15381 // in mongoose 6.16, mg_brodacast() and mg_mgr_handle_ctl_sock() have several problems:
15382 //
15383 // a) "wrong thread" read from mgr->ctl[0], defeating the handshake
15384 //
15385 // b) "lost messages". if more than one message is written to mgr->ctl[0], the second message
15386 // will be "eaten" by mg_mgr_handle_ctl_sock() because of mistatch between number of bytes read and written
15387 // in the two functions. mg_mgr_handle_ctl_sock() always reads about 8000 bytes while mg_broadcast()
15388 // writes 8 bytes per message, (per examples/multithreaded/multithreaded.c. mhttpd messages are a bit longer).
15389 // So if multiple messages are present in the msg->ctl[0] pipe, the read call (of about 8000 bytes)
15390 // in mg_mgr_handle_ctl_sock() will return several messages (last message may be truncated)
15391 // but only the first message will be processed by the code. any additional messages are ignored.
15392 //
15393 // Problems (a) and (b) are easy to fix by using a mutex to serialize mg_broadcast().
15394 //
15395 // c) if the mg_broadcast() message contains pointers to the data buffer to be sent out,
15396 // the caller of mg_broadcast() should not free these data buffers until mg_send() is called
15397 // in "on_work_complete()". In theory, the caller of mg_broadcast() could wait until on_work_complete()
15398 // sets a "done" flag. In practice, if the corresponding network connection is closed before
15399 // mg_mgr_handle_ctl_sock() has looped over it, on_work_complete() will never run
15400 // and the "done" flag will never be set. (Of course, network connections are permitted to close
15401 // at any time without warning, but) the firefox browser closes the network connections "a lot"
15402 // especially when user pressed the "page reload" button at the moment when HTTP transations
15403 // are "in flight". (google-chrome tends to permit these "lame duck" transactions to complete and mongoose
15404 // does not see unexpected socket closures, at least not as many).
15405 //
15406 // To fix problem (c) I need to know when mg_mgr_handle_ctl_sock()'s loop over network connections
15407 // has completed (two cases: (a) my on_work_complete() was hopefully called and finished,
15408 // and (b) the "right" network connection was already closed (for whatever reason) and my on_work_complete()
15409 // was never called).
15410 //
15411 // My solution is to change the handshake between mg_broadcast() and mg_mgr_handle_ctl_sock() by sending
15412 // the handshake reply after looping over the network connections instead of after reading the message
15413 // from msg->ctl[1].
15414 //
15415 // This requires a modification to the code in mongoose.c. If this change is lost/undone, nothing will work.
15416 //
15417
15418 s_mg_broadcast_mutex.lock();
15419 mg_broadcast(&s_mgr, on_work_complete, (void *)&res, sizeof(res));
15420 s_mg_broadcast_mutex.unlock();
15421}
15422
15423static void mongoose_send_501(mg_connection* nc, MongooseWorkObject* w)
15424{
15425 struct work_result res;
15426 res.nc = nc;
15427 res.w = w;
15428 res.rseqno = s_rseqno++; // thread-asfe, s_rseqno is std::atomic_int
15429 res.p1 = 0;
15430 res.s1 = 0;
15431 res.p2 = 0;
15432 res.s2 = 0;
15433 res.close_flag = false;
15434 res.send_501 = true;
15435 //printf("nc: %p, call mg_broadcast()\n", nc);
15436
15437 s_mg_broadcast_mutex.lock();
15438 mg_broadcast(&s_mgr, on_work_complete, (void *)&res, sizeof(res));
15439 s_mg_broadcast_mutex.unlock();
15440}
15441
15442#if 0
15443void *worker_thread_proc(void *param)
15444{
15445 //struct mg_mgr *mgr = (struct mg_mgr *) param;
15446 struct work_request req = {0};
15447
15448 while ((! _abort) && (! s_shutdown)) {
15449 int rd = read(s_sock[1], &req, sizeof(req));
15450 if (rd == 0) {
15451 // socket closed, shutdown the thread
15452 break;
15453 }
15454 if (rd < 0) {
15455 if (_abort || s_shutdown) {
15456 return NULL;
15457 }
15458 fprintf(stderr, "worker_thread_proc: Error: read(s_sock(1)) returned %d, error %d (%s)\n", rd, errno, strerror(errno));
15459 abort();
15460 return NULL;
15461 }
15462
15463 //printf("nc: %p: received request!\n", req.nc);
15464
15465 int response = thread_work_function(req.nc, req.w);
15466
15467 if (response == RESPONSE_501) {
15468 if (trace_mg||verbose_mg)
15469 printf("handle_http_message: sending 501 Not Implemented error\n");
15470 mongoose_send_501(req.nc, req.w);
15471 }
15472
15473 req.w->t->fCompleted = true;
15474 gTraceBuf->AddTraceMTS(req.w->t);
15475
15476 //printf("nc: %p: wseqno: %d, delete work object!\n", req.nc, req.w->wseqno);
15477
15478 delete req.w;
15479 req.w = NULL;
15480 }
15481 return NULL;
15482}
15483#endif
15484
15485static void mongoose_thread(MongooseThreadObject* to)
15486{
15487 //printf("to %p, nc %p: thread %p started!\n", to, to->fNc, to->fThread);
15488
15489 std::unique_lock<std::mutex> ulm(to->fMutex, std::defer_lock);
15490
15491 to->fIsRunning = true;
15492
15493 while ((! _abort) && (! s_shutdown)) {
15494 MongooseWorkObject *w = NULL;
15495
15496 ulm.lock();
15497 while (to->fQueue.empty()) {
15498 //printf("to %p, nc %p, thread %p: waiting!\n", to, to->fNc, to->fThread);
15499 to->fNotify.wait(ulm);
15500 if (_abort || s_shutdown) {
15501 break;
15502 }
15503 }
15504
15505 if (_abort || s_shutdown) {
15506 break;
15507 }
15508
15509 w = to->fQueue.front();
15510 to->fQueue.pop_front();
15511 ulm.unlock();
15512
15513 //printf("to %p, nc %p: wseqno: %d, received request!\n", to, w->nc, w->wseqno);
15514
15515 int response = thread_work_function(w->nc, w);
15516
15517 if (response == RESPONSE_501) {
15518 if (trace_mg||verbose_mg)
15519 printf("handle_http_message: sending 501 Not Implemented error\n");
15520 mongoose_send_501(w->nc, w);
15521 }
15522
15523 // mg_broadcast() called on_work_complete() on the main thread and waited until it finished
15524
15525 if (!w->send_done) {
15526 // NB: careful here, if connection nc was closed, pointer nc points to nowhere! do not dereference it!
15527 // NB: stay quiet about it, nothing special about network connctions closing and opening as they wish.
15528 //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);
15529 }
15530
15531 w->t->fCompleted = true;
15532 gTraceBuf->AddTraceMTS(w->t);
15533
15534 //printf("nc: %p: wseqno: %d, delete work object!\n", w->nc, w->wseqno);
15535
15536 delete w;
15537 }
15538
15539 to->fIsRunning = false;
15540
15541 //printf("to %p, nc %p: thread %p finished!\n", to, to->fNc, to->fThread);
15542}
15543
15544static bool mongoose_hostlist_enabled(const struct mg_connection *nc);
15545
15546static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
15547{
15548 (void) nc;
15549 (void) ev_data;
15550
15551 //if (trace_mg && ev != 0) {
15552 // printf("ev_handler: connection %p, event %d\n", nc, ev);
15553 //}
15554
15555 switch (ev) {
15556 case 0:
15557 break;
15558 default: {
15559 if (trace_mg) {
15560 printf("ev_handler: connection %p, event %d\n", nc, ev);
15561 }
15562 break;
15563 }
15564 case MG_EV_ACCEPT:
15565 assert(nc->user_data == NULL);
15566 nc->user_data = new MongooseNcUserData();
15567
15568 if (trace_mg) {
15569 printf("ev_handler: connection %p, MG_EV_ACCEPT, user_data %p, ncseqno %d\n", nc, nc->user_data, GetNcSeqno(nc));
15570 }
15571 if (s_shutdown) {
15572 //printf("XXX nc %p!\n", nc);
15574 } else if (mongoose_hostlist_enabled(nc)) {
15575 if (!mongoose_check_hostlist(&nc->sa)) {
15577 }
15578 }
15579 break;
15580 case MG_EV_RECV:
15581 if (trace_mg_recv) {
15582 printf("ev_handler: connection %p, MG_EV_RECV, %d bytes\n", nc, *(int*)ev_data);
15583 }
15584 if (s_shutdown) {
15585 //printf("RRR nc %p!\n", nc);
15587 }
15588 break;
15589 case MG_EV_SEND:
15590 if (trace_mg_send) {
15591 printf("ev_handler: connection %p, MG_EV_SEND, %d bytes\n", nc, *(int*)ev_data);
15592 }
15593 break;
15594 case MG_EV_HTTP_CHUNK: {
15595 if (trace_mg) {
15596 printf("ev_handler: connection %p, MG_EV_HTTP_CHUNK\n", nc);
15597 }
15598 if (s_shutdown) {
15599 //printf("RRR1 nc %p!\n", nc);
15601 }
15602 break;
15603 }
15604 case MG_EV_HTTP_REQUEST: {
15605 struct http_message* msg = (struct http_message*)ev_data;
15606 if (trace_mg) {
15607 printf("ev_handler: connection %p, MG_EV_HTTP_REQUEST \"%s\" \"%s\"\n", nc, mgstr(&msg->method).c_str(), mgstr(&msg->uri).c_str());
15608 }
15609 if (s_shutdown) {
15610 //printf("RRR2 nc %p!\n", nc);
15612 } else {
15613 handle_http_message(nc, msg);
15614 }
15615 break;
15616 }
15617 case MG_EV_CLOSE: {
15618 if (trace_mg) {
15619 printf("ev_handler: connection %p, MG_EV_CLOSE, user_data %p, ncseqno %d\n", nc, nc->user_data, GetNcSeqno(nc));
15620 }
15621 //printf("CCC nc %p!\n", nc);
15622 FreeThread(nc);
15623 if (nc->user_data) {
15624 MongooseNcUserData* ncud = (MongooseNcUserData*)nc->user_data;
15625 nc->user_data = NULL;
15626 delete ncud;
15627 ncud = NULL;
15628 }
15629 }
15630 }
15631}
15632
15633#define FLAG_HTTPS MG_F_USER_1
15634#define FLAG_PASSWORDS MG_F_USER_2
15635#define FLAG_HOSTLIST MG_F_USER_3
15636
15637static bool mongoose_passwords_enabled(const struct mg_connection *nc)
15638{
15639 int flags = 0;
15640 if (nc && nc->listener) {
15641 flags = nc->listener->flags;
15642 }
15643 //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);
15644 return flags & FLAG_PASSWORDS;
15645}
15646
15647static bool mongoose_hostlist_enabled(const struct mg_connection *nc)
15648{
15649 int flags = 0;
15650 if (nc && nc->listener) {
15651 flags = nc->listener->flags;
15652 }
15653 //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);
15654 return flags & FLAG_HOSTLIST;
15655}
15656
15657static int mongoose_listen(const char* address, int flags)
15658{
15659#if MG_ENABLE_SSL
15660#else
15661 if (flags & FLAG_HTTPS) {
15662 cm_msg(MERROR, "mongoose_listen", "https port \"%s\" requested, but mhttpd compiled without MG_ENABLE_SSL", address);
15663 return SS_SOCKET_ERROR;
15664 }
15665#endif
15666
15667 struct mg_connection *nc = mg_bind(&s_mgr, address, ev_handler);
15668 if (nc == NULL) {
15669 cm_msg(MERROR, "mongoose_listen", "Cannot mg_bind address \"%s\"", address);
15670 return SS_SOCKET_ERROR;
15671 }
15672
15673 if (flags & FLAG_HTTPS) {
15674#if MG_ENABLE_SSL
15675 std::string cert_file;
15676
15677 int status = find_file_mg("ssl_cert.pem", cert_file, NULL, trace_mg);
15678
15679 if (status != SUCCESS) {
15680 cm_msg(MERROR, "mongoose_listen", "cannot find SSL certificate file \"%s\"", cert_file.c_str());
15681 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");
15682 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");
15683 return SS_FILE_ERROR;
15684 }
15685
15686 printf("Mongoose web server will use https certificate file \"%s\"\n", cert_file.c_str());
15687
15688 const char* errmsg = mg_set_ssl(nc, cert_file.c_str(), NULL);
15689 if (errmsg) {
15690 cm_msg(MERROR, "mongoose_listen", "Cannot enable https with certificate file \"%s\", error: %s", cert_file.c_str(), errmsg);
15691 return SS_SOCKET_ERROR;
15692 }
15693
15694 // NB: where is the warning that the SSL certificate has expired?!? K.O.
15695#else
15696 abort(); // cannot happen!
15697#endif
15698 }
15699
15701
15702 nc->flags |= flags;
15703
15704 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");
15705
15706 return SUCCESS;
15707}
15708
15709static int mongoose_init(MVOdb* odb, bool no_passwords, bool no_hostlist, const std::vector<std::string>& user_hostlist)
15710{
15711 bool enable_localhost_port = true;
15712 int localhost_port = 8080;
15713 bool localhost_port_passwords = false;
15714
15715 bool enable_insecure_port = false;
15716 int insecure_port = 8081;
15717 bool insecure_port_passwords = true;
15718 bool insecure_port_hostlist = true;
15719
15720 bool enable_https_port = false;
15721 int https_port = 8443;
15722 bool https_port_passwords = true;
15723 bool https_port_hostlist = false;
15724
15725 std::vector<std::string> hostlist;
15726 hostlist.push_back("localhost");
15727
15728 bool enable_ipv6 = true;
15729
15730 odb->RB("Enable localhost port", &enable_localhost_port, true);
15731 odb->RI("localhost port", &localhost_port, true);
15732 odb->RB("localhost port passwords", &localhost_port_passwords, true);
15733 odb->RB("Enable insecure port", &enable_insecure_port, true);
15734 odb->RI("insecure port", &insecure_port, true);
15735 odb->RB("insecure port passwords", &insecure_port_passwords, true);
15736 odb->RB("insecure port host list", &insecure_port_hostlist, true);
15737 odb->RB("Enable https port", &enable_https_port, true);
15738 odb->RI("https port", &https_port, true);
15739 odb->RB("https port passwords", &https_port_passwords, true);
15740 odb->RB("https port host list", &https_port_hostlist, true);
15741 odb->RSA("Host list", &hostlist, true, 10, 256);
15742 odb->RB("Enable IPv6", &enable_ipv6, true);
15743
15744 // populate the MIME.types table
15745 gProxyOdb = odb->Chdir("Proxy", true);
15746 std::string proxy_example = "#http://localhost:8080";
15747 gProxyOdb->RS("example", &proxy_example, true);
15748
15749 // populate the MIME.types table
15750 SaveMimetypes(odb->Chdir("mime.types", true));
15751
15752 if (!no_passwords
15753 && ((enable_localhost_port && localhost_port_passwords)
15754 || (enable_insecure_port && insecure_port_passwords)
15755 || (enable_https_port && https_port_passwords))) {
15756 gAuthMg = new Auth();
15757 int status = gAuthMg->Init();
15758 if (status != SUCCESS) {
15759 printf("mongoose_init: Error: Cannot initialize authorization object!\n");
15760 return status;
15761 }
15762 printf("HTTP Digest authentication with realm \"%s\" and password file \"%s\"\n", gAuthMg->realm.c_str(), gAuthMg->passwd_filename.c_str());
15763 } else {
15764 printf("Password protection is off\n");
15765 }
15766
15767 if (!no_hostlist
15768 && ((enable_insecure_port && insecure_port_hostlist)
15769 || (enable_https_port && https_port_hostlist))) {
15770 gAllowedHosts.clear();
15771
15772 // copy the user allowed hosts
15773 for (unsigned int i=0; i<user_hostlist.size(); i++)
15774 gAllowedHosts.push_back(user_hostlist[i]);
15775
15776 for (unsigned i=0; i<hostlist.size(); i++) {
15777 std::string s = hostlist[i];
15778 if (s.length() < 1) // skip emties
15779 continue;
15780
15781 if (s[0] == '#') // skip commented-out entries
15782 continue;
15783
15784 //printf("add allowed hosts %d [%s]\n", i, s.c_str());
15785 gAllowedHosts.push_back(s);
15786 }
15787
15788 printf("Hostlist active, connections will be accepted only from: ");
15789 for (unsigned i=0; i<gAllowedHosts.size(); i++) {
15790 if (i>0)
15791 printf(", ");
15792 printf("%s", gAllowedHosts[i].c_str());
15793 }
15794 printf("\n");
15795 } else {
15796 printf("Hostlist off, connections from anywhere will be accepted\n");
15797 }
15798
15799 mg_mgr_init(&s_mgr, NULL);
15800
15801 bool listen_failed = false;
15802
15803 if (enable_localhost_port) {
15804 char str[256];
15805 sprintf(str, "localhost:%d", localhost_port);
15806 int status = mongoose_listen(str, 0);
15807 if (status != SUCCESS)
15808 listen_failed = true;
15809 if (enable_ipv6) {
15810 sprintf(str, "[::1]:%d", localhost_port);
15811 status = mongoose_listen(str, 0);
15812 if (status != SUCCESS)
15813 listen_failed = true;
15814 }
15815 }
15816
15817 if (enable_insecure_port) {
15818 char str[256];
15819 int flags = 0;
15820 if (insecure_port_passwords)
15821 flags |= FLAG_PASSWORDS;
15822 if (insecure_port_hostlist)
15823 flags |= FLAG_HOSTLIST;
15824 if (enable_ipv6) {
15825 sprintf(str, "[::]:%d", insecure_port);
15826 int status = mongoose_listen(str, flags);
15827 if (status != SUCCESS)
15828 listen_failed = true;
15829 } else {
15830 sprintf(str, "%d", insecure_port);
15831 int status = mongoose_listen(str, flags);
15832 if (status != SUCCESS)
15833 listen_failed = true;
15834 }
15835 }
15836
15837 if (enable_https_port) {
15838 char str[256];
15839 int flags = 0;
15840 if (https_port_passwords)
15841 flags |= FLAG_PASSWORDS;
15842 if (https_port_hostlist)
15843 flags |= FLAG_HOSTLIST;
15844 flags |= FLAG_HTTPS;
15845 if (enable_ipv6) {
15846 sprintf(str, "[::]:%d", https_port);
15847 int status = mongoose_listen(str, flags);
15848 if (status != SUCCESS)
15849 listen_failed = true;
15850 } else {
15851 sprintf(str, "%d", https_port);
15852 int status = mongoose_listen(str, flags);
15853 if (status != SUCCESS)
15854 listen_failed = true;
15855 }
15856 }
15857
15858 if (listen_failed) {
15859 cm_msg(MERROR, "mongoose_init", "Failed to listen on a TCP port enabled in ODB /WebServer");
15860 return SS_SOCKET_ERROR;
15861 }
15862
15863 return SUCCESS;
15864}
15865
15866static void mongoose_poll(int msec = 200)
15867{
15868 mg_mgr_poll(&s_mgr, msec);
15869}
15870
15871static void mongoose_cleanup()
15872{
15873 printf("Mongoose web server shutting down\n");
15874
15875 s_shutdown = true;
15876
15877 // close listener sockets
15878 if (s_mgr.active_connections) {
15879 struct mg_connection* nc = s_mgr.active_connections;
15880 while (nc) {
15881 //printf("nc %p, next %p, user_data %p, listener %p, flags %lu\n", nc, nc->next, nc->user_data, nc->listener, nc->flags);
15882 if (nc->flags & MG_F_LISTENING) {
15884 }
15885 nc = nc->next;
15886 }
15887 }
15888
15889 // tell threads to shut down
15890 for (auto it : gMongooseThreads) {
15891 MongooseThreadObject* to = it;
15892 to->fNotify.notify_one();
15893 }
15894
15895 // wait until all threads stop
15896 for (int i=0; i<10; i++) {
15897 int count_running = 0;
15898 for (auto it : gMongooseThreads) {
15899 MongooseThreadObject* to = it;
15900 //printf("AAA6C %p thread %p running %d!\n", to, to->fThread, to->fIsRunning);
15901 if (to->fIsRunning) {
15902 count_running++;
15903 }
15904 }
15905 printf("Mongoose web server shutting down, %d threads still running\n", count_running);
15906 if (count_running == 0)
15907 break;
15908 mongoose_poll(1000);
15909 }
15910
15911 // delete thread objects
15912 for (auto it : gMongooseThreads) {
15913 MongooseThreadObject* to = it;
15914 //printf("AAA7B %p thread %p running %d!\n", to, to->fThread, to->fIsRunning);
15915 if (to->fIsRunning) {
15916 cm_msg(MERROR, "mongoose", "thread failed to shut down");
15917 continue;
15918 }
15919 to->fThread->join();
15920 delete to->fThread;
15921 delete to;
15922 }
15923 gMongooseThreads.clear();
15924
15925 mg_mgr_free(&s_mgr);
15926
15927 //closesocket(s_sock[0]);
15928 //closesocket(s_sock[1]);
15929
15930 // make leak sanitizer happy!
15931 for (auto e : gHostlistCache) {
15932 delete e;
15933 }
15934 gHostlistCache.clear();
15935 if (gProxyOdb) {
15936 delete gProxyOdb;
15937 gProxyOdb = NULL;
15938 }
15939 if (gMimeTypesOdb) {
15940 delete gMimeTypesOdb;
15941 gMimeTypesOdb = NULL;
15942 }
15943
15944 printf("Mongoose web server shut down\n");
15945}
15946
15947#endif
15948
15949#ifdef HAVE_MONGOOSE6
15950
15951static bool mongoose_passwords_enabled(const struct mg_connection *nc)
15952{
15953 return true;
15954}
15955
15956int start_mg(int user_http_port, int user_https_port, int socket_priviledged_port, int verbose)
15957{
15958 HNDLE hDB;
15959 int size;
15960 int status;
15961
15962 //if (verbose)
15963 // trace_mg = true;
15964
15965 if (verbose)
15966 verbose_mg = true;
15967
15969 assert(status == CM_SUCCESS);
15970
15971 int http_port = 8080;
15972 int https_port = 8443;
15973 int http_redirect_to_https = 1;
15974
15975 size = sizeof(http_port);
15976 db_get_value(hDB, 0, "/Experiment/midas http port", &http_port, &size, TID_INT, TRUE);
15977
15978 size = sizeof(https_port);
15979 db_get_value(hDB, 0, "/Experiment/midas https port", &https_port, &size, TID_INT, TRUE);
15980
15981 size = sizeof(http_redirect_to_https);
15982 db_get_value(hDB, 0, "/Experiment/http redirect to https", &http_redirect_to_https, &size, TID_BOOL, TRUE);
15983
15984 bool need_cert_file = false;
15985 bool need_password_file = false;
15986
15987 if (user_http_port)
15988 http_port = user_http_port;
15989
15990 if (user_https_port)
15991 https_port = user_https_port;
15992
15993 if (https_port) {
15994 need_cert_file = true;
15995 need_password_file = true;
15996 }
15997
15998 if (!https_port)
15999 http_redirect_to_https = 0;
16000
16001 if (http_port && !http_redirect_to_https) {
16002 // no passwords serving over http unless
16003 // http is just a redict to https
16004 need_password_file = false;
16005 }
16006
16007 if (socket_priviledged_port >= 0) {
16008 // no passwords if serving unencrypted http on port 80
16009 need_password_file = false;
16010 printf("Mongoose web server password portection is disabled: serving unencrypted http on port 80\n");
16011 }
16012
16013 bool have_at_least_one_port = false;
16014
16015 std::string cert_file;
16016
16017 if (need_cert_file) {
16018 status = find_file_mg("ssl_cert.pem", cert_file, NULL, trace_mg);
16019
16020 if (status != SUCCESS) {
16021 cm_msg(MERROR, "mongoose", "cannot find SSL certificate file \"%s\"", cert_file.c_str());
16022 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");
16023 return SS_FILE_ERROR;
16024 }
16025
16026 printf("Mongoose web server will use SSL certificate file \"%s\"\n", cert_file.c_str());
16027 }
16028
16029 if (need_password_file) {
16030 gAuthMg = new Auth();
16031 status = gAuthMg->Init();
16032 if (status != SUCCESS) {
16033 printf("Error: Cannot initialize authorization object!\n");
16034 return status;
16035 }
16036 printf("Mongoose web server will use authentication realm \"%s\", password file \"%s\"\n", gAuthMg->realm.c_str(), gAuthMg->passwd_filename.c_str());
16037 } else {
16038 printf("Mongoose web server will not use password protection\n");
16039 }
16040
16041 if (trace_mg)
16042 printf("start_mg!\n");
16043
16044#ifndef OS_WINNT
16045 signal(SIGPIPE, SIG_IGN);
16046#endif
16047
16048 if (!gTraceBuf) {
16050 }
16051
16052 if (!request_mutex) {
16053 status = ss_mutex_create(&request_mutex, FALSE);
16054 assert(status==SS_SUCCESS || status==SS_CREATED);
16055 }
16056
16057 mg_mgr_init(&mgr_mg, NULL);
16058
16059 // use socket bound to priviledged port (setuid-mode)
16060 if (socket_priviledged_port >= 0) {
16061 struct mg_connection* nc = mg_add_sock(&mgr_mg, socket_priviledged_port, handle_event_mg);
16062 if (nc == NULL) {
16063 cm_msg(MERROR, "mongoose", "Cannot create mg_connection for set-uid-root privileged port");
16064 return SS_SOCKET_ERROR;
16065 }
16066
16067 nc->flags |= MG_F_LISTENING;
16068#ifdef MG_ENABLE_THREADS
16070#endif
16072 mg_register_http_endpoint(nc, "/", handle_http_event_mg);
16073
16074 have_at_least_one_port = true;
16075 printf("mongoose web server is listening on the set-uid-root privileged port\n");
16076 }
16077
16078 if (http_port != 80) { // port 80 is already handled by socket_priviledged_port
16079 char str[256];
16080 sprintf(str, "%d", http_port);
16081 struct mg_connection* nc = mg_bind(&mgr_mg, str, handle_event_mg);
16082 if (nc == NULL) {
16083 cm_msg(MERROR, "mongoose", "Cannot bind to port %d", http_port);
16084 return SS_SOCKET_ERROR;
16085 }
16086
16087#ifdef MG_ENABLE_THREADS
16089#endif
16091
16092 if (http_redirect_to_https) {
16093 std::string hostname = ss_gethostname();
16094 char str[256];
16095 sprintf(str, "%d", https_port);
16096 std::string s = hostname + ":" + std::string(str);
16097 nc->user_data = new std::string(s);
16098 mg_register_http_endpoint(nc, "/", handle_http_redirect);
16099 printf("mongoose web server is redirecting HTTP port %d to https://%s\n", http_port, s.c_str());
16100 } else {
16101 mg_register_http_endpoint(nc, "/", handle_http_event_mg);
16102 }
16103
16104 have_at_least_one_port = true;
16105 printf("mongoose web server is listening on the HTTP port %d\n", http_port);
16106 }
16107
16108 if (https_port) {
16109#ifdef MG_ENABLE_SSL
16110 char str[256];
16111 sprintf(str, "%d", https_port);
16112 struct mg_connection* nc = mg_bind(&mgr_mg, str, handle_event_mg);
16113 if (nc == NULL) {
16114 cm_msg(MERROR, "mongoose", "Cannot bind to port %d", https_port);
16115 return SS_SOCKET_ERROR;
16116 }
16117
16118 mg_set_ssl(nc, cert_file.c_str(), NULL);
16119#ifdef MG_ENABLE_THREADS
16121#endif
16123 mg_register_http_endpoint(nc, "/", handle_http_event_mg);
16124
16125 have_at_least_one_port = true;
16126 printf("mongoose web server is listening on the HTTPS port %d\n", https_port);
16127#else
16128 cm_msg(MERROR, "mongoose", "https port %d requested, but mhttpd compiled without MG_ENABLE_SSL", https_port);
16129 return SS_SOCKET_ERROR;
16130#endif
16131 }
16132
16133 if (!have_at_least_one_port) {
16134 cm_msg(MERROR, "mongoose", "cannot start: no ports defined");
16135 return SS_FILE_ERROR;
16136 }
16137
16138 return SUCCESS;
16139}
16140
16141int stop_mg()
16142{
16143 if (trace_mg)
16144 printf("stop_mg!\n");
16145
16146 // Stop the server.
16147 mg_mgr_free(&mgr_mg);
16148
16149 if (trace_mg)
16150 printf("stop_mg done!\n");
16151 return SUCCESS;
16152}
16153
16154int loop_mg()
16155{
16156 int status = SUCCESS;
16157
16158 /* establish Ctrl-C handler - will set _abort to TRUE */
16160
16161 while (!_abort) {
16162
16163 /* cm_yield() is not thread safe, need to take a lock */
16164
16165#ifdef HAVE_MONGOOSE6
16166 status = ss_mutex_wait_for(request_mutex, 0);
16167#endif
16168 gMutex.lock();
16169
16170 /* check for shutdown message */
16171 status = cm_yield(0);
16172 if (status == RPC_SHUTDOWN)
16173 break;
16174
16175 gMutex.unlock();
16176#ifdef HAVE_MONGOOSE6
16177 status = ss_mutex_release(request_mutex);
16178#endif
16179
16180 //ss_sleep(10);
16181
16182 mg_mgr_poll(&mgr_mg, 10);
16183 }
16184
16185 return status;
16186}
16187#endif
16188
16189static MJsonNode* get_http_trace(const MJsonNode* params)
16190{
16191 if (!params) {
16192 MJSO *doc = MJSO::I();
16193 doc->D("get current value of mhttpd http_trace");
16194 doc->P(NULL, 0, "there are no input parameters");
16195 doc->R(NULL, MJSON_INT, "current value of http_trace");
16196 return doc;
16197 }
16198
16199 return mjsonrpc_make_result("http_trace", MJsonNode::MakeInt(http_trace));
16200}
16201
16202static MJsonNode* set_http_trace(const MJsonNode* params)
16203{
16204 if (!params) {
16205 MJSO* doc = MJSO::I();
16206 doc->D("set new value of mhttpd http_trace");
16207 doc->P(NULL, MJSON_INT, "new value of http_trace");
16208 doc->R(NULL, MJSON_INT, "new value of http_trace");
16209 return doc;
16210 }
16211
16212 http_trace = params->GetInt();
16213 return mjsonrpc_make_result("http_trace", MJsonNode::MakeInt(http_trace));
16214}
16215
16217{
16218 mjsonrpc_add_handler("set_http_trace", set_http_trace);
16219 mjsonrpc_add_handler("get_http_trace", get_http_trace);
16220}
16221
16222/*------------------------------------------------------------------*/
16223
16224int main(int argc, const char *argv[])
16225{
16226 int status;
16227 int daemon = FALSE;
16228#ifdef HAVE_MONGOOSE6
16229 int user_http_port = 0;
16230 int user_https_port = 0;
16231#endif
16232#ifdef HAVE_MONGOOSE616
16233 bool no_passwords = false;
16234 bool no_hostlist = false;
16235#endif
16236 const char *myname = "mhttpd";
16237
16238 setbuf(stdout, NULL);
16239 setbuf(stderr, NULL);
16240#ifdef SIGPIPE
16241 /* avoid getting killed by "Broken pipe" signals */
16242 signal(SIGPIPE, SIG_IGN);
16243#endif
16244
16245#ifdef HAVE_MONGOOSE6
16246 //
16247 // if running setuid-root, unconditionally bind to port 80.
16248 //
16249
16250 int socket_priviledged_port = -1;
16251
16252#ifdef OS_UNIX
16253 // in setuid-root mode bind to priviledged port
16254 if (getuid() != geteuid()) {
16255 int port80 = 80;
16256
16257 printf("mhttpd is running in setuid-root mode.\n");
16258
16259 socket_priviledged_port = open_listening_socket(port80);
16260 if (socket_priviledged_port < 0) {
16261 printf("Cannot open listening socket on TCP port %d, aborting.\n", port80);
16262 exit(1);
16263 }
16264
16265 // give up root privilege
16266 status = setuid(getuid());
16267 if (status != 0) {
16268 printf("Cannot give up root privelege, aborting.\n");
16269 exit(1);
16270 }
16271 status = setuid(getuid());
16272 if (status != 0) {
16273 printf("Cannot give up root privelege, aborting.\n");
16274 exit(1);
16275 }
16276 }
16277#endif
16278#endif
16279
16280 char midas_hostname[256];
16281 char midas_expt[256];
16282
16283 /* get default from environment */
16284 cm_get_environment(midas_hostname, sizeof(midas_hostname), midas_expt, sizeof(midas_expt));
16285
16286 /* parse command line parameters */
16287#ifdef HAVE_MONGOOSE6
16288 gUserAllowedHosts.clear();
16289#else
16290 std::vector<std::string> user_hostlist;
16291#endif
16292 for (int i = 1; i < argc; i++) {
16293 if (argv[i][0] == '-' && argv[i][1] == 'D')
16294 daemon = TRUE;
16295 else if (argv[i][0] == '-' && argv[i][1] == 'v')
16296 verbose = TRUE;
16297 else if (argv[i][0] == '-' && argv[i][1] == 'E')
16298 elog_mode = TRUE;
16299 else if (argv[i][0] == '-' && argv[i][1] == 'H') {
16301#ifdef HAVE_MONGOOSE6
16302 } else if (strcmp(argv[i], "--http") == 0) {
16303 if (argv[i+1]) {
16304 user_http_port = atoi(argv[i+1]);
16305 }
16306 } else if (strcmp(argv[i], "--https") == 0) {
16307 if (argv[i+1]) {
16308 user_https_port = atoi(argv[i+1]);
16309 }
16310#endif
16311 } else if (strcmp(argv[i], "--trace-mg") == 0) {
16312 trace_mg = true;
16313 trace_mg_recv = true;
16314 trace_mg_send = true;
16315 } else if (strcmp(argv[i], "--trace-mg-verbose") == 0) {
16316 trace_mg_verbose = true;
16317 } else if (strcmp(argv[i], "--no-trace-mg-recv") == 0) {
16318 trace_mg_recv = false;
16319 } else if (strcmp(argv[i], "--no-trace-mg-send") == 0) {
16320 trace_mg_send = false;
16321 } else if (strcmp(argv[i], "--verbose-mg") == 0) {
16322 verbose_mg = true;
16323#ifdef HAVE_MONGOOSE616
16324 } else if (strcmp(argv[i], "--no-multithread") == 0) {
16325 multithread_mg = false;
16326 } else if (strcmp(argv[i], "--no-passwords") == 0) {
16327 no_passwords = true;
16328 } else if (strcmp(argv[i], "--no-hostlist") == 0) {
16329 no_hostlist = true;
16330#endif
16331 } else if (argv[i][0] == '-') {
16332 if (i + 1 >= argc || argv[i + 1][0] == '-')
16333 goto usage;
16334 if (argv[i][1] == 'h')
16335 mstrlcpy(midas_hostname, argv[++i], sizeof(midas_hostname));
16336 else if (argv[i][1] == 'e')
16337 mstrlcpy(midas_expt, argv[++i], sizeof(midas_hostname));
16338 else if (argv[i][1] == 'a') {
16339#ifdef HAVE_MONGOOSE6
16340 gUserAllowedHosts.push_back(argv[++i]);
16341#else
16342 user_hostlist.push_back(argv[++i]);
16343#endif
16344 } else if (argv[i][1] == 'p') {
16345 printf("Option \"-p port_number\" for the old web server is obsolete.\n");
16346 printf("mongoose web server is the new default, port number is set in ODB or with \"--http port_number\".\n");
16347 printf("To run the obsolete old web server, please use \"--oldserver\" switch.\n");
16348 return 1;
16349 } else {
16350 usage:
16351 printf("usage: %s [-h Hostname[:port]] [-e Experiment] [-v] [-D] [-a Hostname]\n\n", argv[0]);
16352 printf(" -a add hostname to the hostlist of hosts allowed to connect to mhttpd\n");
16353 printf(" -e experiment to connect to\n");
16354 printf(" -h connect to midas server (mserver) on given host\n");
16355 printf(" -v display verbose HTTP communication\n");
16356 printf(" -D become a daemon\n");
16357 printf(" -E only display ELog system\n");
16358 printf(" -H only display history plots\n");
16359#ifdef HAVE_MONGOOSE6
16360 printf(" --http port - bind to specified HTTP port (default is ODB \"/Experiment/midas http port\")\n");
16361 printf(" --https port - bind to specified HTTP port (default is ODB \"/Experiment/midas https port\")\n");
16362#endif
16363 printf(" --verbose-mg - trace mongoose web requests\n");
16364 printf(" --trace-mg - trace mongoose events\n");
16365 printf(" --no-trace-mg-recv - do not trace mongoose recv events\n");
16366 printf(" --no-trace-mg-send - dop not trace mongoose send events\n");
16367#ifdef HAVE_MONGOOSE616
16368 printf(" --no-multithread - disable mongoose multithreading\n");
16369 printf(" --no-passwords - disable password protection\n");
16370 printf(" --no-hostlist - disable access control host list\n");
16371#endif
16372 return 0;
16373 }
16374 }
16375 }
16376
16377 if (daemon) {
16378 printf("Becoming a daemon...\n");
16380 }
16381
16382#ifdef OS_LINUX
16383 /* write PID file */
16384 FILE *f = fopen("/var/run/mhttpd.pid", "w");
16385 if (f != NULL) {
16386 fprintf(f, "%d", ss_getpid());
16387 fclose(f);
16388 }
16389#endif
16390
16391 if (history_mode)
16392 myname = "mhttpd_history";
16393
16394 /*---- connect to experiment ----*/
16395 status = cm_connect_experiment1(midas_hostname, midas_expt, myname, NULL,
16398 return 1;
16399 else if (status == DB_INVALID_HANDLE) {
16400 std::string s = cm_get_error(status);
16401 puts(s.c_str());
16402 } else if (status != CM_SUCCESS) {
16403 std::string s = cm_get_error(status);
16404 puts(s.c_str());
16405 return 1;
16406 }
16407
16408 /* mhttpd needs the watchdog thread until we are sure
16409 * we do not have any long sleeps anywhere in the mhttpd code.
16410 * this includes reads from the history files or databases,
16411 * that can take arbitrary long time */
16413
16414 /* Get ODB handles */
16415
16416 HNDLE hDB;
16417
16419
16420 MVOdb *odb = MakeMidasOdb(hDB);
16421 gOdb = odb;
16422
16423 /* do ODB record checking */
16424 if (!check_odb_records(odb)) {
16425 // check_odb_records() fails with nothing printed to the terminal
16426 // because mhttpd does not print cm_msg(MERROR, ...) messages to the terminal.
16427 // At least print something!
16428 printf("check_odb_records() failed, see messages and midas.log, bye!\n");
16430 return 1;
16431 }
16432
16433#ifdef HAVE_MONGOOSE6
16434 if (init_allowed_hosts() != SUCCESS) {
16435 printf("init_allowed_hosts() failed, see messages and midas.log, bye!\n");
16437 return 1;
16438 }
16439
16440 if (verbose) {
16441 if (gAllowedHosts.size() > 0) {
16442 printf("mhttpd allowed hosts list: ");
16443 for (unsigned int i=0; i<gAllowedHosts.size(); i++) {
16444 if (i>0)
16445 printf(", ");
16446 printf("%s", gAllowedHosts[i].c_str());
16447 }
16448 printf("\n");
16449 } else {
16450 printf("mhttpd allowed hosts list is empty\n");
16451 }
16452 }
16453
16454 // populate the MIME.types table
16455 SaveMimetypes(odb->Chdir("WebServer/mime.types", true));
16456#endif
16457
16458 /* initialize odb entries needed for mhttpd and midas web pages */
16459 init_mhttpd_odb(odb);
16460
16461 /* initialize menu buttons */
16462 init_menu_buttons(odb);
16463
16464 /* initialize elog odb entries */
16465 init_elog_odb();
16466
16467 /* initialize the JSON RPC handlers */
16468 mjsonrpc_init();
16470
16472
16473#ifdef HAVE_MONGOOSE6
16474 status = start_mg(user_http_port, user_https_port, socket_priviledged_port, verbose);
16475 if (status != SUCCESS) {
16476 // At least print something!
16477 printf("could not start the mongoose web server, see messages and midas.log, bye!\n");
16479 return 1;
16480 }
16481#endif
16482
16483#ifdef HAVE_MONGOOSE616
16484
16485#ifdef SIGPIPE
16486#ifdef SIG_IGN
16487 signal(SIGPIPE, SIG_IGN);
16488#endif
16489#endif
16490
16491 if (!gTraceBuf) {
16493 }
16494
16495 //if (!request_mutex) {
16496 // status = ss_mutex_create(&request_mutex, FALSE);
16497 // assert(status==SS_SUCCESS || status==SS_CREATED);
16498 //}
16499
16500 /* establish Ctrl-C handler - will set _abort to TRUE */
16502
16503 MVOdb* o = odb->Chdir("WebServer", true);
16504 status = mongoose_init(o, no_passwords, no_hostlist, user_hostlist);
16505 if (status != SUCCESS) {
16506 // At least print something!
16507 printf("Error: Could not start the mongoose web server, see messages and midas.log, bye!\n");
16509 return 1;
16510 }
16511
16512 delete o;
16513#endif
16514
16515#ifdef HAVE_MONGOOSE6
16516 loop_mg();
16517 stop_mg();
16518#endif
16519
16520#ifdef HAVE_MONGOOSE616
16521 while (!_abort) {
16522
16523 /* cm_yield() is not thread safe, need to take a lock */
16524
16525 //status = ss_mutex_wait_for(request_mutex, 0);
16526 gMutex.lock();
16527
16528 /* check for shutdown message */
16529 status = cm_yield(0);
16530 if (status == RPC_SHUTDOWN)
16531 break;
16532
16533 gMutex.unlock();
16534 //status = ss_mutex_release(request_mutex);
16535
16536 //ss_sleep(10);
16537
16538 mongoose_poll(10);
16539 }
16540
16541 mongoose_cleanup();
16542#endif
16543
16544 if (gMh) {
16545 delete gMh;
16546 gMh = NULL;
16547 gMhkey = 0;
16548 }
16549
16550 mjsonrpc_exit();
16552 return 0;
16553}
16554
16555/* emacs
16556 * Local Variables:
16557 * tab-width: 8
16558 * c-basic-offset: 3
16559 * indent-tabs-mode: nil
16560 * End:
16561 */
#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:13732
std::string passwd_filename
Definition mhttpd.cxx:13731
std::string realm
Definition mhttpd.cxx:13730
int Init()
Definition mhttpd.cxx:13739
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:75
static void usage()
int done
TRIGGER_SETTINGS ts
INT al_get_alarms(std::string *presult)
Definition alarm.cxx:860
INT cm_yield(INT millisec)
Definition midas.cxx:5659
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3026
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2781
INT cm_start_watchdog_thread()
Definition midas.cxx:7365
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:2312
std::string cm_expand_env(const char *str)
Definition midas.cxx:7720
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5478
INT cm_disconnect_experiment(void)
Definition midas.cxx:2861
std::string cm_get_exptab_filename()
Definition midas.cxx:1803
std::string cm_get_path()
Definition midas.cxx:1552
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5860
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2149
const char * cm_get_version()
Definition midas.cxx:1491
std::string cm_get_experiment_name()
Definition midas.cxx:1595
const char * cm_get_revision()
Definition midas.cxx:1499
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7530
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:124
#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:988
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:517
std::string cm_get_error(INT code)
Definition midas.cxx:468
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:930
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1279
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:552
#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:10752
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6664
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:6132
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:7196
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:11581
INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT *buffer_size, const char *path)
Definition odb.cxx:7977
INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7520
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6310
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:12750
INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
Definition odb.cxx:8802
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:5790
INT db_get_link(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:5843
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:13714
INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10688
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7415
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13592
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:6986
INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5496
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:10612
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:10276
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:6427
INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
Definition odb.cxx:6032
INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD *delta)
Definition odb.cxx:5903
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:13805
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12578
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11083
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7270
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:125
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13497
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11852
#define RPC_JRPC
Definition mrpc.h:130
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11598
const char * rpc_tid_name(INT id)
Definition midas.cxx:11789
#define RPC_CNAF24
Definition mrpc.h:126
#define RPC_MANUAL_TRIG
Definition mrpc.h:128
INT rpc_tid_size(INT id)
Definition midas.cxx:11782
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:14178
static bool trace_mg
Definition mhttpd.cxx:13710
static void SaveHistPlotToOdb(MVOdb *odb, const HistPlot &hp, const char *group, const char *panel)
Definition mhttpd.cxx:10411
static bool trace_mg_send
Definition mhttpd.cxx:13712
#define LOG2
Definition mhttpd.cxx:7635
BOOL is_editable(char *eq_name, char *var_name)
Definition mhttpd.cxx:2534
#define LN10
Definition mhttpd.cxx:7634
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:7638
static bool cmp_vars(const HistVar &a, const HistVar &b)
Definition mhttpd.cxx:10107
void output_key(Param *p, Return *r, HNDLE hkey, int index, const char *format)
Definition mhttpd.cxx:4379
static MVOdb * gOdb
Definition mhttpd.cxx:48
void get_elog_url(char *url, int len)
static void DeleteHistPlotDeleted(HistPlot &hp)
Definition mhttpd.cxx:10487
static BOOL history_mode
Definition mhttpd.cxx:95
static void LoadHistPlotFromParam(HistPlot *hp, Param *p)
Definition mhttpd.cxx:10306
static void add_rpc_functions()
Definition mhttpd.cxx:16216
#define RESPONSE_501
Definition mhttpd.cxx:14261
INT check_odb_records(MVOdb *odb)
Definition mhttpd.cxx:13394
static int handle_http_post(struct mg_connection *nc, const http_message *msg, const char *uri, RequestTrace *t)
Definition mhttpd.cxx:14889
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:14147
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:13760
#define HTTP_ENCODING
Definition mhttpd.cxx:213
void strencode(Return *r, const char *text)
Definition mhttpd.cxx:2022
static MVOdb * gMimeTypesOdb
Definition mhttpd.cxx:179
void init_elog_odb()
Definition mhttpd.cxx:1981
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:2562
static void history_watch_callback(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition mhttpd.cxx:8151
void show_custom_file(Return *r, const char *name)
Definition mhttpd.cxx:3614
void show_find_page(Return *r, const char *value)
Definition mhttpd.cxx:7573
#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:6742
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:10157
void decode_cookies(Cookies *c, const http_message *msg)
Definition mhttpd.cxx:14212
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:2251
void show_query_page(Param *p, Return *r)
Definition mhttpd.cxx:9797
static void urlEncode(char *ps, int ps_size)
Definition mhttpd.cxx:921
static std::string NextHistPlotColour(const HistPlot &hp)
Definition mhttpd.cxx:10122
std::string strencode2(const char *text)
Definition mhttpd.cxx:2050
void decode_get(Return *rr, char *string, const Cookies *c, const char *url, const char *query_string, RequestTrace *t)
Definition mhttpd.cxx:13200
static MJsonNode * get_http_trace(const MJsonNode *params)
Definition mhttpd.cxx:16189
void show_error(Return *r, const char *error)
Definition mhttpd.cxx:1841
static HNDLE gMhkey
Definition mhttpd.cxx:8159
static bool read_passwords(Auth *auth)
Definition mhttpd.cxx:13802
void redirect2(Return *r, const char *path)
Definition mhttpd.cxx:1446
static void show_cnaf_page(Param *p, Return *rr)
Definition mhttpd.cxx:5660
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:1869
#define LOG5
Definition mhttpd.cxx:7636
void ctrlc_handler(int sig)
Definition mhttpd.cxx:13458
static void PrintHistPlot(const HistPlot &hp)
Definition mhttpd.cxx:10112
void javascript_commands(Param *p, Return *r, const char *cookie_cpwd)
Definition mhttpd.cxx:4454
#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:14733
std::string time_to_string(time_t t)
Definition mhttpd.cxx:8139
static std::string mgstr(const mg_str *s)
Definition mhttpd.cxx:14142
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:7912
static bool mongoose_passwords_enabled(const struct mg_connection *nc)
static bool verbose_mg
Definition mhttpd.cxx:13709
static int cmp_names(const void *a, const void *b)
Definition mhttpd.cxx:9981
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:13677
void decode_query(Param *pp, const char *query_string)
Definition mhttpd.cxx:13175
static bool gDoReloadHistory
Definition mhttpd.cxx:8149
static std::string check_digest_auth(struct http_message *hm, Auth *auth)
Definition mhttpd.cxx:13898
static MidasHistoryInterface * gMh
Definition mhttpd.cxx:8158
static bool trace_mg_verbose
Definition mhttpd.cxx:13713
static bool gDoSetupHistoryWatch
Definition mhttpd.cxx:8148
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:8527
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:7791
void show_password_page(Return *r, const char *dec_path, const char *password)
Definition mhttpd.cxx:6708
time_t string_to_time(const char *str)
Definition mhttpd.cxx:8123
void show_header(Return *r, const char *title, const char *method, const char *path, int refresh)
Definition mhttpd.cxx:1781
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:2081
static std::vector< std::string > gAllowedHosts
Definition mhttpd.cxx:13468
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:13781
void show_text_header(Return *r)
Definition mhttpd.cxx:1829
char * find_odb_tag(char *p, char *path, char *format, int *edit, char *type, char *pwd, char *tail)
Definition mhttpd.cxx:3096
static std::string UrlDecode(const char *p)
Definition mhttpd.cxx:843
void strencode4(Return *r, const char *text)
Definition mhttpd.cxx:2106
void show_elog_attachment(Param *p, Return *r, const char *path)
Definition mhttpd.cxx:2486
void Unlock(RequestTrace *t)
Definition mhttpd.cxx:12248
int evaluate_src(char *key, char *src, double *fvalue)
Definition mhttpd.cxx:3502
void redirect(Return *r, const char *path)
Definition mhttpd.cxx:1409
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:9786
#define READ_HISTORY_DATA
Definition mhttpd.cxx:8326
bool starts_with(const std::string &s1, const char *s2)
Definition mhttpd.cxx:4438
static const char * cgif_bar_str[]
Definition mhttpd.cxx:3473
#define PARAM_LENGTH
Definition mhttpd.cxx:698
#define RESPONSE_QUEUED
Definition mhttpd.cxx:14260
static MidasHistoryInterface * get_history(bool reset=false)
Definition mhttpd.cxx:8163
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:3280
static int handle_http_get(struct mg_connection *nc, const http_message *msg, const char *uri, RequestTrace *t)
Definition mhttpd.cxx:14806
#define READ_HISTORY_RUNMARKER
Definition mhttpd.cxx:8327
std::string add_custom_path(const std::string &filename)
Definition mhttpd.cxx:3575
void sec_to_label(char *result, int sec, int base, int force_date)
Definition mhttpd.cxx:7756
void show_odb_page(Param *pp, Return *r, const char *dec_path, int write_access)
Definition mhttpd.cxx:6799
void send_icon(Return *r, const char *icon)
Definition mhttpd.cxx:12190
static BOOL elog_mode
Definition mhttpd.cxx:94
void Lock(RequestTrace *t)
Definition mhttpd.cxx:12242
void show_set_page(Param *pp, Return *r, const char *group, int index, const char *value)
Definition mhttpd.cxx:7439
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:13238
#define DELETE(x)
Definition mhttpd.cxx:8241
void show_hist_config_page(MVOdb *odb, Param *p, Return *r, const char *hgroup, const char *hpanel)
Definition mhttpd.cxx:10525
static const std::string find_cookie_mg(const struct http_message *msg, const char *cookie_name)
Definition mhttpd.cxx:14160
void do_jrpc(Param *p, Return *r)
Definition mhttpd.cxx:4325
const unsigned char favicon_ico[]
Definition mhttpd.cxx:282
void init_mhttpd_odb(MVOdb *odb)
Definition mhttpd.cxx:1933
void check_obsolete_odb(HNDLE hDB, const char *odb_path)
Definition mhttpd.cxx:1883
void do_jrpc_rev1(Param *p, Return *r)
Definition mhttpd.cxx:4144
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:11268
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:14263
void gen_odb_attachment(Return *r, const char *path, std::string &bout)
Definition mhttpd.cxx:2137
#define RESPONSE_SENT
Definition mhttpd.cxx:14259
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:1433
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:5426
static MJsonNode * set_http_trace(const MJsonNode *params)
Definition mhttpd.cxx:16202
const bool cmp_tags(const TAG &a, const TAG &b)
Definition mhttpd.cxx:10049
const bool cmp_events1(const std::string &a, const std::string &b)
Definition mhttpd.cxx:10044
#define READ_HISTORY_LAST_WRITTEN
Definition mhttpd.cxx:8328
int read_history(const HistPlot &hp, int index, int flags, time_t tstart, time_t tend, time_t scale, HistoryData *data)
Definition mhttpd.cxx:8362
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:8098
static BOOL verbose
Definition mhttpd.cxx:96
void show_help_page(Return *r, const char *dec_path)
Definition mhttpd.cxx:1542
void do_jrpc_rev0(Param *p, Return *r)
Definition mhttpd.cxx:4019
INT search_callback(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *info)
Definition mhttpd.cxx:1459
void init_menu_buttons(MVOdb *odb)
Definition mhttpd.cxx:1892
std::atomic_bool _abort
Definition mhttpd.cxx:13456
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:8610
#define ALLOC(t, n)
Definition mhttpd.cxx:8240
static const char * cgif_label_str[]
Definition mhttpd.cxx:3433
time_t mktime_with_dst(const struct tm *ptms)
Definition mhttpd.cxx:9751
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:13644
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:11031
#define STRDUP(x)
Definition mhttpd.cxx:8243
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:13792
void interprete(Param *p, Return *r, Attachment *a, const Cookies *c, const char *dec_path, RequestTrace *t)
Definition mhttpd.cxx:12256
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:15015
#define DELETEA(x, n)
Definition mhttpd.cxx:8242
static Auth * gAuthMg
Definition mhttpd.cxx:13758
static void SortHistPlotVars(HistPlot &hp)
Definition mhttpd.cxx:10504
const bool cmp_events(const std::string &a, const std::string &b)
Definition mhttpd.cxx:10039
int xdb_get_data_index(HNDLE hDB, const char *str, void *value, int size, int index, int tid)
Definition mhttpd.cxx:10068
void show_error_404(Return *r, const char *error)
Definition mhttpd.cxx:1856
static void handle_http_message(struct mg_connection *nc, http_message *msg)
Definition mhttpd.cxx:15085
static int xdb_find_key(HNDLE hDB, HNDLE dir, const char *str, HNDLE *hKey, int tid, int size)
Definition mhttpd.cxx:10084
static void AddHistPlotSelectedParam(HistPlot &hp, Param *p)
Definition mhttpd.cxx:10383
void show_custom_gif(Return *rr, const char *name)
Definition mhttpd.cxx:3677
static void LoadHistPlotFromOdb(MVOdb *odb, HistPlot *hp, const char *group, const char *panel)
Definition mhttpd.cxx:10201
static bool trace_mg_recv
Definition mhttpd.cxx:13711
static int NextHistPlotOrder(const HistPlot &hp)
Definition mhttpd.cxx:10148
#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:1606
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:13722
std::string password
Definition mhttpd.cxx:13725
std::string username
Definition mhttpd.cxx:13723
std::string realm
Definition mhttpd.cxx:13724
char bgcolor[8]
Definition mhttpd.cxx:3496
double max
Definition mhttpd.cxx:3494
char src[256]
Definition mhttpd.cxx:3491
BOOL logscale
Definition mhttpd.cxx:3493
double min
Definition mhttpd.cxx:3494
int width
Definition mhttpd.cxx:3492
char fgcolor[8]
Definition mhttpd.cxx:3495
int height
Definition mhttpd.cxx:3492
int direction
Definition mhttpd.cxx:3492
char bdcolor[8]
Definition mhttpd.cxx:3497
char format[32]
Definition mhttpd.cxx:3447
char src[256]
Definition mhttpd.cxx:3446
char bgcolor[8]
Definition mhttpd.cxx:3451
char font[32]
Definition mhttpd.cxx:3448
char fgcolor[8]
Definition mhttpd.cxx:3450
std::string cookie_pwd
Definition mhttpd.cxx:12233
int refresh
Definition mhttpd.cxx:12236
std::string cookie_wpwd
Definition mhttpd.cxx:12234
std::string cookie_cpwd
Definition mhttpd.cxx:12235
std::string timescale
Definition mhttpd.cxx:8346
bool enable_factor
Definition mhttpd.cxx:8355
bool show_fill
Definition mhttpd.cxx:8353
double minimum
Definition mhttpd.cxx:8347
double maximum
Definition mhttpd.cxx:8348
bool show_factor
Definition mhttpd.cxx:8354
bool zero_ylow
Definition mhttpd.cxx:8349
bool show_values
Definition mhttpd.cxx:8352
bool log_axis
Definition mhttpd.cxx:8350
std::vector< HistVar > vars
Definition mhttpd.cxx:8357
bool show_run_markers
Definition mhttpd.cxx:8351
double factor
Definition mhttpd.cxx:8339
std::string tag_name
Definition mhttpd.cxx:8333
double offset
Definition mhttpd.cxx:8340
int order
Definition mhttpd.cxx:8338
std::string formula
Definition mhttpd.cxx:8334
std::string label
Definition mhttpd.cxx:8336
double voffset
Definition mhttpd.cxx:8341
std::string colour
Definition mhttpd.cxx:8335
std::string event_name
Definition mhttpd.cxx:8332
bool show_raw_value
Definition mhttpd.cxx:8337
int * odb_index
Definition mhttpd.cxx:8252
time_t tstart
Definition mhttpd.cxx:8261
void Free()
Definition mhttpd.cxx:8283
int alloc_nvars
Definition mhttpd.cxx:8248
void Allocate(int xnvars)
Definition mhttpd.cxx:8265
time_t tend
Definition mhttpd.cxx:8262
double ** v
Definition mhttpd.cxx:8256
void Print() const
Definition mhttpd.cxx:8298
time_t ** t
Definition mhttpd.cxx:8255
char ** var_names
Definition mhttpd.cxx:8250
int * var_index
Definition mhttpd.cxx:8251
char ** event_names
Definition mhttpd.cxx:8249
int * status
Definition mhttpd.cxx:8253
bool have_last_written
Definition mhttpd.cxx:8258
time_t * last_written
Definition mhttpd.cxx:8259
int * num_entries
Definition mhttpd.cxx:8254
time_t scale
Definition mhttpd.cxx:8263
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:1455
const char * search_name
Definition mhttpd.cxx:1456
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