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