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