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