* fix some logmessages, so not we output the right information
[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 = NewStrBuf();
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                                 break;
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                                 continue; /* Ignore backup files... */
1710                         /* .m.xxx is for mobile useragents! */
1711                         if (d_without_ext > 2)
1712                                 IsMobile = (filedir_entry->d_name[d_without_ext - 1] == 'm') &&
1713                                         (filedir_entry->d_name[d_without_ext - 2] == '.');
1714                         PStart = filedir_entry->d_name;
1715                         StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
1716                         MinorPtr = strchr(filedir_entry->d_name, '.');
1717                         if (MinorPtr != NULL)
1718                                 *MinorPtr = '\0';
1719                         FlushStrBuf(Key);
1720                         if (!Toplevel) {
1721                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1722                                 StrBufAppendBuf(Key, BaseKey, 0);
1723                                 StrBufAppendBufPlain(Key, HKEY("_"), 0);
1724                         }
1725                         StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
1726
1727                         if (LoadTemplates >= 1)
1728                                 lprintf(1, "%s %d %s\n", ChrPtr(FileName), IsMobile, ChrPtr(Key));
1729                         if (LoadTemplates == 0)
1730                                 load_template(FileName, Key, (IsMobile)?wireless:big);
1731                         else
1732                                 prepare_template(FileName, Key, (IsMobile)?wireless:big);
1733                 default:
1734                         break;
1735                 }
1736         }
1737         free(d);
1738         closedir(filedir);
1739         FreeStrBuf(&FileName);
1740         FreeStrBuf(&Key);
1741         FreeStrBuf(&SubDirectory);
1742         FreeStrBuf(&SubKey);
1743         return 1;
1744 }
1745
1746 void InitTemplateCache(void)
1747 {
1748         StrBuf *Key;
1749         StrBuf *Dir;
1750
1751         Dir = NewStrBuf();
1752         Key = NewStrBuf();
1753
1754         /* Primary Template set... */
1755         StrBufPrintf(Dir, "%s/t", static_dirs[0]);
1756         LoadTemplateDir(Dir,
1757                         WirelessTemplateCache,
1758                         TemplateCache, 
1759                         Key);
1760
1761         /* User local Template set */
1762         StrBufPrintf(Dir, "%s/t", static_dirs[1]);
1763         LoadTemplateDir(Dir,
1764                         WirelessLocalTemplateCache,
1765                         LocalTemplateCache, 
1766                         Key);
1767         
1768         /* Debug Templates, just to be loaded while debugging. */
1769         
1770         StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
1771         LoadTemplateDir(Dir,
1772                         WirelessTemplateCache,
1773                         TemplateCache, 
1774                         Key);
1775
1776
1777         FreeStrBuf(&Dir);
1778         FreeStrBuf(&Key);
1779 }
1780
1781
1782
1783 /*-----------------------------------------------------------------------------
1784  *                      Filling & processing Templates
1785  */
1786 /**
1787  * \brief executes one token
1788  * \param Target buffer to append to
1789  * \param Token da to  process.
1790  * \param Template we're iterating
1791  * \param Context Contextpoointer to pass in
1792  * \param state are we in conditional state?
1793  * \param ContextType what type of information does context giv us?
1794  */
1795 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1796 {
1797         const char *AppendMe;
1798         long AppendMeLen;
1799         HashHandler *Handler;
1800         void *vVar;
1801         
1802 /* much output, since pName is not terminated...
1803         lprintf(1,"Doing token: %s\n",Token->pName);
1804 */
1805
1806         switch (TP->Tokens->Flags) {
1807         case SV_GETTEXT:
1808                 TmplGettext(Target, TP);
1809                 break;
1810         case SV_CONDITIONAL: /** Forward conditional evaluation */
1811                 return EvaluateConditional(Target, 1, state, TP);
1812                 break;
1813         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1814                 return EvaluateConditional(Target, 0, state, TP);
1815                 break;
1816         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1817                 if (TP->Tokens->nParameters >= 6) {
1818                         if (EvaluateConditional(Target, 0, state, TP)) {
1819                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1820                                 StrBufAppendBufPlain(Target, 
1821                                                      AppendMe, 
1822                                                      AppendMeLen,
1823                                                      0);
1824                         }
1825                         else{
1826                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1827                                 StrBufAppendBufPlain(Target, 
1828                                                      AppendMe, 
1829                                                      AppendMeLen,
1830                                                      0);
1831                         }
1832                 }
1833                 else  {
1834                         LogTemplateError(
1835                                 Target, "Conditional", ERR_NAME, TP,
1836                                 "needs at least 6 Params!"); 
1837                 }
1838                 break;
1839         case SV_SUBTEMPL:
1840                 if (TP->Tokens->nParameters == 1)
1841                         DoTemplate(TKEY(0), Target, TP);
1842                 break;
1843         case SV_PREEVALUATED:
1844                 Handler = (HashHandler*) TP->Tokens->PreEval;
1845                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1846                         return -1;
1847                 }
1848                 Handler->HandlerFunc(Target, TP);
1849                 break;          
1850         default:
1851                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1852                         Handler = (HashHandler*) vVar;
1853                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1854                                 return -1;
1855                         }
1856                         else {
1857                                 Handler->HandlerFunc(Target, TP);
1858                         }
1859                 }
1860                 else {
1861                         print_value_of(Target, TP);
1862                 }
1863         }
1864         return 0;
1865 }
1866
1867
1868
1869 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1870 {
1871         WCTemplate *pTmpl = Tmpl;
1872         int done = 0;
1873         int i, state;
1874         const char *pData, *pS;
1875         long len;
1876         WCTemplputParams TP;
1877
1878         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1879
1880         TP.Context = CallingTP->Context;
1881         TP.ControlContext = CallingTP->ControlContext;
1882
1883         if (LoadTemplates != 0) {                       
1884                 if (LoadTemplates > 1)
1885                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
1886                                 ChrPtr(Tmpl->FileName));
1887
1888                 pTmpl = load_template(Tmpl->FileName, NULL, NULL);
1889                 if(pTmpl == NULL) {
1890                         StrBufAppendPrintf(
1891                                 Target, 
1892                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1893                                 ChrPtr(Tmpl->FileName));
1894                         return NULL;
1895                 }
1896
1897         }
1898
1899         pS = pData = ChrPtr(pTmpl->Data);
1900         len = StrLength(pTmpl->Data);
1901         i = 0;
1902         state = 0;
1903         while (!done) {
1904                 if (i >= pTmpl->nTokensUsed) {
1905                         StrBufAppendBufPlain(Target, 
1906                                              pData, 
1907                                              len - (pData - pS), 0);
1908                         done = 1;
1909                 }
1910                 else {
1911                         StrBufAppendBufPlain(
1912                                 Target, pData, 
1913                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1914                         TP.Tokens = pTmpl->Tokens[i];
1915                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1916                         state = EvaluateToken(Target, state, &TP);
1917
1918                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1919                         /* condition told us to skip till its end condition */
1920                                 i++;
1921                                 TP.Tokens = pTmpl->Tokens[i];
1922                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1923                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1924                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1925                                         if (state == EvaluateConditional(
1926                                                     Target, 
1927                                                     pTmpl->Tokens[i]->Flags, 
1928                                                     state, 
1929                                                     &TP))
1930                                                 state = 0;
1931                                 }
1932                         }
1933                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1934                         if (i > pTmpl->nTokensUsed)
1935                                 done = 1;
1936                 }
1937         }
1938         if (LoadTemplates != 0) {
1939                 FreeWCTemplate(pTmpl);
1940         }
1941         return Tmpl->MimeType;
1942
1943 }
1944
1945 /**
1946  * \brief Display a variable-substituted template
1947  * \param templatename template file to load
1948  * \returns the mimetype of the template its doing
1949  */
1950 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1951 {
1952         WCTemplputParams LocalTP;
1953         HashList *Static;
1954         HashList *StaticLocal;
1955         void *vTmpl;
1956         
1957         if (Target == NULL)
1958                 Target = WC->WBuf;
1959         if (TP == NULL) {
1960                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1961                 TP = &LocalTP;
1962         }
1963
1964         if (WC->is_mobile > 0) {
1965                 Static = WirelessTemplateCache;
1966                 StaticLocal = WirelessLocalTemplateCache;
1967         }
1968         else {
1969                 Static = TemplateCache;
1970                 StaticLocal = LocalTemplateCache;
1971         }
1972
1973         if (len == 0)
1974         {
1975                 lprintf (1, "Can't to load a template with empty name!\n");
1976                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1977                 return NULL;
1978         }
1979
1980         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1981             !GetHash(Static, templatename, len, &vTmpl)) {
1982                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1983                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1984                                    templatename, len, 
1985                                    (long)strlen(templatename));
1986 #if 0
1987                 dbg_PrintHash(Static, PrintTemplate, NULL);
1988                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1989 #endif
1990                 return NULL;
1991         }
1992         if (vTmpl == NULL) 
1993                 return NULL;
1994         return ProcessTemplate(vTmpl, Target, TP);
1995
1996 }
1997
1998 /*-----------------------------------------------------------------------------
1999  *                      Iterators
2000  */
2001 typedef struct _HashIterator {
2002         HashList *StaticList;
2003         int AdditionalParams;
2004         int ContextType;
2005         int XPectContextType;
2006         int Flags;
2007         RetrieveHashlistFunc GetHash;
2008         HashDestructorFunc Destructor;
2009         SubTemplFunc DoSubTemplate;
2010 } HashIterator;
2011
2012 void RegisterITERATOR(const char *Name, long len, 
2013                       int AdditionalParams, 
2014                       HashList *StaticList, 
2015                       RetrieveHashlistFunc GetHash, 
2016                       SubTemplFunc DoSubTempl,
2017                       HashDestructorFunc Destructor,
2018                       int ContextType, 
2019                       int XPectContextType, 
2020                       int Flags)
2021 {
2022         HashIterator *It;
2023
2024         It = (HashIterator*)malloc(sizeof(HashIterator));
2025         memset(It, 0, sizeof(HashIterator));
2026         It->StaticList = StaticList;
2027         It->AdditionalParams = AdditionalParams;
2028         It->GetHash = GetHash;
2029         It->DoSubTemplate = DoSubTempl;
2030         It->Destructor = Destructor;
2031         It->ContextType = ContextType;
2032         It->XPectContextType = XPectContextType;
2033         It->Flags = Flags;
2034         Put(Iterators, Name, len, It, NULL);
2035 }
2036
2037 typedef struct _iteratestruct {
2038         int GroupChange;
2039         int oddeven;
2040         const char *Key;
2041         long KeyLen;
2042         int n;
2043         int LastN;
2044         }IterateStruct; 
2045
2046 int preeval_iterate(WCTemplateToken *Token)
2047 {
2048         WCTemplputParams TPP;
2049         WCTemplputParams *TP;
2050         void *vIt;
2051
2052         memset(&TPP, 0, sizeof(WCTemplputParams));
2053         TP = &TPP;
2054         TP->Tokens = Token;
2055         if (!GetHash(Iterators, TKEY(0), &vIt)) {
2056                 LogTemplateError(
2057                         NULL, "Iterator", ERR_PARM1, TP,
2058                         "not found");
2059                 return 0;
2060         }
2061         Token->Preeval2 = vIt;
2062         return 1;
2063 }
2064
2065 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
2066 {
2067         HashIterator *It;
2068         HashList *List;
2069         HashPos  *it;
2070         SortStruct *SortBy = NULL;
2071         void *vSortBy;
2072         int DetectGroupChange = 0;
2073         int nMembersUsed;
2074         void *vContext;
2075         void *vLastContext = NULL;
2076         StrBuf *SubBuf;
2077         WCTemplputParams SubTP;
2078         IterateStruct Status;
2079
2080         long StartAt = 0;
2081         long StepWidth = 0;
2082         long StopAt = -1;
2083
2084         memset(&Status, 0, sizeof(IterateStruct));
2085         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2086         
2087         It = (HashIterator*) TP->Tokens->Preeval2;
2088         if (It == NULL) {
2089                 LogTemplateError(
2090                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
2091                 return;
2092         }
2093
2094         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
2095                 LogTemplateError(                               
2096                         Target, "Iterator", ERR_PARM1, TP,
2097                         "doesn't work with %d params", 
2098                         TP->Tokens->nParameters);
2099                 return;
2100         }
2101
2102         if ((It->XPectContextType != CTX_NONE) &&
2103             (It->XPectContextType != TP->Filter.ContextType)) {
2104                 LogTemplateError(
2105                         Target, "Iterator", ERR_PARM1, TP,
2106                         "requires context of type %d, have %d", 
2107                         It->XPectContextType, 
2108                         TP->Filter.ContextType);
2109                 return ;
2110                 
2111         }
2112
2113         if (It->StaticList == NULL)
2114                 List = It->GetHash(Target, TP);
2115         else
2116                 List = It->StaticList;
2117
2118         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
2119         if (DetectGroupChange) {
2120                 const StrBuf *BSort;
2121                 DetectGroupChange = 0;
2122                 if (havebstr("SortBy")) {
2123                         BSort = sbstr("SortBy");
2124                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
2125                             (vSortBy != NULL)) {
2126                                 SortBy = (SortStruct*)vSortBy;
2127                                 /** Ok, its us, lets see in which direction we should sort... */
2128                                 if (havebstr("SortOrder")) {
2129                                         int SortOrder;
2130                                         SortOrder = LBSTR("SortOrder");
2131                                         if (SortOrder != 0)
2132                                                 DetectGroupChange = 1;
2133                                 }
2134                         }
2135                 }
2136         }
2137         nMembersUsed = GetCount(List);
2138         SubBuf = NewStrBuf();
2139         SubTP.Filter.ContextType = It->ContextType;
2140         SubTP.Filter.ControlContextType = CTX_ITERATE;
2141         SubTP.ControlContext = &Status;
2142         
2143         if (HAVE_PARAM(2)) {
2144                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
2145         }
2146         if (HAVE_PARAM(3)) {
2147                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
2148         }
2149         if (HAVE_PARAM(4)) {
2150                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
2151         }
2152         it = GetNewHashPos(List, StepWidth);
2153         if (StopAt < 0) {
2154                 StopAt = GetCount(List);
2155         }
2156         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
2157                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
2158                         if (DetectGroupChange && Status.n > 0) {
2159                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
2160                         }
2161                         Status.LastN = (Status.n + 1) == nMembersUsed;
2162                         SubTP.Context = vContext;
2163                         if (It->DoSubTemplate != NULL)
2164                                 It->DoSubTemplate(SubBuf, &SubTP);
2165                         DoTemplate(TKEY(1), SubBuf, &SubTP);
2166                         
2167                         StrBufAppendBuf(Target, SubBuf, 0);
2168                         FlushStrBuf(SubBuf);
2169                         Status.oddeven = ! Status.oddeven;
2170                         vLastContext = vContext;
2171                 }
2172                 Status.n++;
2173         }
2174         FreeStrBuf(&SubBuf);
2175         DeleteHashPos(&it);
2176         if (It->Destructor != NULL)
2177                 It->Destructor(&List);
2178 }
2179
2180
2181 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2182 {
2183         IterateStruct *Ctx = CCTX;
2184         if (TP->Tokens->nParameters < 3)
2185                 return  Ctx->GroupChange;
2186
2187         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2188 }
2189
2190 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2191 {
2192         IterateStruct *Ctx = CCTX;
2193         if (Ctx->oddeven)
2194                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2195         else
2196                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2197 }
2198
2199
2200 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2201 {
2202         IterateStruct *Ctx = CCTX;
2203
2204         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2205 }
2206
2207
2208 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2209 {
2210         IterateStruct *Ctx = CCTX;
2211         StrBufAppendPrintf(Target, "%d", Ctx->n);
2212 }
2213
2214 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2215 {
2216         IterateStruct *Ctx = CCTX;
2217         return Ctx->n == 0;
2218 }
2219
2220 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2221 {
2222         IterateStruct *Ctx = CCTX;
2223         return Ctx->LastN;
2224 }
2225
2226
2227
2228 /*-----------------------------------------------------------------------------
2229  *                      Conditionals
2230  */
2231 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2232 {
2233         ConditionalStruct *Cond;
2234
2235         if ((TP->Tokens->Params[0]->len == 1) &&
2236             (TP->Tokens->Params[0]->Start[0] == 'X'))
2237                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2238             
2239         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2240         if (Cond == NULL) {
2241                 LogTemplateError(
2242                         Target, "Conditional", ERR_PARM1, TP,
2243                         "unknown!");
2244                 return 1;
2245         }
2246
2247         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2248                 return 0;
2249         }
2250
2251         if (Cond->CondF(Target, TP) == Neg)
2252                 return TP->Tokens->Params[1]->lvalue;
2253         return 0;
2254 }
2255
2256 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2257 {
2258         void *vsubst;
2259         wcsubst *subst;
2260         
2261         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2262                 return 0;
2263         subst = (wcsubst*) vsubst;
2264         
2265         switch(subst->wcs_type) {
2266         case WCS_FUNCTION:
2267                 return (subst->wcs_function!=NULL);
2268         case WCS_SERVCMD:
2269                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2270                 return 1;
2271         case WCS_STRING:
2272         case WCS_STRBUF:
2273         case WCS_STRBUF_REF:
2274                 if (TP->Tokens->nParameters < 4)
2275                         return 1;
2276                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2277         case WCS_LONG:
2278                 if (TP->Tokens->nParameters < 4)
2279                         return (subst->lvalue != 0);
2280                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2281         default:
2282                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2283                 return -1;
2284         }
2285         return 0;
2286 }
2287
2288 void RegisterConditional(const char *Name, long len, 
2289                          int nParams,
2290                          WCConditionalFunc CondF, 
2291                          int ContextRequired)
2292 {
2293         ConditionalStruct *Cond;
2294
2295         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2296         memset(Cond, 0, sizeof(ConditionalStruct));
2297         Cond->PlainName = Name;
2298         Cond->Filter.nMaxArgs = nParams;
2299         Cond->Filter.nMinArgs = nParams;
2300         Cond->CondF = CondF;
2301         Cond->Filter.ContextType = ContextRequired;
2302         Cond->Filter.ControlContextType = CTX_NONE;
2303         Put(Conditionals, Name, len, Cond, NULL);
2304 }
2305
2306 void RegisterControlConditional(const char *Name, long len, 
2307                                 int nParams,
2308                                 WCConditionalFunc CondF, 
2309                                 int ControlContextRequired)
2310 {
2311         ConditionalStruct *Cond;
2312
2313         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2314         memset(Cond, 0, sizeof(ConditionalStruct));
2315         Cond->PlainName = Name;
2316         Cond->Filter.nMaxArgs = nParams;
2317         Cond->Filter.nMinArgs = nParams;
2318         Cond->CondF = CondF;
2319         Cond->Filter.ContextType = CTX_NONE;
2320         Cond->Filter.ControlContextType = ControlContextRequired;
2321         Put(Conditionals, Name, len, Cond, NULL);
2322 }
2323
2324 void RegisterTokenParamDefine(const char *Name, long len, 
2325                               long Value)
2326 {
2327         long *PVal;
2328
2329         PVal = (long*)malloc(sizeof(long));
2330         *PVal = Value;
2331         Put(Defines, Name, len, PVal, NULL);
2332 }
2333
2334 HashList *Defines;
2335
2336 /*-----------------------------------------------------------------------------
2337  *                      Context Strings
2338  */
2339 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2340 {
2341         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2342 }
2343 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2344 {
2345         StrBuf *TokenText = (StrBuf*) CTX;
2346         const char *CompareToken;
2347         long len;
2348
2349         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2350         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2351 }
2352
2353 /*-----------------------------------------------------------------------------
2354  *                      Boxed-API
2355  */
2356
2357 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2358 {
2359         WCTemplputParams SubTP;
2360
2361         StrBuf *Headline = NULL;
2362         if (TP->Tokens->nParameters == 2) {
2363                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2364                         Headline = NewStrBuf();
2365                         DoTemplate(TKEY(1), Headline, TP);
2366                 }
2367                 else {
2368                         const char *Ch;
2369                         long len;
2370                         GetTemplateTokenString(Target, 
2371                                                TP, 
2372                                                1,
2373                                                &Ch,
2374                                                &len);
2375                         Headline = NewStrBufPlain(Ch, len);
2376                 }
2377         }
2378         /* else TODO error? logging? */
2379         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2380         SubTP.Context = Headline;
2381         SubTP.Filter.ContextType = CTX_STRBUF;
2382         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2383         DoTemplate(TKEY(0), Target, TP);
2384         DoTemplate(HKEY("endbox"), Target, TP);
2385         FreeStrBuf(&Headline);
2386 }
2387
2388 /*-----------------------------------------------------------------------------
2389  *                      Tabbed-API
2390  */
2391
2392 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2393 {
2394         StrBuf **TabNames;
2395         int i, ntabs, nTabs;
2396
2397         nTabs = ntabs = TP->Tokens->nParameters / 2;
2398         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2399         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2400
2401         for (i = 0; i < ntabs; i++) {
2402                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2403                     (TP->Tokens->Params[i * 2]->len > 0)) {
2404                         TabNames[i] = NewStrBuf();
2405                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2406                 }
2407                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2408                         const char *Ch;
2409                         long len;
2410                         GetTemplateTokenString(Target, 
2411                                                TP, 
2412                                                i * 2,
2413                                                &Ch,
2414                                                &len);
2415                         TabNames[i] = NewStrBufPlain(Ch, -1);
2416                 }
2417                 else { 
2418                         /** A Tab without subject? we can't count that, add it as silent */
2419                         nTabs --;
2420                 }
2421         }
2422
2423         StrTabbedDialog(Target, nTabs, TabNames);
2424         for (i = 0; i < ntabs; i++) {
2425                 StrBeginTab(Target, i, nTabs);
2426                 DoTemplate(TKEY(i * 2 + 1), Target, TP);
2427                 StrEndTab(Target, i, nTabs);
2428         }
2429 }
2430
2431
2432 /*-----------------------------------------------------------------------------
2433  *                      Sorting-API
2434  */
2435
2436
2437 void RegisterSortFunc(const char *name, long len, 
2438                       const char *prepend, long preplen,
2439                       CompareFunc Forward, 
2440                       CompareFunc Reverse, 
2441                       CompareFunc GroupChange, 
2442                       long ContextType)
2443 {
2444         SortStruct *NewSort;
2445
2446         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2447         memset(NewSort, 0, sizeof(SortStruct));
2448         NewSort->Name = NewStrBufPlain(name, len);
2449         if (prepend != NULL)
2450                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2451         else
2452                 NewSort->PrefPrepend = NULL;
2453         NewSort->Forward = Forward;
2454         NewSort->Reverse = Reverse;
2455         NewSort->GroupChange = GroupChange;
2456         NewSort->ContextType = ContextType;
2457         if (ContextType == CTX_NONE) {
2458                 lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
2459                 exit(1);
2460         }
2461                 
2462         Put(SortHash, name, len, NewSort, DestroySortStruct);
2463 }
2464
2465 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2466                          const char *OtherPrefix, long OtherPrefixLen,
2467                          const char *Default, long ldefault, long DefaultDirection)
2468 {
2469         int isdefault = 0;
2470         const StrBuf *BSort = NULL;
2471         SortStruct *SortBy;
2472         void *vSortBy;
2473         long SortOrder = -1;
2474         
2475         if (havebstr("SortBy")) {
2476                 BSort = sbstr("SortBy");
2477                 if (OtherPrefix == NULL) {
2478                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2479                 }
2480                 else {
2481                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2482                 }
2483         }
2484         else { /** Try to fallback to our remembered values... */
2485                 if (OtherPrefix == NULL) {
2486                         BSort = get_room_pref("sort");
2487                 }
2488                 else {
2489                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2490                 }
2491                 if (BSort != NULL)
2492                         putbstr("SortBy", NewStrBufDup(BSort));
2493                 else {
2494                         StrBuf *Buf;
2495
2496                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2497                         putbstr("SortBy", Buf);
2498                 }
2499         }
2500
2501         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2502             (vSortBy == NULL)) {
2503                 isdefault = 1;
2504                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2505                     (vSortBy == NULL)) {
2506                         LogTemplateError(
2507                                 NULL, "Sorting", ERR_PARM1, TP,
2508                                 "Illegal default sort: [%s]", Default);
2509                         wc_backtrace();
2510                 }
2511         }
2512         SortBy = (SortStruct*)vSortBy;
2513
2514         if (SortBy->ContextType != TP->Filter.ContextType)
2515                 return NULL;
2516
2517         /** Ok, its us, lets see in which direction we should sort... */
2518         if (havebstr("SortOrder")) {
2519                 SortOrder = LBSTR("SortOrder");
2520         }
2521         else { /** Try to fallback to our remembered values... */
2522                 StrBuf *Buf = NULL;
2523                 if (SortBy->PrefPrepend == NULL) {
2524                         Buf = get_room_pref("SortOrder");
2525                         SortOrder = StrTol(Buf);
2526                 }
2527                 else {
2528                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2529                 }
2530
2531                 if (Buf == NULL)
2532                         SortOrder = DefaultDirection;
2533
2534                 Buf = NewStrBufPlain(NULL, 64);
2535                 StrBufPrintf(Buf, "%ld", SortOrder);
2536                 putbstr("SortOrder", Buf);
2537         }
2538         switch (SortOrder) {
2539         default:
2540         case 0:
2541                 return NULL;
2542         case 1:
2543                 return SortBy->Forward;
2544         case 2:
2545                 return SortBy->Reverse;
2546         }
2547 }
2548
2549
2550 enum {
2551         eNO_SUCH_SORT, 
2552         eNOT_SPECIFIED,
2553         eINVALID_PARAM,
2554         eFOUND
2555 };
2556
2557 ConstStr SortIcons[] = {
2558         {HKEY("static/sort_none.gif")},
2559         {HKEY("static/up_pointer.gif")},
2560         {HKEY("static/down_pointer.gif")},
2561 };
2562
2563 ConstStr SortNextOrder[] = {
2564         {HKEY("1")},
2565         {HKEY("2")},
2566         {HKEY("0")},
2567 };
2568
2569
2570 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2571 {
2572         int bSortError = eNOT_SPECIFIED;
2573         const StrBuf *BSort;
2574         void *vSort;
2575         
2576         *SortOrder = 0;
2577         *Next = NULL;
2578         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2579             (vSort == NULL))
2580                 return eNO_SUCH_SORT;
2581         *Param = (SortStruct*) vSort;
2582         
2583
2584         if (havebstr("SortBy")) {
2585                 BSort = sbstr("SortBy");
2586                 bSortError = eINVALID_PARAM;
2587                 if ((*Param)->PrefPrepend == NULL) {
2588                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2589                 }
2590                 else {
2591                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2592                 }
2593         }
2594         else { /** Try to fallback to our remembered values... */
2595                 if ((*Param)->PrefPrepend == NULL) {
2596                         BSort = get_room_pref("sort");
2597                 }
2598                 else {
2599                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2600                 }
2601         }
2602
2603         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2604             (vSort == NULL))
2605                 return bSortError;
2606
2607         *Next = (SortStruct*) vSort;
2608
2609         /** Ok, its us, lets see in which direction we should sort... */
2610         if (havebstr("SortOrder")) {
2611                 *SortOrder = LBSTR("SortOrder");
2612         }
2613         else { /** Try to fallback to our remembered values... */
2614                 if ((*Param)->PrefPrepend == NULL) {
2615                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2616                 }
2617                 else {
2618                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2619                 }
2620         }
2621         if (*SortOrder > 2)
2622                 *SortOrder = 0;
2623
2624         return eFOUND;
2625 }
2626
2627
2628 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2629 {
2630         long SortOrder;
2631         SortStruct *Next;
2632         SortStruct *Param;
2633         const ConstStr *SortIcon;
2634
2635         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2636         case eNO_SUCH_SORT:
2637                 LogTemplateError(
2638                         Target, "Sorter", ERR_PARM1, TP,
2639                         " Sorter [%s] unknown!", 
2640                         TP->Tokens->Params[0]->Start);
2641                 break;          
2642         case eINVALID_PARAM:
2643                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2644                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2645                                  bstr("SortBy"));
2646         case eNOT_SPECIFIED:
2647         case eFOUND:
2648                 if (Next == Param) {
2649                         SortIcon = &SortIcons[SortOrder];
2650                 }
2651                 else { /** Not Us... */
2652                         SortIcon = &SortIcons[0];
2653                 }
2654                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2655         }
2656 }
2657
2658 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2659 {
2660         long SortOrder;
2661         SortStruct *Next;
2662         SortStruct *Param;
2663
2664         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2665         case eNO_SUCH_SORT:
2666                 LogTemplateError(
2667                         Target, "Sorter", ERR_PARM1, TP,                                  
2668                         " Sorter [%s] unknown!", 
2669                         TP->Tokens->Params[0]->Start);
2670                 break;          
2671         case eINVALID_PARAM:
2672                 LogTemplateError(
2673                         NULL, "Sorter", ERR_PARM1, TP,
2674                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2675                         bstr("SortBy"));
2676         case eNOT_SPECIFIED:
2677         case eFOUND:
2678                 StrBufAppendBuf(Target, Param->Name, 0);
2679                 
2680         }
2681 }
2682
2683 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2684 {
2685         long SortOrder;
2686         const ConstStr *SortOrderStr;
2687         SortStruct *Next;
2688         SortStruct *Param;
2689
2690         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2691         case eNO_SUCH_SORT:
2692                 LogTemplateError(
2693                         Target, "Sorter", ERR_PARM1, TP,
2694                         " Sorter [%s] unknown!",
2695                         TP->Tokens->Params[0]->Start);
2696                 break;          
2697         case eINVALID_PARAM:
2698                 LogTemplateError(
2699                         NULL, "Sorter", ERR_PARM1, TP,
2700                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2701                         bstr("SortBy"));
2702         case eNOT_SPECIFIED:
2703         case eFOUND:
2704                 if (Next == Param) {
2705                         SortOrderStr = &SortNextOrder[SortOrder];
2706                 }
2707                 else { /** Not Us... */
2708                         SortOrderStr = &SortNextOrder[0];
2709                 }
2710                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2711         }
2712 }
2713
2714
2715 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2716 {
2717         long *LongVector = (long*) CTX;
2718
2719         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2720             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2721         {
2722                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2723         }
2724         else
2725         {
2726                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2727                         LogTemplateError(
2728                                 Target, "Longvector", ERR_NAME, TP,
2729                                 "needs a numerical Parameter!");
2730                 }
2731                 else {
2732                         LogTemplateError(
2733                                 Target, "LongVector", ERR_PARM1, TP,
2734                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2735                                 TP->Tokens->Params[0]->lvalue,
2736                                 LongVector[0]);
2737                 }
2738         }
2739 }
2740
2741 void dbg_print_longvector(long *LongVector)
2742 {
2743         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2744         int nItems = LongVector[0];
2745         int i;
2746
2747         for (i = 0; i < nItems; i++) {
2748                 if (i + 1 < nItems)
2749                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2750                 else
2751                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2752
2753         }
2754         lprintf(1, ChrPtr(Buf));
2755         FreeStrBuf(&Buf);
2756 }
2757
2758 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2759 {
2760         long *LongVector = (long*) CTX;
2761
2762         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2763             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2764             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2765             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2766         {
2767                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2768                         LongVector[TP->Tokens->Params[3]->lvalue];
2769         }
2770         else
2771         {
2772                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2773                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2774                         LogTemplateError(
2775                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2776                                 "needs two long Parameter!");
2777                 }
2778                 else {
2779                         LogTemplateError(
2780                                 Target, "Longvector", ERR_PARM1, TP,
2781                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2782                                 TP->Tokens->Params[2]->lvalue,
2783                                 TP->Tokens->Params[3]->lvalue,
2784                                 LongVector[0]);
2785                 }
2786         }
2787         return 0;
2788 }
2789
2790
2791 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
2792 {
2793         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
2794 }
2795
2796 void 
2797 InitModule_SUBST
2798 (void)
2799 {
2800         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2801         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
2802         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
2803         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
2804         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
2805         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
2806         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
2807         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, NULL, CTX_NONE);
2808         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
2809         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2810         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2811         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2812
2813         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2814                                    conditional_ITERATE_ISGROUPCHANGE, 
2815                                    CTX_ITERATE);
2816         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2817                                    conditional_ITERATE_LASTN, 
2818                                    CTX_ITERATE);
2819         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2820                                    conditional_ITERATE_FIRSTN, 
2821                                    CTX_ITERATE);
2822
2823         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2824         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2825         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2826         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
2827
2828
2829 }
2830
2831 void
2832 ServerStartModule_SUBST
2833 (void)
2834 {
2835         WirelessTemplateCache = NewHash(1, NULL);
2836         WirelessLocalTemplateCache = NewHash(1, NULL);
2837         LocalTemplateCache = NewHash(1, NULL);
2838         TemplateCache = NewHash(1, NULL);
2839
2840         GlobalNS = NewHash(1, NULL);
2841         Iterators = NewHash(1, NULL);
2842         Conditionals = NewHash(1, NULL);
2843         SortHash = NewHash(1, NULL);
2844         Defines = NewHash(1, NULL);
2845 }
2846
2847 void
2848 FinalizeModule_SUBST
2849 (void)
2850 {
2851
2852 }
2853
2854 void 
2855 ServerShutdownModule_SUBST
2856 (void)
2857 {
2858         DeleteHash(&WirelessTemplateCache);
2859         DeleteHash(&WirelessLocalTemplateCache);
2860         DeleteHash(&TemplateCache);
2861         DeleteHash(&LocalTemplateCache);
2862
2863         DeleteHash(&GlobalNS);
2864         DeleteHash(&Iterators);
2865         DeleteHash(&Conditionals);
2866         DeleteHash(&SortHash);
2867         DeleteHash(&Defines);
2868 }
2869
2870
2871 void
2872 SessionNewModule_SUBST
2873 (wcsession *sess)
2874 {
2875
2876 }
2877
2878 void
2879 SessionAttachModule_SUBST
2880 (wcsession *sess)
2881 {
2882         sess->vars = NewHash(1,NULL);
2883 }
2884
2885 void
2886 SessionDetachModule_SUBST
2887 (wcsession *sess)
2888 {
2889         DeleteHash(&sess->vars);
2890 }
2891
2892 void 
2893 SessionDestroyModule_SUBST  
2894 (wcsession *sess)
2895 {
2896
2897 }