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