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