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