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