]> code.citadel.org Git - citadel.git/blob - webcit/subst.c
* catch empty string conditions in tokens.
[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 *Conditionals;
33 HashList *SortHash;
34
35 int DumpTemplateI18NStrings = 0;
36 int LoadTemplates = 0;
37 int dbg_bactrace_template_errors = 0;
38 WCTemplputParams NoCtx;
39 StrBuf *I18nDump = NULL;
40
41 const char EmptyStr[]="";
42
43 #define SV_GETTEXT 1
44 #define SV_CONDITIONAL 2
45 #define SV_NEG_CONDITIONAL 3
46 #define SV_CUST_STR_CONDITIONAL 4
47 #define SV_SUBTEMPL 5
48 #define SV_PREEVALUATED 6
49
50
51 /*
52  * \brief Dynamic content for variable substitution in templates
53  */
54 typedef struct _wcsubst {
55         ContextFilter Filter;
56         int wcs_type;                       /* which type of Substitution are we */
57         char wcs_key[32];                   /* copy of our hashkey for debugging */
58         StrBuf *wcs_value;                  /* if we're a string, keep it here */
59         long lvalue;                        /* type long? keep data here */
60         WCHandlerFunc wcs_function; /* funcion hook ???*/
61 } wcsubst;
62
63
64 typedef struct _WCTemplate {
65         StrBuf *Data;
66         StrBuf *FileName;
67         int nTokensUsed;
68         int TokenSpace;
69         StrBuf *MimeType;
70         WCTemplateToken **Tokens;
71 } WCTemplate;
72
73 typedef struct _HashHandler {
74         ContextFilter Filter;
75
76         WCHandlerFunc HandlerFunc;
77 }HashHandler;
78
79 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere);
80 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP);
81
82
83
84 typedef struct _SortStruct {
85         StrBuf *Name;
86         StrBuf *PrefPrepend;
87         CompareFunc Forward;
88         CompareFunc Reverse;
89         CompareFunc GroupChange;
90
91         long ContextType;
92 }SortStruct;
93
94 const char *CtxNames[]  = {
95         "Context NONE",
96         "Context SITECFG",
97         "Context SESSION",
98         "Context INETCFG",
99         "Context VNOTE",
100         "Context WHO",
101         "Context PREF",
102         "Context NODECONF",
103         "Context USERLIST",
104         "Context MAILSUM",
105         "Context MIME_ATACH",
106         "Context FILELIST",
107         "Context STRBUF",
108         "Context LONGVECTOR",
109         "Context ROOMS",
110         "Context FLOORS",
111         "Context ITERATE",
112         "Context ICAL",
113         "Context UNKNOWN"
114 };
115
116
117
118 void DestroySortStruct(void *vSort)
119 {
120         SortStruct *Sort = (SortStruct*) vSort;
121         FreeStrBuf(&Sort->Name);
122         FreeStrBuf(&Sort->PrefPrepend);
123         free (Sort);
124 }
125
126 const char *ContextName(int ContextType)
127 {
128         if (ContextType < CTX_UNKNOWN)
129                 return CtxNames[ContextType];
130         else
131                 return CtxNames[CTX_UNKNOWN];
132 }
133
134 void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplputParams *TP, const char *Format, ...)
135 {
136         wcsession *WCC;
137         StrBuf *Header;
138         StrBuf *Error;
139         StrBuf *Info;
140         va_list arg_ptr;
141         const char *Err = NULL;
142
143         Info = NewStrBuf();
144         Error = NewStrBuf();
145
146         va_start(arg_ptr, Format);
147         StrBufVAppendPrintf(Error, Format, arg_ptr);
148         va_end(arg_ptr);
149
150         switch (ErrorPos) {
151         default:
152         case ERR_NAME: /* the main token name... */ 
153                 Err = (TP->Tokens!= NULL)? TP->Tokens->pName:"";
154                 break;
155         case ERR_PARM1:
156                 Err = (TP->Tokens!= NULL)? TP->Tokens->Params[0]->Start:"";
157                 break;
158         case ERR_PARM2:
159                 Err = (TP->Tokens!= NULL)? TP->Tokens->Params[1]->Start:"";
160                 break;
161         }
162         if (TP->Tokens != NULL) 
163         {
164                 lprintf(1, "%s [%s]  (in '%s' line %ld); %s; [%s]\n", 
165                         Type, 
166                         Err, 
167                         ChrPtr(TP->Tokens->FileName),
168                         TP->Tokens->Line, 
169                         ChrPtr(Error), 
170                         ChrPtr(TP->Tokens->FlatToken));
171         }
172         else 
173         {
174                 lprintf(1, "%s: %s;\n", 
175                         Type, 
176                         ChrPtr(Error));
177         }
178 /*
179         if (Target == NULL) 
180                 return;
181 */
182         WCC = WC;
183         if (WCC == NULL)
184                 return;
185
186         Header = NewStrBuf();
187         if (TP->Tokens != NULL) 
188         {
189                 /* deprecated: 
190                 StrBufAppendPrintf(                                                          
191                         Target,                                                              
192                         "<pre>\n%s [%s] (in '%s' line %ld); %s\n[%s]\n</pre>\n",
193                         Type, 
194                         Err, 
195                         ChrPtr(TP->Tokens->FileName),
196                         TP->Tokens->Line,
197                         ChrPtr(Error),
198                         ChrPtr(TP->Tokens->FlatToken));
199                 */
200                 StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
201                              Type, 
202                              Err, 
203                              ChrPtr(Error), 
204                              ChrPtr(TP->Tokens->FlatToken));
205
206
207                 SerializeJson(Header, WildFireException(SKEY(TP->Tokens->FileName),
208                                                         TP->Tokens->Line,
209                                                         Info,
210                                                         1), 1);
211 /*
212                 SerializeJson(Header, WildFireMessage(SKEY(TP->Tokens->FileName),
213                                                       TP->Tokens->Line,
214                                                       Error,
215                                                       eERROR), 1);
216 */
217                 WildFireSerializePayload(Header, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
218         }
219         else
220         {
221                 /* deprecated.
222                 StrBufAppendPrintf(                                                          
223                         Target,                                                              
224                         "<pre>\n%s: %s\n</pre>\n",
225                         Type, 
226                         ChrPtr(Error));
227                 */
228                 StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
229                              Type, 
230                              Err, 
231                              ChrPtr(Error), 
232                              ChrPtr(TP->Tokens->FlatToken));
233                 SerializeJson(Header, WildFireException(HKEY(__FILE__), __LINE__, Info, 1), 1);
234                 WildFireSerializePayload(Header, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
235         }
236         FreeStrBuf(&Header);
237         FreeStrBuf(&Info);
238         FreeStrBuf(&Error);
239 /*
240         if (dbg_bactrace_template_errors)
241                 wc_backtrace(); 
242 */
243 }
244
245
246
247
248 void LogError (StrBuf *Target, const char *Type, const char *Format, ...)
249 {
250         wcsession *WCC;
251         StrBuf *Header;
252         StrBuf *Error;
253         StrBuf *Info;
254         va_list arg_ptr;
255
256         Info = NewStrBuf();
257         Error = NewStrBuf();
258
259         va_start(arg_ptr, Format);
260         StrBufVAppendPrintf(Error, Format, arg_ptr);
261         va_end(arg_ptr);
262
263         lprintf(1, ChrPtr(Error));
264
265         WCC = WC;
266         Header = NewStrBuf();
267
268
269         SerializeJson(Header, WildFireException(Type, strlen(Type),
270                                                 0,
271                                                 Info,
272                                                 1), 1);
273         WildFireSerializePayload(Header, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
274         
275         FreeStrBuf(&Header);
276         FreeStrBuf(&Info);
277         FreeStrBuf(&Error);
278 /*
279         if (dbg_bactrace_template_errors)
280                 wc_backtrace(); 
281 */
282 }
283
284
285 void RegisterNS(const char *NSName, 
286                 long len, 
287                 int nMinArgs, 
288                 int nMaxArgs, 
289                 WCHandlerFunc HandlerFunc, 
290                 int ContextRequired)
291 {
292         HashHandler *NewHandler;
293         
294         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
295         NewHandler->Filter.nMinArgs = nMinArgs;
296         NewHandler->Filter.nMaxArgs = nMaxArgs;
297         NewHandler->Filter.ContextType = ContextRequired;
298         NewHandler->Filter.ControlContextType = CTX_NONE;
299
300         NewHandler->HandlerFunc = HandlerFunc;  
301         Put(GlobalNS, NSName, len, NewHandler, NULL);
302 }
303
304 void RegisterControlNS(const char *NSName, 
305                        long len, 
306                        int nMinArgs, 
307                        int nMaxArgs, 
308                        WCHandlerFunc HandlerFunc, 
309                        int ControlContextRequired)
310 {
311         HashHandler *NewHandler;
312         
313         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
314         NewHandler->Filter.nMinArgs = nMinArgs;
315         NewHandler->Filter.nMaxArgs = nMaxArgs;
316         NewHandler->Filter.ContextType = CTX_NONE;
317         NewHandler->Filter.ControlContextType = ControlContextRequired;
318         NewHandler->HandlerFunc = HandlerFunc;  
319         Put(GlobalNS, NSName, len, NewHandler, NULL);
320 }
321
322
323
324 int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, const char *ErrType)
325 {
326         if ((Need->ContextType != CTX_NONE) && 
327             (Need->ContextType != TP->Filter.ContextType)) {
328                 LogTemplateError(
329                         Target, ErrType, ERR_PARM1, TP,
330                         "  WARNING: requires Context: [%s], have [%s]!", 
331                         ContextName(Need->ContextType), 
332                         ContextName(TP->Filter.ContextType));
333                 return 0;
334         }
335
336         if ((Need->ControlContextType != CTX_NONE) && 
337             (Need->ControlContextType != TP->Filter.ControlContextType)) {
338                 LogTemplateError(
339                         Target, ErrType, ERR_PARM1, TP,
340                         "  WARNING: requires Control Context: [%s], have [%s]!", 
341                         ContextName(Need->ControlContextType), 
342                         ContextName(TP->Filter.ControlContextType));
343                 return 0;
344         }
345 /*                      
346         if (TP->Tokens->nParameters < Need->nMinArgs) {
347                 LogTemplateError(Target, ErrType, ERR_NAME, TP,
348                                  "needs at least %ld params, have %ld", 
349                                  Need->nMinArgs, 
350                                  TP->Tokens->nParameters);
351                 return 0;
352
353         }
354         else if (TP->Tokens->nParameters > Need->nMaxArgs) {
355                 LogTemplateError(Target, ErrType, ERR_NAME, TP,
356                                  "just needs %ld params, you gave %ld",
357                                  Need->nMaxArgs,
358                                  TP->Tokens->nParameters); 
359                 return 0;
360
361         }
362 */
363         return 1;
364 }
365
366 void FreeToken(WCTemplateToken **Token)
367 {
368         int i; 
369         FreeStrBuf(&(*Token)->FlatToken);
370         if ((*Token)->HaveParameters) 
371                 for (i = 0; i < (*Token)->nParameters; i++)
372                         free((*Token)->Params[i]);
373         free(*Token);
374         *Token = NULL;
375 }
376
377
378
379 void FreeWCTemplate(void *vFreeMe)
380 {
381         int i;
382         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
383
384         if (FreeMe->TokenSpace > 0) {
385                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
386                         FreeToken(&FreeMe->Tokens[i]);
387                 }
388                 free(FreeMe->Tokens);
389         }
390         FreeStrBuf(&FreeMe->FileName);
391         FreeStrBuf(&FreeMe->Data);
392         FreeStrBuf(&FreeMe->MimeType);
393         free(FreeMe);
394 }
395
396
397 /**
398  * \brief debugging function to print array to log
399  */
400 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
401 /**
402  * \brief debugging function to print array to log
403  */
404 void VarPrintEntry(const char *Key, void *vSubst, int odd)
405 {
406         wcsubst *ptr;
407         lprintf(1,"Subst[%s] : ", Key);
408         ptr = (wcsubst*) vSubst;
409
410         switch(ptr->wcs_type) {
411         case WCS_STRING:
412                 lprintf(1, "  -> %s\n", ChrPtr(ptr->wcs_value));
413                 break;
414         case WCS_SERVCMD:
415                 lprintf(1, "  -> Server [%s]\n", ChrPtr(ptr->wcs_value));
416                 break;
417         case WCS_FUNCTION:
418                 lprintf(1, "  -> function at [%0xd]\n", ptr->wcs_function);
419                 break;
420         default:
421                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", ptr->wcs_type);
422         }
423 }
424
425
426
427 /**
428  * \brief Clear out the list of substitution variables local to this session
429  */
430 void clear_substs(wcsession *wc) {
431
432         if (wc->vars != NULL) {
433                 DeleteHash(&wc->vars);
434         }
435 }
436
437 /**
438  * \brief Clear out the list of substitution variables local to this session
439  */
440 void clear_local_substs(void) {
441         clear_substs (WC);
442 }
443
444 int NeedNewBuf(type)
445 {
446         switch(type) {
447         case WCS_STRING:
448         case WCS_SERVCMD:
449         case WCS_STRBUF:
450                 return 1;
451         case WCS_FUNCTION:
452         case WCS_STRBUF_REF:
453         case WCS_LONG:
454         default:
455                 return 0;
456         }
457 }
458
459 void FlushPayload(wcsubst *ptr, int reusestrbuf, int type)
460 {
461         int NeedNew = NeedNewBuf(type);
462         switch(ptr->wcs_type) {
463         case WCS_STRING:
464         case WCS_SERVCMD:
465         case WCS_STRBUF:
466                 if (reusestrbuf && NeedNew) {
467                         FlushStrBuf(ptr->wcs_value);
468                 }
469                 else {
470                         
471                         FreeStrBuf(&ptr->wcs_value);
472                         ptr->wcs_value = NULL;
473                 }
474                 break;
475         case WCS_FUNCTION:
476                 ptr->wcs_function = NULL;
477                 if (reusestrbuf && NeedNew)
478                         ptr->wcs_value = NewStrBuf();
479                 break;
480         case WCS_STRBUF_REF:
481                 ptr->wcs_value = NULL;
482                 if (reusestrbuf && NeedNew)
483                         ptr->wcs_value = NewStrBuf();
484                 break;
485         case WCS_LONG:
486                 ptr->lvalue = 0;
487                 if (reusestrbuf && NeedNew)
488                         ptr->wcs_value = NewStrBuf();
489                 break;
490         default:
491                 if (reusestrbuf && NeedNew)
492                         ptr->wcs_value = NewStrBuf();
493                 break;
494         }
495 }
496
497
498 /**
499  * \brief destructor; kill one entry.
500  */
501 void deletevar(void *data)
502 {
503         wcsubst *ptr = (wcsubst*)data;
504         FlushPayload(ptr, 0, ptr->wcs_type);
505         free(ptr);      
506 }
507
508
509 wcsubst *NewSubstVar(const char *keyname, int keylen, int type)
510 {
511         wcsubst* ptr;
512         wcsession *WCC = WC;
513
514         ptr = (wcsubst *) malloc(sizeof(wcsubst));
515         memset(ptr, 0, sizeof(wcsubst));
516
517         ptr->wcs_type = type;
518         safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
519         Put(WCC->vars, keyname, keylen, ptr,  deletevar);
520
521         switch(ptr->wcs_type) {
522         case WCS_STRING:
523         case WCS_SERVCMD:
524                 ptr->wcs_value = NewStrBuf();
525                 break;
526         case WCS_STRBUF:
527         case WCS_FUNCTION:
528         case WCS_STRBUF_REF:
529         case WCS_LONG:
530         default:
531                 break;
532         }
533         return ptr;
534 }
535
536
537 /**
538  * \brief Add a substitution variable (local to this session) (strlen version...)
539  * \param keyname the replacementstring to substitute
540  * \param keytype the kind of the key
541  * \param format the format string ala printf
542  * \param ... the arguments to substitute in the formatstring
543  */
544 void SVPRINTF(char *keyname, int keytype, const char *format,...)
545 {
546         va_list arg_ptr;
547         void *vPtr;
548         wcsubst *ptr = NULL;
549         size_t keylen;
550         wcsession *WCC = WC;
551         
552         keylen = strlen(keyname);
553         /**
554          * First look if we're doing a replacement of
555          * an existing key
556          */
557         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
558         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
559                 ptr = (wcsubst*)vPtr;
560                 FlushPayload(ptr, keytype, keytype);
561                 ptr->wcs_type = keytype;
562         }
563         else    /** Otherwise allocate a new one */
564         {
565                 ptr = NewSubstVar(keyname, keylen, keytype);
566         }
567
568         /** Format the string */
569         va_start(arg_ptr, format);
570         StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
571         va_end(arg_ptr);
572 }
573
574 /**
575  * \brief Add a substitution variable (local to this session)
576  * \param keyname the replacementstring to substitute
577  * \param keytype the kind of the key
578  * \param format the format string ala printf
579  * \param ... the arguments to substitute in the formatstring
580  */
581 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
582 {
583         va_list arg_ptr;
584         void *vPtr;
585         wcsubst *ptr = NULL;
586         wcsession *WCC = WC;
587                 
588         /**
589          * First look if we're doing a replacement of
590          * an existing key
591          */
592         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
593         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
594                 ptr = (wcsubst*)vPtr;
595                 FlushPayload(ptr, 1, keytype);
596                 ptr->wcs_type = keytype;
597         }
598         else    /** Otherwise allocate a new one */
599         {
600                 ptr = NewSubstVar(keyname, keylen, keytype);
601         }
602
603         /** Format the string and save it */
604         va_start(arg_ptr, format);
605         StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
606         va_end(arg_ptr);
607 }
608
609 /**
610  * \brief Add a substitution variable (local to this session)
611  * \param keyname the replacementstring to substitute
612  * \param keytype the kind of the key
613  * \param format the format string ala printf
614  * \param ... the arguments to substitute in the formatstring
615  */
616 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
617 {
618         void *vPtr;
619         wcsubst *ptr = NULL;
620         wcsession *WCC = WC;
621
622         
623         /**
624          * First look if we're doing a replacement of
625          * an existing key
626          */
627         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
628         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
629                 ptr = (wcsubst*)vPtr;
630                 FlushPayload(ptr, 1, keytype);
631                 ptr->wcs_type = keytype;
632         }
633         else    /** Otherwise allocate a new one */
634         {
635                 ptr = NewSubstVar(keyname, keylen, keytype);
636         }
637         StrBufAppendBufPlain(ptr->wcs_value, Data, -1, 0);
638 }
639
640 /**
641  * \brief Add a substitution variable (local to this session)
642  * \param keyname the replacementstring to substitute
643  * \param keytype the kind of the key
644  * \param format the format string ala printf
645  * \param ... the arguments to substitute in the formatstring
646  */
647 void SVPutLong(char *keyname, size_t keylen, long Data)
648 {
649         void *vPtr;
650         wcsubst *ptr = NULL;
651         wcsession *WCC = WC;
652
653         
654         /**
655          * First look if we're doing a replacement of
656          * an existing key
657          */
658         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
659         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
660                 ptr = (wcsubst*)vPtr;
661                 FlushPayload(ptr, 1, WCS_LONG);
662                 ptr->wcs_type = WCS_LONG;
663         }
664         else    /** Otherwise allocate a new one */
665         {
666                 ptr = NewSubstVar(keyname, keylen, WCS_LONG);
667         }
668         ptr->lvalue = Data;
669 }
670
671 /**
672  * \brief Add a substitution variable (local to this session) that does a callback
673  * \param keyname the keystring to substitute
674  * \param fcn_ptr the function callback to give the substitution string
675  */
676 void SVCallback(char *keyname, size_t keylen, WCHandlerFunc fcn_ptr)
677 {
678         wcsubst *ptr;
679         void *vPtr;
680         wcsession *WCC = WC;
681
682         /**
683          * First look if we're doing a replacement of
684          * an existing key
685          */
686         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
687         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
688                 ptr = (wcsubst*)vPtr;
689                 FlushPayload(ptr, 1, WCS_FUNCTION);
690                 ptr->wcs_type = WCS_FUNCTION;
691         }
692         else    /** Otherwise allocate a new one */
693         {
694                 ptr = NewSubstVar(keyname, keylen, WCS_FUNCTION);
695         }
696
697         ptr->wcs_function = fcn_ptr;
698 }
699 inline void SVCALLBACK(char *keyname, WCHandlerFunc fcn_ptr)
700 {
701         SVCallback(keyname, strlen(keyname), fcn_ptr);
702 }
703
704
705
706 void SVPUTBuf(const char *keyname, int keylen, const StrBuf *Buf, int ref)
707 {
708         wcsubst *ptr;
709         void *vPtr;
710         wcsession *WCC = WC;
711
712         /**
713          * First look if we're doing a replacement of
714          * an existing key
715          */
716         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
717         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
718                 ptr = (wcsubst*)vPtr;
719                 FlushPayload(ptr, 0, (ref)?WCS_STRBUF_REF:WCS_STRBUF);
720                 ptr->wcs_type = (ref)?WCS_STRBUF_REF:WCS_STRBUF;
721         }
722         else    /** Otherwise allocate a new one */
723         {
724                 ptr = NewSubstVar(keyname, keylen, (ref)?WCS_STRBUF_REF:WCS_STRBUF);
725         }
726         ptr->wcs_value = (StrBuf*)Buf;
727 }
728
729 /**
730  * \brief back end for print_value_of() ... does a server command
731  * \param servcmd server command to execute on the citadel server
732  */
733 void pvo_do_cmd(StrBuf *Target, StrBuf *servcmd) {
734         char buf[SIZ];
735         int len;
736
737         serv_puts(ChrPtr(servcmd));
738         len = serv_getln(buf, sizeof buf);
739
740         switch(buf[0]) {
741                 case '2':
742                 case '3':
743                 case '5':
744                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
745                         break;
746                 case '1':
747                         _fmout(Target, "CENTER");
748                         break;
749                 case '4':
750                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
751                         serv_puts("000");
752                         break;
753         }
754 }
755
756 void GetTemplateTokenString(StrBuf *Target, 
757                             WCTemplputParams *TP,
758                             int N,
759                             const char **Value, 
760                             long *len)
761 {
762         StrBuf *Buf;
763         WCTemplputParams SubTP;
764
765         if (TP->Tokens->nParameters < N) {
766                 LogTemplateError(Target, 
767                                  "TokenParameter", N, TP, 
768                                  "invalid token %d. this shouldn't have come till here.\n", N);
769                 *Value = "";
770                 *len = 0;
771                 return;
772         }
773
774         switch (TP->Tokens->Params[N]->Type) {
775
776         case TYPE_STR:
777                 *Value = TP->Tokens->Params[N]->Start;
778                 *len = TP->Tokens->Params[N]->len;
779                 break;
780         case TYPE_BSTR:
781                 if (TP->Tokens->Params[N]->len == 0) {
782                         LogTemplateError(Target, 
783                                          "TokenParameter", N, TP, 
784                                          "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
785                         *len = 0;
786                         *Value = EmptyStr;
787                         break;
788                 }
789                 Buf = (StrBuf*) SBstr(TKEY(N));
790                 *Value = ChrPtr(Buf);
791                 *len = StrLength(Buf);
792                 break;
793         case TYPE_PREFSTR:
794                 if (TP->Tokens->Params[N]->len == 0) {
795                         LogTemplateError(Target, 
796                                          "TokenParameter", N, TP, 
797                                          "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
798                         *len = 0;
799                         *Value = EmptyStr;
800                         break;
801                 }
802                 get_PREFERENCE(TKEY(N), &Buf);
803                 *Value = ChrPtr(Buf);
804                 *len = StrLength(Buf);
805                 break;
806         case TYPE_LONG:
807                 LogTemplateError(Target, 
808                                  "TokenParameter", N, TP, 
809                                  "Requesting parameter %d; of type LONG, want string.", N);
810                 break;
811         case TYPE_PREFINT:
812                 LogTemplateError(Target, 
813                                  "TokenParameter", N, TP, 
814                                  "Requesting parameter %d; of type PREFINT, want string.", N);
815                 break;
816         case TYPE_GETTEXT:
817                 *Value = _(TP->Tokens->Params[N]->Start);
818                 *len = strlen(*Value);
819                 break;
820         case TYPE_SUBTEMPLATE:
821                 if (TP->Tokens->Params[N]->len == 0) {
822                         LogTemplateError(Target, 
823                                          "TokenParameter", N, TP, 
824                                          "Requesting parameter %d; of type SUBTEMPLATE, empty lookup string not admitted.", N);
825                         *len = 0;
826                         *Value = EmptyStr;
827                         break;
828                 }
829
830                 memset(&SubTP, 0, sizeof(WCTemplputParams *));
831                 SubTP.Context = TP->Context;
832                 SubTP.Filter.ContextType = TP->Filter.ContextType;
833                 Buf = NewStrBuf();
834                 DoTemplate(TKEY(N), Buf, &SubTP);
835                 *Value = ChrPtr(Buf);
836                 *len = StrLength(Buf);
837                 /* we can't free it here, so we put it into the subst so its discarded later on. */
838                 SVPUTBuf(TKEY(N), Buf, 0);
839                 break;
840
841         default:
842                 LogTemplateError(Target, 
843                                  "TokenParameter", N, TP, 
844                                  "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
845                 break;
846         }
847 }
848
849 long GetTemplateTokenNumber(StrBuf *Target, WCTemplputParams *TP, int N, long dflt)
850 {
851         long Ret;
852         if (TP->Tokens->nParameters < N) {
853                 LogTemplateError(Target, 
854                                  "TokenParameter", N, TP, 
855                                  "invalid token %d. this shouldn't have come till here.\n", N);
856                 wc_backtrace(); 
857                 return 0;
858         }
859
860         switch (TP->Tokens->Params[N]->Type) {
861
862         case TYPE_STR:
863                 return atol(TP->Tokens->Params[N]->Start);
864                 break;
865         case TYPE_BSTR:
866                 if (TP->Tokens->Params[N]->len == 0) {
867                         LogTemplateError(Target, 
868                                          "TokenParameter", N, TP, 
869                                          "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
870                         return 0;
871                 }
872                 return  LBstr(TKEY(N));
873                 break;
874         case TYPE_PREFSTR:
875                 LogTemplateError(Target, 
876                                  "TokenParameter", N, TP, 
877                                  "requesting a prefstring in param %d want a number", N);
878                 if (TP->Tokens->Params[N]->len == 0) {
879                         LogTemplateError(Target, 
880                                          "TokenParameter", N, TP, 
881                                          "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
882                         return 0;
883                 }
884                 if (get_PREF_LONG(TKEY(N), &Ret, dflt))
885                         return Ret;
886                 return 0;               
887         case TYPE_LONG:
888                 return TP->Tokens->Params[N]->lvalue;
889         case TYPE_PREFINT:
890                 if (TP->Tokens->Params[N]->len == 0) {
891                         LogTemplateError(Target, 
892                                          "TokenParameter", N, TP, 
893                                          "Requesting parameter %d; of type PREFINT, empty lookup string not admitted.", N);
894                         return 0;
895                 }
896                 if (get_PREF_LONG(TKEY(N), &Ret, dflt))
897                         return Ret;
898                 return 0;               
899         case TYPE_GETTEXT:
900                 LogTemplateError(Target, 
901                                  "TokenParameter", N, TP, 
902                                  "requesting a I18N string in param %d; want a number", N);
903                 return 0;
904         case TYPE_SUBTEMPLATE:
905                 LogTemplateError(Target, 
906                                  "TokenParameter", N, TP, 
907                                  "requesting a subtemplate in param %d; not supported for numbers", N);
908                 return 0;
909         default:
910                 LogTemplateError(Target, 
911                                  "TokenParameter", N, TP, 
912                                  "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
913                 return 0;
914         }
915 }
916
917
918
919 /**
920  * \brief Print the value of a variable
921  * \param keyname get a key to print
922  */
923 void print_value_of(StrBuf *Target, WCTemplputParams *TP)
924  
925 {
926         wcsession *WCC = WC;
927         wcsubst *ptr;
928         void *vVar;
929
930         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
931         /* TODO: depricated! */
932         if (TP->Tokens->pName[0] == '=') {
933                 DoTemplate(TP->Tokens->pName+1, TP->Tokens->NameEnd - 1, NULL, &NoCtx);
934         }
935 /*/////TODO: if param[1] == "U" -> urlescape
936 /// X -> escputs */
937         /** Page-local variables */
938         if ((WCC->vars!= NULL) && GetHash(WCC->vars, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
939                 ptr = (wcsubst*) vVar;
940                 switch(ptr->wcs_type) {
941                 case WCS_STRING:
942                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
943                         break;
944                 case WCS_SERVCMD:
945                         pvo_do_cmd(Target, ptr->wcs_value);
946                         break;
947                 case WCS_FUNCTION:
948                         (*ptr->wcs_function) (Target, TP);
949                         break;
950                 case WCS_STRBUF:
951                 case WCS_STRBUF_REF:
952                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
953                         break;
954                 case WCS_LONG:
955                         StrBufAppendPrintf(Target, "%ld", ptr->lvalue);
956                         break;
957                 default:
958                         LogTemplateError(
959                                 Target, "Subst", ERR_NAME, TP,
960                                 "WARNING: invalid value in SV-Hash at %s!", TP->Tokens->pName);
961                 }
962         }
963         else {
964                 LogTemplateError(
965                         Target, "Token", ERR_NAME, TP,
966                         "didn't find Handler \"%s\"", TP->Tokens->pName);
967                 wc_backtrace();
968         }
969 }
970
971 int CompareSubstToToken(TemplateParam *ParamToCompare, TemplateParam *ParamToLookup)
972 {
973         wcsession *WCC = WC;
974         wcsubst *ptr;
975         void *vVar;
976
977         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
978                                           ParamToLookup->len, &vVar)) {
979                 ptr = (wcsubst*) vVar;
980                 switch(ptr->wcs_type) {
981                 case WCS_STRING:
982                 case WCS_STRBUF:
983                 case WCS_STRBUF_REF:
984                         if (ParamToCompare->Type == TYPE_STR)
985                                 return ((ParamToCompare->len == StrLength(ptr->wcs_value)) &&
986                                         (strcmp(ParamToCompare->Start, ChrPtr(ptr->wcs_value)) == 0));
987                         else
988                                 return ParamToCompare->lvalue == StrTol(ptr->wcs_value);
989                         break;
990                 case WCS_SERVCMD:
991                         return 1; 
992                         break;
993                 case WCS_FUNCTION:
994                         return 1;
995                 case WCS_LONG:
996                         if (ParamToCompare->Type == TYPE_STR)
997                                 return 0;
998                         else 
999                                 return ParamToCompare->lvalue == ptr->lvalue;
1000                         break;
1001                 default:
1002                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
1003                                 ParamToLookup->Start);
1004                 }
1005         }
1006         return 0;
1007 }
1008
1009 int CompareSubstToStrBuf(StrBuf *Compare, TemplateParam *ParamToLookup)
1010 {
1011         wcsession *WCC = WC;
1012         wcsubst *ptr;
1013         void *vVar;
1014
1015         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
1016                                           ParamToLookup->len, &vVar)) {
1017                 ptr = (wcsubst*) vVar;
1018                 switch(ptr->wcs_type) {
1019                 case WCS_STRING:
1020                 case WCS_STRBUF:
1021                 case WCS_STRBUF_REF:
1022                         return ((StrLength(Compare) == StrLength(ptr->wcs_value)) &&
1023                                 (strcmp(ChrPtr(Compare), ChrPtr(ptr->wcs_value)) == 0));
1024                 case WCS_SERVCMD:
1025                         return 1; 
1026                         break;
1027                 case WCS_FUNCTION:
1028                         return 1;
1029                 case WCS_LONG:
1030                         return StrTol(Compare) == ptr->lvalue;
1031                 default:
1032                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
1033                                 ParamToLookup->Start);
1034                 }
1035         }
1036         return 0;
1037 }
1038
1039
1040
1041 /**
1042  * \brief puts string into the template and computes which escape methon we should use
1043  * \param Source the string we should put into the template
1044  * \param FormatTypeIndex where should we look for escape types if?
1045  */
1046 void StrBufAppendTemplate(StrBuf *Target, 
1047                           WCTemplputParams *TP,
1048                           const StrBuf *Source, int FormatTypeIndex)
1049 {
1050         wcsession *WCC;
1051         char EscapeAs = ' ';
1052
1053         if ((FormatTypeIndex < TP->Tokens->nParameters) &&
1054             (TP->Tokens->Params[FormatTypeIndex]->Type == TYPE_STR) &&
1055             (TP->Tokens->Params[FormatTypeIndex]->len == 1)) {
1056                 EscapeAs = *TP->Tokens->Params[FormatTypeIndex]->Start;
1057         }
1058
1059         switch(EscapeAs)
1060         {
1061         case 'H':
1062                 WCC = WC;
1063                 StrEscAppend(Target, Source, NULL, 0, 2);
1064                 break;
1065         case 'X':
1066                 StrEscAppend(Target, Source, NULL, 0, 0);
1067                 break;
1068         case 'J':
1069                 StrECMAEscAppend(Target, Source, NULL);
1070           break;
1071         case 'U':
1072                 StrBufUrlescAppend(Target, Source, NULL);
1073                 break;
1074         default:
1075                 StrBufAppendBuf(Target, Source, 0);
1076         }
1077 }
1078
1079
1080 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
1081 {
1082         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
1083                 if (Template->TokenSpace <= 0) {
1084                         Template->Tokens = (WCTemplateToken**)malloc(
1085                                 sizeof(WCTemplateToken*) * 10);
1086                         Template->TokenSpace = 10;
1087                 }
1088                 else {
1089                         WCTemplateToken **NewTokens;
1090                         NewTokens= (WCTemplateToken**)malloc(
1091                                 sizeof(WCTemplateToken*) * 
1092                                 Template->TokenSpace * 2);
1093                         memcpy(NewTokens, Template->Tokens, 
1094                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
1095                         free(Template->Tokens);
1096                         Template->TokenSpace *= 2;
1097                         Template->Tokens = NewTokens;
1098                 }
1099         }
1100         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
1101 }
1102
1103 TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, WCTemplateToken *Tokens, WCTemplate *pTmpl)
1104 {
1105         const char *pch = *pCh;
1106         const char *pchs, *pche;
1107         TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
1108         char quote = '\0';
1109         int ParamBrace = 0;
1110
1111         Parm->Type = TYPE_STR;
1112
1113         /* Skip leading whitespaces */
1114         while ((*pch == ' ' )||
1115                (*pch == '\t')||
1116                (*pch == '\r')||
1117                (*pch == '\n')) pch ++;
1118
1119         if (*pch == ':') {
1120                 Parm->Type = TYPE_PREFSTR;
1121                 pch ++;
1122                 if (*pch == '(') {
1123                         pch ++;
1124                         ParamBrace = 1;
1125                 }
1126         }
1127         else if (*pch == ';') {
1128                 Parm->Type = TYPE_PREFINT;
1129                 pch ++;
1130                 if (*pch == '(') {
1131                         pch ++;
1132                         ParamBrace = 1;
1133                 }
1134         }
1135         else if (*pch == '_') {
1136                 Parm->Type = TYPE_GETTEXT;
1137                 pch ++;
1138                 if (*pch == '(') {
1139                         pch ++;
1140                         ParamBrace = 1;
1141                 }
1142         }
1143         else if (*pch == 'B') {
1144                 Parm->Type = TYPE_BSTR;
1145                 pch ++;
1146                 if (*pch == '(') {
1147                         pch ++;
1148                         ParamBrace = 1;
1149                 }
1150         }
1151         else if (*pch == '=') {
1152                 Parm->Type = TYPE_SUBTEMPLATE;
1153                 pch ++;
1154                 if (*pch == '(') {
1155                         pch ++;
1156                         ParamBrace = 1;
1157                 }
1158         }
1159
1160
1161         if (*pch == '"')
1162                 quote = '"';
1163         else if (*pch == '\'')
1164                 quote = '\'';
1165         if (quote != '\0') {
1166                 pch ++;
1167                 pchs = pch;
1168                 while (pch <= pe &&
1169                        ((*pch != quote) ||
1170                         ( (pch > pchs) && (*(pch - 1) == '\\'))
1171                                )) {
1172                         pch ++;
1173                 }
1174                 pche = pch;
1175                 if (*pch != quote) {
1176                         lprintf(1, "Error (in '%s' line %ld); "
1177                                 "evaluating template param [%s] in Token [%s]\n",
1178                                 ChrPtr(pTmpl->FileName),
1179                                 Tokens->Line,
1180                                 ChrPtr(Tokens->FlatToken),
1181                                 *pCh);
1182                         pch ++;
1183                         free(Parm);
1184                         return NULL;
1185                 }
1186                 else {
1187                         StrBufPeek(Buf, pch, -1, '\0');         
1188                         if (LoadTemplates > 1) {                        
1189                                 lprintf(1, "DBG: got param [%s] %ld %ld\n", 
1190                                         pchs, pche - pchs, strlen(pchs));
1191                         }
1192                         Parm->Start = pchs;
1193                         Parm->len = pche - pchs;
1194                         pch ++; /* move after trailing quote */
1195                         if (ParamBrace && (*pch == ')')) {
1196                                 pch ++;
1197                         }
1198
1199                 }
1200         }
1201         else {
1202                 Parm->Type = TYPE_LONG;
1203                 pchs = pch;
1204                 while ((pch <= pe) &&
1205                        (isdigit(*pch) ||
1206                         (*pch == '+') ||
1207                         (*pch == '-')))
1208                         pch ++;
1209                 pch ++;
1210                 if (pch - pchs > 1){
1211                         StrBufPeek(Buf, pch, -1, '\0');
1212                         Parm->lvalue = atol(pchs);
1213                         Parm->Start = pchs;
1214                         pch++;
1215                 }
1216                 else {
1217                         Parm->lvalue = 0;
1218 /* TODO whUT?
1219                         lprintf(1, "Error (in '%s' line %ld); "
1220                                 "evaluating long template param [%s] in Token [%s]\n",
1221                                 ChrPtr(pTmpl->FileName),
1222                                 Tokens->Line,
1223                                 ChrPtr(Tokens->FlatToken),
1224                                 *pCh);
1225                                 */
1226                         free(Parm);
1227                         return NULL;
1228                 }
1229         }
1230         while ((*pch == ' ' )||
1231                (*pch == '\t')||
1232                (*pch == '\r')||
1233                (*pch == ',' )||
1234                (*pch == '\n')) pch ++;
1235
1236         if (DumpTemplateI18NStrings && (Parm->Type == TYPE_GETTEXT)) {
1237                 StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
1238         }
1239         *pCh = pch;
1240         return Parm;
1241 }
1242
1243 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
1244                                        const char *pStart, 
1245                                        const char *pTmplStart, 
1246                                        const char *pTmplEnd, 
1247                                        long Line,
1248                                        WCTemplate *pTmpl)
1249 {
1250         void *vVar;
1251         const char *pch;
1252         TemplateParam *Param;
1253         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
1254         WCTemplputParams TP;
1255
1256         TP.Tokens = NewToken;
1257         NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
1258         NewToken->Flags = 0;
1259         NewToken->Line = Line + 1;
1260         NewToken->pTokenStart = pTmplStart;
1261         NewToken->TokenStart = pTmplStart - pStart;
1262         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
1263         NewToken->pTokenEnd = pTmplEnd;
1264         NewToken->NameEnd = NewToken->TokenEnd - 2;
1265         NewToken->PreEval = NULL;
1266         NewToken->FlatToken = NewStrBufPlain(pTmplStart + 2, pTmplEnd - pTmplStart - 2);
1267         
1268         StrBufPeek(Buf, pTmplStart, + 1, '\0');
1269         StrBufPeek(Buf, pTmplEnd, -1, '\0');
1270         pch = NewToken->pName = pTmplStart + 2;
1271
1272         NewToken->HaveParameters = 0;;
1273         NewToken->nParameters = 0;
1274
1275         while (pch < pTmplEnd - 1) {
1276                 if (*pch == '(') {
1277                         StrBufPeek(Buf, pch, -1, '\0');
1278                         NewToken->NameEnd = pch - NewToken->pName;
1279                         pch ++;
1280                         if (*(pTmplEnd - 1) != ')') {
1281                                 LogTemplateError(
1282                                         NULL, "Parseerror", ERR_NAME, &TP, 
1283                                         "Warning, Non welformed Token; missing right parenthesis");
1284                         }
1285                         while (pch < pTmplEnd - 1) {
1286                                 Param = GetNextParameter(Buf, &pch, pTmplEnd - 1, NewToken, pTmpl);
1287                                 if (Param != NULL) {
1288                                         NewToken->HaveParameters = 1;
1289                                         if (NewToken->nParameters > MAXPARAM) {
1290                                                 LogTemplateError(
1291                                                         NULL, "Parseerror", ERR_NAME, &TP,
1292                                                         "only [%d] Params allowed in Tokens",
1293                                                         MAXPARAM);
1294
1295                                                 free(Param);
1296                                                 FreeToken(&NewToken);
1297                                                 return NULL;
1298                                         }
1299                                         NewToken->Params[NewToken->nParameters++] = Param;
1300                                 }
1301                                 else break;
1302                         }
1303                         if((NewToken->NameEnd == 1) &&
1304                            (NewToken->HaveParameters == 1))
1305                            
1306                         {
1307                                 if (*(NewToken->pName) == '_')
1308                                         NewToken->Flags = SV_GETTEXT;
1309                                 else if (*(NewToken->pName) == '=')
1310                                         NewToken->Flags = SV_SUBTEMPL;
1311                                 else if (*(NewToken->pName) == '%')
1312                                         NewToken->Flags = SV_CUST_STR_CONDITIONAL;
1313                                 else if (*(NewToken->pName) == '?')
1314                                         NewToken->Flags = SV_CONDITIONAL;
1315                                 else if (*(NewToken->pName) == '!')
1316                                         NewToken->Flags = SV_NEG_CONDITIONAL;
1317                         }
1318                 }
1319                 else pch ++;            
1320         }
1321         
1322         switch (NewToken->Flags) {
1323         case 0:
1324                 /* If we're able to find out more about the token, do it now while its fresh. */
1325                 if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
1326                         HashHandler *Handler;
1327                         Handler = (HashHandler*) vVar;
1328                         if ((NewToken->nParameters < Handler->Filter.nMinArgs) || 
1329                             (NewToken->nParameters > Handler->Filter.nMaxArgs)) {
1330                                 LogTemplateError(
1331                                         NULL, "Token", ERR_NAME, &TP,
1332                                         "doesn't work with %d params", 
1333                                         NewToken->nParameters);
1334
1335                         }
1336                         else {
1337                                 NewToken->PreEval = Handler;
1338                                 NewToken->Flags = SV_PREEVALUATED;              
1339                         }
1340                 }
1341                 break;
1342         case SV_GETTEXT:
1343                 if (NewToken->nParameters !=1) {
1344                         LogTemplateError(                               
1345                                 NULL, "Gettext", ERR_NAME, &TP,
1346                                 "requires exactly 1 parameter, you gave %d params", 
1347                                 NewToken->nParameters);
1348                         NewToken->Flags = 0;
1349                         break;
1350                 }
1351                 if (DumpTemplateI18NStrings) {
1352                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
1353                 }
1354                 break;
1355         case SV_SUBTEMPL:
1356                 if (NewToken->nParameters != 1) {
1357                         LogTemplateError(
1358                                 NULL, "Subtemplates", ERR_NAME, &TP,
1359                                 "require exactly 1 parameter, you gave %d params", 
1360                                 NewToken->nParameters);
1361                         break;
1362                 }
1363                 break;
1364         case SV_CUST_STR_CONDITIONAL:
1365         case SV_CONDITIONAL:
1366         case SV_NEG_CONDITIONAL:
1367                 if (NewToken->nParameters <2) {
1368                         LogTemplateError(
1369                                 NULL, "Conditional", ERR_NAME, &TP,
1370                                 "require at least 2 parameters, you gave %d params", 
1371                                 NewToken->nParameters);
1372                         NewToken->Flags = 0;
1373                         break;
1374                 }
1375                 if (NewToken->Params[1]->lvalue == 0) {
1376                         LogTemplateError(
1377                                 NULL, "Conditional", ERR_NAME, &TP,
1378                                 "Conditional ID (Parameter 1) mustn't be 0!");
1379                         NewToken->Flags = 0;
1380                         break;
1381                 }
1382                 if (!GetHash(Conditionals, 
1383                              NewToken->Params[0]->Start, 
1384                              NewToken->Params[0]->len, 
1385                              &vVar) || 
1386                     (vVar == NULL)) {
1387                         if ((NewToken->Params[0]->len == 1) &&
1388                             (NewToken->Params[0]->Start[0] == 'X'))
1389                                 break;
1390                         LogTemplateError(
1391                                 NULL, "Conditional", ERR_NAME, &TP,
1392                                 "Not found!");
1393 /*
1394                         NewToken->Error = NewStrBuf();
1395                         StrBufAppendPrintf(
1396                                 NewToken->Error, 
1397                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
1398                                 NewToken->Params[0]->Start,
1399                                 ChrPtr(pTmpl->FileName),
1400                                 NewToken->Line,
1401                                 ChrPtr(NewToken->FlatToken));
1402 */
1403                 }
1404                 else {
1405                         NewToken->PreEval = vVar;
1406                 }
1407                 break;
1408         }
1409         return NewToken;
1410 }
1411
1412
1413
1414
1415
1416 /**
1417  * \brief Display a variable-substituted template
1418  * \param templatename template file to load
1419  */
1420 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1421 {
1422         WCTemplate *NewTemplate;
1423         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1424         NewTemplate->Data = NULL;
1425         NewTemplate->FileName = NewStrBufDup(filename);
1426         NewTemplate->nTokensUsed = 0;
1427         NewTemplate->TokenSpace = 0;
1428         NewTemplate->Tokens = NULL;
1429         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1430         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1431                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1432         }
1433
1434         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1435         return NewTemplate;
1436 }
1437
1438 /**
1439  * \brief Display a variable-substituted template
1440  * \param templatename template file to load
1441  */
1442 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1443 {
1444         int fd;
1445         struct stat statbuf;
1446         const char *pS, *pE, *pch, *Err;
1447         long Line;
1448         int pos;
1449         WCTemplate *NewTemplate;
1450
1451         fd = open(ChrPtr(filename), O_RDONLY);
1452         if (fd <= 0) {
1453                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
1454                         ChrPtr(filename), strerror(errno));
1455                 return NULL;
1456         }
1457
1458         if (fstat(fd, &statbuf) == -1) {
1459                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
1460                         ChrPtr(filename), strerror(errno));
1461                 return NULL;
1462         }
1463
1464         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1465         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
1466         NewTemplate->FileName = NewStrBufDup(filename);
1467         NewTemplate->nTokensUsed = 0;
1468         NewTemplate->TokenSpace = 0;
1469         NewTemplate->Tokens = NULL;
1470         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1471         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1472                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1473         }
1474
1475         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1476                 close(fd);
1477                 FreeWCTemplate(NewTemplate);
1478                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
1479                         ChrPtr(filename), strerror(errno));
1480                 return NULL;
1481         }
1482         close(fd);
1483
1484         Line = 0;
1485         StrBufShrinkToFit(NewTemplate->Data, 1);
1486         pS = pch = ChrPtr(NewTemplate->Data);
1487         pE = pS + StrLength(NewTemplate->Data);
1488         while (pch < pE) {
1489                 const char *pts, *pte;
1490                 int InQuotes = 0;
1491                 int InDoubleQuotes = 0;
1492
1493                 /** Find one <? > */
1494                 pos = (-1);
1495                 for (; pch < pE; pch ++) {
1496                         if ((*pch=='<')&&(*(pch + 1)=='?'))
1497                                 break;
1498                         if (*pch=='\n') Line ++;
1499                 }
1500                 if (pch >= pE)
1501                         continue;
1502                 pts = pch;
1503
1504                 /** Found one? parse it. */
1505                 for (; pch <= pE - 1; pch ++) {
1506                         if (*pch == '"')
1507                                 InDoubleQuotes = ! InDoubleQuotes;
1508                         else if (*pch == '\'')
1509                                 InQuotes = ! InQuotes;
1510                         else if ((!InQuotes  && !InDoubleQuotes) &&
1511                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1512                                 pch ++;
1513                                 break;
1514                         }
1515                 }
1516                 if (pch + 1 > pE)
1517                         continue;
1518                 pte = pch;
1519                 PutNewToken(NewTemplate, 
1520                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate));
1521                 pch ++;
1522         }
1523         if (LoadTemplates == 0)
1524                 Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1525         return NewTemplate;
1526 }
1527
1528
1529 const char* PrintTemplate(void *vSubst)
1530 {
1531         WCTemplate *Tmpl = vSubst;
1532
1533         return ChrPtr(Tmpl->FileName);
1534
1535 }
1536
1537 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
1538 {
1539         StrBuf *FileName;
1540         StrBuf *Tag;
1541         StrBuf *Dir;
1542         DIR *filedir = NULL;
1543         struct dirent *filedir_entry;
1544         int d_namelen;
1545         int d_without_ext;
1546         int IsMobile;
1547         
1548         Dir = NewStrBuf();
1549         StrBufPrintf(Dir, "%s/t", DirName);
1550         filedir = opendir (ChrPtr(Dir));
1551         if (filedir == NULL) {
1552                 FreeStrBuf(&Dir);
1553                 return 0;
1554         }
1555
1556         FileName = NewStrBuf();
1557         Tag = NewStrBuf();
1558         while ((filedir_entry = readdir(filedir)))
1559         {
1560                 char *MinorPtr;
1561                 char *PStart;
1562 #ifdef _DIRENT_HAVE_D_NAMELEN
1563                 d_namelen = filedir_entry->d_namelen;
1564 #else
1565                 d_namelen = strlen(filedir_entry->d_name);
1566 #endif
1567                 d_without_ext = d_namelen;
1568                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1569                         d_without_ext --;
1570                 if ((d_without_ext == 0) || (d_namelen < 3))
1571                         continue;
1572                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1573                         continue; /* Ignore backup files... */
1574
1575                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
1576                 PStart = filedir_entry->d_name;
1577                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
1578                 MinorPtr = strchr(filedir_entry->d_name, '.');
1579                 if (MinorPtr != NULL)
1580                         *MinorPtr = '\0';
1581                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
1582
1583                 if (LoadTemplates > 1)
1584                         lprintf(1, "%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
1585                 if (LoadTemplates == 0)
1586                         load_template(FileName, Tag, (IsMobile)?wireless:big);
1587                 else
1588                         prepare_template(FileName, Tag, (IsMobile)?wireless:big);
1589         }
1590         closedir(filedir);
1591         FreeStrBuf(&FileName);
1592         FreeStrBuf(&Tag);
1593         FreeStrBuf(&Dir);
1594         return 1;
1595 }
1596
1597 void InitTemplateCache(void)
1598 {
1599         LoadTemplateDir(static_dirs[0],
1600                         WirelessTemplateCache,
1601                         TemplateCache);
1602         LoadTemplateDir(static_dirs[1],
1603                         WirelessLocalTemplateCache,
1604                         LocalTemplateCache);
1605 }
1606
1607
1608
1609 /*-----------------------------------------------------------------------------
1610  *                      Filling & processing Templates
1611  */
1612 /**
1613  * \brief executes one token
1614  * \param Target buffer to append to
1615  * \param Token da to  process.
1616  * \param Template we're iterating
1617  * \param Context Contextpoointer to pass in
1618  * \param state are we in conditional state?
1619  * \param ContextType what type of information does context giv us?
1620  */
1621 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1622 {
1623         const char *AppendMe;
1624         long AppendMeLen;
1625         HashHandler *Handler;
1626         void *vVar;
1627         
1628 /* much output, since pName is not terminated...
1629         lprintf(1,"Doing token: %s\n",Token->pName);
1630 */
1631
1632         switch (TP->Tokens->Flags) {
1633         case SV_GETTEXT:
1634                 TmplGettext(Target, TP);
1635                 break;
1636         case SV_CONDITIONAL: /** Forward conditional evaluation */
1637                 return EvaluateConditional(Target, 1, state, TP);
1638                 break;
1639         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1640                 return EvaluateConditional(Target, 0, state, TP);
1641                 break;
1642         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1643                 if (TP->Tokens->nParameters >= 6) {
1644                         if (EvaluateConditional(Target, 0, state, TP)) {
1645                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1646                                 StrBufAppendBufPlain(Target, 
1647                                                      AppendMe, 
1648                                                      AppendMeLen,
1649                                                      0);
1650                         }
1651                         else{
1652                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1653                                 StrBufAppendBufPlain(Target, 
1654                                                      AppendMe, 
1655                                                      AppendMeLen,
1656                                                      0);
1657                         }
1658                 }
1659                 else  {
1660                         LogTemplateError(
1661                                 Target, "Conditional", ERR_NAME, TP,
1662                                 "needs at least 6 Params!"); 
1663                 }
1664                 break;
1665         case SV_SUBTEMPL:
1666                 if (TP->Tokens->nParameters == 1)
1667                         DoTemplate(TKEY(0), Target, TP);
1668                 break;
1669         case SV_PREEVALUATED:
1670                 Handler = (HashHandler*) TP->Tokens->PreEval;
1671                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1672                         return -1;
1673                 }
1674                 Handler->HandlerFunc(Target, TP);
1675                 break;          
1676         default:
1677                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1678                         Handler = (HashHandler*) vVar;
1679                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1680                                 return -1;
1681                         }
1682                         else {
1683                                 Handler->HandlerFunc(Target, TP);
1684                         }
1685                 }
1686                 else {
1687                         print_value_of(Target, TP);
1688                 }
1689         }
1690         return 0;
1691 }
1692
1693
1694
1695 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1696 {
1697         WCTemplate *pTmpl = Tmpl;
1698         int done = 0;
1699         int i, state;
1700         const char *pData, *pS;
1701         long len;
1702         WCTemplputParams TP;
1703
1704         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1705
1706         TP.Context = CallingTP->Context;
1707         TP.ControlContext = CallingTP->ControlContext;
1708
1709         if (LoadTemplates != 0) {                       
1710                 if (LoadTemplates > 1)
1711                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
1712                                 ChrPtr(Tmpl->FileName));
1713
1714                 pTmpl = load_template(Tmpl->FileName, NULL, NULL);
1715                 if(pTmpl == NULL) {
1716                         StrBufAppendPrintf(
1717                                 Target, 
1718                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1719                                 ChrPtr(Tmpl->FileName));
1720                         return NULL;
1721                 }
1722
1723         }
1724
1725         pS = pData = ChrPtr(pTmpl->Data);
1726         len = StrLength(pTmpl->Data);
1727         i = 0;
1728         state = 0;
1729         while (!done) {
1730                 if (i >= pTmpl->nTokensUsed) {
1731                         StrBufAppendBufPlain(Target, 
1732                                              pData, 
1733                                              len - (pData - pS), 0);
1734                         done = 1;
1735                 }
1736                 else {
1737                         StrBufAppendBufPlain(
1738                                 Target, pData, 
1739                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1740                         TP.Tokens = pTmpl->Tokens[i];
1741                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1742                         state = EvaluateToken(Target, state, &TP);
1743
1744                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1745                         /* condition told us to skip till its end condition */
1746                                 i++;
1747                                 TP.Tokens = pTmpl->Tokens[i];
1748                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1749                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1750                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1751                                         if (state == EvaluateConditional(
1752                                                     Target, 
1753                                                     pTmpl->Tokens[i]->Flags, 
1754                                                     state, 
1755                                                     &TP))
1756                                                 state = 0;
1757                                 }
1758                         }
1759                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1760                         if (i > pTmpl->nTokensUsed)
1761                                 done = 1;
1762                 }
1763         }
1764         if (LoadTemplates != 0) {
1765                 FreeWCTemplate(pTmpl);
1766         }
1767         return Tmpl->MimeType;
1768
1769 }
1770
1771 /**
1772  * \brief Display a variable-substituted template
1773  * \param templatename template file to load
1774  * \returns the mimetype of the template its doing
1775  */
1776 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1777 {
1778         WCTemplputParams LocalTP;
1779         HashList *Static;
1780         HashList *StaticLocal;
1781         void *vTmpl;
1782         
1783         if (Target == NULL)
1784                 Target = WC->WBuf;
1785         if (TP == NULL) {
1786                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1787                 TP = &LocalTP;
1788         }
1789
1790         if (WC->is_mobile) {
1791                 Static = WirelessTemplateCache;
1792                 StaticLocal = WirelessLocalTemplateCache;
1793         }
1794         else {
1795                 Static = TemplateCache;
1796                 StaticLocal = LocalTemplateCache;
1797         }
1798
1799         if (len == 0)
1800         {
1801                 lprintf (1, "Can't to load a template with empty name!\n");
1802                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1803                 return NULL;
1804         }
1805
1806         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1807             !GetHash(Static, templatename, len, &vTmpl)) {
1808                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1809                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1810                                    templatename, len, 
1811                                    (long)strlen(templatename));
1812 #if 0
1813                 dbg_PrintHash(Static, PrintTemplate, NULL);
1814                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1815 #endif
1816                 return NULL;
1817         }
1818         if (vTmpl == NULL) 
1819                 return NULL;
1820         return ProcessTemplate(vTmpl, Target, TP);
1821
1822 }
1823
1824 /*-----------------------------------------------------------------------------
1825  *                      Iterators
1826  */
1827 typedef struct _HashIterator {
1828         HashList *StaticList;
1829         int AdditionalParams;
1830         int ContextType;
1831         int XPectContextType;
1832         int Flags;
1833         RetrieveHashlistFunc GetHash;
1834         HashDestructorFunc Destructor;
1835         SubTemplFunc DoSubTemplate;
1836 } HashIterator;
1837
1838 void RegisterITERATOR(const char *Name, long len, 
1839                       int AdditionalParams, 
1840                       HashList *StaticList, 
1841                       RetrieveHashlistFunc GetHash, 
1842                       SubTemplFunc DoSubTempl,
1843                       HashDestructorFunc Destructor,
1844                       int ContextType, 
1845                       int XPectContextType, 
1846                       int Flags)
1847 {
1848         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
1849         It->StaticList = StaticList;
1850         It->AdditionalParams = AdditionalParams;
1851         It->GetHash = GetHash;
1852         It->DoSubTemplate = DoSubTempl;
1853         It->Destructor = Destructor;
1854         It->ContextType = ContextType;
1855         It->XPectContextType = XPectContextType;
1856         It->Flags = Flags;
1857         Put(Iterators, Name, len, It, NULL);
1858 }
1859
1860 typedef struct _iteratestruct {
1861         int GroupChange;
1862         int oddeven;
1863         const char *Key;
1864         long KeyLen;
1865         int n;
1866         int LastN;
1867         }IterateStruct; 
1868
1869 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1870 {
1871         void *vIt;
1872         HashIterator *It;
1873         HashList *List;
1874         HashPos  *it;
1875         SortStruct *SortBy = NULL;
1876         void *vSortBy;
1877         int DetectGroupChange = 0;
1878         int nMembersUsed;
1879         void *vContext;
1880         void *vLastContext = NULL;
1881         StrBuf *SubBuf;
1882         WCTemplputParams SubTP;
1883         IterateStruct Status;
1884
1885         long StartAt = 0;
1886         long StepWidth = 0;
1887         long StopAt = -1;
1888
1889         memset(&Status, 0, sizeof(IterateStruct));
1890         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
1891         
1892         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1893                 LogTemplateError(
1894                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1895                 return;
1896         }
1897
1898         It = (HashIterator*) vIt;
1899
1900         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1901                 LogTemplateError(                               
1902                         Target, "Iterator", ERR_PARM1, TP,
1903                         "doesn't work with %d params", 
1904                         TP->Tokens->nParameters);
1905                 return;
1906         }
1907
1908         if ((It->XPectContextType != CTX_NONE) &&
1909             (It->XPectContextType != TP->Filter.ContextType)) {
1910                 LogTemplateError(
1911                         Target, "Iterator", ERR_PARM1, TP,
1912                         "requires context of type %d, have %d", 
1913                         It->XPectContextType, 
1914                         TP->Filter.ContextType);
1915                 return ;
1916                 
1917         }
1918
1919         if (It->StaticList == NULL)
1920                 List = It->GetHash(Target, TP);
1921         else
1922                 List = It->StaticList;
1923
1924         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1925         if (DetectGroupChange) {
1926                 const StrBuf *BSort;
1927                 DetectGroupChange = 0;
1928                 if (havebstr("SortBy")) {
1929                         BSort = sbstr("SortBy");
1930                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1931                             (vSortBy != NULL)) {
1932                                 SortBy = (SortStruct*)vSortBy;
1933                                 /** Ok, its us, lets see in which direction we should sort... */
1934                                 if (havebstr("SortOrder")) {
1935                                         int SortOrder;
1936                                         SortOrder = LBSTR("SortOrder");
1937                                         if (SortOrder != 0)
1938                                                 DetectGroupChange = 1;
1939                                 }
1940                         }
1941                 }
1942         }
1943         nMembersUsed = GetCount(List);
1944         SubBuf = NewStrBuf();
1945         SubTP.Filter.ContextType = It->ContextType;
1946         SubTP.Filter.ControlContextType = CTX_ITERATE;
1947         SubTP.ControlContext = &Status;
1948         
1949         if (HAVE_PARAM(2)) {
1950                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1951         }
1952         if (HAVE_PARAM(3)) {
1953                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1954         }
1955         if (HAVE_PARAM(4)) {
1956                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1957         }
1958         it = GetNewHashPos(List, StepWidth);
1959         if (StopAt < 0) {
1960                 StopAt = GetCount(List);
1961         }
1962         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1963                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1964                         if (DetectGroupChange && Status.n > 0) {
1965                                 Status.GroupChange = (SortBy->GroupChange(vContext, vLastContext))? 1:0;
1966                         }
1967                         Status.LastN = (Status.n + 1) == nMembersUsed;
1968                         SubTP.Context = vContext;
1969                         if (It->DoSubTemplate != NULL)
1970                                 It->DoSubTemplate(SubBuf, &SubTP);
1971                         DoTemplate(TKEY(1), SubBuf, &SubTP);
1972                         
1973                         StrBufAppendBuf(Target, SubBuf, 0);
1974                         FlushStrBuf(SubBuf);
1975                         Status.oddeven = ! Status.oddeven;
1976                         vLastContext = vContext;
1977                 }
1978                 Status.n++;
1979         }
1980         FreeStrBuf(&SubBuf);
1981         DeleteHashPos(&it);
1982         if (It->Destructor != NULL)
1983                 It->Destructor(&List);
1984 }
1985
1986
1987 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
1988 {
1989         IterateStruct *Ctx = CCTX;
1990         return Ctx->GroupChange;
1991 }
1992
1993 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
1994 {
1995         IterateStruct *Ctx = CCTX;
1996         if (Ctx->oddeven)
1997                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
1998         else
1999                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2000 }
2001
2002
2003 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2004 {
2005         IterateStruct *Ctx = CCTX;
2006
2007         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2008 }
2009
2010
2011 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2012 {
2013         IterateStruct *Ctx = CCTX;
2014         StrBufAppendPrintf(Target, "%d", Ctx->n);
2015 }
2016
2017 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2018 {
2019         IterateStruct *Ctx = CCTX;
2020         return Ctx->LastN;
2021 }
2022
2023
2024
2025 /*-----------------------------------------------------------------------------
2026  *                      Conditionals
2027  */
2028 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2029 {
2030         ConditionalStruct *Cond;
2031
2032         if ((TP->Tokens->Params[0]->len == 1) &&
2033             (TP->Tokens->Params[0]->Start[0] == 'X'))
2034                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2035             
2036         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2037         if (Cond == NULL) {
2038                 LogTemplateError(
2039                         Target, "Conditional", ERR_PARM1, TP,
2040                         "unknown!");
2041                 return 1;
2042         }
2043
2044         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2045                 return 0;
2046         }
2047
2048         if (Cond->CondF(Target, TP) == Neg)
2049                 return TP->Tokens->Params[1]->lvalue;
2050         return 0;
2051 }
2052
2053 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2054 {
2055         void *vsubst;
2056         wcsubst *subst;
2057         
2058         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2059                 return 0;
2060         subst = (wcsubst*) vsubst;
2061         
2062         switch(subst->wcs_type) {
2063         case WCS_FUNCTION:
2064                 return (subst->wcs_function!=NULL);
2065         case WCS_SERVCMD:
2066                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2067                 return 1;
2068         case WCS_STRING:
2069         case WCS_STRBUF:
2070         case WCS_STRBUF_REF:
2071                 if (TP->Tokens->nParameters < 4)
2072                         return 1;
2073                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2074         case WCS_LONG:
2075                 if (TP->Tokens->nParameters < 4)
2076                         return (subst->lvalue != 0);
2077                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2078         default:
2079                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2080                 return -1;
2081         }
2082         return 0;
2083 }
2084
2085 void RegisterConditional(const char *Name, long len, 
2086                          int nParams,
2087                          WCConditionalFunc CondF, 
2088                          int ContextRequired)
2089 {
2090         ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2091         Cond->PlainName = Name;
2092         Cond->Filter.nMaxArgs = nParams;
2093         Cond->Filter.nMinArgs = nParams;
2094         Cond->CondF = CondF;
2095         Cond->Filter.ContextType = ContextRequired;
2096         Cond->Filter.ControlContextType = CTX_NONE;
2097         Put(Conditionals, Name, len, Cond, NULL);
2098 }
2099
2100 void RegisterControlConditional(const char *Name, long len, 
2101                                 int nParams,
2102                                 WCConditionalFunc CondF, 
2103                                 int ControlContextRequired)
2104 {
2105         ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2106         Cond->PlainName = Name;
2107         Cond->Filter.nMaxArgs = nParams;
2108         Cond->Filter.nMinArgs = nParams;
2109         Cond->CondF = CondF;
2110         Cond->Filter.ContextType = CTX_NONE;
2111         Cond->Filter.ControlContextType = ControlContextRequired;
2112         Put(Conditionals, Name, len, Cond, NULL);
2113 }
2114
2115 /*-----------------------------------------------------------------------------
2116  *                      Context Strings
2117  */
2118 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2119 {
2120         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2121 }
2122 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2123 {
2124         StrBuf *TokenText = (StrBuf*) CTX;
2125         const char *CompareToken;
2126         long len;
2127
2128         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2129         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2130 }
2131
2132 /*-----------------------------------------------------------------------------
2133  *                      Boxed-API
2134  */
2135
2136 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2137 {
2138         WCTemplputParams SubTP;
2139
2140         StrBuf *Headline;
2141         if (TP->Tokens->nParameters == 2) {
2142                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2143                         Headline = NewStrBuf();
2144                         DoTemplate(TKEY(1), Headline, TP);
2145                 }
2146                 else {
2147                         const char *Ch;
2148                         long len;
2149                         GetTemplateTokenString(Target, 
2150                                                TP, 
2151                                                1,
2152                                                &Ch,
2153                                                &len);
2154                         Headline = NewStrBufPlain(Ch, len);
2155                 }
2156         }
2157        memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2158         SubTP.Context = Headline;
2159         SubTP.Filter.ContextType = CTX_STRBUF;
2160         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2161         DoTemplate(TKEY(0), Target, TP);
2162         DoTemplate(HKEY("endbox"), Target, TP);
2163         FreeStrBuf(&Headline);
2164 }
2165
2166 /*-----------------------------------------------------------------------------
2167  *                      Tabbed-API
2168  */
2169
2170 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2171 {
2172         StrBuf **TabNames;
2173         int i, ntabs, nTabs;
2174
2175         nTabs = ntabs = TP->Tokens->nParameters / 2;
2176         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2177
2178         for (i = 0; i < ntabs; i++) {
2179                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2180                     (TP->Tokens->Params[i * 2]->len > 0)) {
2181                         TabNames[i] = NewStrBuf();
2182                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2183                 }
2184                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2185                         const char *Ch;
2186                         long len;
2187                         GetTemplateTokenString(Target, 
2188                                                TP, 
2189                                                i * 2,
2190                                                &Ch,
2191                                                &len);
2192                         TabNames[i] = NewStrBufPlain(Ch, -1);
2193                 }
2194                 else { 
2195                         /** A Tab without subject? we can't count that, add it as silent */
2196                         nTabs --;
2197                 }
2198         }
2199
2200         StrTabbedDialog(Target, nTabs, TabNames);
2201         for (i = 0; i < ntabs; i++) {
2202                 StrBeginTab(Target, i, nTabs);
2203                 DoTemplate(TKEY(i * 2 + 1), Target, TP);
2204                 StrEndTab(Target, i, nTabs);
2205         }
2206 }
2207
2208
2209 /*-----------------------------------------------------------------------------
2210  *                      Sorting-API
2211  */
2212
2213
2214 void RegisterSortFunc(const char *name, long len, 
2215                       const char *prepend, long preplen,
2216                       CompareFunc Forward, 
2217                       CompareFunc Reverse, 
2218                       CompareFunc GroupChange, 
2219                       long ContextType)
2220 {
2221         SortStruct *NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2222         NewSort->Name = NewStrBufPlain(name, len);
2223         if (prepend != NULL)
2224                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2225         else
2226                 NewSort->PrefPrepend = NULL;
2227         NewSort->Forward = Forward;
2228         NewSort->Reverse = Reverse;
2229         NewSort->GroupChange = GroupChange;
2230         NewSort->ContextType = ContextType;
2231         Put(SortHash, name, len, NewSort, DestroySortStruct);
2232 }
2233
2234 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2235                          const char *OtherPrefix, long OtherPrefixLen,
2236                          const char *Default, long ldefault, long DefaultDirection)
2237 {
2238         int isdefault = 0;
2239         const StrBuf *BSort = NULL;
2240         SortStruct *SortBy;
2241         void *vSortBy;
2242         long SortOrder = -1;
2243         
2244         if (havebstr("SortBy")) {
2245                 BSort = sbstr("SortBy");
2246                 if (OtherPrefix == NULL) {
2247                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2248                 }
2249                 else {
2250                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2251                 }
2252         }
2253         else { /** Try to fallback to our remembered values... */
2254                 if (OtherPrefix == NULL) {
2255                         BSort = get_room_pref("sort");
2256                 }
2257                 else {
2258                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2259                 }
2260                 if (BSort != NULL)
2261                         putbstr("SortBy", NewStrBufDup(BSort));
2262                 else {
2263                         StrBuf *Buf;
2264
2265                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2266                         putbstr("SortBy", Buf);
2267                 }
2268         }
2269
2270         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2271             (vSortBy == NULL)) {
2272                 isdefault = 1;
2273                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2274                     (vSortBy == NULL)) {
2275                         LogTemplateError(
2276                                 NULL, "Sorting", ERR_PARM1, TP,
2277                                 "Illegal default sort: [%s]", Default);
2278                         wc_backtrace();
2279                 }
2280         }
2281         SortBy = (SortStruct*)vSortBy;
2282
2283         /** Ok, its us, lets see in which direction we should sort... */
2284         if (havebstr("SortOrder")) {
2285                 SortOrder = LBSTR("SortOrder");
2286         }
2287         else { /** Try to fallback to our remembered values... */
2288                 StrBuf *Buf = NULL;
2289                 if (SortBy->PrefPrepend == NULL) {
2290                         Buf = get_room_pref("SortOrder");
2291                         SortOrder = StrTol(Buf);
2292                 }
2293                 else {
2294                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2295                 }
2296
2297                 if (Buf == NULL)
2298                         SortOrder = DefaultDirection;
2299
2300                 Buf = NewStrBufPlain(NULL, 64);
2301                 StrBufPrintf(Buf, "%ld", SortOrder);
2302                 putbstr("SortOrder", Buf);
2303         }
2304         switch (SortOrder) {
2305         default:
2306         case 0:
2307                 return NULL;
2308         case 1:
2309                 return SortBy->Forward;
2310         case 2:
2311                 return SortBy->Reverse;
2312         }
2313 }
2314
2315
2316 enum {
2317         eNO_SUCH_SORT, 
2318         eNOT_SPECIFIED,
2319         eINVALID_PARAM,
2320         eFOUND
2321 };
2322
2323 ConstStr SortIcons[] = {
2324         {HKEY("static/sort_none.gif")},
2325         {HKEY("static/up_pointer.gif")},
2326         {HKEY("static/down_pointer.gif")},
2327 };
2328
2329 ConstStr SortNextOrder[] = {
2330         {HKEY("1")},
2331         {HKEY("2")},
2332         {HKEY("0")},
2333 };
2334
2335
2336 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2337 {
2338         int bSortError = eNOT_SPECIFIED;
2339         const StrBuf *BSort;
2340         void *vSort;
2341         
2342         *SortOrder = 0;
2343         *Next = NULL;
2344         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2345             (vSort == NULL))
2346                 return eNO_SUCH_SORT;
2347         *Param = (SortStruct*) vSort;
2348         
2349
2350         if (havebstr("SortBy")) {
2351                 BSort = sbstr("SortBy");
2352                 bSortError = eINVALID_PARAM;
2353                 if ((*Param)->PrefPrepend == NULL) {
2354                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2355                 }
2356                 else {
2357                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2358                 }
2359         }
2360         else { /** Try to fallback to our remembered values... */
2361                 if ((*Param)->PrefPrepend == NULL) {
2362                         BSort = get_room_pref("sort");
2363                 }
2364                 else {
2365                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2366                 }
2367         }
2368
2369         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2370             (vSort == NULL))
2371                 return bSortError;
2372
2373         *Next = (SortStruct*) vSort;
2374
2375         /** Ok, its us, lets see in which direction we should sort... */
2376         if (havebstr("SortOrder")) {
2377                 *SortOrder = LBSTR("SortOrder");
2378         }
2379         else { /** Try to fallback to our remembered values... */
2380                 if ((*Param)->PrefPrepend == NULL) {
2381                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2382                 }
2383                 else {
2384                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2385                 }
2386         }
2387         if (*SortOrder > 2)
2388                 *SortOrder = 0;
2389
2390         return eFOUND;
2391 }
2392
2393
2394 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2395 {
2396         long SortOrder;
2397         SortStruct *Next;
2398         SortStruct *Param;
2399         const ConstStr *SortIcon;
2400
2401         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2402         case eNO_SUCH_SORT:
2403                 LogTemplateError(
2404                         Target, "Sorter", ERR_PARM1, TP,
2405                         " Sorter [%s] unknown!", 
2406                         TP->Tokens->Params[0]->Start);
2407                 break;          
2408         case eINVALID_PARAM:
2409                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2410                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2411                                  bstr("SortBy"));
2412         case eNOT_SPECIFIED:
2413         case eFOUND:
2414                 if (Next == Param) {
2415                         SortIcon = &SortIcons[SortOrder];
2416                 }
2417                 else { /** Not Us... */
2418                         SortIcon = &SortIcons[0];
2419                 }
2420                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2421         }
2422 }
2423
2424 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2425 {
2426         long SortOrder;
2427         SortStruct *Next;
2428         SortStruct *Param;
2429
2430         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2431         case eNO_SUCH_SORT:
2432                 LogTemplateError(
2433                         Target, "Sorter", ERR_PARM1, TP,                                  
2434                         " Sorter [%s] unknown!", 
2435                         TP->Tokens->Params[0]->Start);
2436                 break;          
2437         case eINVALID_PARAM:
2438                 LogTemplateError(
2439                         NULL, "Sorter", ERR_PARM1, TP,
2440                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2441                         bstr("SortBy"));
2442         case eNOT_SPECIFIED:
2443         case eFOUND:
2444                 StrBufAppendBuf(Target, Param->Name, 0);
2445                 
2446         }
2447 }
2448
2449 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2450 {
2451         long SortOrder;
2452         const ConstStr *SortOrderStr;
2453         SortStruct *Next;
2454         SortStruct *Param;
2455
2456         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2457         case eNO_SUCH_SORT:
2458                 LogTemplateError(
2459                         Target, "Sorter", ERR_PARM1, TP,
2460                         " Sorter [%s] unknown!",
2461                         TP->Tokens->Params[0]->Start);
2462                 break;          
2463         case eINVALID_PARAM:
2464                 LogTemplateError(
2465                         NULL, "Sorter", ERR_PARM1, TP,
2466                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2467                         bstr("SortBy"));
2468         case eNOT_SPECIFIED:
2469         case eFOUND:
2470                 if (Next == Param) {
2471                         SortOrderStr = &SortNextOrder[SortOrder];
2472                 }
2473                 else { /** Not Us... */
2474                         SortOrderStr = &SortNextOrder[0];
2475                 }
2476                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2477         }
2478 }
2479
2480
2481 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2482 {
2483         long *LongVector = (long*) CTX;
2484
2485         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2486             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2487         {
2488                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2489         }
2490         else
2491         {
2492                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2493                         LogTemplateError(
2494                                 Target, "Longvector", ERR_NAME, TP,
2495                                 "needs a numerical Parameter!");
2496                 }
2497                 else {
2498                         LogTemplateError(
2499                                 Target, "LongVector", ERR_PARM1, TP,
2500                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2501                                 TP->Tokens->Params[0]->lvalue,
2502                                 LongVector[0]);
2503                 }
2504         }
2505 }
2506
2507 void dbg_print_longvector(long *LongVector)
2508 {
2509         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2510         int nItems = LongVector[0];
2511         int i;
2512
2513         for (i = 0; i < nItems; i++) {
2514                 if (i + 1 < nItems)
2515                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2516                 else
2517                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2518
2519         }
2520         lprintf(1, ChrPtr(Buf));
2521         FreeStrBuf(&Buf);
2522 }
2523
2524 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2525 {
2526         long *LongVector = (long*) CTX;
2527
2528         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2529             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2530             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2531             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2532         {
2533                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2534                         LongVector[TP->Tokens->Params[3]->lvalue];
2535         }
2536         else
2537         {
2538                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2539                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2540                         LogTemplateError(
2541                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2542                                 "needs two long Parameter!");
2543                 }
2544                 else {
2545                         LogTemplateError(
2546                                 Target, "Longvector", ERR_PARM1, TP,
2547                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2548                                 TP->Tokens->Params[2]->lvalue,
2549                                 TP->Tokens->Params[3]->lvalue,
2550                                 LongVector[0]);
2551                 }
2552         }
2553         return 0;
2554 }
2555
2556 void 
2557 InitModule_SUBST
2558 (void)
2559 {
2560         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2561         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, CTX_NONE);
2562         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, CTX_NONE);
2563         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, CTX_NONE);
2564         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
2565         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
2566         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
2567         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
2568         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
2569         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2570         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2571         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2572
2573         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2574                                    conditional_ITERATE_ISGROUPCHANGE, 
2575                                    CTX_ITERATE);
2576         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2577                                    conditional_ITERATE_LASTN, 
2578                                    CTX_ITERATE);
2579         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2580         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2581         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2582 }
2583
2584 void
2585 ServerStartModule_SUBST
2586 (void)
2587 {
2588         WirelessTemplateCache = NewHash(1, NULL);
2589         WirelessLocalTemplateCache = NewHash(1, NULL);
2590         LocalTemplateCache = NewHash(1, NULL);
2591         TemplateCache = NewHash(1, NULL);
2592
2593         GlobalNS = NewHash(1, NULL);
2594         Iterators = NewHash(1, NULL);
2595         Conditionals = NewHash(1, NULL);
2596         SortHash = NewHash(1, NULL);
2597 }
2598
2599 void
2600 FinalizeModule_SUBST
2601 (void)
2602 {
2603
2604 }
2605
2606 void 
2607 ServerShutdownModule_SUBST
2608 (void)
2609 {
2610         DeleteHash(&WirelessTemplateCache);
2611         DeleteHash(&WirelessLocalTemplateCache);
2612         DeleteHash(&TemplateCache);
2613         DeleteHash(&LocalTemplateCache);
2614
2615         DeleteHash(&GlobalNS);
2616         DeleteHash(&Iterators);
2617         DeleteHash(&Conditionals);
2618         DeleteHash(&SortHash);
2619
2620 }
2621
2622
2623 void
2624 SessionNewModule_SUBST
2625 (wcsession *sess)
2626 {
2627
2628 }
2629
2630 void
2631 SessionAttachModule_SUBST
2632 (wcsession *sess)
2633 {
2634         sess->vars = NewHash(1,NULL);
2635 }
2636
2637 void
2638 SessionDetachModule_SUBST
2639 (wcsession *sess)
2640 {
2641         DeleteHash(&sess->vars);
2642 }
2643
2644 void 
2645 SessionDestroyModule_SUBST  
2646 (wcsession *sess)
2647 {
2648
2649 }
2650
2651
2652
2653
2654 /*@}*/