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