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