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