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