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