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