83f780875a068ba6024a2c5fda088f42033aea41
[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                 } else {
1078                         LogTemplateError(
1079                                 NULL, "Token ", ERR_NAME, &TP,
1080                                 " isn't known to us.", 
1081                                 NULL);
1082                 }
1083                 break;
1084         case SV_GETTEXT:
1085                 if (NewToken->nParameters !=1) {
1086                         LogTemplateError(                               
1087                                 NULL, "Gettext", ERR_NAME, &TP,
1088                                 "requires exactly 1 parameter, you gave %d params", 
1089                                 NewToken->nParameters);
1090                         NewToken->Flags = 0;
1091                         break;
1092                 }
1093                 if (DumpTemplateI18NStrings) {
1094                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
1095                 }
1096                 break;
1097         case SV_SUBTEMPL:
1098                 if (NewToken->nParameters != 1) {
1099                         LogTemplateError(
1100                                 NULL, "Subtemplates", ERR_NAME, &TP,
1101                                 "require exactly 1 parameter, you gave %d params", 
1102                                 NewToken->nParameters);
1103                         break;
1104                 }
1105                 else {
1106                         void *vTmpl;
1107                         /* well, we don't check the mobile stuff here... */
1108                         if (!GetHash(LocalTemplateCache, 
1109                                      NewToken->Params[0]->Start, 
1110                                      NewToken->Params[0]->len, 
1111                                      &vTmpl) &&
1112                             !GetHash(TemplateCache, 
1113                                      NewToken->Params[0]->Start, 
1114                                      NewToken->Params[0]->len, 
1115                                      &vTmpl)) {
1116                                 LogTemplateError(
1117                                         NULL, "SubTemplate", ERR_PARM1, &TP,
1118                                         "doesn't exist");
1119                         }
1120                 }
1121                 break;
1122         case SV_CUST_STR_CONDITIONAL:
1123         case SV_CONDITIONAL:
1124         case SV_NEG_CONDITIONAL:
1125                 if (NewToken->nParameters <2) {
1126                         LogTemplateError(
1127                                 NULL, "Conditional", ERR_PARM1, &TP,
1128                                 "require at least 2 parameters, you gave %d params", 
1129                                 NewToken->nParameters);
1130                         NewToken->Flags = 0;
1131                         break;
1132                 }
1133                 if (NewToken->Params[1]->lvalue == 0) {
1134                         LogTemplateError(
1135                                 NULL, "Conditional", ERR_PARM1, &TP,
1136                                 "Conditional ID (Parameter 1) mustn't be 0!");
1137                         NewToken->Flags = 0;
1138                         break;
1139                 }
1140                 if (!GetHash(Conditionals, 
1141                              NewToken->Params[0]->Start, 
1142                              NewToken->Params[0]->len, 
1143                              &vVar) || 
1144                     (vVar == NULL)) {
1145                         if ((NewToken->Params[0]->len == 1) &&
1146                             (NewToken->Params[0]->Start[0] == 'X'))
1147                                 break;
1148                         LogTemplateError(
1149                                 NULL, "Conditional", ERR_PARM1, &TP,
1150                                 "Not found!");
1151 /*
1152                         NewToken->Error = NewStrBuf();
1153                         StrBufAppendPrintf(
1154                                 NewToken->Error, 
1155                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
1156                                 NewToken->Params[0]->Start,
1157                                 ChrPtr(pTmpl->FileName),
1158                                 NewToken->Line,
1159                                 ChrPtr(NewToken->FlatToken));
1160 */
1161                 }
1162                 else {
1163                         NewToken->PreEval = vVar;
1164                 }
1165                 break;
1166         }
1167         return NewToken;
1168 }
1169
1170
1171
1172
1173
1174 /**
1175  * \brief Display a variable-substituted template
1176  * \param templatename template file to load
1177  */
1178 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1179 {
1180         WCTemplate *NewTemplate;
1181
1182         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1183         memset(NewTemplate, 0, sizeof(WCTemplate));
1184         NewTemplate->Data = NULL;
1185         NewTemplate->FileName = NewStrBufDup(filename);
1186         StrBufShrinkToFit(NewTemplate->FileName, 1);
1187         NewTemplate->nTokensUsed = 0;
1188         NewTemplate->TokenSpace = 0;
1189         NewTemplate->Tokens = NULL;
1190         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1191         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1192                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1193         }
1194
1195         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1196                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1197         }
1198
1199         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1200         return NewTemplate;
1201 }
1202
1203 /**
1204  * \brief Display a variable-substituted template
1205  * \param templatename template file to load
1206  */
1207 void *duplicate_template(WCTemplate *OldTemplate)
1208 {
1209         WCTemplate *NewTemplate;
1210
1211         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1212         memset(NewTemplate, 0, sizeof(WCTemplate));
1213         NewTemplate->Data = NULL;
1214         NewTemplate->FileName = NewStrBufDup(OldTemplate->FileName);
1215         StrBufShrinkToFit(NewTemplate->FileName, 1);
1216         NewTemplate->nTokensUsed = 0;
1217         NewTemplate->TokenSpace = 0;
1218         NewTemplate->Tokens = NULL;
1219         NewTemplate->MimeType = NewStrBufDup(OldTemplate->MimeType);
1220         return NewTemplate;
1221 }
1222
1223
1224 void SanityCheckTemplate(StrBuf *Target, WCTemplate *CheckMe)
1225 {
1226         int i = 0;
1227         int j;
1228         int FoundConditionalEnd;
1229
1230         for (i = 0; i < CheckMe->nTokensUsed; i++)
1231         {
1232                 switch(CheckMe->Tokens[i]->Flags)
1233                 {
1234                 case SV_CONDITIONAL:
1235                 case SV_NEG_CONDITIONAL:
1236                         FoundConditionalEnd = 0;
1237                         if ((CheckMe->Tokens[i]->Params[0]->len == 1) && 
1238                             (CheckMe->Tokens[i]->Params[0]->Start[0] == 'X'))
1239                                 break;
1240                         for (j = i + 1; j < CheckMe->nTokensUsed; j++)
1241                         {
1242                                 if (((CheckMe->Tokens[j]->Flags == SV_CONDITIONAL) ||
1243                                      (CheckMe->Tokens[j]->Flags == SV_NEG_CONDITIONAL)) && 
1244                                     (CheckMe->Tokens[i]->Params[1]->lvalue == 
1245                                      CheckMe->Tokens[j]->Params[1]->lvalue))
1246                                 {
1247                                         FoundConditionalEnd = 1;
1248                                         break;
1249                                 }
1250
1251                         }
1252                         if (!FoundConditionalEnd)
1253                         {
1254                                 WCTemplputParams TP;
1255                                 memset(&TP, 0, sizeof(WCTemplputParams));
1256                                 TP.Tokens = CheckMe->Tokens[i];
1257                                 LogTemplateError(
1258                                         Target, "Token", ERR_PARM1, &TP,
1259                                         "Conditional without Endconditional"
1260                                         );
1261                         }
1262                         break;
1263                 default:
1264                         break;
1265                 }
1266         }
1267 }
1268
1269 /**
1270  * \brief Display a variable-substituted template
1271  * \param templatename template file to load
1272  */
1273 void *load_template(StrBuf *Target, WCTemplate *NewTemplate)
1274 {
1275         int fd;
1276         struct stat statbuf;
1277         const char *pS, *pE, *pch, *Err;
1278         long Line;
1279
1280         fd = open(ChrPtr(NewTemplate->FileName), O_RDONLY);
1281         if (fd <= 0) {
1282                 syslog(1, "ERROR: could not open template '%s' - %s\n",
1283                         ChrPtr(NewTemplate->FileName), strerror(errno));
1284                 return NULL;
1285         }
1286
1287         if (fstat(fd, &statbuf) == -1) {
1288                 syslog(1, "ERROR: could not stat template '%s' - %s\n",
1289                         ChrPtr(NewTemplate->FileName), strerror(errno));
1290                 return NULL;
1291         }
1292
1293         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size + 1);
1294         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1295                 close(fd);
1296                 syslog(1, "ERROR: reading template '%s' - %s<br>\n",
1297                         ChrPtr(NewTemplate->FileName), strerror(errno));
1298                 return NULL;
1299         }
1300         close(fd);
1301
1302         Line = 0;
1303         StrBufShrinkToFit(NewTemplate->Data, 1);
1304         StrBufShrinkToFit(NewTemplate->MimeType, 1);
1305         pS = pch = ChrPtr(NewTemplate->Data);
1306         pE = pS + StrLength(NewTemplate->Data);
1307         while (pch < pE) {
1308                 const char *pts, *pte;
1309                 char InQuotes = '\0';
1310                 void *pv;
1311
1312                 /** Find one <? > */
1313                 for (; pch < pE; pch ++) {
1314                         if ((*pch=='<')&&(*(pch + 1)=='?') &&
1315                             !((pch == pS) && /* we must ommit a <?xml */
1316                               (*(pch + 2) == 'x') && 
1317                               (*(pch + 3) == 'm') && 
1318                               (*(pch + 4) == 'l')))                          
1319                                 break;
1320                         if (*pch=='\n') Line ++;
1321                 }
1322                 if (pch >= pE)
1323                         continue;
1324                 pts = pch;
1325
1326                 /** Found one? parse it. */
1327                 for (; pch <= pE - 1; pch ++) {
1328                         if ((!InQuotes) &&
1329                             ((*pch == '\'') || (*pch == '"')))
1330                         {
1331                                 InQuotes = *pch;
1332                         }
1333                         else if (InQuotes && (InQuotes == *pch))
1334                         {
1335                                 InQuotes = '\0';
1336                         }
1337                         else if ((InQuotes) &&
1338                                  (*pch == '\\') &&
1339                                  (*(pch + 1) == InQuotes))
1340                         {
1341                                 pch++;
1342                         }
1343                         else if ((!InQuotes) && 
1344                                  (*pch == '>'))
1345                         {
1346                                 break;
1347                         }
1348                 }
1349                 if (pch + 1 > pE)
1350                         continue;
1351                 pte = pch;
1352                 pv = NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate);
1353                 if (pv != NULL) {
1354                         PutNewToken(NewTemplate, pv);
1355                         pch ++;
1356                 }
1357         }
1358
1359         SanityCheckTemplate(NULL, NewTemplate);
1360         return NewTemplate;
1361 }
1362
1363
1364 const char* PrintTemplate(void *vSubst)
1365 {
1366         WCTemplate *Tmpl = vSubst;
1367
1368         return ChrPtr(Tmpl->FileName);
1369
1370 }
1371
1372 int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
1373 {
1374         int Toplevel;
1375         StrBuf *FileName;
1376         StrBuf *Key;
1377         StrBuf *SubKey;
1378         StrBuf *SubDirectory;
1379         DIR *filedir = NULL;
1380         struct dirent *filedir_entry;
1381         struct dirent *d;
1382         int d_type = 0;
1383         int d_namelen;
1384         int d_without_ext;
1385         
1386         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
1387         if (d == NULL) {
1388                 return 0;
1389         }
1390
1391         filedir = opendir (ChrPtr(DirName));
1392         if (filedir == NULL) {
1393                 free(d);
1394                 return 0;
1395         }
1396
1397         Toplevel = StrLength(BaseKey) == 0;
1398         SubDirectory = NewStrBuf();
1399         SubKey = NewStrBuf();
1400         FileName = NewStrBufPlain(NULL, PATH_MAX);
1401         Key = NewStrBuf();
1402         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
1403                (filedir_entry != NULL))
1404         {
1405                 char *MinorPtr;
1406
1407 #ifdef _DIRENT_HAVE_D_NAMELEN
1408                 d_namelen = filedir_entry->d_namelen;
1409                 d_type = filedir_entry->d_type;
1410 #else
1411
1412 #ifndef DT_UNKNOWN
1413 #define DT_UNKNOWN     0
1414 #define DT_DIR         4
1415 #define DT_REG         8
1416 #define DT_LNK         10
1417
1418 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
1419 #define DTTOIF(dirtype)        ((dirtype) << 12)
1420 #endif
1421                 d_namelen = strlen(filedir_entry->d_name);
1422                 d_type = DT_UNKNOWN;
1423 #endif
1424                 d_without_ext = d_namelen;
1425
1426                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1427                         continue; /* Ignore backup files... */
1428
1429                 if ((d_namelen == 1) && 
1430                     (filedir_entry->d_name[0] == '.'))
1431                         continue;
1432
1433                 if ((d_namelen == 2) && 
1434                     (filedir_entry->d_name[0] == '.') &&
1435                     (filedir_entry->d_name[1] == '.'))
1436                         continue;
1437
1438                 if (d_type == DT_UNKNOWN) {
1439                         struct stat s;
1440                         char path[PATH_MAX];
1441                         snprintf(path, PATH_MAX, "%s/%s", 
1442                                  ChrPtr(DirName), filedir_entry->d_name);
1443                         if (stat(path, &s) == 0) {
1444                                 d_type = IFTODT(s.st_mode);
1445                         }
1446                 }
1447                 switch (d_type)
1448                 {
1449                 case DT_DIR:
1450                         /* Skip directories we are not interested in... */
1451                         if (strcmp(filedir_entry->d_name, ".svn") == 0)
1452                                 continue;
1453
1454                         FlushStrBuf(SubKey);
1455                         if (!Toplevel) {
1456                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1457                                 StrBufAppendBuf(SubKey, BaseKey, 0);
1458                                 StrBufAppendBufPlain(SubKey, HKEY("_"), 0);
1459                         }
1460                         StrBufAppendBufPlain(SubKey, filedir_entry->d_name, d_namelen, 0);
1461
1462                         FlushStrBuf(SubDirectory);
1463                         StrBufAppendBuf(SubDirectory, DirName, 0);
1464                         if (ChrPtr(SubDirectory)[StrLength(SubDirectory) - 1] != '/')
1465                                 StrBufAppendBufPlain(SubDirectory, HKEY("/"), 0);
1466                         StrBufAppendBufPlain(SubDirectory, filedir_entry->d_name, d_namelen, 0);
1467
1468                         LoadTemplateDir(SubDirectory, big, SubKey);
1469
1470                         break;
1471                 case DT_LNK: /* TODO: check whether its a file or a directory */
1472                 case DT_REG:
1473
1474
1475                         while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1476                                 d_without_ext --;
1477                         if ((d_without_ext == 0) || (d_namelen < 3))
1478                                 continue;
1479                         if (((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~') ||
1480                             (strcmp(&filedir_entry->d_name[d_without_ext], ".orig") == 0) ||
1481                             (strcmp(&filedir_entry->d_name[d_without_ext], ".swp") == 0))
1482                                 continue; /* Ignore backup files... */
1483                         StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
1484                         MinorPtr = strchr(filedir_entry->d_name, '.');
1485                         if (MinorPtr != NULL)
1486                                 *MinorPtr = '\0';
1487                         FlushStrBuf(Key);
1488                         if (!Toplevel) {
1489                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1490                                 StrBufAppendBuf(Key, BaseKey, 0);
1491                                 StrBufAppendBufPlain(Key, HKEY("_"), 0);
1492                         }
1493                         StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
1494
1495                         if (LoadTemplates >= 1)
1496                                 syslog(1, "%s %s\n", ChrPtr(FileName), ChrPtr(Key));
1497                         prepare_template(FileName, Key, big);
1498                 default:
1499                         break;
1500                 }
1501         }
1502         free(d);
1503         closedir(filedir);
1504         FreeStrBuf(&FileName);
1505         FreeStrBuf(&Key);
1506         FreeStrBuf(&SubDirectory);
1507         FreeStrBuf(&SubKey);
1508         return 1;
1509 }
1510
1511 void InitTemplateCache(void)
1512 {
1513         int i;
1514         StrBuf *Key;
1515         StrBuf *Dir;
1516         HashList *Templates[2];
1517
1518         Dir = NewStrBuf();
1519         Key = NewStrBuf();
1520
1521         /* Primary Template set... */
1522         StrBufPrintf(Dir, "%s/t", static_dirs[0]);
1523         LoadTemplateDir(Dir,
1524                         TemplateCache, 
1525                         Key);
1526
1527         /* User local Template set */
1528         StrBufPrintf(Dir, "%s/t", static_dirs[1]);
1529         LoadTemplateDir(Dir,
1530                         LocalTemplateCache, 
1531                         Key);
1532         
1533         /* Debug Templates, just to be loaded while debugging. */
1534         
1535         StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
1536         LoadTemplateDir(Dir,
1537                         TemplateCache, 
1538                         Key);
1539         Templates[0] = TemplateCache;
1540         Templates[1] = LocalTemplateCache;
1541
1542
1543         if (LoadTemplates == 0) 
1544                 for (i=0; i < 2; i++) {
1545                         const char *Key;
1546                         long KLen;
1547                         HashPos *At;
1548                         void *vTemplate;
1549
1550                         At = GetNewHashPos(Templates[i], 0);
1551                         while (GetNextHashPos(Templates[i], 
1552                                               At, 
1553                                               &KLen,
1554                                               &Key, 
1555                                               &vTemplate) && 
1556                                (vTemplate != NULL))
1557                         {
1558                                 load_template(NULL, (WCTemplate *)vTemplate);
1559                         }
1560                         DeleteHashPos(&At);
1561                 }
1562
1563
1564         FreeStrBuf(&Dir);
1565         FreeStrBuf(&Key);
1566 }
1567
1568
1569
1570 /*-----------------------------------------------------------------------------
1571  *                      Filling & processing Templates
1572  */
1573 /**
1574  * \brief executes one token
1575  * \param Target buffer to append to
1576  * \param Token da to  process.
1577  * \param Template we're iterating
1578  * \param Context Contextpoointer to pass in
1579  * \param state are we in conditional state?
1580  * \param ContextType what type of information does context giv us?
1581  */
1582 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1583 {
1584         const char *AppendMe;
1585         long AppendMeLen;
1586         HashHandler *Handler;
1587         void *vVar;
1588         
1589 /* much output, since pName is not terminated...
1590         syslog(1,"Doing token: %s\n",Token->pName);
1591 */
1592
1593         switch (TP->Tokens->Flags) {
1594         case SV_GETTEXT:
1595                 TmplGettext(Target, TP);
1596                 break;
1597         case SV_CONDITIONAL: /** Forward conditional evaluation */
1598                 Handler = (HashHandler*) TP->Tokens->PreEval;
1599                 if (!CheckContext(Target, &Handler->Filter, TP, "Conditional")) {
1600                         return -1;
1601                 }
1602                 return EvaluateConditional(Target, 1, state, TP);
1603                 break;
1604         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1605                 Handler = (HashHandler*) TP->Tokens->PreEval;
1606                 if (!CheckContext(Target, &Handler->Filter, TP, "Conditional")) {
1607                         return -1;
1608                 }
1609                 return EvaluateConditional(Target, 0, state, TP);
1610                 break;
1611         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1612                 Handler = (HashHandler*) TP->Tokens->PreEval;
1613                 if (!CheckContext(Target, &Handler->Filter, TP, "Conditional")) {
1614                         return -1;
1615                 }
1616                 if (TP->Tokens->nParameters >= 6) {
1617                         if (EvaluateConditional(Target, 0, state, TP)) {
1618                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1619                                 StrBufAppendBufPlain(Target, 
1620                                                      AppendMe, 
1621                                                      AppendMeLen,
1622                                                      0);
1623                         }
1624                         else{
1625                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1626                                 StrBufAppendBufPlain(Target, 
1627                                                      AppendMe, 
1628                                                      AppendMeLen,
1629                                                      0);
1630                         }
1631                 }
1632                 else  {
1633                         LogTemplateError(
1634                                 Target, "Conditional", ERR_NAME, TP,
1635                                 "needs at least 6 Params!"); 
1636                 }
1637                 break;
1638         case SV_SUBTEMPL:
1639                 if (TP->Tokens->nParameters == 1)
1640                         DoTemplate(TKEY(0), Target, TP);
1641                 break;
1642         case SV_PREEVALUATED:
1643                 Handler = (HashHandler*) TP->Tokens->PreEval;
1644                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1645                         return -1;
1646                 }
1647                 Handler->HandlerFunc(Target, TP);
1648                 break;          
1649         default:
1650                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1651                         Handler = (HashHandler*) vVar;
1652                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1653                                 return -1;
1654                         }
1655                         else {
1656                                 Handler->HandlerFunc(Target, TP);
1657                         }
1658                 }
1659                 else {
1660                         LogTemplateError(
1661                                 Target, "Token UNKNOWN", ERR_NAME, TP,
1662                                 "You've specified a token that isn't known to webcit.!");
1663                 }
1664         }
1665         return 0;
1666 }
1667
1668
1669
1670 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1671 {
1672         WCTemplate *pTmpl = Tmpl;
1673         int done = 0;
1674         int i, state;
1675         const char *pData, *pS;
1676         long len;
1677         WCTemplputParams TP;
1678
1679         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1680
1681         TP.Context = CallingTP->Context;
1682         TP.Sub = CallingTP->Sub;
1683         TP.Super = CallingTP->Super;
1684
1685         if (LoadTemplates != 0) {                       
1686                 if (LoadTemplates > 1)
1687                         syslog(1, "DBG: ----- loading:  [%s] ------ \n", 
1688                                 ChrPtr(Tmpl->FileName));
1689                 pTmpl = duplicate_template(Tmpl);
1690                 if(load_template(Target, pTmpl) == NULL) {
1691                         StrBufAppendPrintf(
1692                                 Target, 
1693                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1694                                 ChrPtr(Tmpl->FileName));
1695                         FreeWCTemplate(pTmpl);
1696                         return NULL;
1697                 }
1698
1699         }
1700
1701         pS = pData = ChrPtr(pTmpl->Data);
1702         len = StrLength(pTmpl->Data);
1703         i = 0;
1704         state = 0;
1705         while (!done) {
1706                 if (i >= pTmpl->nTokensUsed) {
1707                         StrBufAppendBufPlain(Target, 
1708                                              pData, 
1709                                              len - (pData - pS), 0);
1710                         done = 1;
1711                 }
1712                 else {
1713                         StrBufAppendBufPlain(
1714                                 Target, pData, 
1715                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1716                         TP.Tokens = pTmpl->Tokens[i];
1717                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1718                         state = EvaluateToken(Target, state, &TP);
1719
1720                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1721                         /* condition told us to skip till its end condition */
1722                                 i++;
1723                                 TP.Tokens = pTmpl->Tokens[i];
1724                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1725                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1726                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1727                                         if (state == EvaluateConditional(
1728                                                     Target, 
1729                                                     pTmpl->Tokens[i]->Flags, 
1730                                                     state, 
1731                                                     &TP))
1732                                                 state = 0;
1733                                 }
1734                         }
1735                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1736                         if (i > pTmpl->nTokensUsed)
1737                                 done = 1;
1738                 }
1739         }
1740         if (LoadTemplates != 0) {
1741                 FreeWCTemplate(pTmpl);
1742         }
1743         return Tmpl->MimeType;
1744
1745 }
1746
1747 /**
1748  * \brief Display a variable-substituted template
1749  * \param templatename template file to load
1750  * \returns the mimetype of the template its doing
1751  */
1752 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1753 {
1754         WCTemplputParams LocalTP;
1755         HashList *Static;
1756         HashList *StaticLocal;
1757         void *vTmpl;
1758         
1759         if (Target == NULL)
1760                 Target = WC->WBuf;
1761         if (TP == NULL) {
1762                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1763                 TP = &LocalTP;
1764         }
1765
1766         Static = TemplateCache;
1767         StaticLocal = LocalTemplateCache;
1768
1769         if (len == 0)
1770         {
1771                 syslog(1, "Can't to load a template with empty name!\n");
1772                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1773                 return NULL;
1774         }
1775
1776         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1777             !GetHash(Static, templatename, len, &vTmpl)) {
1778                 syslog(1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1779                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1780                                    templatename, len, 
1781                                    (long)strlen(templatename));
1782 #if 0
1783                 dbg_PrintHash(Static, PrintTemplate, NULL);
1784                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1785 #endif
1786                 return NULL;
1787         }
1788         if (vTmpl == NULL) 
1789                 return NULL;
1790         return ProcessTemplate(vTmpl, Target, TP);
1791
1792 }
1793
1794
1795 void tmplput_Comment(StrBuf *Target, WCTemplputParams *TP)
1796 {
1797         if (LoadTemplates != 0)
1798         {
1799                 StrBuf *Comment;
1800                 const char *pch;
1801                 long len;
1802
1803                 GetTemplateTokenString(Target, TP, 0, &pch, &len);
1804                 Comment = NewStrBufPlain(pch, len);
1805                 StrBufAppendBufPlain(Target, HKEY("<!--"), 0);
1806                 StrBufAppendTemplate(Target, TP, Comment, 1);
1807                 StrBufAppendBufPlain(Target, HKEY("-->"), 0);
1808                 FreeStrBuf(&Comment);
1809         }
1810 }
1811
1812 /*-----------------------------------------------------------------------------
1813  *                      Iterators
1814  */
1815 typedef struct _HashIterator {
1816         HashList *StaticList;
1817         int AdditionalParams;
1818         int ContextType;
1819         int XPectContextType;
1820         int Flags;
1821         RetrieveHashlistFunc GetHash;
1822         HashDestructorFunc Destructor;
1823         SubTemplFunc DoSubTemplate;
1824 } HashIterator;
1825
1826 void RegisterITERATOR(const char *Name, long len, 
1827                       int AdditionalParams, 
1828                       HashList *StaticList, 
1829                       RetrieveHashlistFunc GetHash, 
1830                       SubTemplFunc DoSubTempl,
1831                       HashDestructorFunc Destructor,
1832                       int ContextType, 
1833                       int XPectContextType, 
1834                       int Flags)
1835 {
1836         HashIterator *It;
1837
1838         It = (HashIterator*)malloc(sizeof(HashIterator));
1839         memset(It, 0, sizeof(HashIterator));
1840         It->StaticList = StaticList;
1841         It->AdditionalParams = AdditionalParams;
1842         It->GetHash = GetHash;
1843         It->DoSubTemplate = DoSubTempl;
1844         It->Destructor = Destructor;
1845         It->ContextType = ContextType;
1846         It->XPectContextType = XPectContextType;
1847         It->Flags = Flags;
1848         Put(Iterators, Name, len, It, NULL);
1849 }
1850
1851 typedef struct _iteratestruct {
1852         int GroupChange;
1853         int oddeven;
1854         const char *Key;
1855         long KeyLen;
1856         int n;
1857         int LastN;
1858         }IterateStruct; 
1859
1860 int preeval_iterate(WCTemplateToken *Token)
1861 {
1862         WCTemplputParams TPP;
1863         WCTemplputParams *TP;
1864         void *vTmpl;
1865         void *vIt;
1866         HashIterator *It;
1867
1868         memset(&TPP, 0, sizeof(WCTemplputParams));
1869         TP = &TPP;
1870         TP->Tokens = Token;
1871         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1872                 LogTemplateError(
1873                         NULL, "Iterator", ERR_PARM1, TP,
1874                         "not found");
1875                 return 0;
1876         }
1877         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
1878                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
1879                                  "Need token with type Subtemplate as param 1, have %s", 
1880                                  TP->Tokens->Params[1]->Start);
1881         }
1882         
1883         /* well, we don't check the mobile stuff here... */
1884         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
1885             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
1886                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
1887                                  "referenced here doesn't exist");
1888         }
1889         Token->Preeval2 = vIt;
1890         It = (HashIterator *) vIt;
1891
1892         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1893                 LogTemplateError(                               
1894                         NULL, "Iterator", ERR_PARM1, TP,
1895                         "doesn't work with %d params", 
1896                         TP->Tokens->nParameters);
1897         }
1898
1899
1900         return 1;
1901 }
1902
1903 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1904 {
1905         HashIterator *It;
1906         HashList *List;
1907         HashPos  *it;
1908         SortStruct *SortBy = NULL;
1909         void *vSortBy;
1910         int DetectGroupChange = 0;
1911         int nMembersUsed;
1912         void *vContext;
1913         void *vLastContext = NULL;
1914         StrBuf *SubBuf;
1915         WCTemplputParams IterateTP;
1916         WCTemplputParams SubTP;
1917         IterateStruct Status;
1918
1919         long StartAt = 0;
1920         long StepWidth = 0;
1921         long StopAt = -1;
1922
1923         memset(&Status, 0, sizeof(IterateStruct));
1924         
1925         It = (HashIterator*) TP->Tokens->Preeval2;
1926         if (It == NULL) {
1927                 LogTemplateError(
1928                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1929                 return;
1930         }
1931
1932         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1933                 LogTemplateError(                               
1934                         Target, "Iterator", ERR_PARM1, TP,
1935                         "doesn't work with %d params", 
1936                         TP->Tokens->nParameters - 1);
1937                 return;
1938         }
1939
1940         if ((It->XPectContextType != CTX_NONE) &&
1941             (It->XPectContextType != TP->Filter.ContextType)) {
1942                 LogTemplateError(
1943                         Target, "Iterator", ERR_PARM1, TP,
1944                         "requires context of type %s, have %s", 
1945                         ContextName(It->XPectContextType), 
1946                         ContextName(TP->Filter.ContextType));
1947                 return ;
1948                 
1949         }
1950
1951         if (It->StaticList == NULL)
1952                 List = It->GetHash(Target, TP);
1953         else
1954                 List = It->StaticList;
1955
1956         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1957         if (DetectGroupChange) {
1958                 const StrBuf *BSort;
1959                 DetectGroupChange = 0;
1960                 if (havebstr("SortBy")) {
1961                         BSort = sbstr("SortBy");
1962                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1963                             (vSortBy != NULL)) {
1964                                 SortBy = (SortStruct*)vSortBy;
1965                                 /* first check whether its intended for us... */
1966                                 if ((SortBy->ContextType == It->ContextType)&&
1967                                 /** Ok, its us, lets see in which direction we should sort... */
1968                                     (havebstr("SortOrder"))) {
1969                                         int SortOrder;
1970                                         SortOrder = LBSTR("SortOrder");
1971                                         if (SortOrder != 0)
1972                                                 DetectGroupChange = 1;
1973                                 }
1974                         }
1975                 }
1976         }
1977         nMembersUsed = GetCount(List);
1978
1979         StackContext (TP, &IterateTP, &Status, CTX_ITERATE, 0, TP->Tokens);
1980         {
1981                 SubBuf = NewStrBuf();
1982         
1983                 if (HAVE_PARAM(2)) {
1984                         StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1985                 }
1986                 if (HAVE_PARAM(3)) {
1987                         StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1988                 }
1989                 if (HAVE_PARAM(4)) {
1990                         StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1991                 }
1992                 it = GetNewHashPos(List, StepWidth);
1993                 if (StopAt < 0) {
1994                         StopAt = GetCount(List);
1995                 }
1996                 while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1997                         if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1998                                 if (DetectGroupChange && Status.n > 0) {
1999                                         Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
2000                                 }
2001                                 Status.LastN = (Status.n + 1) == nMembersUsed;
2002                                 StackContext(&IterateTP, &SubTP, vContext, It->ContextType, 0, NULL);
2003                                 {
2004                                         if (It->DoSubTemplate != NULL)
2005                                                 It->DoSubTemplate(SubBuf, &SubTP);
2006                                         DoTemplate(TKEY(1), SubBuf, &SubTP);
2007
2008                                         StrBufAppendBuf(Target, SubBuf, 0);
2009                                         FlushStrBuf(SubBuf);
2010                                 }
2011                                 UnStackContext(&SubTP);
2012                                 Status.oddeven = ! Status.oddeven;
2013                                 vLastContext = vContext;
2014                         }
2015                         Status.n++;
2016                 }
2017         }
2018         UnStackContext(&IterateTP);
2019         FreeStrBuf(&SubBuf);
2020         DeleteHashPos(&it);
2021         if (It->Destructor != NULL)
2022                 It->Destructor(&List);
2023 }
2024
2025
2026 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2027 {
2028         IterateStruct *Ctx = CTX(CTX_ITERATE);
2029         if (TP->Tokens->nParameters < 3)
2030                 return  Ctx->GroupChange;
2031
2032         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2033 }
2034
2035 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2036 {
2037         IterateStruct *Ctx = CTX(CTX_ITERATE);
2038         if (Ctx->oddeven)
2039                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2040         else
2041                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2042 }
2043
2044
2045 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2046 {
2047         IterateStruct *Ctx = CTX(CTX_ITERATE);
2048
2049         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2050 }
2051
2052
2053 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2054 {
2055         IterateStruct *Ctx = CTX(CTX_ITERATE);
2056         StrBufAppendPrintf(Target, "%d", Ctx->n);
2057 }
2058
2059 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2060 {
2061         IterateStruct *Ctx = CTX(CTX_ITERATE);
2062         return Ctx->n == 0;
2063 }
2064
2065 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2066 {
2067         IterateStruct *Ctx = CTX(CTX_ITERATE);
2068         return Ctx->LastN;
2069 }
2070
2071
2072
2073 /*-----------------------------------------------------------------------------
2074  *                      Conditionals
2075  */
2076 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2077 {
2078         ConditionalStruct *Cond;
2079         int rc = 0;
2080         int res;
2081
2082         if ((TP->Tokens->Params[0]->len == 1) &&
2083             (TP->Tokens->Params[0]->Start[0] == 'X'))
2084                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2085             
2086         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2087         if (Cond == NULL) {
2088                 LogTemplateError(
2089                         Target, "Conditional", ERR_PARM1, TP,
2090                         "unknown!");
2091                 return 1;
2092         }
2093
2094         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2095                 return 0;
2096         }
2097
2098         res = Cond->CondF(Target, TP);
2099         if (res == Neg)
2100                 rc = TP->Tokens->Params[1]->lvalue;
2101         if (LoadTemplates > 5) 
2102                 syslog(1, "<%s> : %d %d==%d\n", 
2103                         ChrPtr(TP->Tokens->FlatToken), 
2104                         rc, res, Neg);
2105         return rc;
2106 }
2107
2108 void RegisterConditional(const char *Name, long len, 
2109                          int nParams,
2110                          WCConditionalFunc CondF, 
2111                          int ContextRequired)
2112 {
2113         ConditionalStruct *Cond;
2114
2115         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2116         memset(Cond, 0, sizeof(ConditionalStruct));
2117         Cond->PlainName = Name;
2118         Cond->Filter.nMaxArgs = nParams;
2119         Cond->Filter.nMinArgs = nParams;
2120         Cond->CondF = CondF;
2121         Cond->Filter.ContextType = ContextRequired;
2122         Put(Conditionals, Name, len, Cond, NULL);
2123 }
2124
2125 void RegisterTokenParamDefine(const char *Name, long len, 
2126                               long Value)
2127 {
2128         long *PVal;
2129
2130         PVal = (long*)malloc(sizeof(long));
2131         *PVal = Value;
2132         Put(Defines, Name, len, PVal, NULL);
2133 }
2134
2135 long GetTokenDefine(const char *Name, long len, 
2136                     long DefValue)
2137 {
2138         void *vPVal;
2139
2140         if (GetHash(Defines, Name, len, &vPVal) &&
2141              (vPVal != NULL))
2142          {
2143                  return *(long*) vPVal;
2144          }
2145          else
2146          {
2147                  return DefValue;
2148          }
2149 }
2150
2151 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2152 {
2153         const char *Str;
2154         long len;
2155         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2156         
2157         StrBufAppendBufPlain(Target, Str, len, 0);
2158 }
2159
2160 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2161 {
2162         int val;
2163
2164         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2165         StrBufAppendPrintf(Target, "%d", val);
2166 }
2167
2168 HashList *Defines;
2169
2170 /*-----------------------------------------------------------------------------
2171  *                      Context Strings
2172  */
2173 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2174 {
2175         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX(CTX_STRBUF), 0);
2176 }
2177 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2178 {
2179         StrBuf *TokenText = (StrBuf*) CTX((CTX_STRBUF));
2180         const char *CompareToken;
2181         long len;
2182
2183         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2184         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2185 }
2186
2187 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2188 {
2189         HashList *Arr = (HashList*) CTX(CTX_STRBUFARR);
2190         void *pV;
2191         int val;
2192
2193         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2194         if (GetHash(Arr, IKEY(val), &pV) && 
2195             (pV != NULL)) {
2196                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2197         }
2198 }
2199 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2200 {
2201         HashList *Arr = (HashList*) CTX(CTX_STRBUFARR);
2202         void *pV;
2203         int val;
2204         const char *CompareToken;
2205         long len;
2206
2207         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2208         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2209         if (GetHash(Arr, IKEY(val), &pV) && 
2210             (pV != NULL)) {
2211                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2212         }
2213         else
2214                 return 0;
2215 }
2216
2217 /*-----------------------------------------------------------------------------
2218  *                      Boxed-API
2219  */
2220
2221 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2222 {
2223         WCTemplputParams SubTP;
2224
2225         StrBuf *Headline = NULL;
2226         if (TP->Tokens->nParameters == 2) {
2227                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2228                         Headline = NewStrBuf();
2229                         DoTemplate(TKEY(1), Headline, TP);
2230                 }
2231                 else {
2232                         const char *Ch;
2233                         long len;
2234                         GetTemplateTokenString(Target, 
2235                                                TP, 
2236                                                1,
2237                                                &Ch,
2238                                                &len);
2239                         Headline = NewStrBufPlain(Ch, len);
2240                 }
2241         }
2242         /* else TODO error? logging? */
2243
2244         StackContext (TP, &SubTP, Headline, CTX_STRBUF, 0, NULL);
2245         {
2246                 DoTemplate(HKEY("box_begin"), Target, &SubTP);
2247         }
2248         UnStackContext(&SubTP);
2249         DoTemplate(TKEY(0), Target, TP);
2250         DoTemplate(HKEY("box_end"), Target, TP);
2251         FreeStrBuf(&Headline);
2252 }
2253
2254 /*-----------------------------------------------------------------------------
2255  *                      Tabbed-API
2256  */
2257
2258 typedef struct _tab_struct {
2259         long CurrentTab;
2260         StrBuf *TabTitle;
2261 } tab_struct;
2262
2263 int preeval_do_tabbed(WCTemplateToken *Token)
2264 {
2265         WCTemplputParams TPP;
2266         WCTemplputParams *TP;
2267         const char *Ch;
2268         long len;
2269         int i, nTabs;
2270
2271         memset(&TPP, 0, sizeof(WCTemplputParams));
2272         TP = &TPP;
2273         TP->Tokens = Token;
2274         nTabs = TP->Tokens->nParameters / 2 - 1;
2275         if (TP->Tokens->nParameters % 2 != 0)
2276         {
2277                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2278                                  "need even number of arguments");
2279                 return 0;
2280
2281         }
2282         else for (i = 0; i < nTabs; i++) {
2283                 if (!HaveTemplateTokenString(NULL, 
2284                                              TP, 
2285                                              i * 2,
2286                                              &Ch,
2287                                              &len) || 
2288                     (TP->Tokens->Params[i * 2]->len == 0))
2289                 {
2290                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2291                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2292                                          i, TP->Tokens->Params[i * 2]->Start);
2293                         return 0;
2294                 }
2295                 if (!HaveTemplateTokenString(NULL, 
2296                                              TP, 
2297                                              i * 2 + 1,
2298                                              &Ch,
2299                                              &len) || 
2300                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2301                 {
2302                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2303                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2304                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2305                         return 0;
2306                 }
2307         }
2308
2309         if (!HaveTemplateTokenString(NULL, 
2310                                      TP, 
2311                                      i * 2 + 1,
2312                                      &Ch,
2313                                      &len) || 
2314             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2315         {
2316                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2317                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2318                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2319                 return 0;
2320         }
2321         return 1;
2322 }
2323
2324
2325 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2326 {
2327         StrBuf **TabNames;
2328         int i, ntabs, nTabs;
2329         tab_struct TS;
2330         WCTemplputParams SubTP;
2331
2332         memset(&TS, 0, sizeof(tab_struct));
2333
2334         nTabs = ntabs = TP->Tokens->nParameters / 2;
2335         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2336         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2337
2338         for (i = 0; i < ntabs; i++) {
2339                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2340                     (TP->Tokens->Params[i * 2]->len > 0)) {
2341                         TabNames[i] = NewStrBuf();
2342                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2343                 }
2344                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2345                         const char *Ch;
2346                         long len;
2347                         GetTemplateTokenString(Target, 
2348                                                TP, 
2349                                                i * 2,
2350                                                &Ch,
2351                                                &len);
2352                         TabNames[i] = NewStrBufPlain(Ch, -1);
2353                 }
2354                 else { 
2355                         /** A Tab without subject? we can't count that, add it as silent */
2356                         nTabs --;
2357                 }
2358         }
2359         StackContext (TP, &SubTP, &TS, CTX_TAB, 0, NULL);
2360         {
2361 ////    TODO jetzt      memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2362 //              SubTP.Filter.ControlContextType = ;
2363
2364                 StrTabbedDialog(Target, nTabs, TabNames);
2365                 for (i = 0; i < ntabs; i++) {
2366                         memset(&TS, 0, sizeof(tab_struct));
2367                         TS.CurrentTab = i;
2368                         TS.TabTitle = TabNames[i];
2369                         StrBeginTab(Target, i, nTabs, TabNames);
2370                         DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
2371                         StrEndTab(Target, i, nTabs);
2372                 }
2373                 for (i = 0; i < ntabs; i++) 
2374                         FreeStrBuf(&TabNames[i]);
2375                 free(TabNames);
2376         }
2377         UnStackContext(&SubTP);
2378 }
2379
2380 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
2381 {
2382         tab_struct *Ctx = CTX(CTX_TAB);
2383
2384         StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
2385 }
2386
2387 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
2388 {
2389         tab_struct *Ctx = CTX(CTX_TAB);
2390         StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
2391 }
2392
2393 /*-----------------------------------------------------------------------------
2394  *                      Sorting-API
2395  */
2396
2397
2398 void RegisterSortFunc(const char *name, long len, 
2399                       const char *prepend, long preplen,
2400                       CompareFunc Forward, 
2401                       CompareFunc Reverse, 
2402                       CompareFunc GroupChange, 
2403                       long ContextType)
2404 {
2405         SortStruct *NewSort;
2406
2407         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2408         memset(NewSort, 0, sizeof(SortStruct));
2409         NewSort->Name = NewStrBufPlain(name, len);
2410         if (prepend != NULL)
2411                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2412         else
2413                 NewSort->PrefPrepend = NULL;
2414         NewSort->Forward = Forward;
2415         NewSort->Reverse = Reverse;
2416         NewSort->GroupChange = GroupChange;
2417         NewSort->ContextType = ContextType;
2418         if (ContextType == CTX_NONE) {
2419                 syslog(1, "sorting requires a context. CTX_NONE won't make it.\n");
2420                 exit(1);
2421         }
2422                 
2423         Put(SortHash, name, len, NewSort, DestroySortStruct);
2424 }
2425
2426 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2427                          const char *OtherPrefix, long OtherPrefixLen,
2428                          const char *Default, long ldefault, long DefaultDirection)
2429 {
2430         const StrBuf *BSort = NULL;
2431         SortStruct *SortBy;
2432         void *vSortBy;
2433         long SortOrder = -1;
2434         
2435         if (havebstr("SortBy")) {
2436                 BSort = sbstr("SortBy");
2437                 if (OtherPrefix == NULL) {
2438                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2439                 }
2440                 else {
2441                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2442                 }
2443         }
2444         else { /** Try to fallback to our remembered values... */
2445                 if (OtherPrefix == NULL) {
2446                         BSort = get_room_pref("sort");
2447                 }
2448                 else {
2449                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2450                 }
2451                 if (BSort != NULL)
2452                         putbstr("SortBy", NewStrBufDup(BSort));
2453                 else {
2454                         StrBuf *Buf;
2455
2456                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2457                         putbstr("SortBy", Buf);
2458                 }
2459         }
2460
2461         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2462             (vSortBy == NULL)) {
2463                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2464                     (vSortBy == NULL)) {
2465                         LogTemplateError(
2466                                 NULL, "Sorting", ERR_PARM1, TP,
2467                                 "Illegal default sort: [%s]", Default);
2468                         wc_backtrace();
2469                 }
2470         }
2471         SortBy = (SortStruct*)vSortBy;
2472
2473         if (SortBy->ContextType != TP->Filter.ContextType)
2474                 return NULL;
2475
2476         /** Ok, its us, lets see in which direction we should sort... */
2477         if (havebstr("SortOrder")) {
2478                 SortOrder = LBSTR("SortOrder");
2479         }
2480         else { /** Try to fallback to our remembered values... */
2481                 StrBuf *Buf = NULL;
2482                 if (SortBy->PrefPrepend == NULL) {
2483                         Buf = get_room_pref("SortOrder");
2484                         SortOrder = StrTol(Buf);
2485                 }
2486                 else {
2487                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2488                 }
2489
2490                 if (Buf == NULL)
2491                         SortOrder = DefaultDirection;
2492
2493                 Buf = NewStrBufPlain(NULL, 64);
2494                 StrBufPrintf(Buf, "%ld", SortOrder);
2495                 putbstr("SortOrder", Buf);
2496         }
2497         switch (SortOrder) {
2498         default:
2499         case 0:
2500                 return NULL;
2501         case 1:
2502                 return SortBy->Forward;
2503         case 2:
2504                 return SortBy->Reverse;
2505         }
2506 }
2507
2508
2509 enum {
2510         eNO_SUCH_SORT, 
2511         eNOT_SPECIFIED,
2512         eINVALID_PARAM,
2513         eFOUND
2514 };
2515
2516 ConstStr SortIcons[] = {
2517         {HKEY("static/webcit_icons/sort_none.gif")},
2518         {HKEY("static/webcit_icons/up_pointer.gif")},
2519         {HKEY("static/webcit_icons/down_pointer.gif")},
2520 };
2521
2522 ConstStr SortNextOrder[] = {
2523         {HKEY("1")},
2524         {HKEY("2")},
2525         {HKEY("0")},
2526 };
2527
2528
2529 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2530 {
2531         int bSortError = eNOT_SPECIFIED;
2532         const StrBuf *BSort;
2533         void *vSort;
2534         
2535         *SortOrder = 0;
2536         *Next = NULL;
2537         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2538             (vSort == NULL))
2539                 return eNO_SUCH_SORT;
2540         *Param = (SortStruct*) vSort;
2541         
2542
2543         if (havebstr("SortBy")) {
2544                 BSort = sbstr("SortBy");
2545                 bSortError = eINVALID_PARAM;
2546                 if ((*Param)->PrefPrepend == NULL) {
2547                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2548                 }
2549                 else {
2550                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2551                 }
2552         }
2553         else { /** Try to fallback to our remembered values... */
2554                 if ((*Param)->PrefPrepend == NULL) {
2555                         BSort = get_room_pref("sort");
2556                 }
2557                 else {
2558                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2559                 }
2560         }
2561
2562         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2563             (vSort == NULL))
2564                 return bSortError;
2565
2566         *Next = (SortStruct*) vSort;
2567
2568         /** Ok, its us, lets see in which direction we should sort... */
2569         if (havebstr("SortOrder")) {
2570                 *SortOrder = LBSTR("SortOrder");
2571         }
2572         else { /** Try to fallback to our remembered values... */
2573                 if ((*Param)->PrefPrepend == NULL) {
2574                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2575                 }
2576                 else {
2577                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2578                 }
2579         }
2580         if (*SortOrder > 2)
2581                 *SortOrder = 0;
2582
2583         return eFOUND;
2584 }
2585
2586
2587 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2588 {
2589         long SortOrder;
2590         SortStruct *Next;
2591         SortStruct *Param;
2592         const ConstStr *SortIcon;
2593
2594         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2595         case eNO_SUCH_SORT:
2596                 LogTemplateError(
2597                         Target, "Sorter", ERR_PARM1, TP,
2598                         " Sorter [%s] unknown!", 
2599                         TP->Tokens->Params[0]->Start);
2600                 break;          
2601         case eINVALID_PARAM:
2602                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2603                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2604                                  bstr("SortBy"));
2605         case eNOT_SPECIFIED:
2606         case eFOUND:
2607                 if (Next == Param) {
2608                         SortIcon = &SortIcons[SortOrder];
2609                 }
2610                 else { /** Not Us... */
2611                         SortIcon = &SortIcons[0];
2612                 }
2613                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2614         }
2615 }
2616
2617 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2618 {
2619         long SortOrder;
2620         SortStruct *Next;
2621         SortStruct *Param;
2622
2623         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2624         case eNO_SUCH_SORT:
2625                 LogTemplateError(
2626                         Target, "Sorter", ERR_PARM1, TP,                                  
2627                         " Sorter [%s] unknown!", 
2628                         TP->Tokens->Params[0]->Start);
2629                 break;          
2630         case eINVALID_PARAM:
2631                 LogTemplateError(
2632                         NULL, "Sorter", ERR_PARM1, TP,
2633                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2634                         bstr("SortBy"));
2635         case eNOT_SPECIFIED:
2636         case eFOUND:
2637                 StrBufAppendBuf(Target, Param->Name, 0);
2638                 
2639         }
2640 }
2641
2642 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2643 {
2644         long SortOrder;
2645         const ConstStr *SortOrderStr;
2646         SortStruct *Next;
2647         SortStruct *Param;
2648
2649         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2650         case eNO_SUCH_SORT:
2651                 LogTemplateError(
2652                         Target, "Sorter", ERR_PARM1, TP,
2653                         " Sorter [%s] unknown!",
2654                         TP->Tokens->Params[0]->Start);
2655                 break;          
2656         case eINVALID_PARAM:
2657                 LogTemplateError(
2658                         NULL, "Sorter", ERR_PARM1, TP,
2659                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2660                         bstr("SortBy"));
2661         case eNOT_SPECIFIED:
2662         case eFOUND:
2663                 if (Next == Param) {
2664                         SortOrderStr = &SortNextOrder[SortOrder];
2665                 }
2666                 else { /** Not Us... */
2667                         SortOrderStr = &SortNextOrder[0];
2668                 }
2669                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2670         }
2671 }
2672
2673
2674 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2675 {
2676         long *LongVector = (long*) CTX(CTX_LONGVECTOR);
2677
2678         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2679             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2680         {
2681                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2682         }
2683         else
2684         {
2685                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2686                         LogTemplateError(
2687                                 Target, "Longvector", ERR_NAME, TP,
2688                                 "needs a numerical Parameter!");
2689                 }
2690                 else {
2691                         LogTemplateError(
2692                                 Target, "LongVector", ERR_PARM1, TP,
2693                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2694                                 TP->Tokens->Params[0]->lvalue,
2695                                 LongVector[0]);
2696                 }
2697         }
2698 }
2699
2700 void dbg_print_longvector(long *LongVector)
2701 {
2702         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2703         int nItems = LongVector[0];
2704         int i;
2705
2706         for (i = 0; i < nItems; i++) {
2707                 if (i + 1 < nItems)
2708                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2709                 else
2710                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2711
2712         }
2713         syslog(1, "%s", ChrPtr(Buf));
2714         FreeStrBuf(&Buf);
2715 }
2716
2717 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2718 {
2719         long *LongVector = (long*) CTX(CTX_LONGVECTOR);
2720
2721         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2722             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2723             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2724             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2725         {
2726                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2727                         LongVector[TP->Tokens->Params[3]->lvalue];
2728         }
2729         else
2730         {
2731                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2732                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2733                         LogTemplateError(
2734                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2735                                 "needs two long Parameter!");
2736                 }
2737                 else {
2738                         LogTemplateError(
2739                                 Target, "Longvector", ERR_PARM1, TP,
2740                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2741                                 TP->Tokens->Params[2]->lvalue,
2742                                 TP->Tokens->Params[3]->lvalue,
2743                                 LongVector[0]);
2744                 }
2745         }
2746         return 0;
2747 }
2748
2749
2750 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
2751 {
2752         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
2753 }
2754
2755 void 
2756 InitModule_SUBST
2757 (void)
2758 {
2759         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2760         RegisterNamespace("--", 0, 2, tmplput_Comment, NULL, CTX_NONE);
2761         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
2762         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
2763         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
2764         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
2765         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUFARR);
2766         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
2767         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
2768         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
2769         RegisterNamespace("TAB:N", 0, 0, tmplput_TAB_N, NULL, CTX_TAB);
2770         RegisterNamespace("TAB:SUBJECT", 0, 1, tmplput_TAB_TITLE, NULL, CTX_TAB);
2771
2772
2773         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
2774
2775
2776         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2777         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
2778         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2779
2780
2781         RegisterConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2782                             conditional_ITERATE_ISGROUPCHANGE, 
2783                             CTX_ITERATE);
2784         RegisterConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2785                             conditional_ITERATE_LASTN, 
2786                             CTX_ITERATE);
2787         RegisterConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2788                             conditional_ITERATE_FIRSTN, 
2789                             CTX_ITERATE);
2790
2791         RegisterNamespace("ITERATE:ODDEVEN", 0, 0, tmplput_ITERATE_ODDEVEN, NULL, CTX_ITERATE);
2792         RegisterNamespace("ITERATE:KEY", 0, 0, tmplput_ITERATE_KEY, NULL, CTX_ITERATE);
2793         RegisterNamespace("ITERATE:N", 0, 0, tmplput_ITERATE_LASTN, NULL, CTX_ITERATE);
2794         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
2795         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
2796         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
2797
2798
2799
2800
2801 }
2802
2803 void
2804 ServerStartModule_SUBST
2805 (void)
2806 {
2807         LocalTemplateCache = NewHash(1, NULL);
2808         TemplateCache = NewHash(1, NULL);
2809
2810         GlobalNS = NewHash(1, NULL);
2811         Iterators = NewHash(1, NULL);
2812         Conditionals = NewHash(1, NULL);
2813         SortHash = NewHash(1, NULL);
2814         Defines = NewHash(1, NULL);
2815 }
2816
2817 void
2818 FinalizeModule_SUBST
2819 (void)
2820 {
2821
2822 }
2823
2824 void 
2825 ServerShutdownModule_SUBST
2826 (void)
2827 {
2828         DeleteHash(&TemplateCache);
2829         DeleteHash(&LocalTemplateCache);
2830
2831         DeleteHash(&GlobalNS);
2832         DeleteHash(&Iterators);
2833         DeleteHash(&Conditionals);
2834         DeleteHash(&SortHash);
2835         DeleteHash(&Defines);
2836 }
2837
2838
2839 void
2840 SessionNewModule_SUBST
2841 (wcsession *sess)
2842 {
2843
2844 }
2845
2846 void
2847 SessionAttachModule_SUBST
2848 (wcsession *sess)
2849 {
2850 }
2851
2852 void
2853 SessionDetachModule_SUBST
2854 (wcsession *sess)
2855 {
2856         FreeStrBuf(&sess->WFBuf);
2857 }
2858
2859 void 
2860 SessionDestroyModule_SUBST  
2861 (wcsession *sess)
2862 {
2863
2864 }