* preeval_iterate(): also check the required parameters.
[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         HashIterator *It;
2150
2151         memset(&TPP, 0, sizeof(WCTemplputParams));
2152         TP = &TPP;
2153         TP->Tokens = Token;
2154         if (!GetHash(Iterators, TKEY(0), &vIt)) {
2155                 LogTemplateError(
2156                         NULL, "Iterator", ERR_PARM1, TP,
2157                         "not found");
2158                 return 0;
2159         }
2160         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
2161                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
2162                                  "Need token with type Subtemplate as param 1, have %s", 
2163                                  TP->Tokens->Params[1]->Start);
2164         }
2165         
2166         /* well, we don't check the mobile stuff here... */
2167         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
2168             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
2169                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
2170                                  "referenced here doesn't exist");
2171         }
2172         Token->Preeval2 = vIt;
2173         It = (HashIterator *) vIt;
2174
2175         if (TP->Tokens->nParameters <= It->AdditionalParams + 2) {
2176                 LogTemplateError(                               
2177                         NULL, "Iterator", ERR_PARM1, TP,
2178                         "doesn't work with %d params", 
2179                         TP->Tokens->nParameters - 1);
2180         }
2181
2182
2183         return 1;
2184 }
2185
2186 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
2187 {
2188         HashIterator *It;
2189         HashList *List;
2190         HashPos  *it;
2191         SortStruct *SortBy = NULL;
2192         void *vSortBy;
2193         int DetectGroupChange = 0;
2194         int nMembersUsed;
2195         void *vContext;
2196         void *vLastContext = NULL;
2197         StrBuf *SubBuf;
2198         WCTemplputParams SubTP;
2199         IterateStruct Status;
2200
2201         long StartAt = 0;
2202         long StepWidth = 0;
2203         long StopAt = -1;
2204
2205         memset(&Status, 0, sizeof(IterateStruct));
2206         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2207         
2208         It = (HashIterator*) TP->Tokens->Preeval2;
2209         if (It == NULL) {
2210                 LogTemplateError(
2211                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
2212                 return;
2213         }
2214
2215         if (TP->Tokens->nParameters <= It->AdditionalParams + 2) {
2216                 LogTemplateError(                               
2217                         Target, "Iterator", ERR_PARM1, TP,
2218                         "doesn't work with %d params", 
2219                         TP->Tokens->nParameters - 1);
2220                 return;
2221         }
2222
2223         if ((It->XPectContextType != CTX_NONE) &&
2224             (It->XPectContextType != TP->Filter.ContextType)) {
2225                 LogTemplateError(
2226                         Target, "Iterator", ERR_PARM1, TP,
2227                         "requires context of type %d, have %d", 
2228                         It->XPectContextType, 
2229                         TP->Filter.ContextType);
2230                 return ;
2231                 
2232         }
2233
2234         if (It->StaticList == NULL)
2235                 List = It->GetHash(Target, TP);
2236         else
2237                 List = It->StaticList;
2238
2239         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
2240         if (DetectGroupChange) {
2241                 const StrBuf *BSort;
2242                 DetectGroupChange = 0;
2243                 if (havebstr("SortBy")) {
2244                         BSort = sbstr("SortBy");
2245                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
2246                             (vSortBy != NULL)) {
2247                                 SortBy = (SortStruct*)vSortBy;
2248                                 /** Ok, its us, lets see in which direction we should sort... */
2249                                 if (havebstr("SortOrder")) {
2250                                         int SortOrder;
2251                                         SortOrder = LBSTR("SortOrder");
2252                                         if (SortOrder != 0)
2253                                                 DetectGroupChange = 1;
2254                                 }
2255                         }
2256                 }
2257         }
2258         nMembersUsed = GetCount(List);
2259         SubBuf = NewStrBuf();
2260         SubTP.Filter.ContextType = It->ContextType;
2261         SubTP.Filter.ControlContextType = CTX_ITERATE;
2262         SubTP.ControlContext = &Status;
2263         
2264         if (HAVE_PARAM(2)) {
2265                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
2266         }
2267         if (HAVE_PARAM(3)) {
2268                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
2269         }
2270         if (HAVE_PARAM(4)) {
2271                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
2272         }
2273         it = GetNewHashPos(List, StepWidth);
2274         if (StopAt < 0) {
2275                 StopAt = GetCount(List);
2276         }
2277         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
2278                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
2279                         if (DetectGroupChange && Status.n > 0) {
2280                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
2281                         }
2282                         Status.LastN = (Status.n + 1) == nMembersUsed;
2283                         SubTP.Context = vContext;
2284                         if (It->DoSubTemplate != NULL)
2285                                 It->DoSubTemplate(SubBuf, &SubTP);
2286                         DoTemplate(TKEY(1), SubBuf, &SubTP);
2287                         
2288                         StrBufAppendBuf(Target, SubBuf, 0);
2289                         FlushStrBuf(SubBuf);
2290                         Status.oddeven = ! Status.oddeven;
2291                         vLastContext = vContext;
2292                 }
2293                 Status.n++;
2294         }
2295         FreeStrBuf(&SubBuf);
2296         DeleteHashPos(&it);
2297         if (It->Destructor != NULL)
2298                 It->Destructor(&List);
2299 }
2300
2301
2302 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2303 {
2304         IterateStruct *Ctx = CCTX;
2305         if (TP->Tokens->nParameters < 3)
2306                 return  Ctx->GroupChange;
2307
2308         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2309 }
2310
2311 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2312 {
2313         IterateStruct *Ctx = CCTX;
2314         if (Ctx->oddeven)
2315                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2316         else
2317                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2318 }
2319
2320
2321 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2322 {
2323         IterateStruct *Ctx = CCTX;
2324
2325         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2326 }
2327
2328
2329 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2330 {
2331         IterateStruct *Ctx = CCTX;
2332         StrBufAppendPrintf(Target, "%d", Ctx->n);
2333 }
2334
2335 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2336 {
2337         IterateStruct *Ctx = CCTX;
2338         return Ctx->n == 0;
2339 }
2340
2341 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2342 {
2343         IterateStruct *Ctx = CCTX;
2344         return Ctx->LastN;
2345 }
2346
2347
2348
2349 /*-----------------------------------------------------------------------------
2350  *                      Conditionals
2351  */
2352 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2353 {
2354         ConditionalStruct *Cond;
2355
2356         if ((TP->Tokens->Params[0]->len == 1) &&
2357             (TP->Tokens->Params[0]->Start[0] == 'X'))
2358                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2359             
2360         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2361         if (Cond == NULL) {
2362                 LogTemplateError(
2363                         Target, "Conditional", ERR_PARM1, TP,
2364                         "unknown!");
2365                 return 1;
2366         }
2367
2368         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2369                 return 0;
2370         }
2371
2372         if (Cond->CondF(Target, TP) == Neg)
2373                 return TP->Tokens->Params[1]->lvalue;
2374         return 0;
2375 }
2376
2377 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2378 {
2379         void *vsubst;
2380         wcsubst *subst;
2381         
2382         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2383                 return 0;
2384         subst = (wcsubst*) vsubst;
2385         
2386         switch(subst->wcs_type) {
2387         case WCS_FUNCTION:
2388                 return (subst->wcs_function!=NULL);
2389         case WCS_SERVCMD:
2390                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2391                 return 1;
2392         case WCS_STRING:
2393         case WCS_STRBUF:
2394         case WCS_STRBUF_REF:
2395                 if (TP->Tokens->nParameters < 4)
2396                         return 1;
2397                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2398         case WCS_LONG:
2399                 if (TP->Tokens->nParameters < 4)
2400                         return (subst->lvalue != 0);
2401                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2402         default:
2403                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2404                 return -1;
2405         }
2406         return 0;
2407 }
2408
2409 void RegisterConditional(const char *Name, long len, 
2410                          int nParams,
2411                          WCConditionalFunc CondF, 
2412                          int ContextRequired)
2413 {
2414         ConditionalStruct *Cond;
2415
2416         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2417         memset(Cond, 0, sizeof(ConditionalStruct));
2418         Cond->PlainName = Name;
2419         Cond->Filter.nMaxArgs = nParams;
2420         Cond->Filter.nMinArgs = nParams;
2421         Cond->CondF = CondF;
2422         Cond->Filter.ContextType = ContextRequired;
2423         Cond->Filter.ControlContextType = CTX_NONE;
2424         Put(Conditionals, Name, len, Cond, NULL);
2425 }
2426
2427 void RegisterControlConditional(const char *Name, long len, 
2428                                 int nParams,
2429                                 WCConditionalFunc CondF, 
2430                                 int ControlContextRequired)
2431 {
2432         ConditionalStruct *Cond;
2433
2434         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2435         memset(Cond, 0, sizeof(ConditionalStruct));
2436         Cond->PlainName = Name;
2437         Cond->Filter.nMaxArgs = nParams;
2438         Cond->Filter.nMinArgs = nParams;
2439         Cond->CondF = CondF;
2440         Cond->Filter.ContextType = CTX_NONE;
2441         Cond->Filter.ControlContextType = ControlContextRequired;
2442         Put(Conditionals, Name, len, Cond, NULL);
2443 }
2444
2445 void RegisterTokenParamDefine(const char *Name, long len, 
2446                               long Value)
2447 {
2448         long *PVal;
2449
2450         PVal = (long*)malloc(sizeof(long));
2451         *PVal = Value;
2452         Put(Defines, Name, len, PVal, NULL);
2453 }
2454
2455 long GetTokenDefine(const char *Name, long len, 
2456                     long DefValue)
2457 {
2458         void *vPVal;
2459
2460         if (GetHash(Defines, Name, len, &vPVal) &&
2461              (vPVal != NULL))
2462          {
2463                  return *(long*) vPVal;
2464          }
2465          else
2466          {
2467                  return DefValue;
2468          }
2469 }
2470
2471 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2472 {
2473         const char *Str;
2474         long len;
2475         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2476         
2477         StrBufAppendBufPlain(Target, Str, len, 0);
2478 }
2479
2480 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2481 {
2482         int val;
2483
2484         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2485         StrBufAppendPrintf(Target, "%d", val);
2486 }
2487
2488 HashList *Defines;
2489
2490 /*-----------------------------------------------------------------------------
2491  *                      Context Strings
2492  */
2493 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2494 {
2495         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2496 }
2497 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2498 {
2499         StrBuf *TokenText = (StrBuf*) CTX;
2500         const char *CompareToken;
2501         long len;
2502
2503         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2504         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2505 }
2506
2507 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2508 {
2509         HashList *Arr = (HashList*) CTX;
2510         void *pV;
2511         int val;
2512
2513         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2514         if (GetHash(Arr, IKEY(val), &pV) && 
2515             (pV != NULL)) {
2516                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2517         }
2518 }
2519 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2520 {
2521         HashList *Arr = (HashList*) CTX;
2522         void *pV;
2523         int val;
2524         const char *CompareToken;
2525         long len;
2526
2527         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2528         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2529         if (GetHash(Arr, IKEY(val), &pV) && 
2530             (pV != NULL)) {
2531                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2532         }
2533         else
2534                 return 0;
2535 }
2536
2537 /*-----------------------------------------------------------------------------
2538  *                      Boxed-API
2539  */
2540
2541 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2542 {
2543         WCTemplputParams SubTP;
2544
2545         StrBuf *Headline = NULL;
2546         if (TP->Tokens->nParameters == 2) {
2547                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2548                         Headline = NewStrBuf();
2549                         DoTemplate(TKEY(1), Headline, TP);
2550                 }
2551                 else {
2552                         const char *Ch;
2553                         long len;
2554                         GetTemplateTokenString(Target, 
2555                                                TP, 
2556                                                1,
2557                                                &Ch,
2558                                                &len);
2559                         Headline = NewStrBufPlain(Ch, len);
2560                 }
2561         }
2562         /* else TODO error? logging? */
2563         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2564         SubTP.Context = Headline;
2565         SubTP.Filter.ContextType = CTX_STRBUF;
2566         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2567         DoTemplate(TKEY(0), Target, TP);
2568         DoTemplate(HKEY("endbox"), Target, TP);
2569         FreeStrBuf(&Headline);
2570 }
2571
2572 /*-----------------------------------------------------------------------------
2573  *                      Tabbed-API
2574  */
2575 int preeval_do_tabbed(WCTemplateToken *Token)
2576 {
2577         WCTemplputParams TPP;
2578         WCTemplputParams *TP;
2579         const char *Ch;
2580         long len;
2581         int i, nTabs;
2582
2583
2584         memset(&TPP, 0, sizeof(WCTemplputParams));
2585         TP = &TPP;
2586         TP->Tokens = Token;
2587         nTabs = TP->Tokens->nParameters / 2 - 1;
2588         if (TP->Tokens->nParameters % 2 != 0)
2589         {
2590                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2591                                  "need even number of arguments");
2592                 return 0;
2593
2594         }
2595         else for (i = 0; i < nTabs; i++) {
2596                 if (!HaveTemplateTokenString(NULL, 
2597                                              TP, 
2598                                              i * 2,
2599                                              &Ch,
2600                                              &len) || 
2601                     (TP->Tokens->Params[i * 2]->len == 0))
2602                 {
2603                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2604                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2605                                          i, TP->Tokens->Params[i * 2]->Start);
2606                         return 0;
2607                 }
2608                 if (!HaveTemplateTokenString(NULL, 
2609                                              TP, 
2610                                              i * 2 + 1,
2611                                              &Ch,
2612                                              &len) || 
2613                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2614                 {
2615                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2616                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2617                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2618                         return 0;
2619                 }
2620         }
2621
2622         if (!HaveTemplateTokenString(NULL, 
2623                                      TP, 
2624                                      i * 2 + 1,
2625                                      &Ch,
2626                                      &len) || 
2627             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2628         {
2629                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2630                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2631                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2632                 return 0;
2633         }
2634         return 1;
2635 }
2636
2637
2638 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2639 {
2640         StrBuf **TabNames;
2641         int i, ntabs, nTabs;
2642
2643         nTabs = ntabs = TP->Tokens->nParameters / 2;
2644         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2645         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2646
2647         for (i = 0; i < ntabs; i++) {
2648                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2649                     (TP->Tokens->Params[i * 2]->len > 0)) {
2650                         TabNames[i] = NewStrBuf();
2651                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2652                 }
2653                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2654                         const char *Ch;
2655                         long len;
2656                         GetTemplateTokenString(Target, 
2657                                                TP, 
2658                                                i * 2,
2659                                                &Ch,
2660                                                &len);
2661                         TabNames[i] = NewStrBufPlain(Ch, -1);
2662                 }
2663                 else { 
2664                         /** A Tab without subject? we can't count that, add it as silent */
2665                         nTabs --;
2666                 }
2667         }
2668
2669         StrTabbedDialog(Target, nTabs, TabNames);
2670         for (i = 0; i < ntabs; i++) {
2671                 StrBeginTab(Target, i, nTabs);
2672                 DoTemplate(TKEY(i * 2 + 1), Target, TP);
2673                 StrEndTab(Target, i, nTabs);
2674         }
2675         for (i = 0; i < ntabs; i++) 
2676                 FreeStrBuf(&TabNames[i]);
2677 }
2678
2679
2680 /*-----------------------------------------------------------------------------
2681  *                      Sorting-API
2682  */
2683
2684
2685 void RegisterSortFunc(const char *name, long len, 
2686                       const char *prepend, long preplen,
2687                       CompareFunc Forward, 
2688                       CompareFunc Reverse, 
2689                       CompareFunc GroupChange, 
2690                       long ContextType)
2691 {
2692         SortStruct *NewSort;
2693
2694         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2695         memset(NewSort, 0, sizeof(SortStruct));
2696         NewSort->Name = NewStrBufPlain(name, len);
2697         if (prepend != NULL)
2698                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2699         else
2700                 NewSort->PrefPrepend = NULL;
2701         NewSort->Forward = Forward;
2702         NewSort->Reverse = Reverse;
2703         NewSort->GroupChange = GroupChange;
2704         NewSort->ContextType = ContextType;
2705         if (ContextType == CTX_NONE) {
2706                 lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
2707                 exit(1);
2708         }
2709                 
2710         Put(SortHash, name, len, NewSort, DestroySortStruct);
2711 }
2712
2713 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2714                          const char *OtherPrefix, long OtherPrefixLen,
2715                          const char *Default, long ldefault, long DefaultDirection)
2716 {
2717         int isdefault = 0;
2718         const StrBuf *BSort = NULL;
2719         SortStruct *SortBy;
2720         void *vSortBy;
2721         long SortOrder = -1;
2722         
2723         if (havebstr("SortBy")) {
2724                 BSort = sbstr("SortBy");
2725                 if (OtherPrefix == NULL) {
2726                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2727                 }
2728                 else {
2729                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2730                 }
2731         }
2732         else { /** Try to fallback to our remembered values... */
2733                 if (OtherPrefix == NULL) {
2734                         BSort = get_room_pref("sort");
2735                 }
2736                 else {
2737                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2738                 }
2739                 if (BSort != NULL)
2740                         putbstr("SortBy", NewStrBufDup(BSort));
2741                 else {
2742                         StrBuf *Buf;
2743
2744                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2745                         putbstr("SortBy", Buf);
2746                 }
2747         }
2748
2749         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2750             (vSortBy == NULL)) {
2751                 isdefault = 1;
2752                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2753                     (vSortBy == NULL)) {
2754                         LogTemplateError(
2755                                 NULL, "Sorting", ERR_PARM1, TP,
2756                                 "Illegal default sort: [%s]", Default);
2757                         wc_backtrace();
2758                 }
2759         }
2760         SortBy = (SortStruct*)vSortBy;
2761
2762         if (SortBy->ContextType != TP->Filter.ContextType)
2763                 return NULL;
2764
2765         /** Ok, its us, lets see in which direction we should sort... */
2766         if (havebstr("SortOrder")) {
2767                 SortOrder = LBSTR("SortOrder");
2768         }
2769         else { /** Try to fallback to our remembered values... */
2770                 StrBuf *Buf = NULL;
2771                 if (SortBy->PrefPrepend == NULL) {
2772                         Buf = get_room_pref("SortOrder");
2773                         SortOrder = StrTol(Buf);
2774                 }
2775                 else {
2776                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2777                 }
2778
2779                 if (Buf == NULL)
2780                         SortOrder = DefaultDirection;
2781
2782                 Buf = NewStrBufPlain(NULL, 64);
2783                 StrBufPrintf(Buf, "%ld", SortOrder);
2784                 putbstr("SortOrder", Buf);
2785         }
2786         switch (SortOrder) {
2787         default:
2788         case 0:
2789                 return NULL;
2790         case 1:
2791                 return SortBy->Forward;
2792         case 2:
2793                 return SortBy->Reverse;
2794         }
2795 }
2796
2797
2798 enum {
2799         eNO_SUCH_SORT, 
2800         eNOT_SPECIFIED,
2801         eINVALID_PARAM,
2802         eFOUND
2803 };
2804
2805 ConstStr SortIcons[] = {
2806         {HKEY("static/sort_none.gif")},
2807         {HKEY("static/up_pointer.gif")},
2808         {HKEY("static/down_pointer.gif")},
2809 };
2810
2811 ConstStr SortNextOrder[] = {
2812         {HKEY("1")},
2813         {HKEY("2")},
2814         {HKEY("0")},
2815 };
2816
2817
2818 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2819 {
2820         int bSortError = eNOT_SPECIFIED;
2821         const StrBuf *BSort;
2822         void *vSort;
2823         
2824         *SortOrder = 0;
2825         *Next = NULL;
2826         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2827             (vSort == NULL))
2828                 return eNO_SUCH_SORT;
2829         *Param = (SortStruct*) vSort;
2830         
2831
2832         if (havebstr("SortBy")) {
2833                 BSort = sbstr("SortBy");
2834                 bSortError = eINVALID_PARAM;
2835                 if ((*Param)->PrefPrepend == NULL) {
2836                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2837                 }
2838                 else {
2839                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2840                 }
2841         }
2842         else { /** Try to fallback to our remembered values... */
2843                 if ((*Param)->PrefPrepend == NULL) {
2844                         BSort = get_room_pref("sort");
2845                 }
2846                 else {
2847                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2848                 }
2849         }
2850
2851         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2852             (vSort == NULL))
2853                 return bSortError;
2854
2855         *Next = (SortStruct*) vSort;
2856
2857         /** Ok, its us, lets see in which direction we should sort... */
2858         if (havebstr("SortOrder")) {
2859                 *SortOrder = LBSTR("SortOrder");
2860         }
2861         else { /** Try to fallback to our remembered values... */
2862                 if ((*Param)->PrefPrepend == NULL) {
2863                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2864                 }
2865                 else {
2866                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2867                 }
2868         }
2869         if (*SortOrder > 2)
2870                 *SortOrder = 0;
2871
2872         return eFOUND;
2873 }
2874
2875
2876 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2877 {
2878         long SortOrder;
2879         SortStruct *Next;
2880         SortStruct *Param;
2881         const ConstStr *SortIcon;
2882
2883         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2884         case eNO_SUCH_SORT:
2885                 LogTemplateError(
2886                         Target, "Sorter", ERR_PARM1, TP,
2887                         " Sorter [%s] unknown!", 
2888                         TP->Tokens->Params[0]->Start);
2889                 break;          
2890         case eINVALID_PARAM:
2891                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2892                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2893                                  bstr("SortBy"));
2894         case eNOT_SPECIFIED:
2895         case eFOUND:
2896                 if (Next == Param) {
2897                         SortIcon = &SortIcons[SortOrder];
2898                 }
2899                 else { /** Not Us... */
2900                         SortIcon = &SortIcons[0];
2901                 }
2902                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2903         }
2904 }
2905
2906 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2907 {
2908         long SortOrder;
2909         SortStruct *Next;
2910         SortStruct *Param;
2911
2912         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2913         case eNO_SUCH_SORT:
2914                 LogTemplateError(
2915                         Target, "Sorter", ERR_PARM1, TP,                                  
2916                         " Sorter [%s] unknown!", 
2917                         TP->Tokens->Params[0]->Start);
2918                 break;          
2919         case eINVALID_PARAM:
2920                 LogTemplateError(
2921                         NULL, "Sorter", ERR_PARM1, TP,
2922                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2923                         bstr("SortBy"));
2924         case eNOT_SPECIFIED:
2925         case eFOUND:
2926                 StrBufAppendBuf(Target, Param->Name, 0);
2927                 
2928         }
2929 }
2930
2931 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2932 {
2933         long SortOrder;
2934         const ConstStr *SortOrderStr;
2935         SortStruct *Next;
2936         SortStruct *Param;
2937
2938         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2939         case eNO_SUCH_SORT:
2940                 LogTemplateError(
2941                         Target, "Sorter", ERR_PARM1, TP,
2942                         " Sorter [%s] unknown!",
2943                         TP->Tokens->Params[0]->Start);
2944                 break;          
2945         case eINVALID_PARAM:
2946                 LogTemplateError(
2947                         NULL, "Sorter", ERR_PARM1, TP,
2948                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2949                         bstr("SortBy"));
2950         case eNOT_SPECIFIED:
2951         case eFOUND:
2952                 if (Next == Param) {
2953                         SortOrderStr = &SortNextOrder[SortOrder];
2954                 }
2955                 else { /** Not Us... */
2956                         SortOrderStr = &SortNextOrder[0];
2957                 }
2958                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2959         }
2960 }
2961
2962
2963 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2964 {
2965         long *LongVector = (long*) CTX;
2966
2967         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2968             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2969         {
2970                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2971         }
2972         else
2973         {
2974                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2975                         LogTemplateError(
2976                                 Target, "Longvector", ERR_NAME, TP,
2977                                 "needs a numerical Parameter!");
2978                 }
2979                 else {
2980                         LogTemplateError(
2981                                 Target, "LongVector", ERR_PARM1, TP,
2982                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2983                                 TP->Tokens->Params[0]->lvalue,
2984                                 LongVector[0]);
2985                 }
2986         }
2987 }
2988
2989 void dbg_print_longvector(long *LongVector)
2990 {
2991         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2992         int nItems = LongVector[0];
2993         int i;
2994
2995         for (i = 0; i < nItems; i++) {
2996                 if (i + 1 < nItems)
2997                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2998                 else
2999                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
3000
3001         }
3002         lprintf(1, ChrPtr(Buf));
3003         FreeStrBuf(&Buf);
3004 }
3005
3006 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
3007 {
3008         long *LongVector = (long*) CTX;
3009
3010         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
3011             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
3012             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
3013             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
3014         {
3015                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
3016                         LongVector[TP->Tokens->Params[3]->lvalue];
3017         }
3018         else
3019         {
3020                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
3021                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
3022                         LogTemplateError(
3023                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
3024                                 "needs two long Parameter!");
3025                 }
3026                 else {
3027                         LogTemplateError(
3028                                 Target, "Longvector", ERR_PARM1, TP,
3029                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
3030                                 TP->Tokens->Params[2]->lvalue,
3031                                 TP->Tokens->Params[3]->lvalue,
3032                                 LongVector[0]);
3033                 }
3034         }
3035         return 0;
3036 }
3037
3038
3039 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
3040 {
3041         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
3042 }
3043
3044 void 
3045 InitModule_SUBST
3046 (void)
3047 {
3048         memset(&NoCtx, 0, sizeof(WCTemplputParams));
3049         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
3050         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
3051         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
3052         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
3053         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUF);
3054         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
3055         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
3056         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
3057         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
3058
3059
3060         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
3061         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
3062         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
3063         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
3064
3065         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
3066                                    conditional_ITERATE_ISGROUPCHANGE, 
3067                                    CTX_ITERATE);
3068         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
3069                                    conditional_ITERATE_LASTN, 
3070                                    CTX_ITERATE);
3071         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
3072                                    conditional_ITERATE_FIRSTN, 
3073                                    CTX_ITERATE);
3074
3075         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
3076         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
3077         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
3078         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
3079         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
3080         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
3081
3082
3083
3084
3085 }
3086
3087 void
3088 ServerStartModule_SUBST
3089 (void)
3090 {
3091         WirelessTemplateCache = NewHash(1, NULL);
3092         WirelessLocalTemplateCache = NewHash(1, NULL);
3093         LocalTemplateCache = NewHash(1, NULL);
3094         TemplateCache = NewHash(1, NULL);
3095
3096         GlobalNS = NewHash(1, NULL);
3097         Iterators = NewHash(1, NULL);
3098         Conditionals = NewHash(1, NULL);
3099         SortHash = NewHash(1, NULL);
3100         Defines = NewHash(1, NULL);
3101 }
3102
3103 void
3104 FinalizeModule_SUBST
3105 (void)
3106 {
3107
3108 }
3109
3110 void 
3111 ServerShutdownModule_SUBST
3112 (void)
3113 {
3114         DeleteHash(&WirelessTemplateCache);
3115         DeleteHash(&WirelessLocalTemplateCache);
3116         DeleteHash(&TemplateCache);
3117         DeleteHash(&LocalTemplateCache);
3118
3119         DeleteHash(&GlobalNS);
3120         DeleteHash(&Iterators);
3121         DeleteHash(&Conditionals);
3122         DeleteHash(&SortHash);
3123         DeleteHash(&Defines);
3124 }
3125
3126
3127 void
3128 SessionNewModule_SUBST
3129 (wcsession *sess)
3130 {
3131
3132 }
3133
3134 void
3135 SessionAttachModule_SUBST
3136 (wcsession *sess)
3137 {
3138         sess->vars = NewHash(1,NULL);
3139 }
3140
3141 void
3142 SessionDetachModule_SUBST
3143 (wcsession *sess)
3144 {
3145         DeleteHash(&sess->vars);
3146         FreeStrBuf(&sess->WFBuf);
3147 }
3148
3149 void 
3150 SessionDestroyModule_SUBST  
3151 (wcsession *sess)
3152 {
3153
3154 }