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