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