* add way to have tokens do their custom parse-time preevaluation; this involves...
[citadel.git] / webcit / subst.c
index 6a9c28baa17945cded85972293fe260ea2285a7c..71b8e3ff3aeabc520e2e4fed59c6bb3ff011e77f 100644 (file)
@@ -1,12 +1,6 @@
 /*
  * $Id$
  */
-/**
- * \defgroup Subst Variable substitution type stuff
- * \ingroup CitadelConfig
- */
-
-/*@{*/
 
 #include "sysdep.h"
 #include <sys/types.h>
@@ -20,7 +14,7 @@
 #include "webcit.h"
 #include "webserver.h"
 
-extern char *static_dirs[PATH_MAX];  /**< Disk representation */
+extern char *static_dirs[PATH_MAX];  /* Disk representation */
 
 HashList *WirelessTemplateCache;
 HashList *WirelessLocalTemplateCache;
@@ -32,8 +26,13 @@ HashList *Iterators;
 HashList *Conditionals;
 HashList *SortHash;
 
+int DumpTemplateI18NStrings = 0;
 int LoadTemplates = 0;
+int dbg_bactrace_template_errors = 0;
 WCTemplputParams NoCtx;
+StrBuf *I18nDump = NULL;
+
+const char EmptyStr[]="";
 
 #define SV_GETTEXT 1
 #define SV_CONDITIONAL 2
@@ -42,23 +41,40 @@ WCTemplputParams NoCtx;
 #define SV_SUBTEMPL 5
 #define SV_PREEVALUATED 6
 
+
+/*
+ * Dynamic content for variable substitution in templates
+ */
+typedef struct _wcsubst {
+       ContextFilter Filter;
+       int wcs_type;                           /* which type of Substitution are we */
+       char wcs_key[32];                       /* copy of our hashkey for debugging */
+       StrBuf *wcs_value;                      /* if we're a string, keep it here */
+       long lvalue;                            /* type long? keep data here */
+       WCHandlerFunc wcs_function;             /* funcion hook ???*/
+} wcsubst;
+
+
 typedef struct _WCTemplate {
        StrBuf *Data;
        StrBuf *FileName;
        int nTokensUsed;
        int TokenSpace;
+       StrBuf *MimeType;
        WCTemplateToken **Tokens;
 } WCTemplate;
 
 typedef struct _HashHandler {
        ContextFilter Filter;
-
+       WCPreevalFunc PreEvalFunc;
        WCHandlerFunc HandlerFunc;
 }HashHandler;
 
 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere);
 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP);
 
+
+
 typedef struct _SortStruct {
        StrBuf *Name;
        StrBuf *PrefPrepend;
@@ -86,7 +102,10 @@ const char *CtxNames[]  = {
        "Context LONGVECTOR",
        "Context ROOMS",
        "Context FLOORS",
-       "Context ITERATE"
+       "Context ITERATE",
+       "Context ICAL",
+       "Context DavNamespace",
+       "Context UNKNOWN"
 };
 
 
@@ -99,8 +118,18 @@ void DestroySortStruct(void *vSort)
        free (Sort);
 }
 
+const char *ContextName(int ContextType)
+{
+       if (ContextType < CTX_UNKNOWN)
+               return CtxNames[ContextType];
+       else
+               return CtxNames[CTX_UNKNOWN];
+}
+
 void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplputParams *TP, const char *Format, ...)
 {
+       wcsession *WCC;
+       StrBuf *Header;
        StrBuf *Error;
        StrBuf *Info;
         va_list arg_ptr;
@@ -116,23 +145,50 @@ void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplpu
        switch (ErrorPos) {
        default:
        case ERR_NAME: /* the main token name... */ 
-               Err = TP->Tokens->pName;
+               Err = (TP->Tokens!= NULL)? TP->Tokens->pName:"";
                break;
        case ERR_PARM1:
-               Err = TP->Tokens->Params[0]->Start;
+               Err = ((TP->Tokens!= NULL) && 
+                      (TP->Tokens->nParameters > 0))? 
+                       TP->Tokens->Params[0]->Start : "";
                break;
        case ERR_PARM2:
-               Err = TP->Tokens->Params[1]->Start;
+               Err = ((TP->Tokens!= NULL) && 
+                      (TP->Tokens->nParameters > 1))? 
+                       TP->Tokens->Params[1]->Start : "";
                break;
        }
-       lprintf(1, "%s [%s]  (in '%s' line %ld); %s; [%s]\n", 
-               Type, 
-               Err, 
-               ChrPtr(TP->Tokens->FileName),
-               TP->Tokens->Line, 
-               ChrPtr(Error), 
-               ChrPtr(TP->Tokens->FlatToken));
-       if (Target != NULL) {
+       if (TP->Tokens != NULL) 
+       {
+               lprintf(1, "%s [%s]  (in '%s' line %ld); %s; [%s]\n", 
+                       Type, 
+                       Err, 
+                       ChrPtr(TP->Tokens->FileName),
+                       TP->Tokens->Line, 
+                       ChrPtr(Error), 
+                       ChrPtr(TP->Tokens->FlatToken));
+       }
+       else 
+       {
+               lprintf(1, "%s: %s;\n", 
+                       Type, 
+                       ChrPtr(Error));
+       }
+/*
+       if (Target == NULL) 
+               return;
+*/
+       WCC = WC;
+       if (WCC == NULL) {
+               FreeStrBuf(&Info);
+               FreeStrBuf(&Error);
+               return; 
+       }
+
+       Header = NewStrBuf();
+       if (TP->Tokens != NULL) 
+       {
+               /* deprecated: 
                StrBufAppendPrintf(                                                          
                        Target,                                                              
                        "<pre>\n%s [%s] (in '%s' line %ld); %s\n[%s]\n</pre>\n",
@@ -142,7 +198,89 @@ void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplpu
                        TP->Tokens->Line,
                        ChrPtr(Error),
                        ChrPtr(TP->Tokens->FlatToken));
+               */
+               StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
+                            Type, 
+                            Err, 
+                            ChrPtr(Error), 
+                            ChrPtr(TP->Tokens->FlatToken));
+
+
+               SerializeJson(Header, WildFireException(SKEY(TP->Tokens->FileName),
+                                                       TP->Tokens->Line,
+                                                       Info,
+                                                       1), 1);
+/*
+               SerializeJson(Header, WildFireMessage(SKEY(TP->Tokens->FileName),
+                                                     TP->Tokens->Line,
+                                                     Error,
+                                                     eERROR), 1);
+*/
+               WildFireSerializePayload(Header, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
        }
+       else
+       {
+               /* deprecated.
+               StrBufAppendPrintf(                                                          
+                       Target,                                                              
+                       "<pre>\n%s: %s\n</pre>\n",
+                       Type, 
+                       ChrPtr(Error));
+               */
+               StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
+                            Type, 
+                            Err, 
+                            ChrPtr(Error), 
+                            ChrPtr(TP->Tokens->FlatToken));
+               SerializeJson(Header, WildFireException(HKEY(__FILE__), __LINE__, Info, 1), 1);
+               WildFireSerializePayload(Header, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
+       }
+       FreeStrBuf(&Header);
+       FreeStrBuf(&Info);
+       FreeStrBuf(&Error);
+/*
+       if (dbg_bactrace_template_errors)
+               wc_backtrace(); 
+*/
+}
+
+
+
+
+void LogError (StrBuf *Target, const char *Type, const char *Format, ...)
+{
+       wcsession *WCC;
+       StrBuf *Header;
+       StrBuf *Error;
+       StrBuf *Info;
+        va_list arg_ptr;
+
+       Info = NewStrBuf();
+       Error = NewStrBuf();
+
+        va_start(arg_ptr, Format);
+       StrBufVAppendPrintf(Error, Format, arg_ptr);
+       va_end(arg_ptr);
+
+       lprintf(1, ChrPtr(Error));
+
+       WCC = WC;
+       Header = NewStrBuf();
+
+
+       SerializeJson(Header, WildFireException(Type, strlen(Type),
+                                               0,
+                                               Info,
+                                               1), 1);
+       WildFireSerializePayload(Header, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
+       
+       FreeStrBuf(&Header);
+       FreeStrBuf(&Info);
+       FreeStrBuf(&Error);
+/*
+       if (dbg_bactrace_template_errors)
+               wc_backtrace(); 
+*/
 }
 
 
@@ -151,16 +289,19 @@ void RegisterNS(const char *NSName,
                int nMinArgs, 
                int nMaxArgs, 
                WCHandlerFunc HandlerFunc, 
+               WCPreevalFunc PreevalFunc,
                int ContextRequired)
 {
        HashHandler *NewHandler;
        
        NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
+       memset(NewHandler, 0, sizeof(HashHandler));
        NewHandler->Filter.nMinArgs = nMinArgs;
        NewHandler->Filter.nMaxArgs = nMaxArgs;
        NewHandler->Filter.ContextType = ContextRequired;
        NewHandler->Filter.ControlContextType = CTX_NONE;
 
+       NewHandler->PreEvalFunc = PreevalFunc;
        NewHandler->HandlerFunc = HandlerFunc;  
        Put(GlobalNS, NSName, len, NewHandler, NULL);
 }
@@ -175,6 +316,7 @@ void RegisterControlNS(const char *NSName,
        HashHandler *NewHandler;
        
        NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
+       memset(NewHandler, 0, sizeof(HashHandler));
        NewHandler->Filter.nMinArgs = nMinArgs;
        NewHandler->Filter.nMaxArgs = nMaxArgs;
        NewHandler->Filter.ContextType = CTX_NONE;
@@ -192,8 +334,8 @@ int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, cons
                 LogTemplateError(
                         Target, ErrType, ERR_PARM1, TP,
                        "  WARNING: requires Context: [%s], have [%s]!", 
-                       CtxNames[Need->ContextType]
-                       CtxNames[TP->Filter.ContextType]);
+                       ContextName(Need->ContextType)
+                       ContextName(TP->Filter.ContextType));
                return 0;
        }
 
@@ -202,8 +344,8 @@ int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, cons
                 LogTemplateError(
                         Target, ErrType, ERR_PARM1, TP,
                        "  WARNING: requires Control Context: [%s], have [%s]!", 
-                       CtxNames[Need->ControlContextType]
-                       CtxNames[TP->Filter.ControlContextType]);
+                       ContextName(Need->ControlContextType)
+                       ContextName(TP->Filter.ControlContextType));
                return 0;
        }
 /*                     
@@ -253,16 +395,18 @@ void FreeWCTemplate(void *vFreeMe)
        }
        FreeStrBuf(&FreeMe->FileName);
        FreeStrBuf(&FreeMe->Data);
+       FreeStrBuf(&FreeMe->MimeType);
        free(FreeMe);
 }
 
 
-/**
- * \brief debugging function to print array to log
+/*
+ * debugging function to print array to log
  */
 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
-/**
- * \brief debugging function to print array to log
+
+/*
+ * debugging function to print array to log
  */
 void VarPrintEntry(const char *Key, void *vSubst, int odd)
 {
@@ -287,8 +431,8 @@ void VarPrintEntry(const char *Key, void *vSubst, int odd)
 
 
 
-/**
- * \brief Clear out the list of substitution variables local to this session
+/*
+ * Clear out the list of substitution variables local to this session
  */
 void clear_substs(wcsession *wc) {
 
@@ -297,8 +441,8 @@ void clear_substs(wcsession *wc) {
        }
 }
 
-/**
- * \brief Clear out the list of substitution variables local to this session
+/*
+ * Clear out the list of substitution variables local to this session
  */
 void clear_local_substs(void) {
        clear_substs (WC);
@@ -358,8 +502,8 @@ void FlushPayload(wcsubst *ptr, int reusestrbuf, int type)
 }
 
 
-/**
- * \brief destructor; kill one entry.
+/*
+ * destructor; kill one entry.
  */
 void deletevar(void *data)
 {
@@ -397,12 +541,12 @@ wcsubst *NewSubstVar(const char *keyname, int keylen, int type)
 }
 
 
-/**
- * \brief Add a substitution variable (local to this session) (strlen version...)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
+/*
+ * Add a substitution variable (local to this session) (strlen version...)
+ * keyname the replacementstring to substitute
+ * keytype the kind of the key
+ * format the format string ala printf
+ * ... the arguments to substitute in the formatstring
  */
 void SVPRINTF(char *keyname, int keytype, const char *format,...)
 {
@@ -413,7 +557,7 @@ void SVPRINTF(char *keyname, int keytype, const char *format,...)
        wcsession *WCC = WC;
        
        keylen = strlen(keyname);
-       /**
+       /*
         * First look if we're doing a replacement of
         * an existing key
         */
@@ -428,18 +572,18 @@ void SVPRINTF(char *keyname, int keytype, const char *format,...)
                ptr = NewSubstVar(keyname, keylen, keytype);
        }
 
-       /** Format the string */
+       /* Format the string */
        va_start(arg_ptr, format);
        StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
        va_end(arg_ptr);
 }
 
-/**
- * \brief Add a substitution variable (local to this session)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
+/*
+ * Add a substitution variable (local to this session)
+ * keyname the replacementstring to substitute
+ * keytype the kind of the key
+ * format the format string ala printf
+ * ... the arguments to substitute in the formatstring
  */
 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
 {
@@ -448,7 +592,7 @@ void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
        wcsubst *ptr = NULL;
        wcsession *WCC = WC;
                
-       /**
+       /*
         * First look if we're doing a replacement of
         * an existing key
         */
@@ -469,12 +613,12 @@ void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
        va_end(arg_ptr);
 }
 
-/**
- * \brief Add a substitution variable (local to this session)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
+/*
+ * Add a substitution variable (local to this session)
+ * keyname the replacementstring to substitute
+ * keytype the kind of the key
+ * format the format string ala printf
+ * ... the arguments to substitute in the formatstring
  */
 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
 {
@@ -483,7 +627,7 @@ void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
        wcsession *WCC = WC;
 
        
-       /**
+       /*
         * First look if we're doing a replacement of
         * an existing key
         */
@@ -616,7 +760,8 @@ void pvo_do_cmd(StrBuf *Target, StrBuf *servcmd) {
        }
 }
 
-void GetTemplateTokenString(WCTemplputParams *TP,
+void GetTemplateTokenString(StrBuf *Target, 
+                           WCTemplputParams *TP,
                            int N,
                            const char **Value, 
                            long *len)
@@ -625,8 +770,9 @@ void GetTemplateTokenString(WCTemplputParams *TP,
        WCTemplputParams SubTP;
 
        if (TP->Tokens->nParameters < N) {
-               lprintf(1, "invalid token. this shouldn't have come till here.\n");
-               wc_backtrace(); 
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "invalid token %d. this shouldn't have come till here.\n", N);
                *Value = "";
                *len = 0;
                return;
@@ -639,23 +785,55 @@ void GetTemplateTokenString(WCTemplputParams *TP,
                *len = TP->Tokens->Params[N]->len;
                break;
        case TYPE_BSTR:
+               if (TP->Tokens->Params[N]->len == 0) {
+                       LogTemplateError(Target, 
+                                        "TokenParameter", N, TP, 
+                                        "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
+                       *len = 0;
+                       *Value = EmptyStr;
+                       break;
+               }
                Buf = (StrBuf*) SBstr(TKEY(N));
                *Value = ChrPtr(Buf);
                *len = StrLength(Buf);
                break;
        case TYPE_PREFSTR:
+               if (TP->Tokens->Params[N]->len == 0) {
+                       LogTemplateError(Target, 
+                                        "TokenParameter", N, TP, 
+                                        "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
+                       *len = 0;
+                       *Value = EmptyStr;
+                       break;
+               }
                get_PREFERENCE(TKEY(N), &Buf);
                *Value = ChrPtr(Buf);
                *len = StrLength(Buf);
                break;
        case TYPE_LONG:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "Requesting parameter %d; of type LONG, want string.", N);
+               break;
        case TYPE_PREFINT:
-               break; /* todo: string to text? */
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "Requesting parameter %d; of type PREFINT, want string.", N);
+               break;
        case TYPE_GETTEXT:
                *Value = _(TP->Tokens->Params[N]->Start);
                *len = strlen(*Value);
                break;
        case TYPE_SUBTEMPLATE:
+               if (TP->Tokens->Params[N]->len == 0) {
+                       LogTemplateError(Target, 
+                                        "TokenParameter", N, TP, 
+                                        "Requesting parameter %d; of type SUBTEMPLATE, empty lookup string not admitted.", N);
+                       *len = 0;
+                       *Value = EmptyStr;
+                       break;
+               }
+
                memset(&SubTP, 0, sizeof(WCTemplputParams *));
                SubTP.Context = TP->Context;
                SubTP.Filter.ContextType = TP->Filter.ContextType;
@@ -668,8 +846,78 @@ void GetTemplateTokenString(WCTemplputParams *TP,
                break;
 
        default:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
+               break;
+       }
+}
+
+long GetTemplateTokenNumber(StrBuf *Target, WCTemplputParams *TP, int N, long dflt)
+{
+       long Ret;
+       if (TP->Tokens->nParameters < N) {
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "invalid token %d. this shouldn't have come till here.\n", N);
+               wc_backtrace(); 
+               return 0;
+       }
+
+       switch (TP->Tokens->Params[N]->Type) {
+
+       case TYPE_STR:
+               return atol(TP->Tokens->Params[N]->Start);
+               break;
+       case TYPE_BSTR:
+               if (TP->Tokens->Params[N]->len == 0) {
+                       LogTemplateError(Target, 
+                                        "TokenParameter", N, TP, 
+                                        "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
+                       return 0;
+               }
+               return  LBstr(TKEY(N));
                break;
-/*/todo log error */
+       case TYPE_PREFSTR:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "requesting a prefstring in param %d want a number", N);
+               if (TP->Tokens->Params[N]->len == 0) {
+                       LogTemplateError(Target, 
+                                        "TokenParameter", N, TP, 
+                                        "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
+                       return 0;
+               }
+               if (get_PREF_LONG(TKEY(N), &Ret, dflt))
+                       return Ret;
+               return 0;               
+       case TYPE_LONG:
+               return TP->Tokens->Params[N]->lvalue;
+       case TYPE_PREFINT:
+               if (TP->Tokens->Params[N]->len == 0) {
+                       LogTemplateError(Target, 
+                                        "TokenParameter", N, TP, 
+                                        "Requesting parameter %d; of type PREFINT, empty lookup string not admitted.", N);
+                       return 0;
+               }
+               if (get_PREF_LONG(TKEY(N), &Ret, dflt))
+                       return Ret;
+               return 0;               
+       case TYPE_GETTEXT:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "requesting a I18N string in param %d; want a number", N);
+               return 0;
+       case TYPE_SUBTEMPLATE:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "requesting a subtemplate in param %d; not supported for numbers", N);
+               return 0;
+       default:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
+               return 0;
        }
 }
 
@@ -722,7 +970,7 @@ void print_value_of(StrBuf *Target, WCTemplputParams *TP)
        else {
                LogTemplateError(
                        Target, "Token", ERR_NAME, TP,
-                       "didn't find Handler");
+                       "didn't find Handler \"%s\"", TP->Tokens->pName);
                wc_backtrace();
        }
 }
@@ -807,7 +1055,6 @@ void StrBufAppendTemplate(StrBuf *Target,
                          const StrBuf *Source, int FormatTypeIndex)
 {
         wcsession *WCC;
-       StrBuf *Buf;
        char EscapeAs = ' ';
 
        if ((FormatTypeIndex < TP->Tokens->nParameters) &&
@@ -820,20 +1067,20 @@ void StrBufAppendTemplate(StrBuf *Target,
        {
        case 'H':
                WCC = WC;
-               Buf = NewStrBufPlain(NULL, StrLength(Source));
-               StrBuf_RFC822_to_Utf8(Buf, 
-                                     Source, 
-                                     (WCC!=NULL)? WCC->DefaultCharset : NULL, 
-                                     NULL);
-               StrEscAppend(Target, Buf, NULL, 0, 0);
-               FreeStrBuf(&Buf);
+               StrEscAppend(Target, Source, NULL, 0, 2);
                break;
        case 'X':
                StrEscAppend(Target, Source, NULL, 0, 0);
                break;
        case 'J':
-         StrECMAEscAppend(Target, Source, NULL);
+               StrECMAEscAppend(Target, Source, NULL);
+         break;
+       case 'K':
+               StrHtmlEcmaEscAppend(Target, Source, NULL, 0, 0);
          break;
+       case 'U':
+               StrBufUrlescAppend(Target, Source, NULL);
+               break;
        default:
                StrBufAppendBuf(Target, Source, 0);
        }
@@ -846,6 +1093,7 @@ void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
                if (Template->TokenSpace <= 0) {
                        Template->Tokens = (WCTemplateToken**)malloc(
                                sizeof(WCTemplateToken*) * 10);
+                       memset(Template->Tokens, 0, sizeof(WCTemplateToken*));
                        Template->TokenSpace = 10;
                }
                else {
@@ -867,10 +1115,12 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
 {
        const char *pch = *pCh;
        const char *pchs, *pche;
-       TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
+       TemplateParam *Parm;
        char quote = '\0';
        int ParamBrace = 0;
 
+       Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
+       memset(Parm, 0, sizeof(TemplateParam));
        Parm->Type = TYPE_STR;
 
        /* Skip leading whitespaces */
@@ -996,6 +1246,9 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
               (*pch == ',' )||
               (*pch == '\n')) pch ++;
 
+       if (DumpTemplateI18NStrings && (Parm->Type == TYPE_GETTEXT)) {
+               StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
+       }
        *pCh = pch;
        return Parm;
 }
@@ -1010,9 +1263,11 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
        void *vVar;
        const char *pch;
        TemplateParam *Param;
-       WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
+       WCTemplateToken *NewToken;
        WCTemplputParams TP;
 
+       NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
+       memset(NewToken, 0, sizeof(WCTemplateToken));
        TP.Tokens = NewToken;
        NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
        NewToken->Flags = 0;
@@ -1024,7 +1279,8 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
        NewToken->NameEnd = NewToken->TokenEnd - 2;
        NewToken->PreEval = NULL;
        NewToken->FlatToken = NewStrBufPlain(pTmplStart + 2, pTmplEnd - pTmplStart - 2);
-       
+       StrBufShrinkToFit(NewToken->FlatToken, 1);
+
        StrBufPeek(Buf, pTmplStart, + 1, '\0');
        StrBufPeek(Buf, pTmplEnd, -1, '\0');
        pch = NewToken->pName = pTmplStart + 2;
@@ -1049,7 +1305,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                                        if (NewToken->nParameters > MAXPARAM) {
                                                LogTemplateError(
                                                        NULL, "Parseerror", ERR_NAME, &TP,
-                                                       "only [%ld] Params allowed in Tokens",
+                                                       "only [%d] Params allowed in Tokens",
                                                        MAXPARAM);
 
                                                free(Param);
@@ -1089,13 +1345,15 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                            (NewToken->nParameters > Handler->Filter.nMaxArgs)) {
                                LogTemplateError(
                                        NULL, "Token", ERR_NAME, &TP,
-                                       "doesn't work with %ld params", 
+                                       "doesn't work with %d params", 
                                        NewToken->nParameters);
 
                        }
                        else {
                                NewToken->PreEval = Handler;
                                NewToken->Flags = SV_PREEVALUATED;              
+                               if (Handler->PreEvalFunc != NULL)
+                                       Handler->PreEvalFunc(NewToken);
                        }
                }
                break;
@@ -1103,17 +1361,20 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                if (NewToken->nParameters !=1) {
                        LogTemplateError(                               
                                NULL, "Gettext", ERR_NAME, &TP,
-                               "requires exactly 1 parameter, you gave %ld params", 
+                               "requires exactly 1 parameter, you gave %d params", 
                                NewToken->nParameters);
                        NewToken->Flags = 0;
                        break;
                }
+               if (DumpTemplateI18NStrings) {
+                       StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
+               }
                break;
        case SV_SUBTEMPL:
                if (NewToken->nParameters != 1) {
                        LogTemplateError(
                                NULL, "Subtemplates", ERR_NAME, &TP,
-                               "require exactly 1 parameter, you gave %ld params", 
+                               "require exactly 1 parameter, you gave %d params", 
                                NewToken->nParameters);
                        break;
                }
@@ -1124,7 +1385,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                if (NewToken->nParameters <2) {
                        LogTemplateError(
                                NULL, "Conditional", ERR_NAME, &TP,
-                               "require at least 2 parameters, you gave %ld params", 
+                               "require at least 2 parameters, you gave %d params", 
                                NewToken->nParameters);
                        NewToken->Flags = 0;
                        break;
@@ -1177,12 +1438,19 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
 {
        WCTemplate *NewTemplate;
+
        NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
+       memset(NewTemplate, 0, sizeof(WCTemplate));
        NewTemplate->Data = NULL;
        NewTemplate->FileName = NewStrBufDup(filename);
+       StrBufShrinkToFit(NewTemplate->FileName, 1);
        NewTemplate->nTokensUsed = 0;
        NewTemplate->TokenSpace = 0;
        NewTemplate->Tokens = NULL;
+       NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
+       if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
+               StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
+       }
 
        Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
        return NewTemplate;
@@ -1215,11 +1483,17 @@ void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
        }
 
        NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
+       memset(NewTemplate, 0, sizeof(WCTemplate));
        NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
        NewTemplate->FileName = NewStrBufDup(filename);
        NewTemplate->nTokensUsed = 0;
        NewTemplate->TokenSpace = 0;
        NewTemplate->Tokens = NULL;
+       NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
+       if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
+               StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
+       }
+
        if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
                close(fd);
                FreeWCTemplate(NewTemplate);
@@ -1230,6 +1504,8 @@ void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
        close(fd);
 
        Line = 0;
+       StrBufShrinkToFit(NewTemplate->Data, 1);
+       StrBufShrinkToFit(NewTemplate->MimeType, 1);
        pS = pch = ChrPtr(NewTemplate->Data);
        pE = pS + StrLength(NewTemplate->Data);
        while (pch < pE) {
@@ -1240,7 +1516,11 @@ void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
                /** Find one <? > */
                pos = (-1);
                for (; pch < pE; pch ++) {
-                       if ((*pch=='<')&&(*(pch + 1)=='?'))
+                       if ((*pch=='<')&&(*(pch + 1)=='?') &&
+                           !((pch == pS) && /* we must ommit a <?xml */
+                             (*(pch + 2) == 'x') && 
+                             (*(pch + 3) == 'm') && 
+                             (*(pch + 4) == 'l')))                          
                                break;
                        if (*pch=='\n') Line ++;
                }
@@ -1389,14 +1669,14 @@ int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
        case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
                if (TP->Tokens->nParameters >= 6) {
                        if (EvaluateConditional(Target, 0, state, TP)) {
-                               GetTemplateTokenString(TP, 5, &AppendMe, &AppendMeLen);
+                               GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
                                StrBufAppendBufPlain(Target, 
                                                     AppendMe, 
                                                     AppendMeLen,
                                                     0);
                        }
                        else{
-                               GetTemplateTokenString(TP, 4, &AppendMe, &AppendMeLen);
+                               GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
                                StrBufAppendBufPlain(Target, 
                                                     AppendMe, 
                                                     AppendMeLen,
@@ -1439,7 +1719,7 @@ int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
 
 
 
-void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context, int ContextType)
+const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
 {
        WCTemplate *pTmpl = Tmpl;
        int done = 0;
@@ -1448,8 +1728,10 @@ void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context, int Contex
        long len;
        WCTemplputParams TP;
 
-       TP.Context = Context;
-       TP.Filter.ContextType = ContextType;
+       memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
+
+       TP.Context = CallingTP->Context;
+       TP.ControlContext = CallingTP->ControlContext;
 
        if (LoadTemplates != 0) {                       
                if (LoadTemplates > 1)
@@ -1462,7 +1744,7 @@ void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context, int Contex
                                Target, 
                                "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
                                ChrPtr(Tmpl->FileName));
-                       return;
+                       return NULL;
                }
 
        }
@@ -1509,20 +1791,29 @@ void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context, int Contex
        if (LoadTemplates != 0) {
                FreeWCTemplate(pTmpl);
        }
+       return Tmpl->MimeType;
+
 }
 
 /**
  * \brief Display a variable-substituted template
  * \param templatename template file to load
+ * \returns the mimetype of the template its doing
  */
-void DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
+const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
 {
+       WCTemplputParams LocalTP;
        HashList *Static;
        HashList *StaticLocal;
        void *vTmpl;
        
        if (Target == NULL)
                Target = WC->WBuf;
+       if (TP == NULL) {
+               memset(&LocalTP, 0, sizeof(WCTemplputParams));
+               TP = &LocalTP;
+       }
+
        if (WC->is_mobile) {
                Static = WirelessTemplateCache;
                StaticLocal = WirelessLocalTemplateCache;
@@ -1536,7 +1827,7 @@ void DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputPa
        {
                lprintf (1, "Can't to load a template with empty name!\n");
                StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
-               return;
+               return NULL;
        }
 
        if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
@@ -1549,11 +1840,12 @@ void DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputPa
                dbg_PrintHash(Static, PrintTemplate, NULL);
                PrintHash(Static, VarPrintTransition, PrintTemplate);
 #endif
-               return;
+               return NULL;
        }
        if (vTmpl == NULL) 
-               return;
-       ProcessTemplate(vTmpl, Target, TP->Context, TP->Filter.ContextType);
+               return NULL;
+       return ProcessTemplate(vTmpl, Target, TP);
+
 }
 
 /*-----------------------------------------------------------------------------
@@ -1580,7 +1872,10 @@ void RegisterITERATOR(const char *Name, long len,
                      int XPectContextType, 
                      int Flags)
 {
-       HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
+       HashIterator *It;
+
+       It = (HashIterator*)malloc(sizeof(HashIterator));
+       memset(It, 0, sizeof(HashIterator));
        It->StaticList = StaticList;
        It->AdditionalParams = AdditionalParams;
        It->GetHash = GetHash;
@@ -1599,11 +1894,29 @@ typedef struct _iteratestruct {
        long KeyLen;
        int n;
        int LastN;
-}IterateStruct;
+       }IterateStruct; 
 
-void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
+int preeval_iterate(WCTemplateToken *Token)
 {
+       WCTemplputParams TPP;
+       WCTemplputParams *TP;
        void *vIt;
+
+       memset(&TPP, 0, sizeof(WCTemplputParams));
+       TP = &TPP;
+       TP->Tokens = Token;
+       if (!GetHash(Iterators, TKEY(0), &vIt)) {
+               LogTemplateError(
+                       NULL, "Iterator", ERR_NAME, TP,
+                       "not found");
+               return 0;
+       }
+       Token->Preeval2 = vIt;
+       return 1;
+}
+
+void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
+{
        HashIterator *It;
        HashList *List;
        HashPos  *it;
@@ -1617,21 +1930,24 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        WCTemplputParams SubTP;
        IterateStruct Status;
 
+       long StartAt = 0;
+       long StepWidth = 0;
+       long StopAt = -1;
+
        memset(&Status, 0, sizeof(IterateStruct));
        memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
        
-       if (!GetHash(Iterators, TKEY(0), &vIt)) {
+       It = (HashIterator*) TP->Tokens->Preeval2;
+       if (It == NULL) {
                LogTemplateError(
                        Target, "Iterator", ERR_PARM1, TP, "Unknown!");
                return;
        }
 
-       It = (HashIterator*) vIt;
-
        if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
                LogTemplateError(                               
                        Target, "Iterator", ERR_PARM1, TP,
-                       "doesn't work with %ld params", 
+                       "doesn't work with %d params", 
                        TP->Tokens->nParameters);
                return;
        }
@@ -1640,7 +1956,7 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
            (It->XPectContextType != TP->Filter.ContextType)) {
                LogTemplateError(
                        Target, "Iterator", ERR_PARM1, TP,
-                       "requires context of type %ld, have %ld", 
+                       "requires context of type %d, have %d", 
                        It->XPectContextType, 
                        TP->Filter.ContextType);
                return ;
@@ -1676,21 +1992,37 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        SubTP.Filter.ContextType = It->ContextType;
        SubTP.Filter.ControlContextType = CTX_ITERATE;
        SubTP.ControlContext = &Status;
-       it = GetNewHashPos(List, 0);
+       
+       if (HAVE_PARAM(2)) {
+               StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
+       }
+       if (HAVE_PARAM(3)) {
+               StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
+       }
+       if (HAVE_PARAM(4)) {
+               StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
+       }
+       it = GetNewHashPos(List, StepWidth);
+       if (StopAt < 0) {
+               StopAt = GetCount(List);
+       }
        while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
-               if (DetectGroupChange && Status.n > 0) {
-                       Status.GroupChange = (SortBy->GroupChange(vContext, vLastContext))? 1:0;
-               }
-               Status.LastN = ++Status.LastN == nMembersUsed;
-               SubTP.Context = vContext;
-               if (It->DoSubTemplate != NULL)
-                       It->DoSubTemplate(SubBuf, &SubTP);
-               DoTemplate(TKEY(1), SubBuf, &SubTP);
+               if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
+                       if (DetectGroupChange && Status.n > 0) {
+                               Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
+                       }
+                       Status.LastN = (Status.n + 1) == nMembersUsed;
+                       SubTP.Context = vContext;
+                       if (It->DoSubTemplate != NULL)
+                               It->DoSubTemplate(SubBuf, &SubTP);
+                       DoTemplate(TKEY(1), SubBuf, &SubTP);
                        
-               StrBufAppendBuf(Target, SubBuf, 0);
-               FlushStrBuf(SubBuf);
-               Status.oddeven = ! Status.oddeven;
-               vLastContext = vContext;
+                       StrBufAppendBuf(Target, SubBuf, 0);
+                       FlushStrBuf(SubBuf);
+                       Status.oddeven = ! Status.oddeven;
+                       vLastContext = vContext;
+               }
+               Status.n++;
        }
        FreeStrBuf(&SubBuf);
        DeleteHashPos(&it);
@@ -1702,7 +2034,10 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
 {
        IterateStruct *Ctx = CCTX;
-       return Ctx->GroupChange;
+       if (TP->Tokens->nParameters < 3)
+               return  Ctx->GroupChange;
+
+       return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
 }
 
 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
@@ -1729,6 +2064,12 @@ void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
        StrBufAppendPrintf(Target, "%d", Ctx->n);
 }
 
+int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+       return Ctx->n == 0;
+}
+
 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
 {
        IterateStruct *Ctx = CCTX;
@@ -1802,7 +2143,10 @@ void RegisterConditional(const char *Name, long len,
                         WCConditionalFunc CondF, 
                         int ContextRequired)
 {
-       ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
+       ConditionalStruct *Cond;
+
+       Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
+       memset(Cond, 0, sizeof(ConditionalStruct));
        Cond->PlainName = Name;
        Cond->Filter.nMaxArgs = nParams;
        Cond->Filter.nMinArgs = nParams;
@@ -1817,7 +2161,10 @@ void RegisterControlConditional(const char *Name, long len,
                                WCConditionalFunc CondF, 
                                int ControlContextRequired)
 {
-       ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
+       ConditionalStruct *Cond;
+
+       Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
+       memset(Cond, 0, sizeof(ConditionalStruct));
        Cond->PlainName = Name;
        Cond->Filter.nMaxArgs = nParams;
        Cond->Filter.nMinArgs = nParams;
@@ -1840,7 +2187,7 @@ int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
        const char *CompareToken;
        long len;
 
-       GetTemplateTokenString(TP, 2, &CompareToken, &len);
+       GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
        return strcmp(ChrPtr(TokenText), CompareToken) == 0;
 }
 
@@ -1861,14 +2208,15 @@ void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
                else {
                        const char *Ch;
                        long len;
-                       GetTemplateTokenString(TP, 
+                       GetTemplateTokenString(Target, 
+                                              TP, 
                                               1,
                                               &Ch,
                                               &len);
                        Headline = NewStrBufPlain(Ch, len);
                }
        }
-       memcpy (&SubTP, TP, sizeof(WCTemplputParams));
+       memcpy (&SubTP, TP, sizeof(WCTemplputParams));
        SubTP.Context = Headline;
        SubTP.Filter.ContextType = CTX_STRBUF;
        DoTemplate(HKEY("beginbox"), Target, &SubTP);
@@ -1888,6 +2236,7 @@ void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
 
        nTabs = ntabs = TP->Tokens->nParameters / 2;
        TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
+       memset(TabNames, 0, ntabs * sizeof(StrBuf*));
 
        for (i = 0; i < ntabs; i++) {
                if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
@@ -1898,7 +2247,8 @@ void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
                else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
                        const char *Ch;
                        long len;
-                       GetTemplateTokenString(TP, 
+                       GetTemplateTokenString(Target, 
+                                              TP, 
                                               i * 2,
                                               &Ch,
                                               &len);
@@ -1931,7 +2281,10 @@ void RegisterSortFunc(const char *name, long len,
                      CompareFunc GroupChange, 
                      long ContextType)
 {
-       SortStruct *NewSort = (SortStruct*) malloc(sizeof(SortStruct));
+       SortStruct *NewSort;
+
+       NewSort = (SortStruct*) malloc(sizeof(SortStruct));
+       memset(NewSort, 0, sizeof(SortStruct));
        NewSort->Name = NewStrBufPlain(name, len);
        if (prepend != NULL)
                NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
@@ -2271,14 +2624,14 @@ InitModule_SUBST
 (void)
 {
        memset(&NoCtx, 0, sizeof(WCTemplputParams));
-       RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, CTX_NONE);
-       RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, CTX_NONE);
-       RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, CTX_NONE);
-       RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
-       RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
-       RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
-       RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
-       RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
+       RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
+       RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
+       RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
+       RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
+       RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
+       RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
+       RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, NULL, CTX_NONE);
+       RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
        RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
        RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
        RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
@@ -2289,9 +2642,78 @@ InitModule_SUBST
        RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
                                   conditional_ITERATE_LASTN, 
                                   CTX_ITERATE);
+       RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
+                                  conditional_ITERATE_FIRSTN, 
+                                  CTX_ITERATE);
+
        RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
        RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
        RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
 }
 
-/*@}*/
+void
+ServerStartModule_SUBST
+(void)
+{
+       WirelessTemplateCache = NewHash(1, NULL);
+       WirelessLocalTemplateCache = NewHash(1, NULL);
+       LocalTemplateCache = NewHash(1, NULL);
+       TemplateCache = NewHash(1, NULL);
+
+       GlobalNS = NewHash(1, NULL);
+       Iterators = NewHash(1, NULL);
+       Conditionals = NewHash(1, NULL);
+       SortHash = NewHash(1, NULL);
+}
+
+void
+FinalizeModule_SUBST
+(void)
+{
+
+}
+
+void 
+ServerShutdownModule_SUBST
+(void)
+{
+       DeleteHash(&WirelessTemplateCache);
+       DeleteHash(&WirelessLocalTemplateCache);
+       DeleteHash(&TemplateCache);
+       DeleteHash(&LocalTemplateCache);
+
+       DeleteHash(&GlobalNS);
+       DeleteHash(&Iterators);
+       DeleteHash(&Conditionals);
+       DeleteHash(&SortHash);
+
+}
+
+
+void
+SessionNewModule_SUBST
+(wcsession *sess)
+{
+
+}
+
+void
+SessionAttachModule_SUBST
+(wcsession *sess)
+{
+       sess->vars = NewHash(1,NULL);
+}
+
+void
+SessionDetachModule_SUBST
+(wcsession *sess)
+{
+       DeleteHash(&sess->vars);
+}
+
+void 
+SessionDestroyModule_SUBST  
+(wcsession *sess)
+{
+
+}