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