35c117d4702120bbf72ec690eccf25c6fee78857
[citadel.git] / webcit / subst.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup Subst Variable substitution type stuff
6  * \ingroup CitadelConfig
7  */
8
9 /*@{*/
10
11 #include "sysdep.h"
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15
16 #include "webcit.h"
17 #include "webserver.h"
18
19 HashList *WirelessTemplateCache;
20 HashList *WirelessLocalTemplateCache;
21 HashList *TemplateCache;
22 HashList *LocalTemplateCache;
23
24 typedef struct _TemplateToken {
25         const char *pTokenStart;
26         size_t TokenStart;
27         size_t TokenEnd;
28         const char *pTokenEnd;
29
30         const char *pName;
31         size_t NameEnd;
32
33         int HaveParameters;
34         int nParameters;
35         size_t ParamStart [10];
36         size_t ParamEnd [10];
37 } WCTemplateToken;
38
39 typedef struct _WCTemplate {
40         StrBuf *Data;
41         int nTokensUsed;
42         int TokenSpace;
43         WCTemplateToken **Tokens;
44 } WCTemplate;
45
46 HashList *GlobalNS;
47
48 typedef struct _HashHandler {
49         int foo;
50 }HashHandler;
51
52 typedef int (*HandlerFunc)(int nArgs, va_list vaarg);
53 void RegisterNS(const char *NSName, int nMinArgs, int nMaxArgs, HandlerFunc Handler)
54 {
55
56 }
57
58
59 /**
60  * \brief debugging function to print array to log
61  */
62 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
63 /**
64  * \brief debugging function to print array to log
65  */
66 void VarPrintEntry(const char *Key, void *vSubst, int odd)
67 {
68         wcsubst *ptr;
69         lprintf(1,"Subst[%s] : ", Key);
70         ptr = (wcsubst*) vSubst;
71
72         switch(ptr->wcs_type) {
73         case WCS_STRING:
74                 lprintf(1, "  -> %s\n", ptr->wcs_value);
75                 break;
76         case WCS_SERVCMD:
77                 lprintf(1, "  -> Server [%s]\n", ptr->wcs_value);
78                 break;
79         case WCS_FUNCTION:
80                 lprintf(1, "  -> function at [%0xd]\n", ptr->wcs_function);
81                 break;
82         default:
83                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", ptr->wcs_type);
84         }
85 }
86
87
88
89 /**
90  * \brief Clear out the list of substitution variables local to this session
91  */
92 void clear_substs(struct wcsession *wc) {
93
94         if (wc->vars != NULL) {
95         
96                 DeleteHash(&wc->vars);
97         }
98 }
99
100 /**
101  * \brief Clear out the list of substitution variables local to this session
102  */
103 void clear_local_substs(void) {
104         clear_substs (WC);
105 }
106
107 /**
108  * \brief destructor; kill one entry.
109  */
110 void deletevar(void *data)
111 {
112         wcsubst *ptr = (wcsubst*)data;
113 //              if ((wc->vars->wcs_type == WCS_STRING)
114 //                 || (wc->vars->wcs_type == WCS_SERVCMD)) {
115         if (ptr->wcs_type != WCS_FUNCTION)
116                 free(ptr->wcs_value);
117         free(ptr);      
118 }
119
120 /**
121  * \brief Add a substitution variable (local to this session) (strlen version...)
122  * \param keyname the replacementstring to substitute
123  * \param keytype the kind of the key
124  * \param format the format string ala printf
125  * \param ... the arguments to substitute in the formatstring
126  */
127 void SVPRINTF(char *keyname, int keytype, const char *format,...)
128 {
129         va_list arg_ptr;
130         char wbuf[SIZ];
131         void *vPtr;
132         wcsubst *ptr = NULL;
133         size_t keylen;
134         struct wcsession *WCC = WC;
135         
136         keylen = strlen(keyname);
137         /**
138          * First look if we're doing a replacement of
139          * an existing key
140          */
141         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
142         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
143                 ptr = (wcsubst*)vPtr;
144                 if (ptr->wcs_value != NULL)
145                         free(ptr->wcs_value);
146         }
147         else    /** Otherwise allocate a new one */
148         {
149                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
150                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
151                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
152         }
153
154         /** Format the string and save it */
155
156         va_start(arg_ptr, format);
157         vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
158         va_end(arg_ptr);
159
160         ptr->wcs_function = NULL;
161         ptr->wcs_type = keytype;
162         ptr->wcs_value = strdup(wbuf);
163 }
164
165 /**
166  * \brief Add a substitution variable (local to this session)
167  * \param keyname the replacementstring to substitute
168  * \param keytype the kind of the key
169  * \param format the format string ala printf
170  * \param ... the arguments to substitute in the formatstring
171  */
172 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
173 {
174         va_list arg_ptr;
175         char wbuf[SIZ];
176         void *vPtr;
177         wcsubst *ptr = NULL;
178         struct wcsession *WCC = WC;
179         size_t len;
180         
181         /**
182          * First look if we're doing a replacement of
183          * an existing key
184          */
185         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
186         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
187                 ptr = (wcsubst*)vPtr;
188                 if (ptr->wcs_value != NULL)
189                         free(ptr->wcs_value);
190         }
191         else    /** Otherwise allocate a new one */
192         {
193                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
194                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
195                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
196         }
197
198         /** Format the string and save it */
199
200         va_start(arg_ptr, format);
201         len = vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
202         va_end(arg_ptr);
203
204         ptr->wcs_value = (char*) malloc(len + 1);
205         memcpy(ptr->wcs_value, wbuf, len + 1);
206         ptr->wcs_function = NULL;
207         ptr->wcs_type = keytype;
208 }
209
210 /**
211  * \brief Add a substitution variable (local to this session)
212  * \param keyname the replacementstring to substitute
213  * \param keytype the kind of the key
214  * \param format the format string ala printf
215  * \param ... the arguments to substitute in the formatstring
216  */
217 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
218 {
219         void *vPtr;
220         wcsubst *ptr = NULL;
221         struct wcsession *WCC = WC;
222
223         
224         /**
225          * First look if we're doing a replacement of
226          * an existing key
227          */
228         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
229         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
230                 ptr = (wcsubst*)vPtr;
231                 if (ptr->wcs_value != NULL)
232                         free(ptr->wcs_value);
233         }
234         else    /** Otherwise allocate a new one */
235         {
236                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
237                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
238                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
239         }
240
241         ptr->wcs_function = NULL;
242         ptr->wcs_type = keytype;
243         ptr->wcs_value = strdup(Data);
244 }
245
246 /**
247  * \brief Add a substitution variable (local to this session) that does a callback
248  * \param keyname the keystring to substitute
249  * \param fcn_ptr the function callback to give the substitution string
250  */
251 void SVCallback(char *keyname, size_t keylen, var_callback_fptr fcn_ptr)
252 {
253         wcsubst *ptr;
254         void *vPtr;
255         struct wcsession *WCC = WC;
256
257         /**
258          * First look if we're doing a replacement of
259          * an existing key
260          */
261         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
262         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
263                 ptr = (wcsubst*)vPtr;
264                 if (ptr->wcs_value != NULL)
265                         free(ptr->wcs_value);
266         }
267         else    /** Otherwise allocate a new one */
268         {
269                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
270                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
271                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
272         }
273
274         ptr->wcs_value = NULL;
275         ptr->wcs_type = WCS_FUNCTION;
276         ptr->wcs_function = fcn_ptr;
277 }
278 inline void SVCALLBACK(char *keyname, var_callback_fptr fcn_ptr)
279 {
280         SVCallback(keyname, strlen(keyname), fcn_ptr);
281 }
282
283
284
285 /**
286  * \brief back end for print_value_of() ... does a server command
287  * \param servcmd server command to execute on the citadel server
288  */
289 void pvo_do_cmd(char *servcmd) {
290         char buf[SIZ];
291
292         serv_puts(servcmd);
293         serv_getln(buf, sizeof buf);
294
295         switch(buf[0]) {
296                 case '2':
297                 case '3':
298                 case '5':
299                         wprintf("%s\n", &buf[4]);
300                         break;
301                 case '1':
302                         fmout("CENTER");
303                         break;
304                 case '4':
305                         wprintf("%s\n", &buf[4]);
306                         serv_puts("000");
307                         break;
308         }
309 }
310
311 /**
312  * \brief Print the value of a variable
313  * \param keyname get a key to print
314  */
315 void print_value_of(const char *keyname, size_t keylen) {
316         struct wcsession *WCC = WC;
317         wcsubst *ptr;
318         void *fcn();
319         void *vVar;
320
321         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
322         if (keyname[0] == '=') {
323                 DoTemplate(keyname+1, keylen - 1);
324         }
325         /** Page-local variables */
326         if ((WCC->vars!= NULL) && GetHash(WCC->vars, keyname, keylen, &vVar)) {
327                 ptr = (wcsubst*) vVar;
328                 switch(ptr->wcs_type) {
329                 case WCS_STRING:
330                         wprintf("%s", (const char*)ptr->wcs_value);
331                         break;
332                 case WCS_SERVCMD:
333                         pvo_do_cmd(ptr->wcs_value);
334                         break;
335                 case WCS_FUNCTION:
336                         (*ptr->wcs_function) ();
337                         break;
338                 default:
339                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!", keyname);
340                 }
341         }
342         else {
343                 char ch;
344                 ch = keyname[keylen];
345                 ((char*) keyname)[keylen] = '\0'; ////TODO
346                 if (!strcasecmp(keyname, "SERV_PID")) {
347                         wprintf("%d", WCC->ctdl_pid);
348                 }
349
350                 else if (!strcasecmp(keyname, "SERV_NODENAME")) {
351                         escputs(serv_info.serv_nodename);
352                 }
353
354                 else if (!strcasecmp(keyname, "SERV_HUMANNODE")) {
355                         escputs(serv_info.serv_humannode);
356                 }
357
358                 else if (!strcasecmp(keyname, "SERV_FQDN")) {
359                         escputs(serv_info.serv_fqdn);
360                 }
361
362                 else if (!strcasecmp(keyname, "SERV_SOFTWARE")) {
363                         escputs(serv_info.serv_software);
364                 }
365
366                 else if (!strcasecmp(keyname, "SERV_REV_LEVEL")) {
367                         wprintf("%d.%02d",
368                                 serv_info.serv_rev_level / 100,
369                                 serv_info.serv_rev_level % 100
370                                 );
371                 }
372
373                 else if (!strcasecmp(keyname, "SERV_BBS_CITY")) {
374                         escputs(serv_info.serv_bbs_city);
375                 }
376
377                 else if (!strcasecmp(keyname, "CURRENT_USER")) {
378                         escputs(WCC->wc_fullname);
379                 }
380
381                 else if (!strcasecmp(keyname, "CURRENT_ROOM")) {
382                         escputs(WCC->wc_roomname);
383                 }
384                 ((char*) keyname)[keylen] = ch;//// TODO
385                 
386         }
387
388 }
389
390 extern char *static_dirs[PATH_MAX];  /**< Disk representation */
391
392
393 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
394 {
395         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
396                 if (Template->TokenSpace <= 0) {
397                         Template->Tokens = (WCTemplateToken**)malloc(
398                                 sizeof(WCTemplateToken*) * 10);
399                         Template->TokenSpace = 10;
400                 }
401                 else {
402                         WCTemplateToken **NewTokens;
403                         NewTokens= (WCTemplateToken**)malloc(
404                                 sizeof(WCTemplateToken*) * 
405                                 Template->TokenSpace * 2);
406                         memcpy(NewTokens, Template->Tokens, 
407                                sizeof(WCTemplateToken) * Template->nTokensUsed);
408                         free(Template->Tokens);
409                         Template->TokenSpace *= 2;
410                         Template->Tokens = NewTokens;
411                 }
412                 
413
414         }
415         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
416 }
417
418 WCTemplateToken *NewTemlpateSubstitute(const char *pStart, const char *pTmplStart, const char *pTmplEnd)
419 {
420         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
421
422         NewToken->pTokenStart = pTmplStart;
423         NewToken->TokenStart = pTmplStart - pStart;
424         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
425         NewToken->pTokenEnd = pTmplEnd;
426         
427         NewToken->pName = pTmplStart + 2;
428         NewToken->NameEnd = NewToken->TokenEnd - 2;
429         NewToken->HaveParameters = 0;;
430         NewToken->nParameters = 0;
431         NewToken->ParamStart[0] = 0;
432         NewToken->ParamEnd[0] = 0;
433         return NewToken;
434 }
435
436 void FreeWCTemplate(void *vFreeMe)
437 {
438         int i;
439         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
440
441         if (FreeMe->TokenSpace > 0) {
442                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
443                         free(FreeMe->Tokens[i]);
444                 }
445                 free(FreeMe->Tokens);
446         }
447         free(FreeMe);
448 }
449
450 void EvaluateToken(StrBuf *Target, WCTemplateToken *Token)
451 {
452
453
454         
455         print_value_of(Token->pName, Token->NameEnd);
456 }
457
458
459 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target)
460 {
461         int done = 0;
462         int i;
463         const char *pData, *pS;
464         long len;
465
466         pS = pData = ChrPtr(Tmpl->Data);
467         len = StrLength(Tmpl->Data);
468         i = 0;
469         while (!done) {
470                 if (i >= Tmpl->nTokensUsed) {
471                         StrBufAppendBufPlain(Target, pData, len, 0);
472                         done = 1;
473                 }
474                 else {
475                         StrBufAppendBufPlain(
476                                 Target, pData, 
477                                 Tmpl->Tokens[i]->pTokenStart - pData, 0);
478                         EvaluateToken(Target, Tmpl->Tokens[i]);
479                         pData = Tmpl->Tokens[i++]->pTokenEnd + 1;
480                 }
481         }
482
483 }
484
485
486
487 /**
488  * \brief Display a variable-substituted template
489  * \param templatename template file to load
490  */
491 void *load_template(const char *templatename, long len) {
492         HashList *pCache;
493         HashList *Static;
494         HashList *StaticLocal;
495         StrBuf *flat_filename;
496         char filename[PATH_MAX];
497         int fd;
498         struct stat statbuf;
499         const char *pS, *pE, *pch, *Err;
500         int pos;
501         struct stat mystat;
502         WCTemplate *NewTemplate;
503
504         flat_filename = NewStrBufPlain(templatename, len);
505         if (WC->is_mobile) {
506                 Static = WirelessTemplateCache;
507                 StaticLocal = WirelessLocalTemplateCache;
508                 StrBufAppendBufPlain(flat_filename, HKEY(".m.html"), 0);
509         }
510         else {
511                 Static = TemplateCache;
512                 StaticLocal = LocalTemplateCache;
513                 StrBufAppendBufPlain(flat_filename, HKEY(".html"), 0);
514         }
515         
516         strcpy(filename, static_dirs[1]);
517         strcat(filename, ChrPtr(flat_filename));
518         pCache = StaticLocal;
519         if (stat(filename, &mystat) == -1)
520         {
521                 pCache = Static;
522                 strcpy(filename, static_dirs[0]);
523                 strcat(filename, ChrPtr(flat_filename));
524         }
525
526         fd = open(filename, O_RDONLY);
527         if (fd <= 0) {
528                 wprintf(_("ERROR: could not open template "));
529                 wprintf("'%s' - %s<br />\n",
530                         (const char*)templatename, strerror(errno));
531                 return NULL;
532         }
533
534         if (fstat(fd, &statbuf) == -1) {
535                 wprintf(_("ERROR: could not stat template "));
536                 wprintf("'%s' - %s<br />\n",
537                         (const char*)templatename, strerror(errno));
538                 return NULL;
539         }
540
541         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
542         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
543         NewTemplate->nTokensUsed = 0;
544         NewTemplate->TokenSpace = 0;
545         NewTemplate->Tokens = NULL;
546         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
547                 close(fd);
548                 FreeWCTemplate(NewTemplate);
549                 wprintf(_("ERROR: reading template "));
550                 wprintf("'%s' - %s<br />\n",
551                         (const char*)templatename, strerror(errno));
552                 return NULL;
553         }
554         close(fd);
555
556         pS = pch = ChrPtr(NewTemplate->Data);
557         pE = pS + StrLength(NewTemplate->Data);
558         while (pch < pE) {
559                 const char *pts, *pte;
560                 int InQuotes = 0;
561                 int InDoubleQuotes = 0;
562                 pos = (-1);
563                 for (; pch < pE; pch ++) {
564                         if ((*pch=='<')&&(*(pch + 1)=='?'))
565                                 break;
566                 }
567                 if (pch >= pE)
568                         continue;
569                 pts = pch;
570                 for (; pch < pE - 1; pch ++) {
571                         if (*pch == '"')
572                                 InDoubleQuotes = ! InDoubleQuotes;
573                         else if (*pch == '\'')
574                                 InQuotes = ! InQuotes;
575                         else if ((!InQuotes  && !InDoubleQuotes) &&
576                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
577                                 pch ++;
578                                 break;
579                         }
580                 }
581                 if (pch + 1 >= pE)
582                         continue;
583                 pte = pch;
584                 PutNewToken(NewTemplate, NewTemlpateSubstitute(pS, pts, pte));
585                 pch ++;
586         }
587         Put(pCache, filename, strlen(filename), NewTemplate, FreeWCTemplate);
588         return NewTemplate;
589 }
590
591 /**
592  * \brief Display a variable-substituted template
593  * \param templatename template file to load
594  */
595 void DoTemplate(const char *templatename, long len) 
596 {
597         HashList *Static;
598         HashList *StaticLocal;
599         void *vTmpl;
600
601         if (WC->is_mobile) {
602                 Static = WirelessTemplateCache;
603                 StaticLocal = WirelessLocalTemplateCache;
604         }
605         else {
606                 Static = TemplateCache;
607                 StaticLocal = LocalTemplateCache;
608         }
609
610         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
611             !GetHash(Static, templatename, len, &vTmpl)) {
612                 vTmpl = load_template(templatename, len); 
613                 //////TODO: lock this!
614         }
615         if (vTmpl == NULL) 
616                 return;
617         ProcessTemplate(vTmpl, WC->WBuf);
618         
619 }
620
621 void 
622 InitModule_SUBST
623 (void)
624 {
625
626 }
627
628 /*@}*/