* cleanup shutdown procedure; free all structures and their properties
[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 *GetNextParameter(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 = GetNextParameter(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 FreeToken(WCTemplateToken **Token)
604 {
605         int i; 
606         if ((*Token)->HaveParameters) 
607                 for (i = 0; i < (*Token)->nParameters; i++)
608                         free((*Token)->Params[i]);
609         free(*Token);
610         *Token = NULL;
611 }
612
613
614
615 void FreeWCTemplate(void *vFreeMe)
616 {
617         int i;
618         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
619
620         if (FreeMe->TokenSpace > 0) {
621                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
622                         FreeToken(&FreeMe->Tokens[i]);
623                 }
624                 free(FreeMe->Tokens);
625         }
626         FreeStrBuf(&FreeMe->Data);
627         free(FreeMe);
628 }
629
630
631 int EvaluateConditional(WCTemplateToken *Token, void *Context, int Neg, int state)
632 {
633         void *vConditional;
634         ConditionalStruct *Cond;
635
636         if ((Token->Params[0]->len == 1) &&
637             (Token->Params[0]->Start[0] == 'X'))
638                 return (state != 0)?Token->Params[1]->lvalue:0;
639
640         if (!GetHash(Contitionals, 
641                  Token->Params[0]->Start,
642                  Token->Params[0]->len,
643                  &vConditional)) {
644                 lprintf(1, "Conditional %s Not found!\n", 
645                         Token->Params[0]->Start);
646         }
647             
648         Cond = (ConditionalStruct *) vConditional;
649
650         if (Cond == NULL) {
651                 lprintf(1, "Conditional %s Not found!\n", 
652                         Token->Params[0]->Start);
653                 return 0;
654         }
655         if (Token->nParameters < Cond->nParams) {
656                 lprintf(1, "Conditional [%s] needs %ld Params!\n", 
657                         Token->Params[0]->Start,
658                         Cond->nParams);
659                 return 0;
660         }
661         if (Cond->CondF(Token, Context) == Neg)
662                 return Token->Params[1]->lvalue;
663         return 0;
664 }
665
666 int EvaluateToken(StrBuf *Target, WCTemplateToken *Token, void *Context, int state)
667 {
668         void *vVar;
669 // much output, since pName is not terminated...
670 //      lprintf(1,"Doing token: %s\n",Token->pName);
671         if (Token->Flags == SV_GETTEXT) {
672                 TmplGettext(Target, Token->nParameters, Token);
673         }
674         else if (Token->Flags == SV_CONDITIONAL) {
675                 return EvaluateConditional(Token, Context, 1, state);
676         }
677         else if (Token->Flags == SV_NEG_CONDITIONAL) {
678                 return EvaluateConditional(Token, Context, 0, state);
679         }
680         else if (GetHash(GlobalNS, Token->pName, Token->NameEnd, &vVar)) {
681                 HashHandler *Handler;
682                 Handler = (HashHandler*) vVar;
683                 if ((Token->nParameters < Handler->nMinArgs) || 
684                     (Token->nParameters > Handler->nMaxArgs)) {
685                         lprintf(1, "Handler [%s] doesn't work with %ld params", 
686                                 Token->pName,
687                                 Token->nParameters);
688                 }
689                 else {
690                         Handler->HandlerFunc(Target, 
691                                              Token->nParameters,
692                                              Token,
693                                              Context); /*TODO: subset of that */
694                 
695                         
696                 }
697         }
698         else {
699                 print_value_of(Target, Token->pName, Token->NameEnd);
700         }
701         return 0;
702 }
703
704 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context)
705 {
706         int done = 0;
707         int i, state;
708         const char *pData, *pS;
709         long len;
710
711         pS = pData = ChrPtr(Tmpl->Data);
712         len = StrLength(Tmpl->Data);
713         i = 0;
714         state = 0;
715         while (!done) {
716                 if (i >= Tmpl->nTokensUsed) {
717                         StrBufAppendBufPlain(Target, 
718                                              pData, 
719                                              len - (pData - pS), 0);
720                         done = 1;
721                 }
722                 else {
723                         StrBufAppendBufPlain(
724                                 Target, pData, 
725                                 Tmpl->Tokens[i]->pTokenStart - pData, 0);
726                         state = EvaluateToken(Target, Tmpl->Tokens[i], Context, state);
727                         while ((state != 0) && (i+1 < Tmpl->nTokensUsed)) {
728                         /* condition told us to skip till its end condition */
729                                 i++;
730                                 if ((Tmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
731                                     (Tmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
732                                         if (state == EvaluateConditional(Tmpl->Tokens[i], 
733                                                                          Context, 
734                                                                          Tmpl->Tokens[i]->Flags,
735                                                                          state))
736                                                 state = 0;
737                                 }
738                         }
739                         pData = Tmpl->Tokens[i++]->pTokenEnd + 1;
740                         if (i > Tmpl->nTokensUsed)
741                                 done = 1;
742                 }
743         }
744 }
745
746
747
748 /**
749  * \brief Display a variable-substituted template
750  * \param templatename template file to load
751  */
752 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
753 {
754         int fd;
755         struct stat statbuf;
756         const char *pS, *pE, *pch, *Err;
757         int pos;
758         WCTemplate *NewTemplate;
759
760         fd = open(ChrPtr(filename), O_RDONLY);
761         if (fd <= 0) {
762                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
763                         ChrPtr(filename), strerror(errno));
764                 return NULL;
765         }
766
767         if (fstat(fd, &statbuf) == -1) {
768                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
769                         ChrPtr(filename), strerror(errno));
770                 return NULL;
771         }
772
773         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
774         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
775         NewTemplate->nTokensUsed = 0;
776         NewTemplate->TokenSpace = 0;
777         NewTemplate->Tokens = NULL;
778         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
779                 close(fd);
780                 FreeWCTemplate(NewTemplate);
781                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
782                         ChrPtr(filename), strerror(errno));
783                 return NULL;
784         }
785         close(fd);
786
787         pS = pch = ChrPtr(NewTemplate->Data);
788         pE = pS + StrLength(NewTemplate->Data);
789         while (pch < pE) {
790                 const char *pts, *pte;
791                 int InQuotes = 0;
792                 int InDoubleQuotes = 0;
793                 pos = (-1);
794                 for (; pch < pE; pch ++) {
795                         if ((*pch=='<')&&(*(pch + 1)=='?'))
796                                 break;
797                 }
798                 if (pch >= pE)
799                         continue;
800                 pts = pch;
801                 for (; pch < pE - 1; pch ++) {
802                         if (*pch == '"')
803                                 InDoubleQuotes = ! InDoubleQuotes;
804                         else if (*pch == '\'')
805                                 InQuotes = ! InQuotes;
806                         else if ((!InQuotes  && !InDoubleQuotes) &&
807                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
808                                 pch ++;
809                                 break;
810                         }
811                 }
812                 if (pch + 1 >= pE)
813                         continue;
814                 pte = pch;
815                 PutNewToken(NewTemplate, 
816                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte));
817                 pch ++;
818         }
819         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
820         return NewTemplate;
821 }
822
823 /**
824  * \brief Display a variable-substituted template
825  * \param templatename template file to load
826  */
827 void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Target) 
828 {
829         HashList *Static;
830         HashList *StaticLocal;
831         void *vTmpl;
832         
833         if (Target == NULL)
834                 Target = WC->WBuf;
835         if (WC->is_mobile) {
836                 Static = WirelessTemplateCache;
837                 StaticLocal = WirelessLocalTemplateCache;
838         }
839         else {
840                 Static = TemplateCache;
841                 StaticLocal = LocalTemplateCache;
842         }
843
844         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
845             !GetHash(Static, templatename, len, &vTmpl)) {
846                 printf ("didn't find %s\n", templatename);
847                 return;
848         }
849         if (vTmpl == NULL) 
850                 return;
851         ProcessTemplate(vTmpl, Target, Context);        
852 }
853
854 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
855 {
856         StrBuf *FileName;
857         StrBuf *Tag;
858         StrBuf *Dir;
859         DIR *filedir = NULL;
860         struct dirent *filedir_entry;
861         int d_namelen;
862         int d_without_ext;
863         int IsMobile;
864         
865         Dir = NewStrBuf();
866         StrBufPrintf(Dir, "%s/t", DirName);
867         filedir = opendir (ChrPtr(Dir));
868         if (filedir == NULL) {
869                 return 0;
870         }
871
872         FileName = NewStrBuf();
873         Tag = NewStrBuf();
874         while ((filedir_entry = readdir(filedir)))
875         {
876                 char *MinorPtr;
877                 char *PStart;
878 #ifdef _DIRENT_HAVE_D_NAMELEN
879                 d_namelen = filedir_entry->d_namelen;
880 #else
881                 d_namelen = strlen(filedir_entry->d_name);
882 #endif
883                 d_without_ext = d_namelen;
884                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
885                         d_without_ext --;
886                 if ((d_without_ext == 0) || (d_namelen < 3))
887                         continue;
888
889                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
890                 PStart = filedir_entry->d_name;
891                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
892                 MinorPtr = strchr(filedir_entry->d_name, '.');
893                 if (MinorPtr != NULL)
894                         *MinorPtr = '\0';
895                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
896
897
898                 printf("%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
899                 load_template(FileName, Tag, (IsMobile)?wireless:big);          
900         }
901         closedir(filedir);
902         FreeStrBuf(&FileName);
903         FreeStrBuf(&Tag);
904         FreeStrBuf(&Dir);
905         return 1;
906 }
907
908 void InitTemplateCache(void)
909 {
910         LoadTemplateDir(static_dirs[0],
911                         WirelessTemplateCache,
912                         TemplateCache);
913         LoadTemplateDir(static_dirs[1],
914                         WirelessLocalTemplateCache,
915                         LocalTemplateCache);
916 }
917
918 void tmplput_serv_ip(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
919 {
920         StrBufAppendPrintf(Target, "%d", WC->ctdl_pid);
921 }
922
923 void tmplput_serv_nodename(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
924 {
925         StrEscAppend(Target, NULL, serv_info.serv_nodename, 0, 0);
926 }
927
928 void tmplput_serv_humannode(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
929 {
930         StrEscAppend(Target, NULL, serv_info.serv_humannode, 0, 0);
931 }
932
933 void tmplput_serv_fqdn(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
934 {
935         StrEscAppend(Target, NULL, serv_info.serv_fqdn, 0, 0);
936 }
937
938 void tmmplput_serv_software(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
939 {
940         StrEscAppend(Target, NULL, serv_info.serv_software, 0, 0);
941 }
942
943 void tmplput_serv_rev_level(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
944 {
945         StrBufAppendPrintf(Target, "%d.%02d",
946                             serv_info.serv_rev_level / 100,
947                             serv_info.serv_rev_level % 100);
948 }
949
950 void tmmplput_serv_bbs_city(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
951 {
952         StrEscAppend(Target, NULL, serv_info.serv_bbs_city, 0, 0);
953 }
954
955 void tmplput_current_user(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
956 {
957         StrEscAppend(Target, NULL, WC->wc_fullname, 0, 0);
958 }
959
960 void tmplput_current_room(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
961 {
962         StrEscAppend(Target, NULL, WC->wc_roomname, 0, 0);
963 }
964
965
966 typedef struct _HashIterator {
967         HashList *StaticList;
968         RetrieveHashlistFunc GetHash;
969         HashDestructorFunc Destructor;
970         SubTemplFunc DoSubTemplate;
971 } HashIterator;
972
973 void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
974 {
975         void *vIt;
976         HashIterator *It;
977         HashList *List;
978         HashPos  *it;
979         long len; 
980         const char *Key;
981         void *vContext;
982         StrBuf *SubBuf;
983         int oddeven = 0;
984         
985         if (!GetHash(Iterators, 
986                      Tokens->Params[0]->Start,
987                      Tokens->Params[0]->len,
988                      &vIt))
989                 return;
990         It = (HashIterator*) vIt;
991         if (It->StaticList == NULL)
992                 List = It->GetHash();
993         else
994                 List = It->StaticList;
995
996         SubBuf = NewStrBuf();
997         it = GetNewHashPos();
998         while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
999                 svprintf(HKEY("ITERATE:ODDEVEN"), WCS_STRING, "%s", (oddeven)?"odd":"even");
1000                 It->DoSubTemplate(SubBuf, vContext);
1001                 DoTemplate(Tokens->Params[1]->Start,
1002                            Tokens->Params[1]->len,
1003                            vContext, SubBuf);
1004                         
1005                 StrBufAppendBuf(Target, SubBuf, 0);
1006                 FlushStrBuf(SubBuf);
1007                 oddeven = ~ oddeven;
1008         }
1009         DeleteHashPos(&it);
1010         It->Destructor(List);
1011 }
1012
1013 int ConditionalVar(WCTemplateToken *Tokens, void *Context)
1014 {
1015         void *vsubst;
1016         wcsubst *subst;
1017         
1018         if (!GetHash(WC->vars, 
1019                      Tokens->Params[2]->Start,
1020                      Tokens->Params[2]->len,
1021                      &vsubst))
1022                 return 0;
1023         subst = (wcsubst*) vsubst;
1024         switch(subst->wcs_type) {
1025         case WCS_FUNCTION:
1026                 return (subst->wcs_function!=NULL);
1027         case WCS_SERVCMD:
1028                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);////todo
1029                 return 1;
1030         case WCS_STRING:
1031         case WCS_STRBUF:
1032         case WCS_STRBUF_REF:
1033                 if (Tokens->nParameters < 4)
1034                         return 1;
1035                 return (strcmp(Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
1036         case WCS_LONG:
1037                 if (Tokens->nParameters < 4)
1038                         return (subst->lvalue != 0);
1039                 return (subst->lvalue == Tokens->Params[3]->lvalue);
1040         default:
1041                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
1042         }
1043         return 0;
1044 }
1045
1046 void RegisterITERATOR(const char *Name, long len, 
1047                       HashList *StaticList, 
1048                       RetrieveHashlistFunc GetHash, 
1049                       SubTemplFunc DoSubTempl,
1050                       HashDestructorFunc Destructor)
1051 {
1052         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
1053         It->StaticList = StaticList;
1054         It->GetHash = GetHash;
1055         It->DoSubTemplate = DoSubTempl;
1056         It->Destructor = Destructor;
1057         Put(Iterators, Name, len, It, NULL);
1058 }
1059
1060 void RegisterConditional(const char *Name, long len, 
1061                          int nParams,
1062                          WCConditionalFunc CondF)
1063 {
1064         ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
1065         Cond->nParams = nParams;
1066         Cond->CondF = CondF;
1067         Put(Contitionals, Name, len, Cond, NULL);
1068 }
1069
1070 void tmpl_do_boxed(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
1071 {
1072         if (nArgs == 2) {
1073                 StrBuf *Headline = NewStrBuf();
1074                 DoTemplate(Tokens->Params[1]->Start, 
1075                            Tokens->Params[1]->len,
1076                            Context, 
1077                            Headline);
1078                 SVPutBuf("BOXTITLE", Headline, 0);
1079         }
1080
1081         DoTemplate(HKEY("beginbox"), Context, Target);
1082         DoTemplate(Tokens->Params[0]->Start, 
1083                    Tokens->Params[0]->len,
1084                    Context, 
1085                    Target);
1086         DoTemplate(HKEY("endbox"), Context, Target);
1087 }
1088
1089 void 
1090 InitModule_SUBST
1091 (void)
1092 {
1093         RegisterNamespace("SERV_PID", 0, 0, tmplput_serv_ip);
1094         RegisterNamespace("SERV_NODENAME", 0, 0, tmplput_serv_nodename);
1095         RegisterNamespace("SERV_HUMANNODE", 0, 0, tmplput_serv_humannode);
1096         RegisterNamespace("SERV_FQDN", 0, 0, tmplput_serv_fqdn);
1097         RegisterNamespace("SERV_SOFTWARE", 0, 0, tmmplput_serv_software);
1098         RegisterNamespace("SERV_REV_LEVEL", 0, 0, tmplput_serv_rev_level);
1099         RegisterNamespace("SERV_BBS_CITY", 0, 0, tmmplput_serv_bbs_city);
1100         RegisterNamespace("CURRENT_USER", 0, 0, tmplput_current_user);
1101         RegisterNamespace("CURRENT_ROOM", 0, 0, tmplput_current_room);
1102         RegisterNamespace("ITERATE", 2, 4, tmpl_iterate_subtmpl);
1103         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed);
1104         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar);
1105 }
1106
1107 /*@}*/