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