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