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