* more work on sitewide config
[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 #include <dirent.h>
16 #include <errno.h>
17 #include <stdarg.h>
18 #define SHOW_ME_VAPPEND_PRINTF
19
20 #include "webcit.h"
21 #include "webserver.h"
22
23 extern char *static_dirs[PATH_MAX];  /**< Disk representation */
24
25 HashList *WirelessTemplateCache;
26 HashList *WirelessLocalTemplateCache;
27 HashList *TemplateCache;
28 HashList *LocalTemplateCache;
29
30 HashList *GlobalNS;
31 HashList *Iterators;
32 HashList *Contitionals;
33
34 int LoadTemplates = 0;
35
36 #define SV_GETTEXT 1
37 #define SV_CONDITIONAL 2
38 #define SV_NEG_CONDITIONAL 3
39 #define SV_SUBTEMPL 4
40
41 typedef struct _WCTemplate {
42         StrBuf *Data;
43         StrBuf *FileName;
44         int nTokensUsed;
45         int TokenSpace;
46         WCTemplateToken **Tokens;
47 } WCTemplate;
48
49 typedef struct _HashHandler {
50         int nMinArgs;
51         int nMaxArgs;
52         WCHandlerFunc HandlerFunc;
53 }HashHandler;
54
55 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere);
56
57 void RegisterNS(const char *NSName, long len, int nMinArgs, int nMaxArgs, WCHandlerFunc HandlerFunc)
58 {
59         HashHandler *NewHandler;
60         
61         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
62         NewHandler->nMinArgs = nMinArgs;
63         NewHandler->nMaxArgs = nMaxArgs;
64         NewHandler->HandlerFunc = HandlerFunc;  
65         Put(GlobalNS, NSName, len, NewHandler, NULL);
66 }
67
68
69 /**
70  * \brief debugging function to print array to log
71  */
72 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
73 /**
74  * \brief debugging function to print array to log
75  */
76 void VarPrintEntry(const char *Key, void *vSubst, int odd)
77 {
78         wcsubst *ptr;
79         lprintf(1,"Subst[%s] : ", Key);
80         ptr = (wcsubst*) vSubst;
81
82         switch(ptr->wcs_type) {
83         case WCS_STRING:
84                 lprintf(1, "  -> %s\n", ChrPtr(ptr->wcs_value));
85                 break;
86         case WCS_SERVCMD:
87                 lprintf(1, "  -> Server [%s]\n", ChrPtr(ptr->wcs_value));
88                 break;
89         case WCS_FUNCTION:
90                 lprintf(1, "  -> function at [%0xd]\n", ptr->wcs_function);
91                 break;
92         default:
93                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", ptr->wcs_type);
94         }
95 }
96
97
98
99 /**
100  * \brief Clear out the list of substitution variables local to this session
101  */
102 void clear_substs(struct wcsession *wc) {
103
104         if (wc->vars != NULL) {
105                 DeleteHash(&wc->vars);
106         }
107 }
108
109 /**
110  * \brief Clear out the list of substitution variables local to this session
111  */
112 void clear_local_substs(void) {
113         clear_substs (WC);
114 }
115
116 int NeedNewBuf(type)
117 {
118         switch(type) {
119         case WCS_STRING:
120         case WCS_SERVCMD:
121         case WCS_STRBUF:
122                 return 1;
123         case WCS_FUNCTION:
124         case WCS_STRBUF_REF:
125         case WCS_LONG:
126         default:
127                 return 0;
128         }
129 }
130
131 void FlushPayload(wcsubst *ptr, int reusestrbuf, int type)
132 {
133         int NeedNew = NeedNewBuf(type);
134         switch(ptr->wcs_type) {
135         case WCS_STRING:
136         case WCS_SERVCMD:
137         case WCS_STRBUF:
138                 if (reusestrbuf && NeedNew) {
139                         FlushStrBuf(ptr->wcs_value);
140                 }
141                 else {
142                         
143                         FreeStrBuf(&ptr->wcs_value);
144                         ptr->wcs_value = NULL;
145                 }
146                 break;
147         case WCS_FUNCTION:
148                 ptr->wcs_function = NULL;
149                 if (reusestrbuf && NeedNew)
150                         ptr->wcs_value = NewStrBuf();
151                 break;
152         case WCS_STRBUF_REF:
153                 ptr->wcs_value = NULL;
154                 if (reusestrbuf && NeedNew)
155                         ptr->wcs_value = NewStrBuf();
156                 break;
157         case WCS_LONG:
158                 ptr->lvalue = 0;
159                 if (reusestrbuf && NeedNew)
160                         ptr->wcs_value = NewStrBuf();
161                 break;
162         default:
163                 if (reusestrbuf && NeedNew)
164                         ptr->wcs_value = NewStrBuf();
165                 break;
166         }
167 }
168
169 /**
170  * \brief destructor; kill one entry.
171  */
172 void deletevar(void *data)
173 {
174         wcsubst *ptr = (wcsubst*)data;
175         FlushPayload(ptr, 0, ptr->wcs_type);
176         free(ptr);      
177 }
178
179
180 wcsubst *NewSubstVar(const char *keyname, int keylen, int type)
181 {
182         wcsubst* ptr;
183         struct wcsession *WCC = WC;
184
185         ptr = (wcsubst *) malloc(sizeof(wcsubst));
186         memset(ptr, 0, sizeof(wcsubst));
187
188         ptr->wcs_type = type;
189         safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
190         Put(WCC->vars, keyname, keylen, ptr,  deletevar);
191
192         switch(ptr->wcs_type) {
193         case WCS_STRING:
194         case WCS_SERVCMD:
195                 ptr->wcs_value = NewStrBuf();
196                 break;
197         case WCS_STRBUF:
198         case WCS_FUNCTION:
199         case WCS_STRBUF_REF:
200         case WCS_LONG:
201         default:
202                 break;
203         }
204         return ptr;
205 }
206
207
208 /**
209  * \brief Add a substitution variable (local to this session) (strlen version...)
210  * \param keyname the replacementstring to substitute
211  * \param keytype the kind of the key
212  * \param format the format string ala printf
213  * \param ... the arguments to substitute in the formatstring
214  */
215 void SVPRINTF(char *keyname, int keytype, const char *format,...)
216 {
217         va_list arg_ptr;
218         void *vPtr;
219         wcsubst *ptr = NULL;
220         size_t keylen;
221         struct wcsession *WCC = WC;
222         
223         keylen = strlen(keyname);
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                 FlushPayload(ptr, keytype, keytype);
232                 ptr->wcs_type = keytype;
233         }
234         else    /** Otherwise allocate a new one */
235         {
236                 ptr = NewSubstVar(keyname, keylen, keytype);
237         }
238
239         /** Format the string */
240         va_start(arg_ptr, format);
241         StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
242         va_end(arg_ptr);
243 }
244
245 /**
246  * \brief Add a substitution variable (local to this session)
247  * \param keyname the replacementstring to substitute
248  * \param keytype the kind of the key
249  * \param format the format string ala printf
250  * \param ... the arguments to substitute in the formatstring
251  */
252 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
253 {
254         va_list arg_ptr;
255         void *vPtr;
256         wcsubst *ptr = NULL;
257         struct wcsession *WCC = WC;
258                 
259         /**
260          * First look if we're doing a replacement of
261          * an existing key
262          */
263         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
264         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
265                 ptr = (wcsubst*)vPtr;
266                 FlushPayload(ptr, 1, keytype);
267                 ptr->wcs_type = keytype;
268         }
269         else    /** Otherwise allocate a new one */
270         {
271                 ptr = NewSubstVar(keyname, keylen, keytype);
272         }
273
274         /** Format the string and save it */
275         va_start(arg_ptr, format);
276         StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
277         va_end(arg_ptr);
278 }
279
280 /**
281  * \brief Add a substitution variable (local to this session)
282  * \param keyname the replacementstring to substitute
283  * \param keytype the kind of the key
284  * \param format the format string ala printf
285  * \param ... the arguments to substitute in the formatstring
286  */
287 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
288 {
289         void *vPtr;
290         wcsubst *ptr = NULL;
291         struct wcsession *WCC = WC;
292
293         
294         /**
295          * First look if we're doing a replacement of
296          * an existing key
297          */
298         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
299         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
300                 ptr = (wcsubst*)vPtr;
301                 FlushPayload(ptr, 1, keytype);
302                 ptr->wcs_type = keytype;
303         }
304         else    /** Otherwise allocate a new one */
305         {
306                 ptr = NewSubstVar(keyname, keylen, keytype);
307         }
308         StrBufAppendBufPlain(ptr->wcs_value, Data, -1, 0);
309 }
310
311 /**
312  * \brief Add a substitution variable (local to this session)
313  * \param keyname the replacementstring to substitute
314  * \param keytype the kind of the key
315  * \param format the format string ala printf
316  * \param ... the arguments to substitute in the formatstring
317  */
318 void SVPutLong(char *keyname, size_t keylen, long Data)
319 {
320         void *vPtr;
321         wcsubst *ptr = NULL;
322         struct wcsession *WCC = WC;
323
324         
325         /**
326          * First look if we're doing a replacement of
327          * an existing key
328          */
329         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
330         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
331                 ptr = (wcsubst*)vPtr;
332                 FlushPayload(ptr, 1, WCS_LONG);
333                 ptr->wcs_type = WCS_LONG;
334         }
335         else    /** Otherwise allocate a new one */
336         {
337                 ptr = NewSubstVar(keyname, keylen, WCS_LONG);
338         }
339         ptr->lvalue = Data;
340 }
341
342 /**
343  * \brief Add a substitution variable (local to this session) that does a callback
344  * \param keyname the keystring to substitute
345  * \param fcn_ptr the function callback to give the substitution string
346  */
347 void SVCallback(char *keyname, size_t keylen, var_callback_fptr fcn_ptr)
348 {
349         wcsubst *ptr;
350         void *vPtr;
351         struct wcsession *WCC = WC;
352
353         /**
354          * First look if we're doing a replacement of
355          * an existing key
356          */
357         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
358         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
359                 ptr = (wcsubst*)vPtr;
360                 FlushPayload(ptr, 1, WCS_FUNCTION);
361                 ptr->wcs_type = WCS_FUNCTION;
362         }
363         else    /** Otherwise allocate a new one */
364         {
365                 ptr = NewSubstVar(keyname, keylen, WCS_FUNCTION);
366         }
367
368         ptr->wcs_function = fcn_ptr;
369 }
370 inline void SVCALLBACK(char *keyname, var_callback_fptr fcn_ptr)
371 {
372         SVCallback(keyname, strlen(keyname), fcn_ptr);
373 }
374
375
376
377 void SVPUTBuf(const char *keyname, int keylen, StrBuf *Buf, int ref)
378 {
379         wcsubst *ptr;
380         void *vPtr;
381         struct wcsession *WCC = WC;
382
383         /**
384          * First look if we're doing a replacement of
385          * an existing key
386          */
387         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
388         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
389                 ptr = (wcsubst*)vPtr;
390                 FlushPayload(ptr, 0, (ref)?WCS_STRBUF_REF:WCS_STRBUF);
391                 ptr->wcs_type = (ref)?WCS_STRBUF_REF:WCS_STRBUF;
392         }
393         else    /** Otherwise allocate a new one */
394         {
395                 ptr = NewSubstVar(keyname, keylen, (ref)?WCS_STRBUF_REF:WCS_STRBUF);
396         }
397         ptr->wcs_value = Buf;
398 }
399
400 /**
401  * \brief back end for print_value_of() ... does a server command
402  * \param servcmd server command to execute on the citadel server
403  */
404 void pvo_do_cmd(StrBuf *Target, StrBuf *servcmd) {
405         char buf[SIZ];
406         int len;
407
408         serv_puts(ChrPtr(servcmd));
409         len = serv_getln(buf, sizeof buf);
410
411         switch(buf[0]) {
412                 case '2':
413                 case '3':
414                 case '5':
415                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
416                         break;
417                 case '1':
418                         _fmout(Target, "CENTER");
419                         break;
420                 case '4':
421                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
422                         serv_puts("000");
423                         break;
424         }
425 }
426
427 /**
428  * \brief Print the value of a variable
429  * \param keyname get a key to print
430  */
431 void print_value_of(StrBuf *Target, const char *keyname, size_t keylen) {
432         struct wcsession *WCC = WC;
433         wcsubst *ptr;
434         void *fcn();
435         void *vVar;
436
437         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
438         /// TODO: debricated!
439         if (keyname[0] == '=') {
440                 DoTemplate(keyname+1, keylen - 1, NULL, NULL);
441         }
442
443 //////TODO: if param[1] == "U" -> urlescape
444 /// X -> escputs
445         /** Page-local variables */
446         if ((WCC->vars!= NULL) && GetHash(WCC->vars, keyname, keylen, &vVar)) {
447                 ptr = (wcsubst*) vVar;
448                 switch(ptr->wcs_type) {
449                 case WCS_STRING:
450                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
451                         break;
452                 case WCS_SERVCMD:
453                         pvo_do_cmd(Target, ptr->wcs_value);
454                         break;
455                 case WCS_FUNCTION:
456                         (*ptr->wcs_function) ();
457                         break;
458                 case WCS_STRBUF:
459                 case WCS_STRBUF_REF:
460                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
461                         break;
462                 case WCS_LONG:
463                         StrBufAppendPrintf(Target, "%ld", ptr->lvalue);
464                         break;
465                 default:
466                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!", keyname);
467                 }
468         }
469 }
470
471
472 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
473 {
474         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
475                 if (Template->TokenSpace <= 0) {
476                         Template->Tokens = (WCTemplateToken**)malloc(
477                                 sizeof(WCTemplateToken*) * 10);
478                         Template->TokenSpace = 10;
479                 }
480                 else {
481                         WCTemplateToken **NewTokens;
482                         NewTokens= (WCTemplateToken**)malloc(
483                                 sizeof(WCTemplateToken*) * 
484                                 Template->TokenSpace * 2);
485                         memcpy(NewTokens, Template->Tokens, 
486                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
487                         free(Template->Tokens);
488                         Template->TokenSpace *= 2;
489                         Template->Tokens = NewTokens;
490                 }
491         }
492         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
493 }
494
495 TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe)
496 {
497         const char *pch = *pCh;
498         const char *pchs, *pche;
499         TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
500         char quote = '\0';
501         
502         /* Skip leading whitespaces */
503         while ((*pch == ' ' )||
504                (*pch == '\t')||
505                (*pch == '\r')||
506                (*pch == '\n')) pch ++;
507         if (*pch == '"')
508                 quote = '"';
509         else if (*pch == '\'')
510                 quote = '\'';
511         if (quote != '\0') {
512                 pch ++;
513                 pchs = pch;
514                 Parm->Type = TYPE_STR;
515                 while (pch <= pe &&
516                        ((*pch != quote) ||
517                         ( (pch > pchs) && (*(pch - 1) == '\\'))
518                                )) {
519                         pch ++;
520                 }
521                 pche = pch;
522                 if (*pch != quote) {
523                         lprintf(1, "Error evaluating template param [%s]\n", *pCh);
524                         pch ++;
525                         free(Parm);
526                         return NULL;
527                 }
528                 else {
529                         StrBufPeek(Buf, pch, -1, '\0');         
530                         lprintf(1, "DBG: got param [%s]\n", pchs);
531                         Parm->Start = pchs;
532                         Parm->len = pche - pchs;
533                         pch ++; /* move after trailing quote */
534                 }
535         }
536         else {
537                 Parm->Type = TYPE_LONG;
538                 pchs = pch;
539                 while ((pch <= pe) &&
540                        (isdigit(*pch) ||
541                         (*pch == '+') ||
542                         (*pch == '-')))
543                         pch ++;
544                 pch ++;
545                 if (pch - pchs > 1){
546                         StrBufPeek(Buf, pch, -1, '\0');
547                         Parm->lvalue = atol(pchs);
548                         Parm->Start = pchs;
549                         pch++;
550                 }
551                 else {
552                         Parm->lvalue = 0;
553                         lprintf(1, "Error evaluating template long param [%s]\n", *pCh);
554                         free(Parm);
555                         return NULL;
556                 }
557         }
558         while ((*pch == ' ' )||
559                (*pch == '\t')||
560                (*pch == '\r')||
561                (*pch == ',' )||
562                (*pch == '\n')) pch ++;
563
564         *pCh = pch;
565         return Parm;
566 }
567
568 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
569                                        const char *pStart, 
570                                        const char *pTmplStart, 
571                                        const char *pTmplEnd)
572 {
573         const char *pch;
574         TemplateParam *Param;
575         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
576
577         NewToken->Flags = 0;
578         NewToken->pTokenStart = pTmplStart;
579         NewToken->TokenStart = pTmplStart - pStart;
580         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
581         NewToken->pTokenEnd = pTmplEnd;
582         NewToken->NameEnd = NewToken->TokenEnd - 2;
583         
584         StrBufPeek(Buf, pTmplStart, + 1, '\0');
585         StrBufPeek(Buf, pTmplEnd, -1, '\0');
586         pch = NewToken->pName = pTmplStart + 2;
587
588         NewToken->HaveParameters = 0;;
589         NewToken->nParameters = 0;
590
591         while (pch < pTmplEnd - 1) {
592                 if (*pch == '(') {
593                         StrBufPeek(Buf, pch, -1, '\0');
594                         NewToken->NameEnd = pch - NewToken->pName;
595                         pch ++;
596                         while (pch < pTmplEnd - 1) {
597                                 Param = GetNextParameter(Buf, &pch, pTmplEnd - 1);
598                                 if (Param != NULL) {
599                                         NewToken->HaveParameters = 1;
600                                         if (NewToken->nParameters > MAXPARAM) {
601                                                 lprintf(1, "Only %ld Tokens supported!\n", MAXPARAM);
602                                                 free(Param);
603                                                 return NULL;
604                                         }
605                                         NewToken->Params[NewToken->nParameters++] = Param;
606                                 }
607                                 else break;
608                         }
609                         if((NewToken->NameEnd == 1) &&
610                            (NewToken->HaveParameters == 1))
611                            
612                         {
613                                 if ((NewToken->nParameters == 1) &&
614                                     (*(NewToken->pName) == '_'))
615                                         NewToken->Flags = SV_GETTEXT;
616                                 else if ((NewToken->nParameters == 1) &&
617                                          (*(NewToken->pName) == '='))
618                                         NewToken->Flags = SV_SUBTEMPL;
619                                 else if ((NewToken->nParameters >= 2) &&
620                                          (*(NewToken->pName) == '?'))
621                                         NewToken->Flags = SV_CONDITIONAL;
622                                 else if ((NewToken->nParameters >=2) &&
623                                          (*(NewToken->pName) == '!'))
624                                         NewToken->Flags = SV_NEG_CONDITIONAL;
625                         }
626                 }
627                 else pch ++;            
628         }
629         return NewToken;
630 }
631
632 void FreeToken(WCTemplateToken **Token)
633 {
634         int i; 
635         if ((*Token)->HaveParameters) 
636                 for (i = 0; i < (*Token)->nParameters; i++)
637                         free((*Token)->Params[i]);
638         free(*Token);
639         *Token = NULL;
640 }
641
642
643
644 void FreeWCTemplate(void *vFreeMe)
645 {
646         int i;
647         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
648
649         if (FreeMe->TokenSpace > 0) {
650                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
651                         FreeToken(&FreeMe->Tokens[i]);
652                 }
653                 free(FreeMe->Tokens);
654         }
655         FreeStrBuf(&FreeMe->FileName);
656         FreeStrBuf(&FreeMe->Data);
657         free(FreeMe);
658 }
659
660
661 int EvaluateConditional(WCTemplateToken *Token, void *Context, int Neg, int state)
662 {
663         void *vConditional;
664         ConditionalStruct *Cond;
665
666         if ((Token->Params[0]->len == 1) &&
667             (Token->Params[0]->Start[0] == 'X'))
668                 return (state != 0)?Token->Params[1]->lvalue:0;
669
670         if (!GetHash(Contitionals, 
671                  Token->Params[0]->Start,
672                  Token->Params[0]->len,
673                  &vConditional)) {
674                 lprintf(1, "Conditional %s Not found!\n", 
675                         Token->Params[0]->Start);
676         }
677             
678         Cond = (ConditionalStruct *) vConditional;
679
680         if (Cond == NULL) {
681                 lprintf(1, "Conditional %s Not found!\n", 
682                         Token->Params[0]->Start);
683                 return 0;
684         }
685         if (Token->nParameters < Cond->nParams) {
686                 lprintf(1, "Conditional [%s] needs %ld Params!\n", 
687                         Token->Params[0]->Start,
688                         Cond->nParams);
689                 return 0;
690         }
691         if (Cond->CondF(Token, Context) == Neg)
692                 return Token->Params[1]->lvalue;
693         return 0;
694 }
695
696 int EvaluateToken(StrBuf *Target, WCTemplateToken *Token, void *Context, int state)
697 {
698         void *vVar;
699 // much output, since pName is not terminated...
700 //      lprintf(1,"Doing token: %s\n",Token->pName);
701         switch (Token->Flags) {
702         case SV_GETTEXT:
703                 TmplGettext(Target, Token->nParameters, Token);
704                 break;
705         case SV_CONDITIONAL:
706                 return EvaluateConditional(Token, Context, 1, state);
707                 break;
708         case SV_NEG_CONDITIONAL:
709                 return EvaluateConditional(Token, Context, 0, state);
710                 break;
711         case SV_SUBTEMPL:
712                 if (Token->nParameters == 1)
713                         DoTemplate(Token->Params[0]->Start, Token->Params[0]->len, NULL, NULL);
714                 break;
715         default:
716                 if (GetHash(GlobalNS, Token->pName, Token->NameEnd, &vVar)) {
717                         HashHandler *Handler;
718                         Handler = (HashHandler*) vVar;
719                         if ((Token->nParameters < Handler->nMinArgs) || 
720                             (Token->nParameters > Handler->nMaxArgs)) {
721                                 lprintf(1, "Handler [%s] doesn't work with %ld params", 
722                                         Token->pName,
723                                         Token->nParameters);
724                         }
725                         else {
726                                 Handler->HandlerFunc(Target, 
727                                                      Token->nParameters,
728                                                      Token,
729                                                      Context); /*TODO: subset of that */
730                                 
731                                 
732                         }
733                 }
734                 else {
735                         print_value_of(Target, Token->pName, Token->NameEnd);
736                 }
737         }
738         return 0;
739 }
740
741 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context)
742 {
743         WCTemplate *pTmpl = Tmpl;
744         int done = 0;
745         int i, state;
746         const char *pData, *pS;
747         long len;
748
749         if (LoadTemplates != 0) {                       
750                 lprintf(1, "DBG: ----- loading:  [%s] ------ \n", ChrPtr(Tmpl->FileName));
751                 pTmpl = load_template(Tmpl->FileName, NULL, NULL);
752         }
753
754         pS = pData = ChrPtr(pTmpl->Data);
755         len = StrLength(pTmpl->Data);
756         i = 0;
757         state = 0;
758         while (!done) {
759                 if (i >= pTmpl->nTokensUsed) {
760                         StrBufAppendBufPlain(Target, 
761                                              pData, 
762                                              len - (pData - pS), 0);
763                         done = 1;
764                 }
765                 else {
766                         StrBufAppendBufPlain(
767                                 Target, pData, 
768                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
769                         state = EvaluateToken(Target, pTmpl->Tokens[i], Context, state);
770                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
771                         /* condition told us to skip till its end condition */
772                                 i++;
773                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
774                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
775                                         if (state == EvaluateConditional(pTmpl->Tokens[i], 
776                                                                          Context, 
777                                                                          pTmpl->Tokens[i]->Flags,
778                                                                          state))
779                                                 state = 0;
780                                 }
781                         }
782                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
783                         if (i > pTmpl->nTokensUsed)
784                                 done = 1;
785                 }
786         }
787         if (LoadTemplates != 0) {
788                 FreeWCTemplate(pTmpl);
789         }
790 }
791
792
793 /**
794  * \brief Display a variable-substituted template
795  * \param templatename template file to load
796  */
797 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
798 {
799         WCTemplate *NewTemplate;
800         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
801         NewTemplate->Data = NULL;
802         NewTemplate->FileName = NewStrBufDup(filename);
803         NewTemplate->nTokensUsed = 0;
804         NewTemplate->TokenSpace = 0;
805         NewTemplate->Tokens = NULL;
806
807         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
808         return NewTemplate;
809 }
810
811 /**
812  * \brief Display a variable-substituted template
813  * \param templatename template file to load
814  */
815 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
816 {
817         int fd;
818         struct stat statbuf;
819         const char *pS, *pE, *pch, *Err;
820         int pos;
821         WCTemplate *NewTemplate;
822
823         fd = open(ChrPtr(filename), O_RDONLY);
824         if (fd <= 0) {
825                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
826                         ChrPtr(filename), strerror(errno));
827                 return NULL;
828         }
829
830         if (fstat(fd, &statbuf) == -1) {
831                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
832                         ChrPtr(filename), strerror(errno));
833                 return NULL;
834         }
835
836         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
837         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
838         NewTemplate->FileName = NULL;
839         NewTemplate->nTokensUsed = 0;
840         NewTemplate->TokenSpace = 0;
841         NewTemplate->Tokens = NULL;
842         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
843                 close(fd);
844                 FreeWCTemplate(NewTemplate);
845                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
846                         ChrPtr(filename), strerror(errno));
847                 return NULL;
848         }
849         close(fd);
850
851         pS = pch = ChrPtr(NewTemplate->Data);
852         pE = pS + StrLength(NewTemplate->Data);
853         while (pch < pE) {
854                 const char *pts, *pte;
855                 int InQuotes = 0;
856                 int InDoubleQuotes = 0;
857                 pos = (-1);
858                 for (; pch < pE; pch ++) {
859                         if ((*pch=='<')&&(*(pch + 1)=='?'))
860                                 break;
861                 }
862                 if (pch >= pE)
863                         continue;
864                 pts = pch;
865                 for (; pch < pE - 1; pch ++) {
866                         if (*pch == '"')
867                                 InDoubleQuotes = ! InDoubleQuotes;
868                         else if (*pch == '\'')
869                                 InQuotes = ! InQuotes;
870                         else if ((!InQuotes  && !InDoubleQuotes) &&
871                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
872                                 pch ++;
873                                 break;
874                         }
875                 }
876                 if (pch + 1 >= pE)
877                         continue;
878                 pte = pch;
879                 PutNewToken(NewTemplate, 
880                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte));
881                 pch ++;
882         }
883         if (LoadTemplates == 0)
884                 Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
885         return NewTemplate;
886 }
887
888
889 void PrintTemplate(void *vTemplate)
890 {
891
892
893 }
894
895
896 /**
897  * \brief Display a variable-substituted template
898  * \param templatename template file to load
899  */
900 void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Target) 
901 {
902         HashList *Static;
903         HashList *StaticLocal;
904         void *vTmpl;
905         
906         if (Target == NULL)
907                 Target = WC->WBuf;
908         if (WC->is_mobile) {
909                 Static = WirelessTemplateCache;
910                 StaticLocal = WirelessLocalTemplateCache;
911         }
912         else {
913                 Static = TemplateCache;
914                 StaticLocal = LocalTemplateCache;
915         }
916
917         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
918             !GetHash(Static, templatename, len, &vTmpl)) {
919                 printf ("didn't find %s\n", templatename);
920                 //print_hash(Static);
921                 return;
922         }
923         if (vTmpl == NULL) 
924                 return;
925         ProcessTemplate(vTmpl, Target, Context);        
926 }
927
928 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
929 {
930         StrBuf *FileName;
931         StrBuf *Tag;
932         StrBuf *Dir;
933         DIR *filedir = NULL;
934         struct dirent *filedir_entry;
935         int d_namelen;
936         int d_without_ext;
937         int IsMobile;
938         
939         Dir = NewStrBuf();
940         StrBufPrintf(Dir, "%s/t", DirName);
941         filedir = opendir (ChrPtr(Dir));
942         if (filedir == NULL) {
943                 return 0;
944         }
945
946         FileName = NewStrBuf();
947         Tag = NewStrBuf();
948         while ((filedir_entry = readdir(filedir)))
949         {
950                 char *MinorPtr;
951                 char *PStart;
952 #ifdef _DIRENT_HAVE_D_NAMELEN
953                 d_namelen = filedir_entry->d_namelen;
954 #else
955                 d_namelen = strlen(filedir_entry->d_name);
956 #endif
957                 d_without_ext = d_namelen;
958                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
959                         d_without_ext --;
960                 if ((d_without_ext == 0) || (d_namelen < 3))
961                         continue;
962
963                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
964                 PStart = filedir_entry->d_name;
965                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
966                 MinorPtr = strchr(filedir_entry->d_name, '.');
967                 if (MinorPtr != NULL)
968                         *MinorPtr = '\0';
969                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
970
971
972                 printf("%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
973                 if (LoadTemplates == 0)
974                         load_template(FileName, Tag, (IsMobile)?wireless:big);
975                 else
976                         prepare_template(FileName, Tag, (IsMobile)?wireless:big);
977         }
978         closedir(filedir);
979         FreeStrBuf(&FileName);
980         FreeStrBuf(&Tag);
981         FreeStrBuf(&Dir);
982         return 1;
983 }
984
985 void InitTemplateCache(void)
986 {
987         LoadTemplateDir(static_dirs[0],
988                         WirelessTemplateCache,
989                         TemplateCache);
990         LoadTemplateDir(static_dirs[1],
991                         WirelessLocalTemplateCache,
992                         LocalTemplateCache);
993 }
994
995 void tmplput_serv_ip(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
996 {
997         StrBufAppendPrintf(Target, "%d", WC->ctdl_pid);
998 }
999
1000 void tmplput_serv_nodename(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1001 {
1002         StrEscAppend(Target, NULL, serv_info.serv_nodename, 0, 0);
1003 }
1004
1005 void tmplput_serv_humannode(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1006 {
1007         StrEscAppend(Target, NULL, serv_info.serv_humannode, 0, 0);
1008 }
1009
1010 void tmplput_serv_fqdn(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1011 {
1012         StrEscAppend(Target, NULL, serv_info.serv_fqdn, 0, 0);
1013 }
1014
1015 void tmmplput_serv_software(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1016 {
1017         StrEscAppend(Target, NULL, serv_info.serv_software, 0, 0);
1018 }
1019
1020 void tmplput_serv_rev_level(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1021 {
1022         StrBufAppendPrintf(Target, "%d.%02d",
1023                             serv_info.serv_rev_level / 100,
1024                             serv_info.serv_rev_level % 100);
1025 }
1026
1027 void tmmplput_serv_bbs_city(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1028 {
1029         StrEscAppend(Target, NULL, serv_info.serv_bbs_city, 0, 0);
1030 }
1031
1032 void tmplput_current_user(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1033 {
1034         StrEscAppend(Target, NULL, WC->wc_fullname, 0, 0);
1035 }
1036
1037 void tmplput_current_room(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1038 {
1039         StrEscAppend(Target, NULL, WC->wc_roomname, 0, 0);
1040 }
1041
1042
1043 typedef struct _HashIterator {
1044         HashList *StaticList;
1045         RetrieveHashlistFunc GetHash;
1046         HashDestructorFunc Destructor;
1047         SubTemplFunc DoSubTemplate;
1048 } HashIterator;
1049
1050 void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1051 {
1052         void *vIt;
1053         HashIterator *It;
1054         HashList *List;
1055         HashPos  *it;
1056         long len; 
1057         const char *Key;
1058         void *vContext;
1059         StrBuf *SubBuf;
1060         int oddeven = 0;
1061         
1062         if (!GetHash(Iterators, 
1063                      Tokens->Params[0]->Start,
1064                      Tokens->Params[0]->len,
1065                      &vIt))
1066                 return;
1067         It = (HashIterator*) vIt;
1068         if (It->StaticList == NULL)
1069                 List = It->GetHash();
1070         else
1071                 List = It->StaticList;
1072
1073         SubBuf = NewStrBuf();
1074         it = GetNewHashPos();
1075         while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
1076                 svprintf(HKEY("ITERATE:ODDEVEN"), WCS_STRING, "%s", (oddeven)?"odd":"even");
1077                 It->DoSubTemplate(SubBuf, vContext);
1078                 DoTemplate(Tokens->Params[1]->Start,
1079                            Tokens->Params[1]->len,
1080                            vContext, SubBuf);
1081                         
1082                 StrBufAppendBuf(Target, SubBuf, 0);
1083                 FlushStrBuf(SubBuf);
1084                 oddeven = ~ oddeven;
1085         }
1086         FreeStrBuf(&SubBuf);
1087         DeleteHashPos(&it);
1088         It->Destructor(List);
1089 }
1090
1091 int ConditionalVar(WCTemplateToken *Tokens, void *Context)
1092 {
1093         void *vsubst;
1094         wcsubst *subst;
1095         
1096         if (!GetHash(WC->vars, 
1097                      Tokens->Params[2]->Start,
1098                      Tokens->Params[2]->len,
1099                      &vsubst))
1100                 return 0;
1101         subst = (wcsubst*) vsubst;
1102         switch(subst->wcs_type) {
1103         case WCS_FUNCTION:
1104                 return (subst->wcs_function!=NULL);
1105         case WCS_SERVCMD:
1106                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);////todo
1107                 return 1;
1108         case WCS_STRING:
1109         case WCS_STRBUF:
1110         case WCS_STRBUF_REF:
1111                 if (Tokens->nParameters < 4)
1112                         return 1;
1113                 return (strcmp(Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
1114         case WCS_LONG:
1115                 if (Tokens->nParameters < 4)
1116                         return (subst->lvalue != 0);
1117                 return (subst->lvalue == Tokens->Params[3]->lvalue);
1118         default:
1119                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
1120         }
1121         return 0;
1122 }
1123
1124 void RegisterITERATOR(const char *Name, long len, 
1125                       HashList *StaticList, 
1126                       RetrieveHashlistFunc GetHash, 
1127                       SubTemplFunc DoSubTempl,
1128                       HashDestructorFunc Destructor)
1129 {
1130         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
1131         It->StaticList = StaticList;
1132         It->GetHash = GetHash;
1133         It->DoSubTemplate = DoSubTempl;
1134         It->Destructor = Destructor;
1135         Put(Iterators, Name, len, It, NULL);
1136 }
1137
1138 void RegisterConditional(const char *Name, long len, 
1139                          int nParams,
1140                          WCConditionalFunc CondF)
1141 {
1142         ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
1143         Cond->nParams = nParams;
1144         Cond->CondF = CondF;
1145         Put(Contitionals, Name, len, Cond, NULL);
1146 }
1147
1148 void tmpl_do_boxed(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1149 {
1150         if (nArgs == 2) {
1151                 StrBuf *Headline = NewStrBuf();
1152                 DoTemplate(Tokens->Params[1]->Start, 
1153                            Tokens->Params[1]->len,
1154                            Context, 
1155                            Headline);
1156                 SVPutBuf("BOXTITLE", Headline, 0);
1157         }
1158
1159         DoTemplate(HKEY("beginbox"), Context, Target);
1160         DoTemplate(Tokens->Params[0]->Start, 
1161                    Tokens->Params[0]->len,
1162                    Context, 
1163                    Target);
1164         DoTemplate(HKEY("endbox"), Context, Target);
1165 }
1166
1167 void tmpl_do_tabbed(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1168 {
1169         StrBuf **TabNames;
1170         int i, ntabs;
1171
1172         ntabs = Tokens->nParameters / 2;
1173         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
1174
1175         for (i = 0; i < ntabs; i++) {
1176                 TabNames[i] = NewStrBuf();
1177                 DoTemplate(Tokens->Params[i * 2]->Start, 
1178                            Tokens->Params[0]->len,
1179                            Context,
1180                            TabNames[i]);
1181         }
1182
1183         StrTabbedDialog(Target, ntabs, TabNames);
1184         for (i = 0; i < ntabs; i++) {
1185                 StrBeginTab(Target, ntabs, i);
1186
1187                 DoTemplate(Tokens->Params[i * 2 + 1]->Start, 
1188                            Tokens->Params[i * 2 + 1]->len,
1189                            Context, 
1190                            Target);
1191                 StrEndTab(Target, ntabs, i);
1192         }
1193 }
1194
1195 void 
1196 InitModule_SUBST
1197 (void)
1198 {
1199         RegisterNamespace("SERV:PID", 0, 0, tmplput_serv_ip);
1200         RegisterNamespace("SERV:NODENAME", 0, 0, tmplput_serv_nodename);
1201         RegisterNamespace("SERV:HUMANNODE", 0, 0, tmplput_serv_humannode);
1202         RegisterNamespace("SERV:FQDN", 0, 0, tmplput_serv_fqdn);
1203         RegisterNamespace("SERV:SOFTWARE", 0, 0, tmmplput_serv_software);
1204         RegisterNamespace("SERV:REV_LEVEL", 0, 0, tmplput_serv_rev_level);
1205         RegisterNamespace("SERV:BBS_CITY", 0, 0, tmmplput_serv_bbs_city);
1206 ///     RegisterNamespace("SERV:LDAP_SUPP", 0, 0, tmmplput_serv_ldap_enabled);
1207         RegisterNamespace("CURRENT_USER", 0, 0, tmplput_current_user);
1208         RegisterNamespace("CURRENT_ROOM", 0, 0, tmplput_current_room);
1209         RegisterNamespace("ITERATE", 2, 4, tmpl_iterate_subtmpl);
1210         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed);
1211         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed);
1212         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar);
1213 }
1214
1215 /*@}*/