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