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