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