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