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