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