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