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