+ new way to retrieve integers from templates (bstr, pref...)
[citadel.git] / webcit / subst.c
index 351793545f0d95e6bd4f64c3fb9711e183ad814c..394af9ab8dc56f59244ad50ca4c188ac01a6bd9b 100644 (file)
@@ -33,6 +33,7 @@ HashList *Conditionals;
 HashList *SortHash;
 
 int LoadTemplates = 0;
+int dbg_bactrace_template_errors = 0;
 WCTemplputParams NoCtx;
 
 #define SV_GETTEXT 1
@@ -47,19 +48,21 @@ typedef struct _WCTemplate {
        StrBuf *FileName;
        int nTokensUsed;
        int TokenSpace;
+       StrBuf *MimeType;
        WCTemplateToken **Tokens;
 } WCTemplate;
 
 typedef struct _HashHandler {
-       int nMinArgs;
-       int nMaxArgs;
-       int ContextRequired;
+       ContextFilter Filter;
+
        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;
@@ -70,6 +73,29 @@ typedef struct _SortStruct {
        long ContextType;
 }SortStruct;
 
+const char *CtxNames[]  = {
+       "Context NONE",
+       "Context SITECFG",
+       "Context SESSION",
+       "Context INETCFG",
+       "Context VNOTE",
+       "Context WHO",
+       "Context PREF",
+       "Context NODECONF",
+       "Context USERLIST",
+       "Context MAILSUM",
+       "Context MIME_ATACH",
+       "Context FILELIST",
+       "Context STRBUF",
+       "Context LONGVECTOR",
+       "Context ROOMS",
+       "Context FLOORS",
+       "Context ITERATE",
+       "Context UNKNOWN"
+};
+
+
+
 void DestroySortStruct(void *vSort)
 {
        SortStruct *Sort = (SortStruct*) vSort;
@@ -78,8 +104,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;
@@ -95,23 +131,38 @@ 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->Params[0]->Start:"";
                break;
        case ERR_PARM2:
-               Err = TP->Tokens->Params[1]->Start;
+               Err = (TP->Tokens!= NULL)? 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;
+       Header = NewStrBuf();
+       if (TP->Tokens != NULL) 
+       {
+               /* deprecated: 
                StrBufAppendPrintf(                                                          
                        Target,                                                              
                        "<pre>\n%s [%s] (in '%s' line %ld); %s\n[%s]\n</pre>\n",
@@ -121,7 +172,50 @@ 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->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->nWildfireHeaders, NULL);
+       }
+       FreeStrBuf(&Header);
+       FreeStrBuf(&Info);
+       FreeStrBuf(&Error);
+/*
+       if (dbg_bactrace_template_errors)
+               wc_backtrace(); 
+*/
 }
 
 
@@ -135,13 +229,77 @@ void RegisterNS(const char *NSName,
        HashHandler *NewHandler;
        
        NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
-       NewHandler->nMinArgs = nMinArgs;
-       NewHandler->nMaxArgs = nMaxArgs;
+       NewHandler->Filter.nMinArgs = nMinArgs;
+       NewHandler->Filter.nMaxArgs = nMaxArgs;
+       NewHandler->Filter.ContextType = ContextRequired;
+       NewHandler->Filter.ControlContextType = CTX_NONE;
+
        NewHandler->HandlerFunc = HandlerFunc;  
-       NewHandler->ContextRequired = ContextRequired;
        Put(GlobalNS, NSName, len, NewHandler, NULL);
 }
 
+void RegisterControlNS(const char *NSName, 
+                      long len, 
+                      int nMinArgs, 
+                      int nMaxArgs, 
+                      WCHandlerFunc HandlerFunc, 
+                      int ControlContextRequired)
+{
+       HashHandler *NewHandler;
+       
+       NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
+       NewHandler->Filter.nMinArgs = nMinArgs;
+       NewHandler->Filter.nMaxArgs = nMaxArgs;
+       NewHandler->Filter.ContextType = CTX_NONE;
+       NewHandler->Filter.ControlContextType = ControlContextRequired;
+       NewHandler->HandlerFunc = HandlerFunc;  
+       Put(GlobalNS, NSName, len, NewHandler, NULL);
+}
+
+
+
+int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, const char *ErrType)
+{
+       if ((Need->ContextType != CTX_NONE) && 
+           (Need->ContextType != TP->Filter.ContextType)) {
+                LogTemplateError(
+                        Target, ErrType, ERR_PARM1, TP,
+                       "  WARNING: requires Context: [%s], have [%s]!", 
+                       ContextName(Need->ContextType), 
+                       ContextName(TP->Filter.ContextType));
+               return 0;
+       }
+
+       if ((Need->ControlContextType != CTX_NONE) && 
+           (Need->ControlContextType != TP->Filter.ControlContextType)) {
+                LogTemplateError(
+                        Target, ErrType, ERR_PARM1, TP,
+                       "  WARNING: requires Control Context: [%s], have [%s]!", 
+                       ContextName(Need->ControlContextType), 
+                       ContextName(TP->Filter.ControlContextType));
+               return 0;
+       }
+/*                     
+       if (TP->Tokens->nParameters < Need->nMinArgs) {
+               LogTemplateError(Target, ErrType, ERR_NAME, TP,
+                                "needs at least %ld params, have %ld", 
+                                Need->nMinArgs, 
+                                TP->Tokens->nParameters);
+               return 0;
+
+       }
+       else if (TP->Tokens->nParameters > Need->nMaxArgs) {
+               LogTemplateError(Target, ErrType, ERR_NAME, TP,
+                                "just needs %ld params, you gave %ld",
+                                Need->nMaxArgs,
+                                TP->Tokens->nParameters); 
+               return 0;
+
+       }
+*/
+       return 1;
+}
+
 void FreeToken(WCTemplateToken **Token)
 {
        int i; 
@@ -168,6 +326,7 @@ void FreeWCTemplate(void *vFreeMe)
        }
        FreeStrBuf(&FreeMe->FileName);
        FreeStrBuf(&FreeMe->Data);
+       FreeStrBuf(&FreeMe->MimeType);
        free(FreeMe);
 }
 
@@ -531,16 +690,19 @@ 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)
 {
        StrBuf *Buf;
+       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;
@@ -563,15 +725,86 @@ void GetTemplateTokenString(WCTemplputParams *TP,
                *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:
+               memset(&SubTP, 0, sizeof(WCTemplputParams *));
+               SubTP.Context = TP->Context;
+               SubTP.Filter.ContextType = TP->Filter.ContextType;
+               Buf = NewStrBuf();
+               DoTemplate(TKEY(N), Buf, &SubTP);
+               *Value = ChrPtr(Buf);
+               *len = StrLength(Buf);
+               /* we can't free it here, so we put it into the subst so its discarded later on. */
+               SVPUTBuf(TKEY(N), Buf, 0);
+               break;
+
        default:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
                break;
-/*/todo log error */
+       }
+}
+
+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:
+               return  LBstr(TKEY(N));
+               break;
+       case TYPE_PREFSTR:
+               LogTemplateError(Target, 
+                                "TokenParameter", N, TP, 
+                                "requesting a prefstring in param %d want a number", N);
+               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 (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;
        }
 }
 
@@ -624,7 +857,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();
        }
 }
@@ -709,7 +942,6 @@ void StrBufAppendTemplate(StrBuf *Target,
                          const StrBuf *Source, int FormatTypeIndex)
 {
         wcsession *WCC;
-       StrBuf *Buf;
        char EscapeAs = ' ';
 
        if ((FormatTypeIndex < TP->Tokens->nParameters) &&
@@ -722,17 +954,14 @@ 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);
+         break;
        default:
                StrBufAppendBuf(Target, Source, 0);
        }
@@ -768,7 +997,7 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
        const char *pchs, *pche;
        TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
        char quote = '\0';
-       
+       int ParamBrace = 0;
 
        Parm->Type = TYPE_STR;
 
@@ -781,20 +1010,42 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
        if (*pch == ':') {
                Parm->Type = TYPE_PREFSTR;
                pch ++;
+               if (*pch == '(') {
+                       pch ++;
+                       ParamBrace = 1;
+               }
        }
        else if (*pch == ';') {
                Parm->Type = TYPE_PREFINT;
                pch ++;
+               if (*pch == '(') {
+                       pch ++;
+                       ParamBrace = 1;
+               }
        }
        else if (*pch == '_') {
                Parm->Type = TYPE_GETTEXT;
                pch ++;
-               if (*pch == '(')
+               if (*pch == '(') {
                        pch ++;
+                       ParamBrace = 1;
+               }
        }
        else if (*pch == 'B') {
                Parm->Type = TYPE_BSTR;
                pch ++;
+               if (*pch == '(') {
+                       pch ++;
+                       ParamBrace = 1;
+               }
+       }
+       else if (*pch == '=') {
+               Parm->Type = TYPE_SUBTEMPLATE;
+               pch ++;
+               if (*pch == '(') {
+                       pch ++;
+                       ParamBrace = 1;
+               }
        }
 
 
@@ -832,7 +1083,7 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
                        Parm->Start = pchs;
                        Parm->len = pche - pchs;
                        pch ++; /* move after trailing quote */
-                       if ((Parm->Type == TYPE_GETTEXT) && (*pch == ')')) {
+                       if (ParamBrace && (*pch == ')')) {
                                pch ++;
                        }
 
@@ -926,7 +1177,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);
@@ -962,11 +1213,11 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
                        HashHandler *Handler;
                        Handler = (HashHandler*) vVar;
-                       if ((NewToken->nParameters < Handler->nMinArgs) || 
-                           (NewToken->nParameters > Handler->nMaxArgs)) {
+                       if ((NewToken->nParameters < Handler->Filter.nMinArgs) || 
+                           (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);
 
                        }
@@ -980,7 +1231,7 @@ 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;
@@ -990,7 +1241,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                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;
                }
@@ -1001,7 +1252,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;
@@ -1097,6 +1348,11 @@ void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
        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);
@@ -1248,6 +1504,7 @@ int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
        long AppendMeLen;
        HashHandler *Handler;
        void *vVar;
+       
 /* much output, since pName is not terminated...
        lprintf(1,"Doing token: %s\n",Token->pName);
 */
@@ -1265,14 +1522,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,
@@ -1287,43 +1544,21 @@ int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
                break;
        case SV_SUBTEMPL:
                if (TP->Tokens->nParameters == 1)
-                       DoTemplate(TKEY(0), NULL, TP);
+                       DoTemplate(TKEY(0), Target, TP);
                break;
        case SV_PREEVALUATED:
                Handler = (HashHandler*) TP->Tokens->PreEval;
-               if ((Handler->ContextRequired != CTX_NONE) &&
-                   (Handler->ContextRequired != TP->ContextType)) {
-                       LogTemplateError(
-                               Target, "Token", ERR_NAME, TP,
-                               "requires context of type %ld, have %ld", 
-                               Handler->ContextRequired, 
-                               TP->ContextType);
+               if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
                        return -1;
-
                }
                Handler->HandlerFunc(Target, TP);
                break;          
        default:
                if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
                        Handler = (HashHandler*) vVar;
-                       if ((Handler->ContextRequired != CTX_NONE) &&
-                           (Handler->ContextRequired != TP->ContextType)) {
-                               LogTemplateError(
-                                       Target, "Token", ERR_NAME, TP,
-                                       "requires context of type %ld, have %ld",
-                                       Handler->ContextRequired, 
-                                       TP->ContextType);
+                       if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
                                return -1;
                        }
-                       else if ((TP->Tokens->nParameters < Handler->nMinArgs) || 
-                                (TP->Tokens->nParameters > Handler->nMaxArgs)) {
-                               LogTemplateError(
-                                       Target, "Token", ERR_NAME, TP,
-                                       "doesn't work with %ld params need  > %ld  < %ld", 
-                                       TP->Tokens->nParameters, 
-                                       Handler->nMaxArgs,
-                                       Handler->nMinArgs);
-                       }
                        else {
                                Handler->HandlerFunc(Target, TP);
                        }
@@ -1337,7 +1572,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;
@@ -1346,8 +1581,10 @@ void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context, int Contex
        long len;
        WCTemplputParams TP;
 
-       TP.Context = Context;
-       TP.ContextType = ContextType;
+       memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
+
+       TP.Context = CallingTP->Context;
+       TP.ControlContext = CallingTP->ControlContext;
 
        if (LoadTemplates != 0) {                       
                if (LoadTemplates > 1)
@@ -1360,7 +1597,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;
                }
 
        }
@@ -1407,20 +1644,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;
@@ -1434,7 +1680,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) &&
@@ -1447,11 +1693,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->ContextType);
+               return NULL;
+       return ProcessTemplate(vTmpl, Target, TP);
+
 }
 
 /*-----------------------------------------------------------------------------
@@ -1490,6 +1737,15 @@ void RegisterITERATOR(const char *Name, long len,
        Put(Iterators, Name, len, It, NULL);
 }
 
+/* typedef struct _iteratestruct {
+       int GroupChange;
+       int oddeven;
+       const char *Key;
+       long KeyLen;
+       int n;
+       int LastN;
+       }IterateStruct; */
+
 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
 {
        void *vIt;
@@ -1500,15 +1756,17 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        void *vSortBy;
        int DetectGroupChange = 0;
        int nMembersUsed;
-       int nMembersCounted = 0;
-       long len; 
-       const char *Key;
        void *vContext;
        void *vLastContext = NULL;
        StrBuf *SubBuf;
-       int oddeven = 0;
        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)) {
@@ -1522,18 +1780,18 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        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;
        }
 
        if ((It->XPectContextType != CTX_NONE) &&
-           (It->XPectContextType != TP->ContextType)) {
+           (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->ContextType);
+                       TP->Filter.ContextType);
                return ;
                
        }
@@ -1564,29 +1822,40 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        }
        nMembersUsed = GetCount(List);
        SubBuf = NewStrBuf();
-       SubTP.ContextType = It->ContextType;
-       it = GetNewHashPos(List, 0);
-       while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
-               if (DetectGroupChange && nMembersCounted > 0) {
-                       if (SortBy->GroupChange(vContext, vLastContext))
-                               svputlong("ITERATE:ISGROUPCHANGE", 1);                  
-                       else
-                               svputlong("ITERATE:ISGROUPCHANGE", 0);
-               }
-               svprintf(HKEY("ITERATE:ODDEVEN"), WCS_STRING, "%s", 
-                        (oddeven) ? "odd" : "even");
-               svprintf(HKEY("ITERATE:KEY"), WCS_STRING, "%s", Key);
-               svputlong("ITERATE:N", nMembersCounted);
-               svputlong("ITERATE:LASTN", ++nMembersCounted == nMembersUsed);
-               SubTP.Context = vContext;
-               if (It->DoSubTemplate != NULL)
-                       It->DoSubTemplate(SubBuf, &SubTP);
-               DoTemplate(TKEY(1), SubBuf, &SubTP);
+       SubTP.Filter.ContextType = It->ContextType;
+       SubTP.Filter.ControlContextType = CTX_ITERATE;
+       SubTP.ControlContext = &Status;
+       
+       if (HAVE_PARAM(3)) {
+               StartAt = GetTemplateTokenNumber(Target, TP, 3, 0);
+       }
+       if (HAVE_PARAM(4)) {
+               StepWidth = GetTemplateTokenNumber(Target, TP, 4, 0);
+       }
+       if (HAVE_PARAM(5)) {
+               StopAt = GetTemplateTokenNumber(Target, TP, 5, -1);
+       }
+       if (StopAt < 0) {
+               StopAt = GetCount(List)  + 1;
+       }
+       it = GetNewHashPos(List, StepWidth);
+       while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
+               if ((Status.n > StartAt) && (Status.n < StopAt)) {
+                       if (DetectGroupChange && Status.n > 0) {
+                               Status.GroupChange = (SortBy->GroupChange(vContext, vLastContext))? 1:0;
+                       }
+                       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);
-               oddeven = ~ oddeven;
-               vLastContext = vContext;
+                       StrBufAppendBuf(Target, SubBuf, 0);
+                       FlushStrBuf(SubBuf);
+                       Status.oddeven = ! Status.oddeven;
+                       vLastContext = vContext;
+               }
+               Status.n++;
        }
        FreeStrBuf(&SubBuf);
        DeleteHashPos(&it);
@@ -1595,6 +1864,43 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
 }
 
 
+int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+       return Ctx->GroupChange;
+}
+
+void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+       if (Ctx->oddeven)
+               StrBufAppendBufPlain(Target, HKEY("odd"), 0);
+       else
+               StrBufAppendBufPlain(Target, HKEY("even"), 0);
+}
+
+
+void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+
+       StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
+}
+
+
+void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+       StrBufAppendPrintf(Target, "%d", Ctx->n);
+}
+
+int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+       return Ctx->LastN;
+}
+
+
 
 /*-----------------------------------------------------------------------------
  *                      Conditionals
@@ -1615,14 +1921,10 @@ int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP
                return 1;
        }
 
-       if (TP->Tokens->nParameters < Cond->nParams) {
-               LogTemplateError(                               
-                       Target, "Conditional", ERR_PARM1, TP,
-                       "needs %ld Params, have %ld!", 
-                       Cond->nParams,
-                       TP->Tokens->nParameters);
+       if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
                return 0;
        }
+
        if (Cond->CondF(Target, TP) == Neg)
                return TP->Tokens->Params[1]->lvalue;
        return 0;
@@ -1636,15 +1938,7 @@ int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
        if (!GetHash(WC->vars, TKEY(2), &vsubst))
                return 0;
        subst = (wcsubst*) vsubst;
-       if ((subst->ContextRequired != CTX_NONE) &&
-           (subst->ContextRequired != TP->ContextType)) {
-                LogTemplateError(
-                        Target, "ConditionalVar", ERR_PARM1, TP,
-                       "  WARNING: Conditional requires Context: [%ld], have [%ld]!", 
-                       subst->ContextRequired, CTX);
-               return -1;
-       }
-
+       
        switch(subst->wcs_type) {
        case WCS_FUNCTION:
                return (subst->wcs_function!=NULL);
@@ -1668,7 +1962,6 @@ int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
        return 0;
 }
 
-
 void RegisterConditional(const char *Name, long len, 
                         int nParams,
                         WCConditionalFunc CondF, 
@@ -1676,9 +1969,26 @@ void RegisterConditional(const char *Name, long len,
 {
        ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
        Cond->PlainName = Name;
-       Cond->nParams = nParams;
+       Cond->Filter.nMaxArgs = nParams;
+       Cond->Filter.nMinArgs = nParams;
        Cond->CondF = CondF;
-       Cond->ContextRequired = ContextRequired;
+       Cond->Filter.ContextType = ContextRequired;
+       Cond->Filter.ControlContextType = CTX_NONE;
+       Put(Conditionals, Name, len, Cond, NULL);
+}
+
+void RegisterControlConditional(const char *Name, long len, 
+                               int nParams,
+                               WCConditionalFunc CondF, 
+                               int ControlContextRequired)
+{
+       ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
+       Cond->PlainName = Name;
+       Cond->Filter.nMaxArgs = nParams;
+       Cond->Filter.nMinArgs = nParams;
+       Cond->CondF = CondF;
+       Cond->Filter.ContextType = CTX_NONE;
+       Cond->Filter.ControlContextType = ControlContextRequired;
        Put(Conditionals, Name, len, Cond, NULL);
 }
 
@@ -1695,7 +2005,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;
 }
 
@@ -1716,7 +2026,8 @@ void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
                else {
                        const char *Ch;
                        long len;
-                       GetTemplateTokenString(TP, 
+                       GetTemplateTokenString(Target, 
+                                              TP, 
                                               1,
                                               &Ch,
                                               &len);
@@ -1725,7 +2036,7 @@ void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
        }
        memcpy (&SubTP, TP, sizeof(WCTemplputParams));
        SubTP.Context = Headline;
-       SubTP.ContextType = CTX_STRBUF;
+       SubTP.Filter.ContextType = CTX_STRBUF;
        DoTemplate(HKEY("beginbox"), Target, &SubTP);
        DoTemplate(TKEY(0), Target, TP);
        DoTemplate(HKEY("endbox"), Target, TP);
@@ -1753,7 +2064,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);
@@ -1799,7 +2111,8 @@ void RegisterSortFunc(const char *name, long len,
        Put(SortHash, name, len, NewSort, DestroySortStruct);
 }
 
-CompareFunc RetrieveSort(WCTemplputParams *TP, const char *OtherPrefix, 
+CompareFunc RetrieveSort(WCTemplputParams *TP, 
+                        const char *OtherPrefix, long OtherPrefixLen,
                         const char *Default, long ldefault, long DefaultDirection)
 {
        int isdefault = 0;
@@ -1810,13 +2123,19 @@ CompareFunc RetrieveSort(WCTemplputParams *TP, const char *OtherPrefix,
        
        if (havebstr("SortBy")) {
                BSort = sbstr("SortBy");
+               if (OtherPrefix == NULL) {
+                       set_room_pref("sort", NewStrBufDup(BSort), 0);
+               }
+               else {
+                       set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
+               }
        }
        else { /** Try to fallback to our remembered values... */
                if (OtherPrefix == NULL) {
                        BSort = get_room_pref("sort");
                }
                else {
-                       /*TODO: nail prefprepend to sort, and lookup this! */
+                       BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
                }
                if (BSort != NULL)
                        putbstr("SortBy", NewStrBufDup(BSort));
@@ -1852,7 +2171,7 @@ CompareFunc RetrieveSort(WCTemplputParams *TP, const char *OtherPrefix,
                        SortOrder = StrTol(Buf);
                }
                else {
-                       /* TODO: nail prefprepend to sort, and lookup this! */
+                       BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
                }
 
                if (Buf == NULL)
@@ -1894,7 +2213,7 @@ ConstStr SortNextOrder[] = {
 };
 
 
-int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder)
+int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
 {
        int bSortError = eNOT_SPECIFIED;
        const StrBuf *BSort;
@@ -1911,13 +2230,19 @@ int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, l
        if (havebstr("SortBy")) {
                BSort = sbstr("SortBy");
                bSortError = eINVALID_PARAM;
+               if ((*Param)->PrefPrepend == NULL) {
+                       set_room_pref("sort", NewStrBufDup(BSort), 0);
+               }
+               else {
+                       set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
+               }
        }
        else { /** Try to fallback to our remembered values... */
                if ((*Param)->PrefPrepend == NULL) {
                        BSort = get_room_pref("sort");
                }
                else {
-                       BSort = NULL;/* TODO: nail prefprepend to sort, and lookup this! */
+                       BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
                }
        }
 
@@ -1936,7 +2261,7 @@ int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, l
                        *SortOrder = StrTol(get_room_pref("SortOrder"));
                }
                else {
-                       *SortOrder = 0;/* TODO: nail prefprepend to sort, and lookup this! */
+                       *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
                }
        }
        if (*SortOrder > 2)
@@ -1953,7 +2278,7 @@ void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
        SortStruct *Param;
        const ConstStr *SortIcon;
 
-       switch (GetSortMetric(TP, &Next, &Param, &SortOrder)){
+       switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
        case eNO_SUCH_SORT:
                 LogTemplateError(
                         Target, "Sorter", ERR_PARM1, TP,
@@ -1982,7 +2307,7 @@ void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
        SortStruct *Next;
        SortStruct *Param;
 
-       switch (GetSortMetric(TP, &Next, &Param, &SortOrder)){
+       switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
        case eNO_SUCH_SORT:
                 LogTemplateError(
                         Target, "Sorter", ERR_PARM1, TP,                                  
@@ -2008,7 +2333,7 @@ void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
        SortStruct *Next;
        SortStruct *Param;
 
-       switch (GetSortMetric(TP, &Next, &Param, &SortOrder)){
+       switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
        case eNO_SUCH_SORT:
                 LogTemplateError(
                         Target, "Sorter", ERR_PARM1, TP,
@@ -2113,9 +2438,9 @@ InitModule_SUBST
 (void)
 {
        memset(&NoCtx, 0, sizeof(WCTemplputParams));
-       RegisterNamespace("SORT:ICON", 1, 1, tmplput_SORT_ICON, CTX_NONE);
-       RegisterNamespace("SORT:ORDER", 1, 1, tmplput_SORT_ORDER, CTX_NONE);
-       RegisterNamespace("SORT:NEXT", 1, 1, tmplput_SORT_NEXT, CTX_NONE);
+       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);
@@ -2124,6 +2449,16 @@ InitModule_SUBST
        RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
        RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
        RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
+
+       RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
+                                  conditional_ITERATE_ISGROUPCHANGE, 
+                                  CTX_ITERATE);
+       RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
+                                  conditional_ITERATE_LASTN, 
+                                  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);
 }
 
 /*@}*/