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