* migrate the WHO struct to strbuf
authorWilfried Göesgens <willi@citadel.org>
Mon, 18 Aug 2008 19:47:07 +0000 (19:47 +0000)
committerWilfried Göesgens <willi@citadel.org>
Mon, 18 Aug 2008 19:47:07 +0000 (19:47 +0000)
* add SVPUTBuf() which can handle strbuffers
* register the ITERATOR("WHO")
* provide tokens via the who iterator func: WHO:NAME, WHO:ROOM, WHO:HOST, WHO:REALROOM, WHO:REALHOST, WHO:LASTACTIVE, WHO:SESSION, WHO:IDLE, WHO:NSESSIONS
* some fixes to do_template, and the whole other stuff

webcit/preferences.c
webcit/serv_func.c
webcit/siteconfig.c
webcit/subst.c
webcit/webcit.c
webcit/webcit.h
webcit/webserver.c
webcit/who.c

index ecec9e05db178afc52315bdc9d6832eb4fe5eda0..116b5431670111dfdb97fde292013904ac128a9b 100644 (file)
@@ -688,6 +688,11 @@ void tmplput_CFG_Descr(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *
 }
 
 
+void CfgZoneTempl(StrBuf *TemplBuffer, void *Context)
+{
+
+}
+
 
 void 
 InitModule_PREFERENCES
@@ -709,5 +714,6 @@ InitModule_PREFERENCES
        
        RegisterNamespace("PREF:VALUE", 1, 1, tmplput_CFG_Value);
        RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr);
+       RegisterIterator("PREF:ZONE", ZoneHash, NULL, CfgZoneTempl, NULL);
 }
 /*@}*/
index a9ff29458403911fba1438fd61841caa65a80bf1..bce6cd5cac9af77dff927ea331808a7d5198c4e2 100644 (file)
@@ -109,13 +109,18 @@ void get_serv_info(char *browser_host, char *user_agent)
  * \brief Read Citadel variformat text and spit it out as HTML.
  * \param align html align string
  */
-void fmout(char *align)
+inline void fmout(char *align)
+{
+       _fmout(WC->WBuf, align);
+}
+
+void _fmout(StrBuf *Target, char *align)
 {
        int intext = 0;
        int bq = 0;
        char buf[SIZ];
 
-       wprintf("<div align=%s>\n", align);
+       StrBufAppendPrintf(Target, "<div align=%s>\n", align);
        while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
 
                if ((intext == 1) && (isspace(buf[0]))) {
@@ -129,10 +134,10 @@ void fmout(char *align)
                 * " >" quotes and will convert to <BLOCKQUOTE> tags.
                 */
                if ((bq == 0) && (!strncmp(buf, " >", 2))) {
-                       wprintf("<BLOCKQUOTE>");
+                       StrBufAppendBufPlain(Target, HKEY("<BLOCKQUOTE>"), 0);
                        bq = 1;
                } else if ((bq == 1) && (strncmp(buf, " >", 2))) {
-                       wprintf("</BLOCKQUOTE>");
+                       StrBufAppendBufPlain(Target, HKEY("</BLOCKQUOTE>"), 0);
                        bq = 0;
                }
                if ((bq == 1) && (!strncmp(buf, " >", 2))) {
@@ -141,8 +146,8 @@ void fmout(char *align)
                /** Activate embedded URL's */
                url(buf, sizeof(buf));
 
-               escputs(buf);
-               wprintf("\n");
+               escputs(buf);//// TODO: Target
+               StrBufAppendBufPlain(Target, HKEY("\n"), 0);
        }
        if (bq == 1) {
                wprintf("</I>");
index 1a32942eaecb66f947e6e0ca303add2901298224..25e8ae034235c5afbb39b4d7581cba35bb892b4d 100644 (file)
@@ -8,6 +8,39 @@
 #include "webcit.h"
 #include "webserver.h"
 
+
+HashList *ZoneHash = NULL;
+
+
+
+void LoadZoneFiles(void)
+{
+       icalarray *zones;
+       int z;
+       long len;
+       char this_zone[128];
+       char *ZName;
+       HashPos  *it;
+
+       ZoneHash = NewHash(1, NULL);
+       len = sizeof("UTC") + 1;
+       ZName = malloc(len + 1);
+       memcpy(ZName, "UTC", len + 1);
+       Put(ZoneHash, ZName, len, ZName, NULL);
+       zones = icaltimezone_get_builtin_timezones();
+       for (z = 0; z < zones->num_elements; ++z) {
+               strcpy(this_zone, icaltimezone_get_location(icalarray_element_at(zones, z)));
+               len = strlen(this_zone);
+               ZName = (char*)malloc(len +1);
+               memcpy(ZName, this_zone, len + 1);
+               Put(ZoneHash, ZName, len, ZName, NULL);
+       }
+       SortByHashKey(ZoneHash, 0);
+}
+
+
+
+
 /*
  * \brief display all configuration items
  */
index cd0afa979543d81917988a297c1564d5cb18a283..6bff320902448bca0f52d2c4136207aefb7fafa1 100644 (file)
@@ -279,27 +279,56 @@ inline void SVCALLBACK(char *keyname, var_callback_fptr fcn_ptr)
 
 
 
+void SVPUTBuf(const char *keyname, int keylen, StrBuf *Buf, int ref)
+{
+       wcsubst *ptr;
+       void *vPtr;
+       struct wcsession *WCC = WC;
+
+       /**
+        * First look if we're doing a replacement of
+        * an existing key
+        */
+       /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
+       if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
+               ptr = (wcsubst*)vPtr;
+               if (ptr->wcs_value != NULL)
+                       free(ptr->wcs_value);///TODO: respect type
+       }
+       else    /** Otherwise allocate a new one */
+       {
+               ptr = (wcsubst *) malloc(sizeof(wcsubst));
+               safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
+               Put(WCC->vars, keyname, keylen, ptr,  deletevar);
+       }
+
+       ptr->wcs_value = NULL;
+       ptr->wcs_type = (ref)?WCS_STRBUF:WCS_STRBUF_REF;
+       ptr->wcs_function = (var_callback_fptr) Buf; ////TODO
+}
+
 /**
  * \brief back end for print_value_of() ... does a server command
  * \param servcmd server command to execute on the citadel server
  */
-void pvo_do_cmd(char *servcmd) {
+void pvo_do_cmd(StrBuf *Target, char *servcmd) {
        char buf[SIZ];
+       int len;
 
        serv_puts(servcmd);
-       serv_getln(buf, sizeof buf);
+       len = serv_getln(buf, sizeof buf);
 
        switch(buf[0]) {
                case '2':
                case '3':
                case '5':
-                       wprintf("%s\n", &buf[4]);
+                       StrBufAppendPrintf(Target, "%s\n", &buf[4]);
                        break;
                case '1':
-                       fmout("CENTER");
+                       _fmout(Target, "CENTER");
                        break;
                case '4':
-                       wprintf("%s\n", &buf[4]);
+                       StrBufAppendPrintf(Target, "%s\n", &buf[4]);
                        serv_puts("000");
                        break;
        }
@@ -309,7 +338,7 @@ void pvo_do_cmd(char *servcmd) {
  * \brief Print the value of a variable
  * \param keyname get a key to print
  */
-void print_value_of(const char *keyname, size_t keylen) {
+void print_value_of(StrBuf *Target, const char *keyname, size_t keylen) {
        struct wcsession *WCC = WC;
        wcsubst *ptr;
        void *fcn();
@@ -324,14 +353,18 @@ void print_value_of(const char *keyname, size_t keylen) {
                ptr = (wcsubst*) vVar;
                switch(ptr->wcs_type) {
                case WCS_STRING:
-                       wprintf("%s", (const char*)ptr->wcs_value);
+                       StrBufAppendBufPlain(Target, (const char*)ptr->wcs_value, -1, 0);
                        break;
                case WCS_SERVCMD:
-                       pvo_do_cmd(ptr->wcs_value);
+                       pvo_do_cmd(Target, ptr->wcs_value);
                        break;
                case WCS_FUNCTION:
                        (*ptr->wcs_function) ();
                        break;
+               case WCS_STRBUF:
+               case WCS_STRBUF_REF:
+                       StrBufAppendBuf(Target, (StrBuf*) ptr->wcs_function, 0);
+                       break;
                default:
                        lprintf(1,"WARNING: invalid value in SV-Hash at %s!", keyname);
                }
@@ -397,6 +430,7 @@ TemplateParam *GetNextParamter(StrBuf *Buf, const char **pCh, const char *pe)
                }
                else {
                        StrBufPeek(Buf, pch, -1, '\0');         
+                       lprintf(1, "DBG: got param [%s]\n", pchs);
                        Parm->Start = pchs;
                        Parm->len = pche - pchs;
                        pch ++; /* move after trailing quote */
@@ -419,7 +453,7 @@ TemplateParam *GetNextParamter(StrBuf *Buf, const char **pCh, const char *pe)
                }
                else {
                        Parm->lvalue = 0;
-                       lprintf(1, "Error evalating template long param [%s]", *pCh);
+                       lprintf(1, "Error evaluating template long param [%s]", *pCh);
                        free(Parm);
                        return NULL;
                }
@@ -427,6 +461,7 @@ TemplateParam *GetNextParamter(StrBuf *Buf, const char **pCh, const char *pe)
        while ((*pch == ' ' )||
               (*pch == '\t')||
               (*pch == '\r')||
+              (*pch == ',' )||
               (*pch == '\n')) pch ++;
 
        *pCh = pch;
@@ -456,12 +491,12 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
        NewToken->HaveParameters = 0;;
        NewToken->nParameters = 0;
 
-       while (pch <= pTmplEnd - 1) {
+       while (pch < pTmplEnd - 1) {
                if (*pch == '(') {
                        StrBufPeek(Buf, pch, -1, '\0');
                        NewToken->NameEnd = pch - NewToken->pName;
                        pch ++;
-                       while (pch <= pTmplEnd - 1) {
+                       while (pch < pTmplEnd - 1) {
                                Param = GetNextParamter(Buf, &pch, pTmplEnd - 1);
                                if (Param != NULL) {
                                        NewToken->HaveParameters = 1;
@@ -509,7 +544,7 @@ void EvaluateToken(StrBuf *Target, WCTemplateToken *Token, void *Context)
                HashHandler *Handler;
                Handler = (HashHandler*) vVar;
                if ((Token->nParameters < Handler->nMinArgs) || 
-                   (Token->nParameters < Handler->nMaxArgs)) {
+                   (Token->nParameters > Handler->nMaxArgs)) {
                        lprintf(1, "Handler [%s] doesn't work with %ld params", 
                                Token->pName,
                                Token->nParameters);
@@ -524,7 +559,7 @@ void EvaluateToken(StrBuf *Target, WCTemplateToken *Token, void *Context)
                }
        }
        else {
-               print_value_of(Token->pName, Token->NameEnd);
+               print_value_of(Target, Token->pName, Token->NameEnd);
        }
 }
 
@@ -540,7 +575,9 @@ void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context)
        i = 0;
        while (!done) {
                if (i >= Tmpl->nTokensUsed) {
-                       StrBufAppendBufPlain(Target, pData, len, 0);
+                       StrBufAppendBufPlain(Target, 
+                                            pData, 
+                                            len - (pData - pS), 0);
                        done = 1;
                }
                else {
@@ -639,7 +676,9 @@ void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Targe
        HashList *Static;
        HashList *StaticLocal;
        void *vTmpl;
-
+       
+       if (Target == NULL)
+               Target = WC->WBuf;
        if (WC->is_mobile) {
                Static = WirelessTemplateCache;
                StaticLocal = WirelessLocalTemplateCache;
@@ -656,7 +695,7 @@ void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Targe
        }
        if (vTmpl == NULL) 
                return;
-       ProcessTemplate(vTmpl, WC->WBuf, Context);      
+       ProcessTemplate(vTmpl, Target, Context);        
 }
 
 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
@@ -804,8 +843,8 @@ void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, vo
        it = GetNewHashPos();
        while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
                It->DoSubTemplate(SubBuf, vContext);
-               DoTemplate(Tokens->Params[0]->Start,
-                          Tokens->Params[0]->len,
+               DoTemplate(Tokens->Params[1]->Start,
+                          Tokens->Params[1]->len,
                           vContext, SubBuf);
                        
                StrBufAppendBuf(Target, SubBuf, 0);
index 4f488f392a44b74e07f6cfdbd262fd6f496b2b83..3034419c8fed3628886f72f0bbec74ec68e2bd06 100644 (file)
@@ -1094,7 +1094,10 @@ void blank_page(void) {
  * A template has been requested
  */
 void url_do_template(void) {
-       do_template(bstr("template"), NULL);
+       const StrBuf *Tmpl = sbstr("template");
+       begin_burst();
+       DoTemplate(ChrPtr(Tmpl), StrLength(Tmpl), NULL, NULL);
+       end_burst();
 }
 
 
@@ -1498,9 +1501,9 @@ void session_loop(struct httprequest *req)
                BuffSize = ContentLength + SIZ;
                content = malloc(BuffSize);
                memset(content, 0, BuffSize);
-               snprintf(content,  BuffSize, "Content-type: %s\n"
-                               "Content-length: %d\n\n",
-                               ContentType, ContentLength);
+               hprintf("Content-type: %s\n"
+                       "Content-length: %d\n\n",
+                       ContentType, ContentLength);
                body_start = strlen(content);
 
                /** Read the entire input data at once. */
index bee4cf009d07ede55f1ad8d8ebcd625b27c29042..7f01648f66a656a81058aa6b4582e8b0f50153bf 100644 (file)
@@ -332,16 +332,18 @@ void RegisterITERATOR(const char *Name, long len,
                      RetrieveHashlistFunc GetHash, 
                      SubTemplFunc DoSubTempl,
                      HashDestructorFunc Destructor);
-#define RegisterIterator(a, b, c, d) RegisterITERATOR(a, sizeof(a)-1, b, c, d)
+#define RegisterIterator(a, b, c, d, e) RegisterITERATOR(a, sizeof(a)-1, b, c, d, e)
 
 
 /**
  * \brief Values for wcs_type
  */
 enum {
-       WCS_STRING,   /**< its a string */
-       WCS_FUNCTION, /**< its a function callback */
-       WCS_SERVCMD   /**< its a command to send to the citadel server */
+       WCS_STRING,       /**< its a string */
+       WCS_FUNCTION,     /**< its a function callback */
+       WCS_SERVCMD,      /**< its a command to send to the citadel server */
+       WCS_STRBUF,       /**< its a strbuf we own */
+       WCS_STRBUF_REF    /**< its a strbuf we mustn't free */
 };
 
 /**
@@ -520,6 +522,9 @@ extern HashList *TemplateCache;
 extern HashList *LocalTemplateCache;
 extern HashList *GlobalNS;
 extern HashList *Iterators;
+extern HashList *ZoneHash;
+
+
 
 void InitialiseSemaphores(void);
 void begin_critical_section(int which_one);
@@ -553,6 +558,7 @@ void who(void);
 void who_inner_div(void);
 void ajax_mini_calendar(void);
 void fmout(char *align);
+void _fmout(StrBuf *Targt, char *align);
 void pullquote_fmout(void);
 void wDumpContent(int);
 
@@ -662,6 +668,9 @@ void SVCALLBACK(char *keyname, var_callback_fptr fcn_ptr);
 void SVCallback(char *keyname, size_t keylen,  var_callback_fptr fcn_ptr);
 #define svcallback(a, b) SVCallback(a, sizeof(a) - 1, b)
 
+void SVPUTBuf(const char *keyname, int keylen, StrBuf *Buf, int ref);
+#define SVPutBuf(a, b, c); SVPUTBuf(a, sizeof(a) - 1, b, c)
+
 void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Target);
 #define do_template(a, b) DoTemplate(a, sizeof(a) -1, b, NULL);
 
index 32887f47c642a0a0bd0421431bb751bde7aa1c07..a550f49280dd0a5b46028806009e3bacd5a37d55 100644 (file)
@@ -589,6 +589,7 @@ const char *nix(void *vptr) {snprintf(foobuf, 32, "%0x", (long) vptr); return fo
 #endif 
 void InitTemplateCache(void);
 
+extern void LoadZoneFiles(void);
 /*
  * \brief Here's where it all begins.
  * \param argc number of commandline args
@@ -624,6 +625,7 @@ int main(int argc, char **argv)
        TemplateCache = NewHash(1, NULL);
        GlobalNS = NewHash(1, NULL);
        Iterators = NewHash(1, NULL);
+       LoadZoneFiles();
 
 
 #ifdef DBG_PRINNT_HOOKS_AT_START
index 00a06fdb0164581a0f71301b4ef6b2f3a737e1c5..47f152aa77320df5a99d8d150aeb80e7dc532272 100644 (file)
@@ -5,16 +5,11 @@
 #include "webcit.h"
 
 typedef struct UserStateStruct {
-       char *UserName;
-       long UserNameLen;
-       char *Room;
-       long RoomLen;
-       char *Host;
-       long HostLen;
-       char *RealRoom;
-       long RealRoomLen;
-       char *RealHost;
-       long RealHostLen;
+       StrBuf *UserName;
+       StrBuf *Room;
+       StrBuf *Host;
+       StrBuf *RealRoom;
+       StrBuf *RealHost;
        long LastActive;
        int Session;
        int Idle;
@@ -24,11 +19,11 @@ typedef struct UserStateStruct {
 void DestroyUserStruct(void *vUser)
 {
        UserStateStruct *User = (UserStateStruct*) vUser;
-       free(User->UserName);
-       free(User->Room);
-       free(User->Host);
-       free(User->RealRoom);
-       free(User->RealHost);
+       FreeStrBuf(&User->UserName);
+       FreeStrBuf(&User->Room);
+       FreeStrBuf(&User->Host);
+       FreeStrBuf(&User->RealRoom);
+       FreeStrBuf(&User->RealHost);
        free(User);
 }
 
@@ -39,53 +34,54 @@ int CompareUserStruct(const void *VUser1, const void *VUser2)
        
        if (User1->Idle != User2->Idle)
                return User1->Idle > User2->Idle;
-       return strcasecmp(User1->UserName, User2->UserName);
+       return strcasecmp(ChrPtr(User1->UserName), 
+                         ChrPtr(User2->UserName));
 }
 
 
 int GetWholistSection(HashList *List, time_t now)
 {
+       StrBuf *Buf, *XBuf;
        struct wcsession *WCC = WC;     /* This is done to make it run faster; WC is a function */
        UserStateStruct *User, *OldUser;
        void *VOldUser;
-       char buf[SIZ], user[SIZ], room[SIZ], host[SIZ],
-               realroom[SIZ], realhost[SIZ];
        size_t BufLen;
+       char buf[SIZ];
 
        serv_puts("RWHO");
        serv_getln(buf, sizeof buf);
        if (buf[0] == '1') {
-               while (BufLen = serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               Buf = NewStrBuf();
+               XBuf = NewStrBuf();
+               while (BufLen = StrBuf_ServGetln(Buf), strcmp(ChrPtr(Buf), "000")) {
                        if (BufLen <= 0)
                            continue;
                        User = (UserStateStruct*) malloc(sizeof(UserStateStruct));
-                       User->Session = extract_int(buf, 0);
+                       User->Session = StrBufExtract_int(Buf, 0, '|');
 
-                       User->UserNameLen = extract_token(user, buf, 1, '|', sizeof user);
-                       User->UserName = malloc(User->UserNameLen + 1);
-                       memcpy(User->UserName, user, User->UserNameLen + 1);
+                       StrBufExtract_token(XBuf, Buf, 1, '|');
+                       User->UserName = NewStrBufDup(XBuf);
 
-                       User->RoomLen = extract_token(room, buf, 2, '|', sizeof room);
-                       User->Room = malloc(User->RoomLen + 1);
-                       memcpy(User->Room, room, User->RoomLen + 1);
+                       StrBufExtract_token(XBuf, Buf, 2, '|');
+                       User->Room = NewStrBufDup(XBuf);
 
-                       User->HostLen = extract_token(host, buf, 3, '|', sizeof host);
-                       User->Host = malloc(User->HostLen + 1);
-                       memcpy(User->Host, host, User->HostLen + 1);
+                       StrBufExtract_token(XBuf, Buf, 3, '|');
+                       User->Host = NewStrBufDup(XBuf);
 
-                       User->RealRoomLen = extract_token(realroom, buf, 9, '|', sizeof realroom);
-                       User->RealRoom = malloc(User->RealRoomLen + 1);
-                       memcpy(User->RealRoom, realroom, User->RealRoomLen + 1);
+                       StrBufExtract_token(XBuf, Buf, 9, '|');
+                       User->RealRoom = NewStrBufDup(XBuf);
 
-                       User->RealHostLen = extract_token(realhost, buf, 10, '|', sizeof realhost);
-                       User->RealHost = malloc(User->RealHostLen + 1);
-                       memcpy(User->RealHost, realhost, User->RealHostLen + 1);
+                       StrBufExtract_token(XBuf, Buf, 10, '|');
+                       User->RealHost = NewStrBufDup(XBuf);
                        
-                       User->LastActive = extract_long(buf, 5);
+                       User->LastActive = StrBufExtract_long(Buf, 5, '|');
                        User->Idle = (now - User->LastActive) > 900L;
                        User->SessionCount = 1;
 
-                       if (GetHash(List, User->UserName, User->UserNameLen, &VOldUser)) {
+                       if (GetHash(List, 
+                                   ChrPtr(User->UserName), 
+                                   StrLength(User->UserName), 
+                                   &VOldUser)) {
                                OldUser = VOldUser;
                                OldUser->SessionCount++;
                                if (!User->Idle) {
@@ -98,9 +94,15 @@ int GetWholistSection(HashList *List, time_t now)
                                DestroyUserStruct(User);
                        }
                        else
-                               Put(List, User->UserName, User->UserNameLen, User, DestroyUserStruct);
+                               Put(List, 
+                                   ChrPtr(User->UserName), 
+                                   StrLength(User->UserName), 
+                                   User, DestroyUserStruct);
                }
                SortByPayload(List, CompareUserStruct);
+
+               FreeStrBuf(&XBuf);
+               FreeStrBuf(&Buf);
                return 1;
        }
        else
@@ -165,7 +167,7 @@ void who_inner_div(void) {
 
                        /* (link to page this user) */
                        wprintf("<td width=\"5%%\"><a href=\"display_page?recp=");
-                       urlescputs(User->UserName);
+                       urlescputs(ChrPtr(User->UserName));
                        wprintf("\">"
                                "<img align=\"middle\" "
                                "src=\"static/citadelchat_24x.gif\" "
@@ -196,17 +198,17 @@ void who_inner_div(void) {
 
                        /* username (link to user bio/photo page) */
                        wprintf("<a href=\"showuser?who=");
-                       urlescputs(User->UserName);
+                       urlescputs(ChrPtr(User->UserName));
                        wprintf("\">");
-                       escputs(User->UserName);
+                       escputs(ChrPtr(User->UserName));
                        if (User->SessionCount > 1)
                                wprintf(" [%d] ", User->SessionCount);
                        wprintf("</a>");
 
                        /* room */
                        wprintf("</td>\n\t<td>");
-                       escputs(User->Room);
-                       if (!IsEmptyStr(User->RealRoom) ) {
+                       escputs(ChrPtr(User->Room));
+                       if (StrLength(User->RealRoom) > 0) {
                                wprintf("<br /><i>");
                                escputs(User->RealRoom);
                                wprintf("</i>");
@@ -214,8 +216,8 @@ void who_inner_div(void) {
                        wprintf("</td>\n\t<td class=\"host_col\">");
 
                        /* hostname */
-                       escputs(User->Host);
-                       if (!IsEmptyStr(User->RealHost)) {
+                       escputs(ChrPtr(User->Host));
+                       if (StrLength(User->RealHost) > 0) {
                                wprintf("<br /><i>");
                                escputs(User->RealHost);
                                wprintf("</i>");
@@ -415,7 +417,7 @@ void wholist_section(void) {
                it = GetNewHashPos();
                while (GetNextHashPos(List, it, &len, &UserName, &VUser)) {
                        User = VUser;
-                       if (strcmp(User->UserName, NLI)) {
+                       if (strcmp(ChrPtr(User->UserName), NLI)) {
                                wprintf("<li class=\"");
                                if (User->Idle) {
                                        wprintf("inactiveuser");
@@ -424,9 +426,9 @@ void wholist_section(void) {
                                        wprintf("activeuser");
                                }
                                wprintf("\"><a href=\"showuser?who=");
-                               urlescputs(User->UserName);
+                               urlescputs(ChrPtr(User->UserName));
                                wprintf("\">");
-                               escputs(User->UserName);
+                               escputs(ChrPtr(User->UserName));
                                wprintf("</a></li>");
                        }
                }
@@ -440,6 +442,47 @@ void _terminate_session(void) {
        terminate_session();
 }
 
+HashList *GetWholistHash(void)
+{
+       HashList *List;
+       char buf[SIZ];
+        time_t now;
+
+       serv_puts("TIME");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               now = extract_long(&buf[4], 0);
+       }
+       else {
+               now = time(NULL);
+       }
+
+       List = NewHash(1, NULL);
+       GetWholistSection(List, now);
+       return List;
+}
+
+void WholistSubst(StrBuf *TemplBuffer, void *vContext)
+{
+       UserStateStruct *User = (UserStateStruct*) vContext;
+
+       SVPutBuf("WHO:NAME", User->UserName, 1);
+       SVPutBuf("WHO:ROOM", User->Room, 1);
+       SVPutBuf("WHO:HOST", User->Host, 1);
+       SVPutBuf("WHO:REALROOM", User->RealRoom, 1);
+       SVPutBuf("WHO:REALHOST", User->RealHost, 1);
+       svprintf(HKEY("WHO:LASTACTIVE"), WCS_STRING, "%ld", User->LastActive);
+       svprintf(HKEY("WHO:SESSION"), WCS_STRING, "%d", User->Session);
+       svprintf(HKEY("WHO:IDLE"), WCS_STRING, "%s", (User->Idle)? "Idle":"Active");
+       svprintf(HKEY("WHO:NSESSIONS"), WCS_STRING, "%d", User->SessionCount);
+       
+}
+
+void DeleteWholistHash(HashList *KillMe)
+{
+       DeleteHash(&KillMe);
+}
+
 void 
 InitModule_WHO
 (void)
@@ -449,4 +492,6 @@ InitModule_WHO
        WebcitAddUrlHandler(HKEY("wholist_section"), wholist_section, AJAX);
        WebcitAddUrlHandler(HKEY("terminate_session"), _terminate_session, 0);
        WebcitAddUrlHandler(HKEY("edit_me"), edit_me, 0);
+
+       RegisterIterator("WHOLIST", NULL, GetWholistHash, WholistSubst, DeleteWholistHash);
 }