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