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