* check whether a template exists
[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 void GetTemplateTokenString(StrBuf *Target, 
770                             WCTemplputParams *TP,
771                             int N,
772                             const char **Value, 
773                             long *len)
774 {
775         StrBuf *Buf;
776         WCTemplputParams SubTP;
777
778         if (N >= TP->Tokens->nParameters) {
779                 LogTemplateError(Target, 
780                                  "TokenParameter", N, TP, 
781                                  "invalid token %d. this shouldn't have come till here.\n", N);
782                 *Value = "";
783                 *len = 0;
784                 return;
785         }
786
787         switch (TP->Tokens->Params[N]->Type) {
788
789         case TYPE_INTDEFINE:
790         case TYPE_STR:
791                 *Value = TP->Tokens->Params[N]->Start;
792                 *len = TP->Tokens->Params[N]->len;
793                 break;
794         case TYPE_BSTR:
795                 if (TP->Tokens->Params[N]->len == 0) {
796                         LogTemplateError(Target, 
797                                          "TokenParameter", N, TP, 
798                                          "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
799                         *len = 0;
800                         *Value = EmptyStr;
801                         break;
802                 }
803                 Buf = (StrBuf*) SBstr(TKEY(N));
804                 *Value = ChrPtr(Buf);
805                 *len = StrLength(Buf);
806                 break;
807         case TYPE_PREFSTR:
808                 if (TP->Tokens->Params[N]->len == 0) {
809                         LogTemplateError(Target, 
810                                          "TokenParameter", N, TP, 
811                                          "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
812                         *len = 0;
813                         *Value = EmptyStr;
814                         break;
815                 }
816                 get_PREFERENCE(TKEY(N), &Buf);
817                 *Value = ChrPtr(Buf);
818                 *len = StrLength(Buf);
819                 break;
820         case TYPE_LONG:
821                 LogTemplateError(Target, 
822                                  "TokenParameter", N, TP, 
823                                  "Requesting parameter %d; of type LONG, want string.", N);
824                 break;
825         case TYPE_PREFINT:
826                 LogTemplateError(Target, 
827                                  "TokenParameter", N, TP, 
828                                  "Requesting parameter %d; of type PREFINT, want string.", N);
829                 break;
830         case TYPE_GETTEXT:
831                 *Value = _(TP->Tokens->Params[N]->Start);
832                 *len = strlen(*Value);
833                 break;
834         case TYPE_SUBTEMPLATE:
835                 if (TP->Tokens->Params[N]->len == 0) {
836                         LogTemplateError(Target, 
837                                          "TokenParameter", N, TP, 
838                                          "Requesting parameter %d; of type SUBTEMPLATE, empty lookup string not admitted.", N);
839                         *len = 0;
840                         *Value = EmptyStr;
841                         break;
842                 }
843
844                 memset(&SubTP, 0, sizeof(WCTemplputParams *));
845                 SubTP.Context = TP->Context;
846                 SubTP.Filter.ContextType = TP->Filter.ContextType;
847                 Buf = NewStrBuf();
848                 DoTemplate(TKEY(N), Buf, &SubTP);
849                 *Value = ChrPtr(Buf);
850                 *len = StrLength(Buf);
851                 /* we can't free it here, so we put it into the subst so its discarded later on. */
852                 SVPUTBuf(TKEY(N), Buf, 0);
853                 break;
854
855         default:
856                 LogTemplateError(Target, 
857                                  "TokenParameter", N, TP, 
858                                  "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
859                 break;
860         }
861 }
862
863 long GetTemplateTokenNumber(StrBuf *Target, WCTemplputParams *TP, int N, long dflt)
864 {
865         long Ret;
866         if (N >= TP->Tokens->nParameters) {
867                 LogTemplateError(Target, 
868                                  "TokenParameter", N, TP, 
869                                  "invalid token %d. this shouldn't have come till here.\n", N);
870                 wc_backtrace(); 
871                 return 0;
872         }
873
874         switch (TP->Tokens->Params[N]->Type) {
875
876         case TYPE_STR:
877                 return atol(TP->Tokens->Params[N]->Start);
878                 break;
879         case TYPE_BSTR:
880                 if (TP->Tokens->Params[N]->len == 0) {
881                         LogTemplateError(Target, 
882                                          "TokenParameter", N, TP, 
883                                          "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
884                         return 0;
885                 }
886                 return  LBstr(TKEY(N));
887                 break;
888         case TYPE_PREFSTR:
889                 LogTemplateError(Target, 
890                                  "TokenParameter", N, TP, 
891                                  "requesting a prefstring in param %d want a number", N);
892                 if (TP->Tokens->Params[N]->len == 0) {
893                         LogTemplateError(Target, 
894                                          "TokenParameter", N, TP, 
895                                          "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
896                         return 0;
897                 }
898                 if (get_PREF_LONG(TKEY(N), &Ret, dflt))
899                         return Ret;
900                 return 0;
901         case TYPE_INTDEFINE:
902         case TYPE_LONG:
903                 return TP->Tokens->Params[N]->lvalue;
904         case TYPE_PREFINT:
905                 if (TP->Tokens->Params[N]->len == 0) {
906                         LogTemplateError(Target, 
907                                          "TokenParameter", N, TP, 
908                                          "Requesting parameter %d; of type PREFINT, empty lookup string not admitted.", N);
909                         return 0;
910                 }
911                 if (get_PREF_LONG(TKEY(N), &Ret, dflt))
912                         return Ret;
913                 return 0;               
914         case TYPE_GETTEXT:
915                 LogTemplateError(Target, 
916                                  "TokenParameter", N, TP, 
917                                  "requesting a I18N string in param %d; want a number", N);
918                 return 0;
919         case TYPE_SUBTEMPLATE:
920                 LogTemplateError(Target, 
921                                  "TokenParameter", N, TP, 
922                                  "requesting a subtemplate in param %d; not supported for numbers", N);
923                 return 0;
924         default:
925                 LogTemplateError(Target, 
926                                  "TokenParameter", N, TP, 
927                                  "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
928                 return 0;
929         }
930 }
931
932
933
934 /**
935  * \brief Print the value of a variable
936  * \param keyname get a key to print
937  */
938 void print_value_of(StrBuf *Target, WCTemplputParams *TP)
939  
940 {
941         wcsession *WCC = WC;
942         wcsubst *ptr;
943         void *vVar;
944
945         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
946         /* TODO: depricated! */
947         if (TP->Tokens->pName[0] == '=') {
948                 DoTemplate(TP->Tokens->pName+1, TP->Tokens->NameEnd - 1, NULL, &NoCtx);
949         }
950 /*/////TODO: if param[1] == "U" -> urlescape
951 /// X -> escputs */
952         /** Page-local variables */
953         if ((WCC->vars!= NULL) && GetHash(WCC->vars, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
954                 ptr = (wcsubst*) vVar;
955                 switch(ptr->wcs_type) {
956                 case WCS_STRING:
957                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
958                         break;
959                 case WCS_SERVCMD:
960                         pvo_do_cmd(Target, ptr->wcs_value);
961                         break;
962                 case WCS_FUNCTION:
963                         (*ptr->wcs_function) (Target, TP);
964                         break;
965                 case WCS_STRBUF:
966                 case WCS_STRBUF_REF:
967                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
968                         break;
969                 case WCS_LONG:
970                         StrBufAppendPrintf(Target, "%ld", ptr->lvalue);
971                         break;
972                 default:
973                         LogTemplateError(
974                                 Target, "Subst", ERR_NAME, TP,
975                                 "WARNING: invalid value in SV-Hash at %s!", TP->Tokens->pName);
976                 }
977         }
978         else {
979                 LogTemplateError(
980                         Target, "Token", ERR_NAME, TP,
981                         "didn't find Handler \"%s\"", TP->Tokens->pName);
982                 wc_backtrace();
983         }
984 }
985
986 int CompareSubstToToken(TemplateParam *ParamToCompare, TemplateParam *ParamToLookup)
987 {
988         wcsession *WCC = WC;
989         wcsubst *ptr;
990         void *vVar;
991
992         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
993                                           ParamToLookup->len, &vVar)) {
994                 ptr = (wcsubst*) vVar;
995                 switch(ptr->wcs_type) {
996                 case WCS_STRING:
997                 case WCS_STRBUF:
998                 case WCS_STRBUF_REF:
999                         if (ParamToCompare->Type == TYPE_STR)
1000                                 return ((ParamToCompare->len == StrLength(ptr->wcs_value)) &&
1001                                         (strcmp(ParamToCompare->Start, ChrPtr(ptr->wcs_value)) == 0));
1002                         else
1003                                 return ParamToCompare->lvalue == StrTol(ptr->wcs_value);
1004                         break;
1005                 case WCS_SERVCMD:
1006                         return 1; 
1007                         break;
1008                 case WCS_FUNCTION:
1009                         return 1;
1010                 case WCS_LONG:
1011                         if (ParamToCompare->Type == TYPE_STR)
1012                                 return 0;
1013                         else 
1014                                 return ParamToCompare->lvalue == ptr->lvalue;
1015                         break;
1016                 default:
1017                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
1018                                 ParamToLookup->Start);
1019                 }
1020         }
1021         return 0;
1022 }
1023
1024 int CompareSubstToStrBuf(StrBuf *Compare, TemplateParam *ParamToLookup)
1025 {
1026         wcsession *WCC = WC;
1027         wcsubst *ptr;
1028         void *vVar;
1029
1030         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
1031                                           ParamToLookup->len, &vVar)) {
1032                 ptr = (wcsubst*) vVar;
1033                 switch(ptr->wcs_type) {
1034                 case WCS_STRING:
1035                 case WCS_STRBUF:
1036                 case WCS_STRBUF_REF:
1037                         return ((StrLength(Compare) == StrLength(ptr->wcs_value)) &&
1038                                 (strcmp(ChrPtr(Compare), ChrPtr(ptr->wcs_value)) == 0));
1039                 case WCS_SERVCMD:
1040                         return 1; 
1041                         break;
1042                 case WCS_FUNCTION:
1043                         return 1;
1044                 case WCS_LONG:
1045                         return StrTol(Compare) == ptr->lvalue;
1046                 default:
1047                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
1048                                 ParamToLookup->Start);
1049                 }
1050         }
1051         return 0;
1052 }
1053
1054
1055
1056 /**
1057  * \brief puts string into the template and computes which escape methon we should use
1058  * \param Source the string we should put into the template
1059  * \param FormatTypeIndex where should we look for escape types if?
1060  */
1061 void StrBufAppendTemplate(StrBuf *Target, 
1062                           WCTemplputParams *TP,
1063                           const StrBuf *Source, int FormatTypeIndex)
1064 {
1065         wcsession *WCC;
1066         char EscapeAs = ' ';
1067
1068         if ((FormatTypeIndex < TP->Tokens->nParameters) &&
1069             (TP->Tokens->Params[FormatTypeIndex]->Type == TYPE_STR) &&
1070             (TP->Tokens->Params[FormatTypeIndex]->len == 1)) {
1071                 EscapeAs = *TP->Tokens->Params[FormatTypeIndex]->Start;
1072         }
1073
1074         switch(EscapeAs)
1075         {
1076         case 'H':
1077                 WCC = WC;
1078                 StrEscAppend(Target, Source, NULL, 0, 2);
1079                 break;
1080         case 'X':
1081                 StrEscAppend(Target, Source, NULL, 0, 0);
1082                 break;
1083         case 'J':
1084                 StrECMAEscAppend(Target, Source, NULL);
1085           break;
1086         case 'K':
1087                 StrHtmlEcmaEscAppend(Target, Source, NULL, 0, 0);
1088           break;
1089         case 'U':
1090                 StrBufUrlescAppend(Target, Source, NULL);
1091                 break;
1092         default:
1093                 StrBufAppendBuf(Target, Source, 0);
1094         }
1095 }
1096
1097
1098 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
1099 {
1100         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
1101                 if (Template->TokenSpace <= 0) {
1102                         Template->Tokens = (WCTemplateToken**)malloc(
1103                                 sizeof(WCTemplateToken*) * 10);
1104                         memset(Template->Tokens, 0, sizeof(WCTemplateToken*) * 10);
1105                         Template->TokenSpace = 10;
1106                 }
1107                 else {
1108                         WCTemplateToken **NewTokens;
1109
1110                         NewTokens= (WCTemplateToken**) malloc(
1111                                 sizeof(WCTemplateToken*) * Template->TokenSpace * 2);
1112
1113                         memset(NewTokens, 
1114                                0, sizeof(WCTemplateToken*) * Template->TokenSpace * 2);
1115
1116                         memcpy(NewTokens, 
1117                                Template->Tokens, 
1118                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
1119
1120                         free(Template->Tokens);
1121                         Template->TokenSpace *= 2;
1122                         Template->Tokens = NewTokens;
1123                 }
1124         }
1125         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
1126 }
1127
1128 TemplateParam *GetNextParameter(StrBuf *Buf, 
1129                                 const char **pCh, 
1130                                 const char *pe, 
1131                                 WCTemplateToken *Tokens, 
1132                                 WCTemplate *pTmpl, 
1133                                 WCTemplputParams *TP)
1134 {
1135         const char *pch = *pCh;
1136         const char *pchs, *pche;
1137         TemplateParam *Parm;
1138         char quote = '\0';
1139         int ParamBrace = 0;
1140
1141         Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
1142         memset(Parm, 0, sizeof(TemplateParam));
1143         Parm->Type = TYPE_STR;
1144
1145         /* Skip leading whitespaces */
1146         while ((*pch == ' ' )||
1147                (*pch == '\t')||
1148                (*pch == '\r')||
1149                (*pch == '\n')) pch ++;
1150
1151         if (*pch == ':') {
1152                 Parm->Type = TYPE_PREFSTR;
1153                 pch ++;
1154                 if (*pch == '(') {
1155                         pch ++;
1156                         ParamBrace = 1;
1157                 }
1158         }
1159         else if (*pch == ';') {
1160                 Parm->Type = TYPE_PREFINT;
1161                 pch ++;
1162                 if (*pch == '(') {
1163                         pch ++;
1164                         ParamBrace = 1;
1165                 }
1166         }
1167         else if (*pch == '#') {
1168                 Parm->Type = TYPE_INTDEFINE;
1169                 pch ++;
1170         }
1171         else if (*pch == '_') {
1172                 Parm->Type = TYPE_GETTEXT;
1173                 pch ++;
1174                 if (*pch == '(') {
1175                         pch ++;
1176                         ParamBrace = 1;
1177                 }
1178         }
1179         else if (*pch == 'B') {
1180                 Parm->Type = TYPE_BSTR;
1181                 pch ++;
1182                 if (*pch == '(') {
1183                         pch ++;
1184                         ParamBrace = 1;
1185                 }
1186         }
1187         else if (*pch == '=') {
1188                 Parm->Type = TYPE_SUBTEMPLATE;
1189                 pch ++;
1190                 if (*pch == '(') {
1191                         pch ++;
1192                         ParamBrace = 1;
1193                 }
1194         }
1195
1196
1197         if (*pch == '"')
1198                 quote = '"';
1199         else if (*pch == '\'')
1200                 quote = '\'';
1201         if (quote != '\0') {
1202                 pch ++;
1203                 pchs = pch;
1204                 while (pch <= pe &&
1205                        ((*pch != quote) ||
1206                         ( (pch > pchs) && (*(pch - 1) == '\\'))
1207                                )) {
1208                         pch ++;
1209                 }
1210                 pche = pch;
1211                 if (*pch != quote) {
1212                         lprintf(1, "Error (in '%s' line %ld); "
1213                                 "evaluating template param [%s] in Token [%s]\n",
1214                                 ChrPtr(pTmpl->FileName),
1215                                 Tokens->Line,
1216                                 ChrPtr(Tokens->FlatToken),
1217                                 *pCh);
1218                         pch ++;
1219                         free(Parm);
1220                         return NULL;
1221                 }
1222                 else {
1223                         StrBufPeek(Buf, pch, -1, '\0');         
1224                         if (LoadTemplates > 1) {                        
1225                                 lprintf(1, "DBG: got param [%s] %ld %ld\n", 
1226                                         pchs, pche - pchs, strlen(pchs));
1227                         }
1228                         Parm->Start = pchs;
1229                         Parm->len = pche - pchs;
1230                         pch ++; /* move after trailing quote */
1231                         if (ParamBrace && (*pch == ')')) {
1232                                 pch ++;
1233                         }
1234
1235                 }
1236         }
1237         else {
1238                 Parm->Type = TYPE_LONG;
1239                 pchs = pch;
1240                 while ((pch <= pe) &&
1241                        (isdigit(*pch) ||
1242                         (*pch == '+') ||
1243                         (*pch == '-')))
1244                         pch ++;
1245                 pch ++;
1246                 if (pch - pchs > 1){
1247                         StrBufPeek(Buf, pch, -1, '\0');
1248                         Parm->lvalue = atol(pchs);
1249                         Parm->Start = pchs;
1250                         pch++;
1251                 }
1252                 else {
1253                         Parm->lvalue = 0;
1254 /* TODO whUT?
1255                         lprintf(1, "Error (in '%s' line %ld); "
1256                                 "evaluating long template param [%s] in Token [%s]\n",
1257                                 ChrPtr(pTmpl->FileName),
1258                                 Tokens->Line,
1259                                 ChrPtr(Tokens->FlatToken),
1260                                 *pCh);
1261                                 */
1262                         free(Parm);
1263                         return NULL;
1264                 }
1265         }
1266         while ((*pch == ' ' )||
1267                (*pch == '\t')||
1268                (*pch == '\r')||
1269                (*pch == ',' )||
1270                (*pch == '\n')) pch ++;
1271
1272         switch (Parm->Type)
1273         {
1274         case TYPE_GETTEXT:
1275                 if (DumpTemplateI18NStrings) {
1276                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
1277                 }
1278                 break;
1279         case TYPE_INTDEFINE: {
1280                 void *vPVal;
1281                 
1282                 if (GetHash(Defines, Parm->Start, Parm->len, &vPVal) &&
1283                     (vPVal != NULL))
1284                 {
1285                         long *PVal;
1286                         PVal = (long*) vPVal;
1287                 
1288                         Parm->lvalue = *PVal;
1289                 }
1290                 else 
1291                 {
1292                         LogTemplateError(NULL, "Define", ERR_PARM1, TP,
1293                                          "%s isn't known!!",
1294                                          Parm->Start);
1295                 }}
1296                 break;
1297         case TYPE_SUBTEMPLATE:{
1298                 void *vTmpl;
1299                 /* well, we don't check the mobile stuff here... */
1300                 if (!GetHash(LocalTemplateCache, Parm->Start, Parm->len, &vTmpl) &&
1301                     !GetHash(TemplateCache, Parm->Start, Parm->len, &vTmpl)) {
1302                         LogTemplateError(
1303                                 NULL, "SubTemplate", ERR_PARM1, TP,
1304                                 "referenced here doesn't exist");
1305                 }}
1306                 break;
1307         }
1308         *pCh = pch;
1309         return Parm;
1310 }
1311
1312 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
1313                                        const char *pStart, 
1314                                        const char *pTokenStart, 
1315                                        const char *pTokenEnd, 
1316                                        long Line,
1317                                        WCTemplate *pTmpl)
1318 {
1319         void *vVar;
1320         const char *pch;
1321         TemplateParam *Param;
1322         WCTemplateToken *NewToken;
1323         WCTemplputParams TP;
1324
1325         NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
1326         memset(NewToken, 0, sizeof(WCTemplateToken));
1327         TP.Tokens = NewToken;
1328         NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
1329         NewToken->Flags = 0;
1330         NewToken->Line = Line + 1;
1331         NewToken->pTokenStart = pTokenStart;
1332         NewToken->TokenStart = pTokenStart - pStart;
1333         NewToken->TokenEnd =  (pTokenEnd - pStart) - NewToken->TokenStart;
1334         NewToken->pTokenEnd = pTokenEnd;
1335         NewToken->NameEnd = NewToken->TokenEnd - 2;
1336         NewToken->PreEval = NULL;
1337         NewToken->FlatToken = NewStrBufPlain(pTokenStart + 2, pTokenEnd - pTokenStart - 2);
1338         StrBufShrinkToFit(NewToken->FlatToken, 1);
1339
1340         StrBufPeek(Buf, pTokenStart, + 1, '\0');
1341         StrBufPeek(Buf, pTokenEnd, -1, '\0');
1342         pch = NewToken->pName = pTokenStart + 2;
1343
1344         NewToken->HaveParameters = 0;;
1345         NewToken->nParameters = 0;
1346
1347         while (pch < pTokenEnd - 1) {
1348                 if (*pch == '(') {
1349                         StrBufPeek(Buf, pch, -1, '\0');
1350                         NewToken->NameEnd = pch - NewToken->pName;
1351                         pch ++;
1352                         if (*(pTokenEnd - 1) != ')') {
1353                                 LogTemplateError(
1354                                         NULL, "Parseerror", ERR_NAME, &TP, 
1355                                         "Warning, Non welformed Token; missing right parenthesis");
1356                         }
1357                         while (pch < pTokenEnd - 1) {
1358                                 Param = GetNextParameter(Buf, &pch, pTokenEnd - 1, NewToken, pTmpl, &TP);
1359                                 if (Param != NULL) {
1360                                         NewToken->HaveParameters = 1;
1361                                         if (NewToken->nParameters > MAXPARAM) {
1362                                                 LogTemplateError(
1363                                                         NULL, "Parseerror", ERR_NAME, &TP,
1364                                                         "only [%d] Params allowed in Tokens",
1365                                                         MAXPARAM);
1366
1367                                                 free(Param);
1368                                                 FreeToken(&NewToken);
1369                                                 return NULL;
1370                                         }
1371                                         NewToken->Params[NewToken->nParameters++] = Param;
1372                                 }
1373                                 else break;
1374                         }
1375                         if((NewToken->NameEnd == 1) &&
1376                            (NewToken->HaveParameters == 1))
1377                            
1378                         {
1379                                 if (*(NewToken->pName) == '_')
1380                                         NewToken->Flags = SV_GETTEXT;
1381                                 else if (*(NewToken->pName) == '=')
1382                                         NewToken->Flags = SV_SUBTEMPL;
1383                                 else if (*(NewToken->pName) == '%')
1384                                         NewToken->Flags = SV_CUST_STR_CONDITIONAL;
1385                                 else if (*(NewToken->pName) == '?')
1386                                         NewToken->Flags = SV_CONDITIONAL;
1387                                 else if (*(NewToken->pName) == '!')
1388                                         NewToken->Flags = SV_NEG_CONDITIONAL;
1389                         }
1390                 }
1391                 else pch ++;            
1392         }
1393         
1394         switch (NewToken->Flags) {
1395         case 0:
1396                 /* If we're able to find out more about the token, do it now while its fresh. */
1397                 if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
1398                         HashHandler *Handler;
1399                         Handler = (HashHandler*) vVar;
1400                         if ((NewToken->nParameters < Handler->Filter.nMinArgs) || 
1401                             (NewToken->nParameters > Handler->Filter.nMaxArgs)) {
1402                                 LogTemplateError(
1403                                         NULL, "Token", ERR_NAME, &TP,
1404                                         "doesn't work with %d params", 
1405                                         NewToken->nParameters);
1406
1407                         }
1408                         else {
1409                                 NewToken->PreEval = Handler;
1410                                 NewToken->Flags = SV_PREEVALUATED;              
1411                                 if (Handler->PreEvalFunc != NULL)
1412                                         Handler->PreEvalFunc(NewToken);
1413                         }
1414                 }
1415                 break;
1416         case SV_GETTEXT:
1417                 if (NewToken->nParameters !=1) {
1418                         LogTemplateError(                               
1419                                 NULL, "Gettext", ERR_NAME, &TP,
1420                                 "requires exactly 1 parameter, you gave %d params", 
1421                                 NewToken->nParameters);
1422                         NewToken->Flags = 0;
1423                         break;
1424                 }
1425                 if (DumpTemplateI18NStrings) {
1426                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
1427                 }
1428                 break;
1429         case SV_SUBTEMPL:
1430                 if (NewToken->nParameters != 1) {
1431                         LogTemplateError(
1432                                 NULL, "Subtemplates", ERR_NAME, &TP,
1433                                 "require exactly 1 parameter, you gave %d params", 
1434                                 NewToken->nParameters);
1435                         break;
1436                 }
1437                 else {
1438                         void *vTmpl;
1439                         /* well, we don't check the mobile stuff here... */
1440                         if (!GetHash(LocalTemplateCache, 
1441                                      NewToken->Params[0]->Start, 
1442                                      NewToken->Params[0]->len, 
1443                                      &vTmpl) &&
1444                             !GetHash(TemplateCache, 
1445                                      NewToken->Params[0]->Start, 
1446                                      NewToken->Params[0]->len, 
1447                                      &vTmpl)) {
1448                                 LogTemplateError(
1449                                         NULL, "SubTemplate", ERR_PARM1, &TP,
1450                                         "doesn't exist");
1451                         }
1452                 }
1453                 break;
1454         case SV_CUST_STR_CONDITIONAL:
1455         case SV_CONDITIONAL:
1456         case SV_NEG_CONDITIONAL:
1457                 if (NewToken->nParameters <2) {
1458                         LogTemplateError(
1459                                 NULL, "Conditional", ERR_PARM1, &TP,
1460                                 "require at least 2 parameters, you gave %d params", 
1461                                 NewToken->nParameters);
1462                         NewToken->Flags = 0;
1463                         break;
1464                 }
1465                 if (NewToken->Params[1]->lvalue == 0) {
1466                         LogTemplateError(
1467                                 NULL, "Conditional", ERR_PARM1, &TP,
1468                                 "Conditional ID (Parameter 1) mustn't be 0!");
1469                         NewToken->Flags = 0;
1470                         break;
1471                 }
1472                 if (!GetHash(Conditionals, 
1473                              NewToken->Params[0]->Start, 
1474                              NewToken->Params[0]->len, 
1475                              &vVar) || 
1476                     (vVar == NULL)) {
1477                         if ((NewToken->Params[0]->len == 1) &&
1478                             (NewToken->Params[0]->Start[0] == 'X'))
1479                                 break;
1480                         LogTemplateError(
1481                                 NULL, "Conditional", ERR_PARM1, &TP,
1482                                 "Not found!");
1483 /*
1484                         NewToken->Error = NewStrBuf();
1485                         StrBufAppendPrintf(
1486                                 NewToken->Error, 
1487                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
1488                                 NewToken->Params[0]->Start,
1489                                 ChrPtr(pTmpl->FileName),
1490                                 NewToken->Line,
1491                                 ChrPtr(NewToken->FlatToken));
1492 */
1493                 }
1494                 else {
1495                         NewToken->PreEval = vVar;
1496                 }
1497                 break;
1498         }
1499         return NewToken;
1500 }
1501
1502
1503
1504
1505
1506 /**
1507  * \brief Display a variable-substituted template
1508  * \param templatename template file to load
1509  */
1510 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1511 {
1512         WCTemplate *NewTemplate;
1513
1514         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1515         memset(NewTemplate, 0, sizeof(WCTemplate));
1516         NewTemplate->Data = NULL;
1517         NewTemplate->FileName = NewStrBufDup(filename);
1518         StrBufShrinkToFit(NewTemplate->FileName, 1);
1519         NewTemplate->nTokensUsed = 0;
1520         NewTemplate->TokenSpace = 0;
1521         NewTemplate->Tokens = NULL;
1522         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1523         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1524                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1525         }
1526
1527         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1528                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1529         }
1530
1531         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1532         return NewTemplate;
1533 }
1534
1535 /**
1536  * \brief Display a variable-substituted template
1537  * \param templatename template file to load
1538  */
1539 void *duplicate_template(WCTemplate *OldTemplate)
1540 {
1541         WCTemplate *NewTemplate;
1542
1543         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1544         memset(NewTemplate, 0, sizeof(WCTemplate));
1545         NewTemplate->Data = NULL;
1546         NewTemplate->FileName = NewStrBufDup(OldTemplate->FileName);
1547         StrBufShrinkToFit(NewTemplate->FileName, 1);
1548         NewTemplate->nTokensUsed = 0;
1549         NewTemplate->TokenSpace = 0;
1550         NewTemplate->Tokens = NULL;
1551         NewTemplate->MimeType = NewStrBufDup(OldTemplate->MimeType);
1552         return NewTemplate;
1553 }
1554
1555 /**
1556  * \brief Display a variable-substituted template
1557  * \param templatename template file to load
1558  */
1559 void *load_template(WCTemplate *NewTemplate)
1560 {
1561         int fd;
1562         struct stat statbuf;
1563         const char *pS, *pE, *pch, *Err;
1564         long Line;
1565         int pos;
1566
1567         fd = open(ChrPtr(NewTemplate->FileName), O_RDONLY);
1568         if (fd <= 0) {
1569                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
1570                         ChrPtr(NewTemplate->FileName), strerror(errno));
1571                 return NULL;
1572         }
1573
1574         if (fstat(fd, &statbuf) == -1) {
1575                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
1576                         ChrPtr(NewTemplate->FileName), strerror(errno));
1577                 return NULL;
1578         }
1579
1580         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size + 1);
1581         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1582                 close(fd);
1583                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
1584                         ChrPtr(NewTemplate->FileName), strerror(errno));
1585                 //FreeWCTemplate(NewTemplate);/////tODO
1586                 return NULL;
1587         }
1588         close(fd);
1589
1590         Line = 0;
1591         StrBufShrinkToFit(NewTemplate->Data, 1);
1592         StrBufShrinkToFit(NewTemplate->MimeType, 1);
1593         pS = pch = ChrPtr(NewTemplate->Data);
1594         pE = pS + StrLength(NewTemplate->Data);
1595         while (pch < pE) {
1596                 const char *pts, *pte;
1597                 int InQuotes = 0;
1598                 int InDoubleQuotes = 0;
1599
1600                 /** Find one <? > */
1601                 pos = (-1);
1602                 for (; pch < pE; pch ++) {
1603                         if ((*pch=='<')&&(*(pch + 1)=='?') &&
1604                             !((pch == pS) && /* we must ommit a <?xml */
1605                               (*(pch + 2) == 'x') && 
1606                               (*(pch + 3) == 'm') && 
1607                               (*(pch + 4) == 'l')))                          
1608                                 break;
1609                         if (*pch=='\n') Line ++;
1610                 }
1611                 if (pch >= pE)
1612                         continue;
1613                 pts = pch;
1614
1615                 /** Found one? parse it. */
1616                 for (; pch <= pE - 1; pch ++) {
1617                         if (*pch == '"')
1618                                 InDoubleQuotes = ! InDoubleQuotes;
1619                         else if (*pch == '\'')
1620                                 InQuotes = ! InQuotes;
1621                         else if ((!InQuotes  && !InDoubleQuotes) &&
1622                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1623                                 pch ++;
1624                                 break;
1625                         }
1626                 }
1627                 if (pch + 1 > pE)
1628                         continue;
1629                 pte = pch;
1630                 PutNewToken(NewTemplate, 
1631                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate));
1632                 pch ++;
1633         }
1634         return NewTemplate;
1635 }
1636
1637
1638 const char* PrintTemplate(void *vSubst)
1639 {
1640         WCTemplate *Tmpl = vSubst;
1641
1642         return ChrPtr(Tmpl->FileName);
1643
1644 }
1645
1646 int LoadTemplateDir(const StrBuf *DirName, HashList *wireless, HashList *big, const StrBuf *BaseKey)
1647 {
1648         int Toplevel;
1649         StrBuf *FileName;
1650         StrBuf *Key;
1651         StrBuf *SubKey;
1652         StrBuf *SubDirectory;
1653         DIR *filedir = NULL;
1654         struct dirent *filedir_entry;
1655         struct dirent *d;
1656         int d_type = 0;
1657         int d_namelen;
1658         int d_without_ext;
1659         int IsMobile;
1660         
1661         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
1662         if (d == NULL) {
1663                 return 0;
1664         }
1665
1666         filedir = opendir (ChrPtr(DirName));
1667         if (filedir == NULL) {
1668                 free(d);
1669                 return 0;
1670         }
1671
1672         Toplevel = StrLength(BaseKey) == 0;
1673         SubDirectory = NewStrBuf();
1674         SubKey = NewStrBuf();
1675         FileName = NewStrBufPlain(NULL, PATH_MAX);
1676         Key = NewStrBuf();
1677         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
1678                (filedir_entry != NULL))
1679         {
1680                 char *MinorPtr;
1681                 char *PStart;
1682 #ifdef _DIRENT_HAVE_D_NAMELEN
1683                 d_namelen = filedir_entry->d_namelen;
1684                 d_type = filedir_entry->d_type;
1685 #else
1686
1687 #ifndef DT_UNKNOWN
1688 #define DT_UNKNOWN     0
1689 #define DT_DIR         4
1690 #define DT_REG         8
1691 #define DT_LNK         10
1692
1693 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
1694 #define DTTOIF(dirtype)        ((dirtype) << 12)
1695 #endif
1696                 d_namelen = strlen(filedir_entry->d_name);
1697                 d_type = DT_UNKNOWN;
1698 #endif
1699                 d_without_ext = d_namelen;
1700
1701                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1702                         continue; /* Ignore backup files... */
1703
1704                 if ((d_namelen == 1) && 
1705                     (filedir_entry->d_name[0] == '.'))
1706                         continue;
1707
1708                 if ((d_namelen == 2) && 
1709                     (filedir_entry->d_name[0] == '.') &&
1710                     (filedir_entry->d_name[1] == '.'))
1711                         continue;
1712
1713                 if (d_type == DT_UNKNOWN) {
1714                         struct stat s;
1715                         char path[PATH_MAX];
1716                         snprintf(path, PATH_MAX, "%s/%s", 
1717                                  ChrPtr(DirName), filedir_entry->d_name);
1718                         if (stat(path, &s) == 0) {
1719                                 d_type = IFTODT(s.st_mode);
1720                         }
1721                 }
1722                 switch (d_type)
1723                 {
1724                 case DT_DIR:
1725                         /* Skip directories we are not interested in... */
1726                         if (strcmp(filedir_entry->d_name, ".svn") == 0)
1727                                 continue;
1728
1729                         FlushStrBuf(SubKey);
1730                         if (!Toplevel) {
1731                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1732                                 StrBufAppendBuf(SubKey, BaseKey, 0);
1733                                 StrBufAppendBufPlain(SubKey, HKEY("_"), 0);
1734                         }
1735                         StrBufAppendBufPlain(SubKey, filedir_entry->d_name, d_namelen, 0);
1736
1737                         FlushStrBuf(SubDirectory);
1738                         StrBufAppendBuf(SubDirectory, DirName, 0);
1739                         if (ChrPtr(SubDirectory)[StrLength(SubDirectory) - 1] != '/')
1740                                 StrBufAppendBufPlain(SubDirectory, HKEY("/"), 0);
1741                         StrBufAppendBufPlain(SubDirectory, filedir_entry->d_name, d_namelen, 0);
1742
1743                         LoadTemplateDir(SubDirectory, wireless, big, SubKey);
1744
1745                         break;
1746                 case DT_LNK: /* TODO: check whether its a file or a directory */
1747                 case DT_REG:
1748
1749
1750                         while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1751                                 d_without_ext --;
1752                         if ((d_without_ext == 0) || (d_namelen < 3))
1753                                 continue;
1754                         if (((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~') ||
1755                             (strcmp(&filedir_entry->d_name[d_without_ext], ".orig") == 0) ||
1756                             (strcmp(&filedir_entry->d_name[d_without_ext], ".swp") == 0))
1757                                 continue; /* Ignore backup files... */
1758                         /* .m.xxx is for mobile useragents! */
1759                         IsMobile = 0;
1760                         if (d_without_ext > 2)
1761                                 IsMobile = (filedir_entry->d_name[d_without_ext - 1] == 'm') &&
1762                                         (filedir_entry->d_name[d_without_ext - 2] == '.');
1763                         PStart = filedir_entry->d_name;
1764                         StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
1765                         MinorPtr = strchr(filedir_entry->d_name, '.');
1766                         if (MinorPtr != NULL)
1767                                 *MinorPtr = '\0';
1768                         FlushStrBuf(Key);
1769                         if (!Toplevel) {
1770                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1771                                 StrBufAppendBuf(Key, BaseKey, 0);
1772                                 StrBufAppendBufPlain(Key, HKEY("_"), 0);
1773                         }
1774                         StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
1775
1776                         if (LoadTemplates >= 1)
1777                                 lprintf(1, "%s %d %s\n", ChrPtr(FileName), IsMobile, ChrPtr(Key));
1778                         prepare_template(FileName, Key, (IsMobile)?wireless:big);
1779                 default:
1780                         break;
1781                 }
1782         }
1783         free(d);
1784         closedir(filedir);
1785         FreeStrBuf(&FileName);
1786         FreeStrBuf(&Key);
1787         FreeStrBuf(&SubDirectory);
1788         FreeStrBuf(&SubKey);
1789         return 1;
1790 }
1791
1792 void InitTemplateCache(void)
1793 {
1794         int i;
1795         StrBuf *Key;
1796         StrBuf *Dir;
1797         HashList *Templates[4];
1798
1799         Dir = NewStrBuf();
1800         Key = NewStrBuf();
1801
1802         /* Primary Template set... */
1803         StrBufPrintf(Dir, "%s/t", static_dirs[0]);
1804         LoadTemplateDir(Dir,
1805                         WirelessTemplateCache,
1806                         TemplateCache, 
1807                         Key);
1808
1809         /* User local Template set */
1810         StrBufPrintf(Dir, "%s/t", static_dirs[1]);
1811         LoadTemplateDir(Dir,
1812                         WirelessLocalTemplateCache,
1813                         LocalTemplateCache, 
1814                         Key);
1815         
1816         /* Debug Templates, just to be loaded while debugging. */
1817         
1818         StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
1819         LoadTemplateDir(Dir,
1820                         WirelessTemplateCache,
1821                         TemplateCache, 
1822                         Key);
1823         Templates[0] = WirelessTemplateCache;
1824         Templates[1] = TemplateCache;
1825         Templates[2] = WirelessLocalTemplateCache;
1826         Templates[3] = LocalTemplateCache;
1827
1828
1829         if (LoadTemplates == 0) 
1830                 for (i=0; i < 4; i++) {
1831                         const char *Key;
1832                         long KLen;
1833                         HashPos *At;
1834                         void *vTemplate;
1835
1836                         At = GetNewHashPos(Templates[i], 0);
1837                         while (GetNextHashPos(Templates[i], 
1838                                               At, 
1839                                               &KLen,
1840                                               &Key, 
1841                                               &vTemplate) && 
1842                                (vTemplate != NULL))
1843                         {
1844                                 load_template((WCTemplate *)vTemplate);
1845                         }
1846                         DeleteHashPos(&At);
1847                 }
1848
1849
1850         FreeStrBuf(&Dir);
1851         FreeStrBuf(&Key);
1852 }
1853
1854
1855
1856 /*-----------------------------------------------------------------------------
1857  *                      Filling & processing Templates
1858  */
1859 /**
1860  * \brief executes one token
1861  * \param Target buffer to append to
1862  * \param Token da to  process.
1863  * \param Template we're iterating
1864  * \param Context Contextpoointer to pass in
1865  * \param state are we in conditional state?
1866  * \param ContextType what type of information does context giv us?
1867  */
1868 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1869 {
1870         const char *AppendMe;
1871         long AppendMeLen;
1872         HashHandler *Handler;
1873         void *vVar;
1874         
1875 /* much output, since pName is not terminated...
1876         lprintf(1,"Doing token: %s\n",Token->pName);
1877 */
1878
1879         switch (TP->Tokens->Flags) {
1880         case SV_GETTEXT:
1881                 TmplGettext(Target, TP);
1882                 break;
1883         case SV_CONDITIONAL: /** Forward conditional evaluation */
1884                 return EvaluateConditional(Target, 1, state, TP);
1885                 break;
1886         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1887                 return EvaluateConditional(Target, 0, state, TP);
1888                 break;
1889         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1890                 if (TP->Tokens->nParameters >= 6) {
1891                         if (EvaluateConditional(Target, 0, state, TP)) {
1892                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1893                                 StrBufAppendBufPlain(Target, 
1894                                                      AppendMe, 
1895                                                      AppendMeLen,
1896                                                      0);
1897                         }
1898                         else{
1899                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1900                                 StrBufAppendBufPlain(Target, 
1901                                                      AppendMe, 
1902                                                      AppendMeLen,
1903                                                      0);
1904                         }
1905                 }
1906                 else  {
1907                         LogTemplateError(
1908                                 Target, "Conditional", ERR_NAME, TP,
1909                                 "needs at least 6 Params!"); 
1910                 }
1911                 break;
1912         case SV_SUBTEMPL:
1913                 if (TP->Tokens->nParameters == 1)
1914                         DoTemplate(TKEY(0), Target, TP);
1915                 break;
1916         case SV_PREEVALUATED:
1917                 Handler = (HashHandler*) TP->Tokens->PreEval;
1918                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1919                         return -1;
1920                 }
1921                 Handler->HandlerFunc(Target, TP);
1922                 break;          
1923         default:
1924                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1925                         Handler = (HashHandler*) vVar;
1926                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1927                                 return -1;
1928                         }
1929                         else {
1930                                 Handler->HandlerFunc(Target, TP);
1931                         }
1932                 }
1933                 else {
1934                         print_value_of(Target, TP);
1935                 }
1936         }
1937         return 0;
1938 }
1939
1940
1941
1942 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1943 {
1944         WCTemplate *pTmpl = Tmpl;
1945         int done = 0;
1946         int i, state;
1947         const char *pData, *pS;
1948         long len;
1949         WCTemplputParams TP;
1950
1951         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1952
1953         TP.Context = CallingTP->Context;
1954         TP.ControlContext = CallingTP->ControlContext;
1955
1956         if (LoadTemplates != 0) {                       
1957                 if (LoadTemplates > 1)
1958                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
1959                                 ChrPtr(Tmpl->FileName));
1960                 pTmpl = duplicate_template(Tmpl);
1961                 if(load_template(pTmpl) == NULL) {
1962                         StrBufAppendPrintf(
1963                                 Target, 
1964                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1965                                 ChrPtr(Tmpl->FileName));
1966                         FreeWCTemplate(pTmpl);
1967                         return NULL;
1968                 }
1969
1970         }
1971
1972         pS = pData = ChrPtr(pTmpl->Data);
1973         len = StrLength(pTmpl->Data);
1974         i = 0;
1975         state = 0;
1976         while (!done) {
1977                 if (i >= pTmpl->nTokensUsed) {
1978                         StrBufAppendBufPlain(Target, 
1979                                              pData, 
1980                                              len - (pData - pS), 0);
1981                         done = 1;
1982                 }
1983                 else {
1984                         StrBufAppendBufPlain(
1985                                 Target, pData, 
1986                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1987                         TP.Tokens = pTmpl->Tokens[i];
1988                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1989                         state = EvaluateToken(Target, state, &TP);
1990
1991                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1992                         /* condition told us to skip till its end condition */
1993                                 i++;
1994                                 TP.Tokens = pTmpl->Tokens[i];
1995                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1996                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1997                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1998                                         if (state == EvaluateConditional(
1999                                                     Target, 
2000                                                     pTmpl->Tokens[i]->Flags, 
2001                                                     state, 
2002                                                     &TP))
2003                                                 state = 0;
2004                                 }
2005                         }
2006                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
2007                         if (i > pTmpl->nTokensUsed)
2008                                 done = 1;
2009                 }
2010         }
2011         if (LoadTemplates != 0) {
2012                 FreeWCTemplate(pTmpl);
2013         }
2014         return Tmpl->MimeType;
2015
2016 }
2017
2018 /**
2019  * \brief Display a variable-substituted template
2020  * \param templatename template file to load
2021  * \returns the mimetype of the template its doing
2022  */
2023 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
2024 {
2025         WCTemplputParams LocalTP;
2026         HashList *Static;
2027         HashList *StaticLocal;
2028         void *vTmpl;
2029         
2030         if (Target == NULL)
2031                 Target = WC->WBuf;
2032         if (TP == NULL) {
2033                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
2034                 TP = &LocalTP;
2035         }
2036
2037         if (WC->is_mobile > 0) {
2038                 Static = WirelessTemplateCache;
2039                 StaticLocal = WirelessLocalTemplateCache;
2040         }
2041         else {
2042                 Static = TemplateCache;
2043                 StaticLocal = LocalTemplateCache;
2044         }
2045
2046         if (len == 0)
2047         {
2048                 lprintf (1, "Can't to load a template with empty name!\n");
2049                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
2050                 return NULL;
2051         }
2052
2053         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
2054             !GetHash(Static, templatename, len, &vTmpl)) {
2055                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
2056                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
2057                                    templatename, len, 
2058                                    (long)strlen(templatename));
2059 #if 0
2060                 dbg_PrintHash(Static, PrintTemplate, NULL);
2061                 PrintHash(Static, VarPrintTransition, PrintTemplate);
2062 #endif
2063                 return NULL;
2064         }
2065         if (vTmpl == NULL) 
2066                 return NULL;
2067         return ProcessTemplate(vTmpl, Target, TP);
2068
2069 }
2070
2071 /*-----------------------------------------------------------------------------
2072  *                      Iterators
2073  */
2074 typedef struct _HashIterator {
2075         HashList *StaticList;
2076         int AdditionalParams;
2077         int ContextType;
2078         int XPectContextType;
2079         int Flags;
2080         RetrieveHashlistFunc GetHash;
2081         HashDestructorFunc Destructor;
2082         SubTemplFunc DoSubTemplate;
2083 } HashIterator;
2084
2085 void RegisterITERATOR(const char *Name, long len, 
2086                       int AdditionalParams, 
2087                       HashList *StaticList, 
2088                       RetrieveHashlistFunc GetHash, 
2089                       SubTemplFunc DoSubTempl,
2090                       HashDestructorFunc Destructor,
2091                       int ContextType, 
2092                       int XPectContextType, 
2093                       int Flags)
2094 {
2095         HashIterator *It;
2096
2097         It = (HashIterator*)malloc(sizeof(HashIterator));
2098         memset(It, 0, sizeof(HashIterator));
2099         It->StaticList = StaticList;
2100         It->AdditionalParams = AdditionalParams;
2101         It->GetHash = GetHash;
2102         It->DoSubTemplate = DoSubTempl;
2103         It->Destructor = Destructor;
2104         It->ContextType = ContextType;
2105         It->XPectContextType = XPectContextType;
2106         It->Flags = Flags;
2107         Put(Iterators, Name, len, It, NULL);
2108 }
2109
2110 typedef struct _iteratestruct {
2111         int GroupChange;
2112         int oddeven;
2113         const char *Key;
2114         long KeyLen;
2115         int n;
2116         int LastN;
2117         }IterateStruct; 
2118
2119 int preeval_iterate(WCTemplateToken *Token)
2120 {
2121         WCTemplputParams TPP;
2122         WCTemplputParams *TP;
2123         void *vTmpl;
2124         void *vIt;
2125
2126         memset(&TPP, 0, sizeof(WCTemplputParams));
2127         TP = &TPP;
2128         TP->Tokens = Token;
2129         if (!GetHash(Iterators, TKEY(0), &vIt)) {
2130                 LogTemplateError(
2131                         NULL, "Iterator", ERR_PARM1, TP,
2132                         "not found");
2133                 return 0;
2134         }
2135         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
2136                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
2137                                  "Need token with type Subtemplate as param 1, have %s", 
2138                                  TP->Tokens->Params[1]->Start);
2139         }
2140         
2141         /* well, we don't check the mobile stuff here... */
2142         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
2143             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
2144                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
2145                                  "referenced here doesn't exist");
2146         }
2147         Token->Preeval2 = vIt;
2148         return 1;
2149 }
2150
2151 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
2152 {
2153         HashIterator *It;
2154         HashList *List;
2155         HashPos  *it;
2156         SortStruct *SortBy = NULL;
2157         void *vSortBy;
2158         int DetectGroupChange = 0;
2159         int nMembersUsed;
2160         void *vContext;
2161         void *vLastContext = NULL;
2162         StrBuf *SubBuf;
2163         WCTemplputParams SubTP;
2164         IterateStruct Status;
2165
2166         long StartAt = 0;
2167         long StepWidth = 0;
2168         long StopAt = -1;
2169
2170         memset(&Status, 0, sizeof(IterateStruct));
2171         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2172         
2173         It = (HashIterator*) TP->Tokens->Preeval2;
2174         if (It == NULL) {
2175                 LogTemplateError(
2176                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
2177                 return;
2178         }
2179
2180         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
2181                 LogTemplateError(                               
2182                         Target, "Iterator", ERR_PARM1, TP,
2183                         "doesn't work with %d params", 
2184                         TP->Tokens->nParameters);
2185                 return;
2186         }
2187
2188         if ((It->XPectContextType != CTX_NONE) &&
2189             (It->XPectContextType != TP->Filter.ContextType)) {
2190                 LogTemplateError(
2191                         Target, "Iterator", ERR_PARM1, TP,
2192                         "requires context of type %d, have %d", 
2193                         It->XPectContextType, 
2194                         TP->Filter.ContextType);
2195                 return ;
2196                 
2197         }
2198
2199         if (It->StaticList == NULL)
2200                 List = It->GetHash(Target, TP);
2201         else
2202                 List = It->StaticList;
2203
2204         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
2205         if (DetectGroupChange) {
2206                 const StrBuf *BSort;
2207                 DetectGroupChange = 0;
2208                 if (havebstr("SortBy")) {
2209                         BSort = sbstr("SortBy");
2210                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
2211                             (vSortBy != NULL)) {
2212                                 SortBy = (SortStruct*)vSortBy;
2213                                 /** Ok, its us, lets see in which direction we should sort... */
2214                                 if (havebstr("SortOrder")) {
2215                                         int SortOrder;
2216                                         SortOrder = LBSTR("SortOrder");
2217                                         if (SortOrder != 0)
2218                                                 DetectGroupChange = 1;
2219                                 }
2220                         }
2221                 }
2222         }
2223         nMembersUsed = GetCount(List);
2224         SubBuf = NewStrBuf();
2225         SubTP.Filter.ContextType = It->ContextType;
2226         SubTP.Filter.ControlContextType = CTX_ITERATE;
2227         SubTP.ControlContext = &Status;
2228         
2229         if (HAVE_PARAM(2)) {
2230                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
2231         }
2232         if (HAVE_PARAM(3)) {
2233                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
2234         }
2235         if (HAVE_PARAM(4)) {
2236                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
2237         }
2238         it = GetNewHashPos(List, StepWidth);
2239         if (StopAt < 0) {
2240                 StopAt = GetCount(List);
2241         }
2242         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
2243                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
2244                         if (DetectGroupChange && Status.n > 0) {
2245                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
2246                         }
2247                         Status.LastN = (Status.n + 1) == nMembersUsed;
2248                         SubTP.Context = vContext;
2249                         if (It->DoSubTemplate != NULL)
2250                                 It->DoSubTemplate(SubBuf, &SubTP);
2251                         DoTemplate(TKEY(1), SubBuf, &SubTP);
2252                         
2253                         StrBufAppendBuf(Target, SubBuf, 0);
2254                         FlushStrBuf(SubBuf);
2255                         Status.oddeven = ! Status.oddeven;
2256                         vLastContext = vContext;
2257                 }
2258                 Status.n++;
2259         }
2260         FreeStrBuf(&SubBuf);
2261         DeleteHashPos(&it);
2262         if (It->Destructor != NULL)
2263                 It->Destructor(&List);
2264 }
2265
2266
2267 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2268 {
2269         IterateStruct *Ctx = CCTX;
2270         if (TP->Tokens->nParameters < 3)
2271                 return  Ctx->GroupChange;
2272
2273         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2274 }
2275
2276 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2277 {
2278         IterateStruct *Ctx = CCTX;
2279         if (Ctx->oddeven)
2280                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2281         else
2282                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2283 }
2284
2285
2286 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2287 {
2288         IterateStruct *Ctx = CCTX;
2289
2290         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2291 }
2292
2293
2294 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2295 {
2296         IterateStruct *Ctx = CCTX;
2297         StrBufAppendPrintf(Target, "%d", Ctx->n);
2298 }
2299
2300 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2301 {
2302         IterateStruct *Ctx = CCTX;
2303         return Ctx->n == 0;
2304 }
2305
2306 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2307 {
2308         IterateStruct *Ctx = CCTX;
2309         return Ctx->LastN;
2310 }
2311
2312
2313
2314 /*-----------------------------------------------------------------------------
2315  *                      Conditionals
2316  */
2317 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2318 {
2319         ConditionalStruct *Cond;
2320
2321         if ((TP->Tokens->Params[0]->len == 1) &&
2322             (TP->Tokens->Params[0]->Start[0] == 'X'))
2323                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2324             
2325         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2326         if (Cond == NULL) {
2327                 LogTemplateError(
2328                         Target, "Conditional", ERR_PARM1, TP,
2329                         "unknown!");
2330                 return 1;
2331         }
2332
2333         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2334                 return 0;
2335         }
2336
2337         if (Cond->CondF(Target, TP) == Neg)
2338                 return TP->Tokens->Params[1]->lvalue;
2339         return 0;
2340 }
2341
2342 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2343 {
2344         void *vsubst;
2345         wcsubst *subst;
2346         
2347         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2348                 return 0;
2349         subst = (wcsubst*) vsubst;
2350         
2351         switch(subst->wcs_type) {
2352         case WCS_FUNCTION:
2353                 return (subst->wcs_function!=NULL);
2354         case WCS_SERVCMD:
2355                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2356                 return 1;
2357         case WCS_STRING:
2358         case WCS_STRBUF:
2359         case WCS_STRBUF_REF:
2360                 if (TP->Tokens->nParameters < 4)
2361                         return 1;
2362                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2363         case WCS_LONG:
2364                 if (TP->Tokens->nParameters < 4)
2365                         return (subst->lvalue != 0);
2366                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2367         default:
2368                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2369                 return -1;
2370         }
2371         return 0;
2372 }
2373
2374 void RegisterConditional(const char *Name, long len, 
2375                          int nParams,
2376                          WCConditionalFunc CondF, 
2377                          int ContextRequired)
2378 {
2379         ConditionalStruct *Cond;
2380
2381         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2382         memset(Cond, 0, sizeof(ConditionalStruct));
2383         Cond->PlainName = Name;
2384         Cond->Filter.nMaxArgs = nParams;
2385         Cond->Filter.nMinArgs = nParams;
2386         Cond->CondF = CondF;
2387         Cond->Filter.ContextType = ContextRequired;
2388         Cond->Filter.ControlContextType = CTX_NONE;
2389         Put(Conditionals, Name, len, Cond, NULL);
2390 }
2391
2392 void RegisterControlConditional(const char *Name, long len, 
2393                                 int nParams,
2394                                 WCConditionalFunc CondF, 
2395                                 int ControlContextRequired)
2396 {
2397         ConditionalStruct *Cond;
2398
2399         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2400         memset(Cond, 0, sizeof(ConditionalStruct));
2401         Cond->PlainName = Name;
2402         Cond->Filter.nMaxArgs = nParams;
2403         Cond->Filter.nMinArgs = nParams;
2404         Cond->CondF = CondF;
2405         Cond->Filter.ContextType = CTX_NONE;
2406         Cond->Filter.ControlContextType = ControlContextRequired;
2407         Put(Conditionals, Name, len, Cond, NULL);
2408 }
2409
2410 void RegisterTokenParamDefine(const char *Name, long len, 
2411                               long Value)
2412 {
2413         long *PVal;
2414
2415         PVal = (long*)malloc(sizeof(long));
2416         *PVal = Value;
2417         Put(Defines, Name, len, PVal, NULL);
2418 }
2419
2420 HashList *Defines;
2421
2422 /*-----------------------------------------------------------------------------
2423  *                      Context Strings
2424  */
2425 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2426 {
2427         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2428 }
2429 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2430 {
2431         StrBuf *TokenText = (StrBuf*) CTX;
2432         const char *CompareToken;
2433         long len;
2434
2435         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2436         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2437 }
2438
2439 /*-----------------------------------------------------------------------------
2440  *                      Boxed-API
2441  */
2442
2443 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2444 {
2445         WCTemplputParams SubTP;
2446
2447         StrBuf *Headline = NULL;
2448         if (TP->Tokens->nParameters == 2) {
2449                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2450                         Headline = NewStrBuf();
2451                         DoTemplate(TKEY(1), Headline, TP);
2452                 }
2453                 else {
2454                         const char *Ch;
2455                         long len;
2456                         GetTemplateTokenString(Target, 
2457                                                TP, 
2458                                                1,
2459                                                &Ch,
2460                                                &len);
2461                         Headline = NewStrBufPlain(Ch, len);
2462                 }
2463         }
2464         /* else TODO error? logging? */
2465         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2466         SubTP.Context = Headline;
2467         SubTP.Filter.ContextType = CTX_STRBUF;
2468         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2469         DoTemplate(TKEY(0), Target, TP);
2470         DoTemplate(HKEY("endbox"), Target, TP);
2471         FreeStrBuf(&Headline);
2472 }
2473
2474 /*-----------------------------------------------------------------------------
2475  *                      Tabbed-API
2476  */
2477
2478 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2479 {
2480         StrBuf **TabNames;
2481         int i, ntabs, nTabs;
2482
2483         nTabs = ntabs = TP->Tokens->nParameters / 2;
2484         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2485         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2486
2487         for (i = 0; i < ntabs; i++) {
2488                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2489                     (TP->Tokens->Params[i * 2]->len > 0)) {
2490                         TabNames[i] = NewStrBuf();
2491                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2492                 }
2493                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2494                         const char *Ch;
2495                         long len;
2496                         GetTemplateTokenString(Target, 
2497                                                TP, 
2498                                                i * 2,
2499                                                &Ch,
2500                                                &len);
2501                         TabNames[i] = NewStrBufPlain(Ch, -1);
2502                 }
2503                 else { 
2504                         /** A Tab without subject? we can't count that, add it as silent */
2505                         nTabs --;
2506                 }
2507         }
2508
2509         StrTabbedDialog(Target, nTabs, TabNames);
2510         for (i = 0; i < ntabs; i++) {
2511                 StrBeginTab(Target, i, nTabs);
2512                 DoTemplate(TKEY(i * 2 + 1), Target, TP);
2513                 StrEndTab(Target, i, nTabs);
2514         }
2515 }
2516
2517
2518 /*-----------------------------------------------------------------------------
2519  *                      Sorting-API
2520  */
2521
2522
2523 void RegisterSortFunc(const char *name, long len, 
2524                       const char *prepend, long preplen,
2525                       CompareFunc Forward, 
2526                       CompareFunc Reverse, 
2527                       CompareFunc GroupChange, 
2528                       long ContextType)
2529 {
2530         SortStruct *NewSort;
2531
2532         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2533         memset(NewSort, 0, sizeof(SortStruct));
2534         NewSort->Name = NewStrBufPlain(name, len);
2535         if (prepend != NULL)
2536                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2537         else
2538                 NewSort->PrefPrepend = NULL;
2539         NewSort->Forward = Forward;
2540         NewSort->Reverse = Reverse;
2541         NewSort->GroupChange = GroupChange;
2542         NewSort->ContextType = ContextType;
2543         if (ContextType == CTX_NONE) {
2544                 lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
2545                 exit(1);
2546         }
2547                 
2548         Put(SortHash, name, len, NewSort, DestroySortStruct);
2549 }
2550
2551 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2552                          const char *OtherPrefix, long OtherPrefixLen,
2553                          const char *Default, long ldefault, long DefaultDirection)
2554 {
2555         int isdefault = 0;
2556         const StrBuf *BSort = NULL;
2557         SortStruct *SortBy;
2558         void *vSortBy;
2559         long SortOrder = -1;
2560         
2561         if (havebstr("SortBy")) {
2562                 BSort = sbstr("SortBy");
2563                 if (OtherPrefix == NULL) {
2564                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2565                 }
2566                 else {
2567                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2568                 }
2569         }
2570         else { /** Try to fallback to our remembered values... */
2571                 if (OtherPrefix == NULL) {
2572                         BSort = get_room_pref("sort");
2573                 }
2574                 else {
2575                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2576                 }
2577                 if (BSort != NULL)
2578                         putbstr("SortBy", NewStrBufDup(BSort));
2579                 else {
2580                         StrBuf *Buf;
2581
2582                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2583                         putbstr("SortBy", Buf);
2584                 }
2585         }
2586
2587         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2588             (vSortBy == NULL)) {
2589                 isdefault = 1;
2590                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2591                     (vSortBy == NULL)) {
2592                         LogTemplateError(
2593                                 NULL, "Sorting", ERR_PARM1, TP,
2594                                 "Illegal default sort: [%s]", Default);
2595                         wc_backtrace();
2596                 }
2597         }
2598         SortBy = (SortStruct*)vSortBy;
2599
2600         if (SortBy->ContextType != TP->Filter.ContextType)
2601                 return NULL;
2602
2603         /** Ok, its us, lets see in which direction we should sort... */
2604         if (havebstr("SortOrder")) {
2605                 SortOrder = LBSTR("SortOrder");
2606         }
2607         else { /** Try to fallback to our remembered values... */
2608                 StrBuf *Buf = NULL;
2609                 if (SortBy->PrefPrepend == NULL) {
2610                         Buf = get_room_pref("SortOrder");
2611                         SortOrder = StrTol(Buf);
2612                 }
2613                 else {
2614                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2615                 }
2616
2617                 if (Buf == NULL)
2618                         SortOrder = DefaultDirection;
2619
2620                 Buf = NewStrBufPlain(NULL, 64);
2621                 StrBufPrintf(Buf, "%ld", SortOrder);
2622                 putbstr("SortOrder", Buf);
2623         }
2624         switch (SortOrder) {
2625         default:
2626         case 0:
2627                 return NULL;
2628         case 1:
2629                 return SortBy->Forward;
2630         case 2:
2631                 return SortBy->Reverse;
2632         }
2633 }
2634
2635
2636 enum {
2637         eNO_SUCH_SORT, 
2638         eNOT_SPECIFIED,
2639         eINVALID_PARAM,
2640         eFOUND
2641 };
2642
2643 ConstStr SortIcons[] = {
2644         {HKEY("static/sort_none.gif")},
2645         {HKEY("static/up_pointer.gif")},
2646         {HKEY("static/down_pointer.gif")},
2647 };
2648
2649 ConstStr SortNextOrder[] = {
2650         {HKEY("1")},
2651         {HKEY("2")},
2652         {HKEY("0")},
2653 };
2654
2655
2656 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2657 {
2658         int bSortError = eNOT_SPECIFIED;
2659         const StrBuf *BSort;
2660         void *vSort;
2661         
2662         *SortOrder = 0;
2663         *Next = NULL;
2664         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2665             (vSort == NULL))
2666                 return eNO_SUCH_SORT;
2667         *Param = (SortStruct*) vSort;
2668         
2669
2670         if (havebstr("SortBy")) {
2671                 BSort = sbstr("SortBy");
2672                 bSortError = eINVALID_PARAM;
2673                 if ((*Param)->PrefPrepend == NULL) {
2674                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2675                 }
2676                 else {
2677                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2678                 }
2679         }
2680         else { /** Try to fallback to our remembered values... */
2681                 if ((*Param)->PrefPrepend == NULL) {
2682                         BSort = get_room_pref("sort");
2683                 }
2684                 else {
2685                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2686                 }
2687         }
2688
2689         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2690             (vSort == NULL))
2691                 return bSortError;
2692
2693         *Next = (SortStruct*) vSort;
2694
2695         /** Ok, its us, lets see in which direction we should sort... */
2696         if (havebstr("SortOrder")) {
2697                 *SortOrder = LBSTR("SortOrder");
2698         }
2699         else { /** Try to fallback to our remembered values... */
2700                 if ((*Param)->PrefPrepend == NULL) {
2701                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2702                 }
2703                 else {
2704                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2705                 }
2706         }
2707         if (*SortOrder > 2)
2708                 *SortOrder = 0;
2709
2710         return eFOUND;
2711 }
2712
2713
2714 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2715 {
2716         long SortOrder;
2717         SortStruct *Next;
2718         SortStruct *Param;
2719         const ConstStr *SortIcon;
2720
2721         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2722         case eNO_SUCH_SORT:
2723                 LogTemplateError(
2724                         Target, "Sorter", ERR_PARM1, TP,
2725                         " Sorter [%s] unknown!", 
2726                         TP->Tokens->Params[0]->Start);
2727                 break;          
2728         case eINVALID_PARAM:
2729                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2730                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2731                                  bstr("SortBy"));
2732         case eNOT_SPECIFIED:
2733         case eFOUND:
2734                 if (Next == Param) {
2735                         SortIcon = &SortIcons[SortOrder];
2736                 }
2737                 else { /** Not Us... */
2738                         SortIcon = &SortIcons[0];
2739                 }
2740                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2741         }
2742 }
2743
2744 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2745 {
2746         long SortOrder;
2747         SortStruct *Next;
2748         SortStruct *Param;
2749
2750         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2751         case eNO_SUCH_SORT:
2752                 LogTemplateError(
2753                         Target, "Sorter", ERR_PARM1, TP,                                  
2754                         " Sorter [%s] unknown!", 
2755                         TP->Tokens->Params[0]->Start);
2756                 break;          
2757         case eINVALID_PARAM:
2758                 LogTemplateError(
2759                         NULL, "Sorter", ERR_PARM1, TP,
2760                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2761                         bstr("SortBy"));
2762         case eNOT_SPECIFIED:
2763         case eFOUND:
2764                 StrBufAppendBuf(Target, Param->Name, 0);
2765                 
2766         }
2767 }
2768
2769 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2770 {
2771         long SortOrder;
2772         const ConstStr *SortOrderStr;
2773         SortStruct *Next;
2774         SortStruct *Param;
2775
2776         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2777         case eNO_SUCH_SORT:
2778                 LogTemplateError(
2779                         Target, "Sorter", ERR_PARM1, TP,
2780                         " Sorter [%s] unknown!",
2781                         TP->Tokens->Params[0]->Start);
2782                 break;          
2783         case eINVALID_PARAM:
2784                 LogTemplateError(
2785                         NULL, "Sorter", ERR_PARM1, TP,
2786                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2787                         bstr("SortBy"));
2788         case eNOT_SPECIFIED:
2789         case eFOUND:
2790                 if (Next == Param) {
2791                         SortOrderStr = &SortNextOrder[SortOrder];
2792                 }
2793                 else { /** Not Us... */
2794                         SortOrderStr = &SortNextOrder[0];
2795                 }
2796                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2797         }
2798 }
2799
2800
2801 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2802 {
2803         long *LongVector = (long*) CTX;
2804
2805         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2806             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2807         {
2808                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2809         }
2810         else
2811         {
2812                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2813                         LogTemplateError(
2814                                 Target, "Longvector", ERR_NAME, TP,
2815                                 "needs a numerical Parameter!");
2816                 }
2817                 else {
2818                         LogTemplateError(
2819                                 Target, "LongVector", ERR_PARM1, TP,
2820                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2821                                 TP->Tokens->Params[0]->lvalue,
2822                                 LongVector[0]);
2823                 }
2824         }
2825 }
2826
2827 void dbg_print_longvector(long *LongVector)
2828 {
2829         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2830         int nItems = LongVector[0];
2831         int i;
2832
2833         for (i = 0; i < nItems; i++) {
2834                 if (i + 1 < nItems)
2835                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2836                 else
2837                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2838
2839         }
2840         lprintf(1, ChrPtr(Buf));
2841         FreeStrBuf(&Buf);
2842 }
2843
2844 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2845 {
2846         long *LongVector = (long*) CTX;
2847
2848         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2849             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2850             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2851             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2852         {
2853                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2854                         LongVector[TP->Tokens->Params[3]->lvalue];
2855         }
2856         else
2857         {
2858                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2859                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2860                         LogTemplateError(
2861                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2862                                 "needs two long Parameter!");
2863                 }
2864                 else {
2865                         LogTemplateError(
2866                                 Target, "Longvector", ERR_PARM1, TP,
2867                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2868                                 TP->Tokens->Params[2]->lvalue,
2869                                 TP->Tokens->Params[3]->lvalue,
2870                                 LongVector[0]);
2871                 }
2872         }
2873         return 0;
2874 }
2875
2876
2877 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
2878 {
2879         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
2880 }
2881
2882 void 
2883 InitModule_SUBST
2884 (void)
2885 {
2886         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2887         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
2888         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
2889         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
2890         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
2891         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
2892         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
2893         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, NULL, CTX_NONE);
2894         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
2895         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2896         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2897         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2898
2899         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2900                                    conditional_ITERATE_ISGROUPCHANGE, 
2901                                    CTX_ITERATE);
2902         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2903                                    conditional_ITERATE_LASTN, 
2904                                    CTX_ITERATE);
2905         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2906                                    conditional_ITERATE_FIRSTN, 
2907                                    CTX_ITERATE);
2908
2909         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2910         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2911         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2912         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
2913
2914
2915 }
2916
2917 void
2918 ServerStartModule_SUBST
2919 (void)
2920 {
2921         WirelessTemplateCache = NewHash(1, NULL);
2922         WirelessLocalTemplateCache = NewHash(1, NULL);
2923         LocalTemplateCache = NewHash(1, NULL);
2924         TemplateCache = NewHash(1, NULL);
2925
2926         GlobalNS = NewHash(1, NULL);
2927         Iterators = NewHash(1, NULL);
2928         Conditionals = NewHash(1, NULL);
2929         SortHash = NewHash(1, NULL);
2930         Defines = NewHash(1, NULL);
2931 }
2932
2933 void
2934 FinalizeModule_SUBST
2935 (void)
2936 {
2937
2938 }
2939
2940 void 
2941 ServerShutdownModule_SUBST
2942 (void)
2943 {
2944         DeleteHash(&WirelessTemplateCache);
2945         DeleteHash(&WirelessLocalTemplateCache);
2946         DeleteHash(&TemplateCache);
2947         DeleteHash(&LocalTemplateCache);
2948
2949         DeleteHash(&GlobalNS);
2950         DeleteHash(&Iterators);
2951         DeleteHash(&Conditionals);
2952         DeleteHash(&SortHash);
2953         DeleteHash(&Defines);
2954 }
2955
2956
2957 void
2958 SessionNewModule_SUBST
2959 (wcsession *sess)
2960 {
2961
2962 }
2963
2964 void
2965 SessionAttachModule_SUBST
2966 (wcsession *sess)
2967 {
2968         sess->vars = NewHash(1,NULL);
2969 }
2970
2971 void
2972 SessionDetachModule_SUBST
2973 (wcsession *sess)
2974 {
2975         DeleteHash(&sess->vars);
2976 }
2977
2978 void 
2979 SessionDestroyModule_SUBST  
2980 (wcsession *sess)
2981 {
2982
2983 }