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