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