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