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