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