//
//  File:   hdsMaster.cpp
//
//  Legal Notice:   This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
//  The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
//  Software Foundation, either version 3 of the License, or any later version.
//
//  The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
//  A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
//  Description: Dissemino Web-Masters Management Facility - Manages website resources, such as pages, article groups and include blocks. Allocates ids and manages version numbers.
//
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <stdarg.h>
#include <dirent.h>
#include <netdb.h>
#include <signal.h>
#include "hzErrcode.h"
#include "hzTokens.h"
#include "hzTextproc.h"
#include "hzDissemino.h"
using namespace std ;
/*
**  Variables
*/
static  const hzString  s_master_head_nav =
    "<!DOCTYPE html>\n"
    "<html>\n"
    "<head>\n"
    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n"
    "<meta http-equiv=\"Expires\" content=\"-1\"/>\n"
    "<meta http-equiv=\"Cache-Control\" content=\"no-cache\"/>\n"
    "<meta http-equiv=\"Cache-Control\" content=\"no-store\"/>\n"
    "<meta http-equiv=\"Pragma\" content=\"no-cache\"/>\n"
    "<meta http-equiv=\"Pragma\" content=\"no-store\"/>\n"
    "<style>\n"
    "body           { margin:0px; background-color:#FFFFFF; }\n"
    ".main          { text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000; }\n"
    ".title         { text-decoration:none; font-family:verdana; font-size:15px; font-weight:normal; color:#000000; }\n"
    ".nbar          { text-decoration:none; font-family:verdana; font-size:10px; font-weight:bold; color:#FFFFFF; cursor:hand; }\n"
    ".nfld          { list-style-type:none; font-family:verdana; font-size:10px; font-weight:bold; color:#000000; cursor:hand; }\n"
    ".objtitle      { height:30px; border:0px; margin:0px; list-style-type:none; background-color:#A0F0AF; overflow-x:none; overflow-y:none; }\n"
    ".navtree       { height:calc(100% - 88px); border:1px; margin:0px; list-style-type:none; white-space:nowrap; background-color:#E0E0E0; overflow-x:auto; overflow-y:scroll; }\n"
    ".objpage       { height:calc(100% - 118px); border:0px; margin:0px; list-style-type:none; white-space:nowrap; background-color:#A0F0AF; overflow-x:auto; overflow-y:auto; }\n"
    ".fixVP         { height:calc(100% - 118px); width:auto; position:relative; background-color:#FFFFFF; overflow-x:hidden; overflow-y:hidden; table-layout:auto; border-collapse:collapse; }\n"
    ".fixVP th      { text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; padding:2px; }\n"
    ".fixVP td      { text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; padding:2px; }\n"
    ".fixVP tbody   { overflow-y:auto; }\n"
    ".fixVP thead   { background:white; color:#000000; }\n"
    "</style>\n"
    "<script>\n"
    "function mgwp()\n"
    "{\n"
    "   var screenW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;\n"
    "   var screenH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;\n"
    "   var txt;\n"
    "   var h;\n"
    "   h=screenH-88;\n"
    "   txt=h+\"px\";\n"
    "   var x=document.getElementById(\"navlhs\");          x.style.minHeight=txt;  x.style.maxHeight=txt;\n"
    "   h=screenH-118;\n"
    "   txt=h+\"px\";\n"
    "   var y=document.getElementById(\"articlecontent\");  y.style.minHeight=txt;  y.style.maxHeight=txt;\n"
    "}\n"
    "</script>\n"
    "</head>\n\n"
    "<body onload=\"mgwp()\" onpageshow=\"mgwp()\" onresize=\"mgwp()\">\n\n" ;
    //  ".fixVP         { width:auto; position:fixed; background-color:#FFFFFF; overflow-x:hidden; overflow-y:hidden; table-layout:auto; border-collapse:collapse; }\n"
static  const hzString  s_master_entry_pt = "masterEntryPt" ;
static  const hzString  s_articleTitle = "x-title" ;            //  Name of HTTP header supplied upon request of an article, used to convey article title
static  const hzString  s_articleScript = "x-script" ;          //  Name of HTTP header supplied upon request of an article, used to convey article script
extern  hzString    _dsmScript_tog ;            //  Navtree toggle script
extern  hzString    _dsmScript_loadArticle ;    //  Navtree load article script
extern  hzString    _dsmScript_navtree ;        //  Navtree script
/*
**  Prototypes
*/
hzEcode _masterAppParam     (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterCfgList      (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterFldSpecs     (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterEnumEdit     (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterClassEdit    (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterMbrEdit      (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterReposEdit    (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterFileList     (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
//hzEcode   _masterUSL          (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterStrGen       (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterStrFix       (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterEmaddr       (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterDomain       (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterMemstat      (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterBanned       (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterVisList      (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterResList      (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE) ;
hzEcode _masterCfgEdit      (hzHttpEvent* pE, const hzString& cname) ;
hzEcode _masterMainMenu     (hzHttpEvent* pE) ;
/*
**  Init Functions
*/
void    hdsApp::SetupMasterMenu (void)
{
    //  Setup the webmaster menu as a tree of C-Interface articles.
    //
    //  This creates the master page templates for viewing/managing the data model. Includes the forms and form handlers for creating/editing classes, class members and repositories.
    _hzfunc("hdsApp::SetupDataModel") ;
    const hdbMember*        pMbr ;      //  Class member
    const hdbDatatype*      pType ;     //  Data type (base class)
    const hdbClass*         pSub ;      //  Sub-class
    const hdbEnum*          pEnum ;     //  Data Enum
    const hdbClass*         pClass ;    //  Class
    const hdbObjRepos*      pRepos ;    //  Repository
    //hdsArticle*       pRoot ;         //  Tree item
    //hdsArticle*       pHead ;         //  Tree heading
    //hdsArticle*       pItem_E ;       //  Tree item "Enums"
    //hdsArticle*       pItem_C ;       //  Tree item "Classes"
    //hdsArticle*       pItem_R ;       //  Tree item "Repositories"
    //hdsArticle*       pItemCC ;       //  Tree item of current class
    hdsArticleCIF*  pArtCIF ;       //  Current article
    hzString        root ;          //  Header title
    hzString        H ;             //  Header ref name
    hzString        X ;             //  Subheader ref name
    hzString        Y ;             //  sub sub header ref name
    hzString        R ;             //  Article ref name
    hzString        T ;             //  Article title
    hzString        S ;             //  Temp string
    uint32_t        n ;             //  Loop iterator
    uint32_t        mbrNo ;         //  Member number
    char            namBuf[16] ;    //  Numeric based name
    if (!this)
        hzexit(E_SEQUENCE, "No application") ;
    //  Set name of hdsApp::m_DataModel to 'master'
    m_DataModel.m_Groupname = "master" ;
    //m_DataModel.m_USL.SetGroup(m_ArticleGroups.Count()) ;
    m_ArticleGroups.Insert(m_DataModel.m_Groupname, &m_DataModel) ;
    //  LOAD TREE
    root.Clear() ;
    H = "Master_Main_Menu" ;
    T = m_Appname ;
    m_DataModel.AddHead(root, H, T, true) ;
    root = H ;
    //  Add the menu items applicable at the Sphere level
    H = "SphereLevel" ;
    T = "Sphere Level Resources" ;
    m_DataModel.AddHead(root, H, T, true) ;
    R = "S.1" ; T = "Blacklisted IPs" ; pArtCIF = new hdsArticleCIF(R, T, &_masterBanned) ;     m_DataModel.AddItem(H, R, T, pArtCIF, false) ;  
    R = "S.2" ; T = "Memory Stats" ;    pArtCIF = new hdsArticleCIF(R, T, &_masterMemstat) ;    m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "S.3" ; T = "Domain names" ;    pArtCIF = new hdsArticleCIF(R, T, &_masterDomain) ;     m_DataModel.AddItem(H, R, T, pArtCIF, false) ;  
    R = "S.4" ; T = "Email addresses" ; pArtCIF = new hdsArticleCIF(R, T, &_masterEmaddr) ;     m_DataModel.AddItem(H, R, T, pArtCIF, false) ;  
    R = "S.5" ; T = "Strings (Fixed)" ; pArtCIF = new hdsArticleCIF(R, T, &_masterStrFix) ;     m_DataModel.AddItem(H, R, T, pArtCIF, false) ;  
    //  Add the resources and stats stuff
    H = "AppResoures" ;
    T = "Application Resources" ;
    m_DataModel.AddHead(root, H, T, true) ;
    R = "A.1" ; T = "Application Parameters" ;  pArtCIF = new hdsArticleCIF(R, T, &_masterAppParam) ;   m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "A.2" ; T = "Config Files" ;            pArtCIF = new hdsArticleCIF(R, T, &_masterCfgList) ;    m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "A.3" ; T = "Misc Fixed Files" ;        pArtCIF = new hdsArticleCIF(R, T, &_masterFileList) ;   m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "A.4" ; T = "Pages" ;                   pArtCIF = new hdsArticleCIF(R, T, &_masterResList) ;    m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "A.5" ; T = "Article Groups" ;          pArtCIF = new hdsArticleCIF(R, T, &_masterResList) ;    m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "A.6" ; T = "Site Visitors" ;           pArtCIF = new hdsArticleCIF(R, T, &_masterVisList) ;    m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    R = "A.7" ; T = "Strings (Standard)" ;      pArtCIF = new hdsArticleCIF(R, T, &_masterStrGen) ;     m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    //R = "A.8" ;   T = "USL Assignments" ;         pArtCIF = new hdsArticleCIF(R, T, &_masterUSL) ;        m_DataModel.AddItem(H, R, T, pArtCIF, false) ;
    //  Add the Data Object Model stuff
    H = "masterDOM" ;
    T = "Data Object Model" ;
    m_DataModel.AddHead(root, H, T, true) ;
    //  Any enums
    //if (m_ADP.CountDataEnum())        { R = "enum" ;  T = "Enums" ;           pItem_E = m_DataModel.AddHead(H, R, T, true) ; }
    //if (m_ADP.CountDataClass())       { R = "class" ; T = "Classes" ;         pItem_C = m_DataModel.AddHead(H, R, T, true) ; }
    //if (m_ADP.CountObjRepos())        { R = "repos" ; T = "Repositories" ;    pItem_R = m_DataModel.AddHead(H, R, T, true) ; }
    //  Enums
    if (m_ADP.CountDataEnum())
    {
        X = "enums" ;
        T = "Enums" ;
        m_DataModel.AddHead(H, X, T, true) ;
        threadLog("%d enums\n", m_ADP.CountDataEnum()) ;
        for (n = 0 ; n < m_ADP.CountDataEnum() ; n++)
        {
            pEnum = m_ADP.GetDataEnum(n) ;
            if (pEnum)
            {
                sprintf(namBuf, "E.%d", n+1) ;
                R = namBuf ;
                T = pEnum->strType() ;
                pArtCIF = new hdsArticleCIF(R, T, &_masterEnumEdit) ;
                pArtCIF->m_flagVE |= VE_ACTIVE ;
                m_DataModel.AddItem(X, R, T, pArtCIF, false) ;
            }
        }
    }
    //  Classes
    if (m_ADP.CountDataClass())
    {
        X = "class" ;
        T = "Classes" ;
        m_DataModel.AddHead(H, X, T, true) ;
        threadLog("%d classes\n", m_ADP.CountDataClass()) ;
        for (n = 0 ; n < m_ADP.CountDataClass() ; n++)
        {
            pClass = m_ADP.GetDataClass(n) ;
            if (pClass)
            {
                sprintf(namBuf, "C.%d", pClass->ClassId()) ;
                Y = namBuf ;
                T = pClass->strType() ;
                pArtCIF = new hdsArticleCIF(Y, T, &_masterClassEdit) ;
                pArtCIF->m_flagVE |= VE_ACTIVE ;
                m_DataModel.AddItem(X, Y, T, pArtCIF, false) ;
                for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; mbrNo++)
                {
                    pMbr = pClass->GetMember(mbrNo) ;
                    pType = pMbr->Datatype() ;
                    pSub = dynamic_cast<const hdbClass*>(pType) ;
                    if (pSub)
                        sprintf(namBuf, "C%d.S.%d",pClass->ClassId(), mbrNo) ;
                    else
                        sprintf(namBuf, "C%d.M.%d",pClass->ClassId(), mbrNo) ;
                    R = namBuf ;
                    T = pMbr->strName() ;
                    pArtCIF = new hdsArticleCIF(R, T, &_masterMbrEdit) ;
                    pArtCIF->m_flagVE |= VE_ACTIVE ;
                    m_DataModel.AddItem(Y, R, T, pArtCIF, false) ;
                }
            }
        }
    }
    //  Repositories
    if (m_ADP.CountObjRepos())
    {
        X = "repos" ;
        T = "Repositories" ;
        m_DataModel.AddHead(H, X, T, true) ;
        threadLog("%d repos\n", m_ADP.CountObjRepos()) ;
        for (n = 0 ; n < m_ADP.CountObjRepos() ; n++)
        {
            pRepos = m_ADP.GetObjRepos(n) ;
            if (pRepos)
            {
                sprintf(namBuf, "R.%d", n+1) ;
                R = namBuf ;
                T = pRepos->strName() ;
                pArtCIF = new hdsArticleCIF(R, T, &_masterReposEdit) ;
                pArtCIF->m_flagVE |= VE_ACTIVE ;
                m_DataModel.AddItem(X, R, T, pArtCIF, false) ;
            }
        }
    }
    //  CREATE PAGES and ARTICLES
    threadLog("done\n") ;
}
/*
**  Webmaster Display Functions
*/
hzEcode _masterLoginPage    (hzHttpEvent* pE)
{
    //  Display the master account login form
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("hdsApp::_masterLoginPage") ;
    hzChain     C ;         //  Page output
    hdsApp*     pApp ;      //  Application
    pApp = (hdsApp*) pE->m_pContextApp ;
    C << s_master_head_nav ;
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "   <tr height=\"50\"><td align=\"center\" class=\"title\">Dissemino Master Account Login</td></tr>\n"
    "</table>\n"
    "<form method=\"POST\" action=\"master\">\n"
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "   <tr><td height=\"20\"> </td></tr>\n"
    "   <tr><td align=\"center\">" << pApp->m_Domain << "</td></tr>\n"
    "   <tr><td height=\"60\"> </td></tr>\n"
    "</table>\n"
    "<table width=\"66%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "   <tr height=\"25\"><td>Username: </td><td><input type=\"text\" name=\"username_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "   <tr height=\"25\"><td>Password: </td><td><input type=\"password\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "</table>\n"
    "<table width=\"20%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
    "   <tr><td height=\"200\"> </td></tr>\n"
    "   <tr>\n"
    "       <td><input type=\"button\" onclick=\"location.href='/index.html';\" value=\"Abort\"/></td>\n"
    "       <td> </td>\n"
    "       <td align=\"right\"><input type=\"submit\" name=\"x-action\" value=\"Login\"></td>\n"
    "   </tr>\n"
    "</table>\n"
    "</form>\n"
    "</body>\n"
    "</html>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterProcAuth (hzHttpEvent* pE)
{
    //  Authenticate the master account login. If the supplied username and password is a match, display the master account main menu. If not redisplay the master account login
    //  page.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("hdsApp::_masterProcAuth") ;
    hdsApp*     pApp ;      //  Application
    hdsInfo*    pInfo ;     //  Session
    hzAtom      atom ;      //  For session value
    hzString    unam ;      //  Submitted username
    hzString    pass ;      //  Submitted password
    hzString    unamFld ;   //  Username field
    hzString    passFld ;   //  Password field
    hzSysID     newCookie ; //  For cookie generation
    hzIpaddr    ipa ;       //  Client IP address
    hzSDate     expires ;   //  For cookie expiry
    pApp = (hdsApp*) pE->m_pContextApp ;
    unamFld = "username_master" ;
    passFld = "password_master" ;
    unam = pE->m_mapStrings[unamFld] ;
    pass = pE->m_mapStrings[passFld] ;
    threadLog("Trying username (%s) password (%s)\n", *unam, *pass) ;
    threadLog("Trying username (%s) password (%s)\n", *pApp->m_MasterUser, *pApp->m_MasterPass) ;
    if (unam == pApp->m_MasterUser && pass == pApp->m_MasterPass)
    {
        threadLog("MASTER LOGIN SUCCESS!!!\n") ;
        pInfo = (hdsInfo*) pE->Session() ;
        if (!pInfo)
        {
            newCookie = pApp->MakeCookie(pE->ClientIP(), pE->EventNo()) ;
            expires.SysDate() ;
            expires += 365 ;
            pE->SetSessCookie(newCookie) ;
            pInfo = new hdsInfo() ;
            pE->SetSession(pInfo) ;
            pApp->m_pLog->Out("%s: new cookie %016x new info %p\n", *_fn, newCookie, pInfo) ;
            pApp->m_SessCookie.Insert(newCookie, pInfo) ;
        }
        pInfo->m_Access |= ACCESS_ADMIN ;
        atom = pE->Referer() ;
        pInfo->m_Sessvals.Insert(s_master_entry_pt, atom) ;
        return _masterMainMenu(pE) ;
    }
    return _masterLoginPage(pE) ;
}
hzEcode _masterMainMenu (hzHttpEvent* pE)
{
    //  Display the master list of config files for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterMainMenu") ;
    hzChain         C ;         //  Page output
    hdsApp*         pApp ;      //  Application
    //hdsArticle*       pRoot ;     //  Tree item
    //hdsArticle*       pItem_E ;   //  Tree item "Enums"
    hzString        refname ;   //  Reference name of tree item
    hzString        title ;     //  Title of tree item
    pApp = (hdsApp*) pE->m_pContextApp ;
    C << s_master_head_nav ;
    C << "\n<script language=\"javascript\">\n" ;
    C << _dsmScript_tog ;
    C << _dsmScript_loadArticle ;
    C << _dsmScript_navtree ;
    pApp->m_DataModel.ExportDataScript(C) ;
    C << "</script>\n\n" ;
    //  Create HTML table for listing
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "   <tr height=\"44\"><td align=\"center\" class=\"title\">Webmaster Control Panel</td></tr>\n"
    "</table>\n" ;
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "<tr>\n"
    "   <td width=\"100%\">\n"
    "   <table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
    "   <tr>\n"
    "       <td width=\"20%\" valign=\"top\" class=\"main\">\n"
    "       <div id=\"navlhs\" class=\"navtree\">\n"
    "           <script type=\"text/javascript\">makeTree('master');</script>\n"
    "       </div>\n"
    "       </td>\n"
    "       <td width=\"80%\" valign=\"top\">\n"
    "           <table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
    "           <tr><td align=\"center\" class=\"title\"><div id=\"articletitle\" class=\"objtitle\"></div></td></tr>\n"
    "           <tr><td valign=\"top\" class=\"main\"><div id=\"articlecontent\" class=\"objpage\"></div></td></tr>\n"
    "           </table>\n"
    "       </td>\n"
    "   </tr>\n"
    "   </table>\n"
    "   </td>\n"
    "</tr>\n"
    "</table>\n" ;
    C << 
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "   <tr height=\"44\"><td align=\"center\"><input type=\"button\" onclick=\"location.href='/masterLogout';\" value=\"Webmaster Account Exit\"/></td></tr>\n"
    "</table>\n"
    "</body>\n"
    "</html>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterLogout   (hzHttpEvent* pE)
{
    //  Log the user out from the master section. Then use a redirect to take user back to the 'master entry point' - the page they were on before entering the master section.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterLogout") ;
    hdsApp*     pApp ;      //  Application
    hdsInfo*    pInfo ;     //  Session
    hzAtom      atom ;      //  For session value
    pApp = (hdsApp*) pE->m_pContextApp ;
    pInfo = (hdsInfo*) pE->Session() ;
    atom = pInfo->m_Sessvals[s_master_entry_pt] ;
    pApp->m_SessCookie.Delete(pE->Cookie()) ;
    delete pInfo ;
    pE->SetSession(0) ;
    return pE->Redirect(atom.Str(), 0, false) ;
}
/*
**  C-Interface Article Functions
*/
hzEcode _masterAppParam (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the application parameters
    _hzfunc(__func__) ;
    hdsApp*         pApp ;      //  Application
    //  Set the article title
    pE->SetHdr(s_articleTitle, pArtCIF->m_Title) ;
    //  Set the article content
    pApp = (hdsApp*) pE->m_pContextApp ;
    C.Clear() ;
    C << "<table>\n" ;
    C.Printf("<tr><td>Name:</td>        <td>%s</td><tr>\n", *pApp->m_Appname) ;
    C.Printf("<tr><td>Docroot:</td>     <td>%s</td><tr>\n", *pApp->m_Docroot) ;
    C.Printf("<tr><td>Configs:</td>     <td>%s</td><tr>\n", *pApp->m_Configdir) ;
    C.Printf("<tr><td>Datadir:</td>     <td>%s</td><tr>\n", *pApp->m_Datadir) ;
    C.Printf("<tr><td>Logroot:</td>     <td>%s</td><tr>\n", *pApp->m_Logroot) ;
    C.Printf("<tr><td>Masterpath:</td>  <td>%s</td><tr>\n", *pApp->m_MasterPath) ;
    C.Printf("<tr><td>Masteruser:</td>  <td>%s</td><tr>\n", *pApp->m_MasterUser) ;
    C.Printf("<tr><td>Masterpass:</td>  <td>%s</td><tr>\n", *pApp->m_MasterPass) ;
    C.Printf("<tr><td>SmtpAddr:</td>    <td>%s</td><tr>\n", *pApp->m_SmtpAddr) ;
    C.Printf("<tr><td>SmtpUser:</td>    <td>%s</td><tr>\n", *pApp->m_SmtpUser) ;
    C.Printf("<tr><td>SmtpPass:</td>    <td>%s</td><tr>\n", *pApp->m_SmtpPass) ;
    C.Printf("<tr><td>UsernameFld:</td> <td>%s</td><tr>\n", *pApp->m_UsernameFld) ;
    C.Printf("<tr><td>UserpassFld:</td> <td>%s</td><tr>\n", *pApp->m_UserpassFld) ;
    C.Printf("<tr><td>Domain:</td>      <td>%s</td><tr>\n", *pApp->m_Domain) ;
    C.Printf("<tr><td>Loghits:</td>     <td>%s</td><tr>\n", *pApp->m_AllHits) ;
    C.Printf("<tr><td>Language:</td>    <td>%s</td><tr>\n", *pApp->m_DefaultLang) ;
    C.Printf("<tr><td>Login:</td>       <td>%s</td><tr>\n", pApp->m_OpFlags & DS_APP_SUBSCRIBERS ? "true":"false") ;
    C.Printf("<tr><td>Robot:</td>       <td>%s</td><tr>\n", pApp->m_OpFlags & DS_APP_ROBOT ? "true":"false") ;
    C.Printf("<tr><td>SiteGuide:</td>   <td>%s</td><tr>\n", pApp->m_OpFlags & DS_APP_GUIDE ? "true":"false") ;
    C.Printf("<tr><td>PortSTD:</td>     <td>%d</td><tr>\n", pApp->m_nPortSTD) ;
    C.Printf("<tr><td>PortSSL:</td>     <td>%d</td><tr>\n", pApp->m_nPortSSL) ;
    C << "</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterCfgList  (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master account list of config files for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("hdsApp::_masterCfgList") ;
    hdsApp*         pApp ;      //  Application
    hzChain         Z ;         //  XML Content of key resource (page/navbar/stylesheet etc)
    hzDirent        de ;        //  Config file info
    hzXDate         date ;      //  File date
    hzString        title ;     //  Article Title
    uint32_t        n ;         //  Loop iterator
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Set the article title
    C.Clear() ;
    C << "Config File Listing (" << FormalNumber(pApp->m_Configs.Count()) << " items)" ;
    title = C ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table width=\"100%\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"main\">\n"
    "<tr>\n"
    "   <th>Last Modified</th>\n"
    "   <th>Size</th>\n"
    "   <th>Config File Name</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 0 ; n < pApp->m_Configs.Count() ; n++)
    {
        /* FIX
        de = pApp->m_Configs.GetObj(n) ;
        date.SetByEpoch(de.Mtime()) ;
        C.Printf("<tr><td>%s</td><td>%s</td><td><a href=\"/masterAction?CfgEdit=%d\">%s</a></td></tr>\n", *date, FormalNumber(de.Size()), n, de.txtName()) ;
        */
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterResList  (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list of resources for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterResList") ;
    hdsApp*         pApp ;      //  Application
    hdsResource*    pRes ;      //  Resource pointer
    hzString        title ;     //  Article title
    uint32_t        n ;         //  Loop iterator
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Set the article title
    C.Clear() ;
    C << "Resource Listing (" << FormalNumber(pApp->m_ResourcesPath.Count()) << " items)" ;
    title = C ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<tr>\n"
    "   <th>Type</th>\n"
    "   <th>Title</th>\n"
    "   <th>URL</th>\n"
    "   <th>Hits</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 0 ; n < pApp->m_ResourcesPath.Count() ; n++)
    {
        pRes = pApp->m_ResourcesPath.GetObj(n) ;
        C << "<tr><td width=\"100\">" ;
        switch  (pRes->Whatami())
        {
        case DS_RES_PAGE:       C << "PAGE</td>" ;      break ;
        case DS_RES_ARTICLE:    C << "ARTICLE</td>" ;   break ;
        case DS_RES_FILE:       C << "FILE</td>" ;      break ;
        case DS_RES_PAGE_CIF:   C << "PAGE_CIF</td>" ;  break ;
        }
        //  If the resource is a page, give link to page editor
        if (pRes->Whatami() == DS_RES_PAGE)
            C.Printf("<td width=\"240\"><a href='#' onclick=\"loadArticle('/masterAction?editfile=%s');\">%s</a></td><td width=\"320\">%s</td>",
                *pRes->m_Url, *pRes->m_Title, *pRes->m_Url) ;
        else
            C.Printf("<td width=\"240\">%s</td><td width=\"320\">%s</td>", *pRes->m_Title, *pRes->m_Url) ;
        C.Printf("<td align=\"right\" width=\"140\">%s</td></tr>\n", FormalNumber(pRes->m_HitCount)) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterDomain   (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list of resources for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterDomain") ;
    const char* i ;     //  String value
    hzString    S ;     //  Temp string
    uint32_t    n ;     //  Loop iterator
    //  Set the article title
    C.Clear() ;
    C << "Known Domains (" << FormalNumber(_hzGlobal_setDomains.Count()) << " items)" ;
    S = C ;
    pE->SetHdr(s_articleTitle, S) ;
    //  Set the article content
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>String Number</th>\n"
    "   <th>String Value</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 1 ; n <= _hzGlobal_setDomains.Count() ; n++)
    {
        S = FormalNumber(n) ;
        i = _hzGlobal_setDomains.GetObj(n) ;
        if (i)
            C.Printf("\t<tr><td align=\"right\">%s</td><td>%s</td></tr>\n", *S, i) ;
        else
            C.Printf("\t<tr><td align=\"right\">%s</td><td>NULL</td></tr>\n", *S) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterEmaddr   (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list of resources for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterEmaddr") ;
    hzString    S ;     //  Temp string
    hzEmaddr    ema ;   //  Email address
    uint32_t    n ;     //  Loop iterator
    //  Set the article title
    C.Clear() ;
    C << "Known Email Addresses (" << FormalNumber(_hzGlobal_setEmaddrs.Count()) << " items)" ;
    S = C ;
    pE->SetHdr(s_articleTitle, S) ;
    //  Set the article content
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>String Number</th>\n"
    "   <th>String Value</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 1 ; n <= _hzGlobal_setEmaddrs.Count() ; n++)
    {
        S = FormalNumber(n) ;
        ema = _hzGlobal_setEmaddrs.GetObj(n - 1) ;
        if (ema)
            C.Printf("\t<tr><td align=\"right\">%s</td><td>%s</td></tr>\n", *S, *ema) ;
        else
            C.Printf("\t<tr><td align=\"right\">%s</td><td>NULL</td></tr>\n", *S) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterStrFix   (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list of resources for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterStrList") ;
    const char* i ;     //  String value
    hzString    S ;     //  Temp string
    uint32_t    n ;     //  Loop iterator
    //  Set the article title
    C.Clear() ;
    C << "Fixed String List (" << FormalNumber(_hzGlobal_setStrings.Count()) << " items)" ;
    S = C ;
    pE->SetHdr(s_articleTitle, S) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>String Number</th>\n"
    "   <th>String Value</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 1 ; n <= _hzGlobal_setStrings.Count() ; n++)
    {
        S = FormalNumber(n) ;
        i = _hzGlobal_setStrings.GetObj(n) ;
        if (i)
            C.Printf("\t<tr><td>%s</td><td>%s</td></tr>\n", *S, i) ;
        else
            C.Printf("\t<tr><td>%s</td><td>NULL</td></tr>\n", *S) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterStrGen   (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the current set of hzString instances.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterStrGen") ;
    hzString        title ;     //  Article title
    //  Set the article title
    title = "Dynamic String Allocations" ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>Size</th>\n"
    "   <th>Population</th>\n"
    "   <th>Free</th>\n"
    "   <th>Memory Used</th></tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    ReportStringAllocations(C, true) ;
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterVisList  (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list of resources for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterVisList") ;
    hdsApp*         pApp ;      //  Application
    hdsProfile*     pProf ;     //  Visitor profile pointer
    hzString        title ;     //  Article title
    uint32_t        n ;         //  Loop iterator
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Set the article title
    C.Clear() ;
    C << "Dissemino Hit List (" << FormalNumber(pApp->m_Visitors.Count()) << " visitors)" ;
    title = C ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>IP addr</th>\n"
    "   <th>Locn</th>\n"
    "   <th>Robot</th>\n"
    "   <th>Favicon</th>\n"
    "   <th>Articles</th>\n"
    "   <th>Pages</th>\n"
    "   <th>Scripts</th>\n"
    "   <th>Images</th>\n"
    "   <th>Special</th>\n"
    "   <th>Fixed</th>\n"
    "   <th>Posts</th>\n"
    "   <th>Fail-G</th>\n"
    "   <th>Fail-P</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 0 ; n < pApp->m_Visitors.Count() ; n++)
    {
        pProf = pApp->m_Visitors.GetObj(n) ;
        C << "<tr align=\"right\">" ;
        C.Printf("<td align=\"left\">%s</td>", pProf->m_addr.Full()) ;
        C.Printf("<td align=\"left\">%s</td>", GetIpLocation(pProf->m_addr)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_robot)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_favicon)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_art)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_page)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_scr)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_img)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_spec)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_fix)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_post)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_G404)) ;
        C.Printf("<td>%s</td>", FormalNumber(pProf->m_P404)) ;
        C << "</tr>\n" ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterBanned   (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display IP blacklist and number of attempts by each banned IP
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterBanned") ;
    hzMapM<uint16_t,hzIpaddr>   mapTmp ;    //  Temp map, in order of number of attempts
    hzIpinfo    ipi ;       //  IP address block note
    hzIpaddr    ipa ;       //  Blacklisted IP
    hzString    title ;     //  Article title
    uint32_t    n ;         //  Loop iterator
    uint32_t    nAtt ;      //  Number of attempts
    hzEcode     rc ;        //  Return code
    //  Re-order blacklist by number of attempts
    for (n = 0 ; n < _hzGlobal_StatusIP.Count() ; n++)
    {
        ipa = _hzGlobal_StatusIP.GetKey(n) ;
        ipi = _hzGlobal_StatusIP.GetObj(n) ;
        mapTmp.Insert(ipi.m_nSince, ipa) ;
    }
    //  Set the article title
    C.Clear() ;
    C << "Blacklisted IP Addresses (" << FormalNumber(mapTmp.Count()) << " items)" ;
    title = C ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>IP Address</th>\n"
    "   <th>No Attempts</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = mapTmp.Count()-1 ; n ; n--)
    {
        nAtt = mapTmp.GetKey(n) ;
        ipa = mapTmp.GetObj(n) ;
        C.Printf("<tr><td>%s</td><td align=\"right\">%s</td></tr>\n", ipa.Full(), FormalNumber(nAtt)) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterMemstat  (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    _hzfunc("_masterMemstat") ;
    //  Set the article title
    pE->SetHdr(s_articleTitle, pArtCIF->m_Title) ;
    //  Set the article content
    C.Clear() ;
    ReportMemoryUsage(C, true) ;
    //C <<
    //"<center><td align=\"center\"><input type=\"button\" onclick=\"location.href='/masterMainMenu';\" value=\"Main Menu\"/></center>\n"
    //"</body>\n"
    //"</html>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
#if 0
hzEcode _masterUSL  (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list of resources for the current website. These may be selected for editing.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterVisList") ;
    hdsApp*         pApp ;      //  Application
    hdsUSL          usl ;       //  USL
    hzString        str ;       //  String value
    uint32_t        n ;         //  Loop iterator
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Set the article title
    C.Clear() ;
    C << "USL Assignements (" << FormalNumber(pApp->m_pDfltLang->m_MiscStrings.Count()) << " items)" ;
    str = C ;
    pE->SetHdr(s_articleTitle, str) ;
    //  Create HTML table for listing
    C.Clear() ;
    C <<
    "<table id=\"theTable\" align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th>Order</th>\n"
    "   <th>USL</th>\n"
    "   <th>String Value</th>\n"
    "</tr>\n"
    "</thead>\n"
    "<tbody>\n" ;
    for (n = 0 ; n < pApp->m_pDfltLang->m_MiscStrings.Count() ; n++)
    {
        //usl = pApp->m_pDfltLang->m_MiscStrings.GetKey(n) ;
        str = pApp->m_pDfltLang->m_MiscStrings.GetObj(n) ;
        if (str)
            //C.Printf("\t<tr><td align=\"right\">%u</td><td>%s</td><td>%s</td></tr>\n", n, *usl, *str) ;
            C.Printf("\t<tr><td align=\"right\">%u</td><td>%u</td><td>%s</td></tr>\n", n, n+1, *str) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
#endif
hzEcode _masterFileList (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  Display the master's list files for the current website that may be selected for editing. These must not be data files (viewable under a separate
    //  master menu item) and nor must the be config files (editable under a separate master menu item), but they can be text files for any other purpose.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterFileList") ;
    hdsApp*         pApp ;      //  Application
    hzChain         Z ;         //  XML Content of key resource (page/navbar/stylesheet etc)
    hzDirent        de ;        //  Directory entry
    hzString        title ;     //  Article title
    hzXDate         date ;      //  File date (Mtime)
    uint32_t        n ;         //  Loop iterator
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Set the article title
    C.Clear() ;
    C.Printf("Misc Files (%d items)", pApp->m_Misc.Count()) ;
    title = C ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    C.Clear() ;
    C <<
    "<table align=\"center\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\" class=\"fixVP\">\n"
    "<thead>\n"
    "<tr>\n"
    "   <th width=180>Date</th>\n"
    "   <th width=150>Size</th>\n"
    "   <th width=350>Dir</th>\n"
    "   <th width=350>Filename</th>\n"
    "</tr>\n" 
    "<thead>\n"
    "<tbody>\n" ;
    for (n = 0 ; n < pApp->m_Misc.Count() ; n++)
    {
        de = pApp->m_Misc.GetObj(n) ;
        date.SetByEpoch(de.Mtime()) ;
        C.Printf("<tr><td width=180>%s</td><td width=150>%s</td><td width=350>%s</td><td width=350><a href=\"/masterFileEdit-%d\">%s</a></td></tr>\n",
            *date, FormalNumber(de.Size()), *de.Path(), n, de.txtName()) ;
    }
    C << "</tbody>\n</table>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterFileEdit (hzHttpEvent* pE)
{
    //  Display the master's edit resource page. This essentially comprises a large text box and a submit button. The text box is populated with the XML
    //  for the resource if it exists. Note that the argument is numeric and identifies the object (directory entry) in the m_Misc map.
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("_masterFileEdit") ;
    ifstream        is ;        //  For reading file in
    hzChain         C ;         //  Page output
    hzChain         Z ;         //  File content
    hzMD5           md5 ;       //  MD5 for the old content
    hzDirent        de ;        //  Directory entry
    hdsApp*         pApp ;      //  Application
    const char*     pArg ;      //  File identifier
    hzString        fpath ;     //  File path of original
    hzString        fnew ;      //  File path plus .new
    uint32_t        posn ;      //  Posn of file in m_Misc
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    pArg = pE->GetResource() ;
    pArg += 16 ;
    if (pArg[0] && IsPosint(posn, pArg))
    {
        de = pApp->m_Misc.GetObj(posn) ;
        fpath = de.Path() + "/" + de.strName() ;
        fnew = fpath + ".new" ;
        rc = TestFile(*fnew) ;
        if (rc == E_OK)
            fpath = fnew ;
        is.open(*fpath) ;
        if (is.fail())
            Z << "Error: Cannot Open File" << fpath ;
        else
        {
            Z << is ;
            is.close() ;
        }
    }
    //  Set the article title
    C.Clear() ;
    if (!de.txtName())
        fpath = "Dissemino Misc Files Editor: Untitled" ;
    else
    {
        C.Printf("Dissemino Misc Files Editor: %s", *fnew) ;
        fpath = C ;
    }
    fpath = C ;
    pE->SetHdr(s_articleTitle, fpath) ;
    //  Set the article content
    C << "<form method=\"POST\" action=\"masterFileEditHdl\">\n" ;
    if (fnew)
        C.Printf("<input type=\"hidden\" name=\"fname\" value=\"%s\"/>\n", *fnew) ;
    else
        C.Printf("Filename: <input type=\"text\" name=\"fname\"/>\n") ;
    C <<
    "<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "<tr>\n"
    "\t<td>\n"
    "\t<textarea name=\"pgData\" rows=\"40\" cols=\"200\" class=\"main\">" ;
    if (Z.Size())
        C << Z ;
    C << "</textarea>\n" ;
    C <<
    "\t</td>\n"
    "</tr>\n"
    "</table>\n"
    "<table width=\"20%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "<tr>\n"
    "<td align=\"left\"><input type=\"button\" onclick=\"location.href='/masterFileList';\" value=\"Abort Edit\"/></td>\n"
    "<td align=\"rigth\"><input type=\"submit\" name=\"x-action\" value=\"Submit\"></td>\n"
    "</tr>\n"
    "</table>\n"
    "</form>\n"
    "</body>\n"
    "</html>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterFileEditHdl  (hzHttpEvent* pE)
{
    //  This just saves the file
    ofstream    os ;        //  Output file
    hzChain     C ;         //  Page output
    hzChain     Z ;         //  File content
    hzString    val ;       //  The submitted textarea
    hzString    S ;         //  Temp value
    hzString    fnew ;      //  File path plus .new
    hzEcode     rc ;        //  Return code
    _hzfunc("_masterFileEditHdl") ;
    //  Get file name
    S = "fname" ;
    fnew = pE->m_mapStrings[S] ;
    //  Get new file data
    S = "pgData" ;
    val = pE->m_mapStrings[S] ;
    Z = val ;
    //  Write data to file.new
    fnew += ".new" ;
    os.open(*fnew) ;
    if (os.fail())
        threadLog("Open file failed (%s)\n", *fnew) ;
    else
    {
        os << Z ;
        os.close() ;
    }
    //  CHECK THIS
    C << "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n" ;
    if (!fnew)
        C << "\t<tr height=\"50\"><td align=\"center\" class=\"title\">Dissemino Misc Files Editor: Untitled</td></tr>\n" ;
    else
        C.Printf("\t<tr height=\"50\"><td align=\"center\" class=\"title\">Dissemino Misc Files Editor: %s</td></tr>\n", *fnew) ;
    C << "</table>\n" ;
    C << "<form method=\"POST\" action=\"masterFileEditHdl\">\n" ;
    if (fnew)
        C.Printf("<input type=\"hidden\" name=\"fname\" value=\"%s\"/>\n", *fnew) ;
    else
        C.Printf("Filename: <input type=\"text\" name=\"fname\"/>\n") ;
    C <<
    "<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "<tr>\n"
    "\t<td>\n"
    "\t<textarea name=\"pgData\" rows=\"40\" cols=\"200\" class=\"main\">" ;
    if (Z.Size())
        C << Z ;
    C << "</textarea>\n" ;
    C <<
    "\t</td>\n"
    "</tr>\n"
    "</table>\n"
    "<table width=\"20%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "<tr>\n"
    "<td align=\"left\"><input type=\"button\" onclick=\"location.href='/masterFileList';\" value=\"Back to files\"/></td>\n"
    "<td align=\"rigth\"><input type=\"submit\" name=\"x-action\" value=\"Commit changes\"></td>\n"
    "</tr>\n"
    "</table>\n"
    "</form>\n"
    "</body>\n"
    "</html>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterFldSpecs (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterEnumEdit (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    _hzfunc(__func__) ;
    const hdbEnum*  pEnum = 0 ;     //  Data Enum
    hdsApp*         pApp ;          //  Application
    hdsInfo*        pInfo ;         //  Session
    const char*     pRes ;          //  Requested resource
    const char*     i ;             //  Requested resource iterator
    hzString        S ;             //  Temp string
    //hzString      entName ;       //  Name of enum/class/member/repos
    uint32_t        numId ;         //  Numeric id
    uint32_t        n ;             //  Loop iterator
    //  Enum is obtained by decoding the resoure query component
    pInfo = (hdsInfo*) pE->Session() ;
    if (!pInfo)
    {
        threadLog("Forbiden\n") ;
        pE->SendAjaxResult(HTTPMSG_FORBIDDEN, "No Access") ;
        return E_OK ;
    }
    pApp = (hdsApp*) pE->m_pContextApp ;
    pRes = pE->GetResource() ;
    pApp->m_pLog->Out("%s: Called with res = [%s] cif = [%s]\n", *_fn, pRes, *pArtCIF->m_Title) ;
    //  Locate Enum
    i = *pArtCIF->m_Title ;
    if (IsPosint(numId, i + 2))
        pEnum = pApp->m_ADP.GetDataEnum(numId-1) ;
    //  if (pEnum)
    //      entName = pEnum->txtType() ;
    //  Set the article title
    pE->SetHdr(s_articleTitle, pArtCIF->m_Title) ;
    //  Set the article content
    C.Clear() ;
    if (!pEnum)
        C << "<p>No Enum found</p>\n" ;
    else
    {
        C <<
        "<table width=\"100%\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
        "<tr>\n"
        "   <th>Name</th>\n"
        "   <th>Value</th>\n"
        "</tr>\n" ;
        for (n = 0 ; n < pEnum->Count() ; n++)
        {
            S = pEnum->GetStr(n) ;
            C.Printf("<tr><td>%d</td><td>%s</td></tr>\n", n+1, *S) ;
        }
        C << "</table>\n" ;
    }
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterClassEdit    (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    _hzfunc(__func__) ;
    const hdbClass*     pClass = 0 ;    //  Class
    const hdbMember*    pMbr = 0 ;      //  Class member
    hdsApp*             pApp ;          //  Application
    hdsInfo*            pInfo ;         //  Session
    const char*         i ;             //  Requested class
    hzString            title ;         //  Name of class
    uint32_t            mbrNo ;         //  Member number
    uint32_t            numId ;         //  Numeric id
    //uint32_t          n ;             //  Loop iterator
    pInfo = (hdsInfo*) pE->Session() ;
    if (!pInfo)
    {
        threadLog("Forbiden\n") ;
        pE->SendAjaxResult(HTTPMSG_FORBIDDEN, "No Access") ;
        return E_OK ;
    }
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Obtain the class in question
    i = *pArtCIF->m_Title ;
    if (IsPosint(numId, i + 2))
        pClass = pApp->m_ADP.GetDataClass(numId) ;
    //  Set the article title
    C.Clear() ;
    if (pClass)
        C << "Editing Data Class " << pClass->txtType() ;
    else
        C << "Create New Data Class" ;
    title = C ;
    pE->SetHdr(s_articleTitle, title) ;
    //  Set the article content
    if (pClass)
    {
        C.Clear() ;
        C <<
        "<table width=\"100%\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
        "<tr>\n"
        "   <th>Posn</th>\n"
        "   <th>Name</th>\n"
        "   <th>Data type</th>\n"
        "   <th>Pop Control</th>\n"
        "   <th>Description</th>\n"
        "</tr>\n" ;
        for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; mbrNo++)
        {
            pMbr = pClass->GetMember(mbrNo) ;
            C << "<tr>" ;
            C.Printf("<td align=\"right\">%d</td>", mbrNo) ;
            C.Printf("<td><a href=\"#\" onclick=\"loadArticle('/master_dme-M.%d.%d');\">%s</a></td>", pClass->ClassId(), mbrNo, pMbr->txtName()) ;
            C.Printf("<td>%s</td>", pMbr->Datatype()->txtType()) ;
            C.Printf("<td>%s</td>", PopCtl2Txt(pMbr->PopCtl())) ;
            C.Printf("<td>%s</td>", pMbr->txtDesc()) ;
            C << "</tr>" ;
        }
        C << "</table>\n" ;
    }
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterMbrEdit  (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    _hzfunc(__func__) ;
    const hdbClass*     pClass = 0 ;    //  Class
    const hdbMember*    pMbr = 0 ;      //  Class member
    //  Set the article title
    pE->SetHdr(s_articleTitle, pArtCIF->m_Title) ;
    //  Set the article content
    C.Clear() ;
    if (pMbr)
        C.Printf("<tr><td align=\"center\">%s::%s</td></tr>\n", pClass->txtType(), pMbr->txtName()) ;
    else
        C << "<tr><td align=\"center\">Class Member Editor</td></tr>\n" ;
    C <<
    "<tr><td height=\"60\"> </td></tr>\n"
    "</table>\n"
    "<table width=\"66%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
    C <<
    "<tr height=\"25\"><td>Name: </td><td><input type=\"text\"   name=\"username_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "<tr height=\"25\"><td>Type: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "<tr height=\"25\"><td>Min Value: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "<tr height=\"25\"><td>Max Value: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "<tr height=\"25\"><td>Compusory: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
    "<tr height=\"25\"><td>Multiple: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterReposEdit    (hzChain& C, hdsArticleCIF* pArtCIF, hzHttpEvent* pE)
{
    //  MUST DO THIS!
    _hzfunc(__func__) ;
    //  Set the article title
    pE->SetHdr(s_articleTitle, pArtCIF->m_Title) ;
    //  Set the article content
    //const hdbObjRepos*    pRepos = 0 ;    //  Repository
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
void    hdsApp::MasterArticle   (hzHttpEvent* pE)
{
    //  Serve an master article. These are articles in that a partial page is served by AJAX but they are an in-built feature of Dissemino rather than defined as
    //  a part of the application configs. At present the only example of AJAX in the master section is the Data Model Editing regime. Here, there is a host page
    //  and a navigation tree listing all data enums, classes and class members, and repositories in the current application data model. An article containing a
    //  form exists for each of the following:-
    //
    //  1)  Addition of a new enum or displaying/editing an existing enum. The enum values are listed in a table with an blank row and an add button for adding
    //      new values. The values have a checkbox for setting them active/inactive and there is a checkbox for setting the enum as a whole to active/inactive.
    //      The URL of the blank article/form for adding a new enum is /master_dme_enum with no further arguments. This will be extended by -enum_name for an
    //      existing enum.
    //
    //  2)  Addition of a new class or displaying/editing an existing class. The class members are listed in a table showing the data type and other parameters.
    //      The table cannot be edited in-situ. The class members, which can themselves be classes, are edited by clicking on the member name. The URL of the
    //      blank article/form for adding a new class is /master_dme_class and for an existing class this will be extended by -N where N is the class number.
    //
    //  3)  Addition of a new class member or the editing of an existing one. This included the ability to set the member ative/inactive. The URL of the blank
    //      is /master_dme_mbr and for an existing member this will be extended by -N where N is the member number. Note it is not necessary to have a class id and
    //      a member id.
    //
    //  4)  Addition of a new respository or the editing of an existing one. This must name a single host class.
    _hzfunc("hdsApp::MasterArticle") ;
    const hdbEnum*      pEnum = 0 ;     //  Data Enum
    const hdbClass*     pClass = 0 ;    //  Class
    const hdbMember*    pMbr = 0 ;      //  Class member
    const hdbObjRepos*  pRepos = 0 ;    //  Repository
    hzChain         Z ;                 //  For building partial HTML output
    hdsInfo*        pInfo ;             //  Session
    const char*     pRes ;              //  Requested resource
    const char*     i ;                 //  Requested resource iterator
    hzString        entName ;           //  Name of enum/class/member/repos
    hzString        argA ;              //  1st argument (e.g. lang subdir or article group name)
    hzString        argB ;              //  2nd argument (e.g. article name)
    hzString        S ;                 //  Temp string
    uint32_t        numId ;             //  Numeric id
    uint32_t        mbrNo ;             //  Member number
    uint32_t        n ;                 //  Loop iterator
    pInfo = (hdsInfo*) pE->Session() ;
    //  No HTTP session?
    if (!pInfo)
    {
        threadLog("Forbiden\n") ;
        pE->SendAjaxResult(HTTPMSG_FORBIDDEN, "No Access") ;
        return ;
    }
    pRes = pE->GetResource() ;
    if (memcmp(pRes, "/masterAction", 13))
    {
        //  Not a web-master option so illegal
        threadLog("No such path\n") ;
        pE->SendAjaxResult(HTTPMSG_NOTFOUND, "No such path") ;
        return ;
    }
    if (!pE->QueryLen())
    {
        threadLog("Called with no arguments\n") ;
        pE->SendAjaxResult(HTTPMSG_NOTFOUND, "No such path") ;
        return ;
    }
    argA = pE->m_mapStrings.GetKey(0) ;
    argB = pE->m_mapStrings.GetObj(0) ;
    if (argA == "editfile")
    {
        _masterCfgEdit(pE, argB) ;
        return ;
    }
    //  This may be a webmaster command unrelated to the data object model
    //  if (!memcmp(pRes + 8, "A.0", 4))    { _masterCfgList(pE) ;  return ; }
    //  if (!memcmp(pRes + 8, "A.1", 4))    { _masterResList(pE) ;  return ; }
    //  if (!memcmp(pRes + 8, "A.2", 4))    { _masterVisList(pE) ;  return ; }
    //  if (!memcmp(pRes + 8, "A.3", 4))    { _masterDomain(pE) ;   return ; }
    //  if (!memcmp(pRes + 8, "A.4", 4))    { _masterEmaddr(pE) ;   return ; }
    //  if (!memcmp(pRes + 8, "A.5", 4))    { _masterStrFix(pE) ;   return ; }
    //  if (!memcmp(pRes + 8, "A.6", 4))    { _masterStrGen(pE) ;   return ; }
    //  if (!memcmp(pRes + 8, "A.7", 4))    { _masterBanned(pE) ;   return ; }
    //  if (!memcmp(pRes + 8, "A.8", 4))    { _masterMemstat(pE) ;  return ; }
    //  if (!memcmp(pRes + 8, "A.9", 4))    { _masterUSL(pE) ;      return ; }
    //  if (!memcmp(pRes + 8, "A.10", 5))   { _masterFileList(pE) ; return ; }
    //  if (!memcmp(pRes + 8, "A.11", 5))   { _masterFileEdit(pE) ; return ; }
    //  URL not of the form /master_dme- ?
    if (memcmp(pRes, "/master_dme-", 12))
    {
        threadLog("No such path\n") ;
        pE->SendAjaxResult(HTTPMSG_NOTFOUND, "No such path") ;
        return ;
    }
    //  Invalid URL?
    if (pRes[12] != 'E' && pRes[12] != 'C' && pRes[12] != 'M' && pRes[12] != 'R')
    {
        threadLog("No such path\n") ;
        pE->SendAjaxResult(HTTPMSG_NOTFOUND, "No such path") ;
        return ;
    }
    //  DERIVE ENTITY. A is general article (not related to Data Object Model), E is enum, C is class, M is class member, R is repository.
    if (pRes[13] == CHAR_PERIOD)
    {
        switch  (pRes[12])
        {
        case 'E':   if (IsPosint(numId, pRes + 14))
                        //pEnum = m_ADP.mapEnums.GetObj(numId) ;
                        pEnum = m_ADP.GetDataEnum(numId) ;
                    if (pEnum)
                        entName = pEnum->txtType() ;
                    break ;
        case 'C':   if (IsPosint(numId, pRes + 14))
                    {
                        for (n = 0 ; n < m_ADP.CountDataClass() ; n++)
                        {
                            pClass = m_ADP.GetDataClass(n) ;
                            if (pClass->ClassId() == numId)
                            {
                                entName = pClass->txtType() ;
                                break ;
                            }
                        }
                        if (!entName)
                            pClass = 0 ;
                    }
        case 'M':   if (IsPosint(numId, pRes + 14))
                    {
                        for (n = 0 ; n < m_ADP.CountDataClass() ; n++)
                        {
                            pClass = m_ADP.GetDataClass(n) ;
                            if (pClass->ClassId() == numId)
                            {
                                entName = pClass->txtType() ;
                                break ;
                            }
                        }
                        if (!entName)
                            pClass = 0 ;
                        //pClass = m_ADP.mapClasses.GetObj(numId-1) ;
                        if (pClass)
                        {
                            for (i = pRes + 14 ; *i && *i != CHAR_PERIOD ; i++) ;
                            if (*i == CHAR_PERIOD)
                            {
                                if (IsPosint(numId, i+1))
                                    pMbr = pClass->GetMember(numId) ;
                            }
                        }
                    }
                    if (pMbr)
                        { entName = pClass->txtType() ; entName += "::" ; entName += pMbr->strName() ; }
        case 'R':   if (IsPosint(numId, pRes + 14))
                        pRepos = m_ADP.GetObjRepos(numId) ;
                    if (pRepos)
                        entName = pRepos->strName() ;
        }
    }
    //  CREATE HTML
    Z.Printf("<form method=\"POST\" action=\"%s\">\n", pRes) ;
    Z << "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
    //  Deal with non-data model requests
    switch  (pRes[9])
    {
    case 'E':
        Z <<
        "<tr><td align=\"center\">Enum Editor</td></tr>\n"
        "<tr><td height=\"20\"> </td></tr>\n"
        "</table>\n"
        "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
        Z.Printf("<tr height=\"25\"><td>Name: </td><td><input type=\"text\" name=\"username_master\" size=\"40\" maxlen=\"40\" value=\"%s\"/></td></tr>\n", *entName) ;
        Z <<
        "</table>\n"
        "<table width=\"100%\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
        for (n = 0 ; n < pEnum->Count() ; n++)
        {
            S = pEnum->GetStr(n) ;
            Z.Printf("<tr><td>%d</td><td>%s</td></tr>\n", n+1, *S) ;
        }
        break ;
    case 'C':
        Z <<
        "<tr><td align=\"center\">Class Editor</td></tr>\n"
        "<tr><td height=\"20\"> </td></tr>\n"
        "</table>\n"
        "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
        Z.Printf("<tr height=\"25\"><td>Name: </td><td><input type=\"text\" name=\"username_master\" size=\"40\" maxlen=\"40\" value=\"%s\"/></td></tr>\n", *entName) ;
        Z <<
        "</table>\n"
        "<table width=\"100%\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
        "<tr>\n"
        "   <th>Posn</th>\n"
        "   <th>Name</th>\n"
        "   <th>Data type</th>\n"
        "   <th>MinP</th>\n"
        "   <th>MaxP</th>\n"
        "   <th>Description</th>\n"
        "</tr>\n" ;
        for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; mbrNo++)
        {
            pMbr = pClass->GetMember(mbrNo) ;
            Z << "<tr>" ;
            Z.Printf("<td align=\"right\">%d</td>", mbrNo) ;
            Z.Printf("<td><a href=\"#\" onclick=\"loadArticle('/master_dme-M.%d.%d');\">%s</a></td>", pClass->ClassId(), mbrNo, pMbr->txtName()) ;
            Z.Printf("<td>%s</td>", pMbr->Datatype()->txtType()) ;
            Z.Printf("<td>%s</td>", PopCtl2Txt(pMbr->PopCtl())) ;
            Z.Printf("<td>%s</td>", pMbr->txtDesc()) ;
            Z << "</tr>" ;
        }
        break ;
    case 'M':
    
        if (pMbr)
            Z.Printf("<tr><td align=\"center\">%s::%s</td></tr>\n", pClass->txtType(), pMbr->txtName()) ;
        else
            Z << "<tr><td align=\"center\">Class Member Editor</td></tr>\n" ;
        Z <<
        "<tr><td height=\"60\"> </td></tr>\n"
        "</table>\n"
        "<table width=\"66%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
        Z <<
        "<tr height=\"25\"><td>Name: </td><td><input type=\"text\"   name=\"username_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
        "<tr height=\"25\"><td>Type: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
        "<tr height=\"25\"><td>Min Value: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
        "<tr height=\"25\"><td>Max Value: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
        "<tr height=\"25\"><td>Compusory: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n"
        "<tr height=\"25\"><td>Multiple: </td><td><input type=\"select\" name=\"password_master\" size=\"40\" maxlen=\"40\"/></td></tr>\n" ;
        break ;
    case 'R':   if (pRes[10] == CHAR_PERIOD)
                {
                    if (IsPosint(numId, pRes + 11))
                        pRepos = m_ADP.GetObjRepos(numId) ;
                }
                break ;
    }
    Z <<
    "</table>\n"
    "</form>\n" ;
    //  Send result
    if (entName)
        pE->SetHdr("x-title", entName) ;
    else
        pE->SetHdr("x-title", pRes) ;
    pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, Z, 0, false) ;
}
#if 0
hzEcode _masterDataModel    (hzHttpEvent* pE)
{
    //  Display the currently applicable data model as a tree of classes, class members and repositories.
    //
    //  The display comprises a tree on the LHS and a form on the RHS. The root node is automatically named after the application and is displayed as open. This
    //  will have a visible subnode of 'Classes' and if any classes have been defined, another of "Repositories". These will display the '+' expansion symbol if
    //  there are clases defined and/or repositories declared. Clinking on the subnode name will produce on the RHS, a blank form to create a new class or add a
    //  repostory.
    //
    //  Under 'Classes' the tree uses the folder symbol to represent classes and the document symbol to represent class members. The class entries link to a form which
    //  Under "Repositories" the folder 
    //
    //  Argument:   pE  The HTTP event
    //
    //  Returns:    None
    _hzfunc("hdsApp::_masterDataModel") ;
    hdsNavtree      T ;             //  Temp tree for object model display
    hzChain         Z ;             //  XML Content of key resource (page/navbar/stylesheet etc)
    hzChain         C ;             //  Page output
    hzDirent        de ;            //  Config file info
    hdsApp*         pApp ;          //  Application
    hzXDate         date ;          //  File date
    hzString        editorCls ;     //  Class editor base URL
    hzString        editorMbr ;     //  Class member editor base URL
    hzString        S ;             //  Temp string
    hzEcode         rc ;            //  Return code
    //  BUILD HTML
    pApp = (hdsApp*) pE->m_pContextApp ;
    C << s_master_head_std ;
    C << "\n<script language=\"javascript\">\n" ;
    C << _dsmScript_tog ;
    C << _dsmScript_loadArticle ;
    C << _dsmScript_navtree ;
    pApp->m_DataModel.ExportDataScript(C) ;
    C << "</script>\n\n" ;
    //  Create HTML table for listing
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "   <tr height=\"50\"><td align=\"center\" class=\"title\">Object Model Editor</td></tr>\n"
    "</table>\n" ;
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
    "<tr>\n"
    "   <td width=\"100%\">\n"
    "   <table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
    "   <tr>\n"
    "       <td width=\"20%\" valign=\"top\">\n"
    "       <div id=\"navlhs\" class=\"navtree\">\n"
    "       <script type=\"text/javascript\">makeTree('master');</script>\n"
    "       </div>\n"
    "       </td>\n"
    "       <td width=\"80%\" valign=\"top\">\n"
    "       <table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
    "           <tr height=\"44\">  <td align=\"center\" class=\"title\">   <div id=\"articletitle\"></div>                     </td></tr>\n"
    "           <tr>                <td valign=\"top\" class=\"main\">      <div id=\"articlecontent\" class=\"objpage\"></div> </td></tr>\n"
    "       </table>\n"
    "       </td>\n"
    "   </tr>\n"
    "   </table>\n"
    "   </td>\n"
    "</tr>\n"
    "</table>\n" ;
    //  Show buttons
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "<tr height=\"50\">\n"
    "   <td align=\"center\">\n" ;
    C << "\t\t<input type=\"button\" onclick=\"location.href='/masterMainMenu';\" value=\"Master Account Menu\"/>" ;
    if (pApp->m_CfgEdits.Count())
        C << " <input type=\"button\" onclick=\"location.href='/masterMainMenu';\" value=\"Apply Changes\"/>" ;
    C <<
    "\n\t</td>\n"
    "</tr>\n"
    "</table>\n"
    "</body>\n"
    "</html>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
#endif
/*
**  CONFIG CHANGES
*/
hzEcode _masterPageEdit (hzHttpEvent* pE, const hzString& rname)
{
    //  Display the form for creating a new page or editing an existing page. This essentially comprises a large text box, populated with the XML for the page (empty in the case of
    //  a new page). In addition there will be a 'Save' and a 'Test' button. Save simply saves the XML to a staging file for the page, which will exist until the page is committed.
    //  Test does not test the page but checks the proposed configs are viable. Pages are instead tested in a separate tab of the browser. This will return the same cookie i.e. the
    //  one associated with the webmaster session. The page displayed to a logged in webmaster will be the test version, if there is one.
    //
    //
    _hzfunc("_masterPageEdit") ;
    hzChain         C ;         //  HTML chain
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterCfgEdit  (hzHttpEvent* pE, const hzString& cname)
{
    //  Display the master edit config page. This essentially comprises a large text box and a submit button. The text box is populated with the XML for the resource if it exists.
    //
    //  Argument:   pE  pE      The HTTP event
    //              2)  cname   Argument (part beyond URL /masterCfgEdit-)
    //
    //  Returns:    None
    _hzfunc("hdsApp::_masterCfgEdit") ;
    hdsApp*         pApp ;      //  Application
    ifstream        is ;        //  Input stream
    hzDirent        de ;        //  Config file info
    hzMD5           md5 ;       //  MD5 for the old content
    hzAtom          atom ;      //  For session values
    hzChain         C ;         //  HTML chain
    hzChain         Z ;         //  Config file content
    const char*     pArg ;      //  File identifier
    hzString        fpath ;     //  File path of original
    hzString        fnew ;      //  File path plus .new
    uint32_t        posn ;      //  Posn of file in m_Configs
    hzEcode         rc ;        //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    pArg = pE->GetResource() ;
    pArg += 12 ;
    if (pArg[0] && IsPosint(posn, pArg))
    {
        //de = pApp->m_Configs.GetObj(posn) ;
        fpath = pApp->m_Configdir + "/" ;
        fpath += pApp->m_Configs.GetKey(posn) ;
        //fpath = de.Path() + "/" + de.strName() ;
        fnew = fpath + ".new" ;
        rc = TestFile(*fnew) ;
        if (rc == E_OK)
            fpath = fnew ;
            
        is.open(*fpath) ;
        if (is.fail())
            Z << "Error: Cannot Open File" << fpath ;
        else
        {
            Z << is ;
            is.close() ;
        }
    }
    //  Set the article title
    C.Clear() ;
    C << "Dissemino Config Editor: Page " << fpath ;
    fnew = C ;
    pE->SetHdr(s_articleTitle, fnew) ;
    //  Set the article content
    C.Clear() ;
    C << "<form method=\"POST\" action=\"masterCfgEditHdl1\">\n" ;
    if (fpath)
        C.Printf("<input type=\"hidden\" name=\"fname\" value=\"%s\"/>\n", *fpath) ;
    else
        C.Printf("Filename: <input type=\"text\" name=\"fname\"/>\n") ;
    C << "<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
    C << "<tr>\n" ;
    C << "<td>\n" ;
    C << "<textarea name=\"pgData\" rows=\"40\" cols=\"200\" class=\"main\">" ;
    if (Z.Size())
        C << Z ;
    C << "</textarea>\n" ;
    C <<
    "\t</td>\n"
    "</tr>\n"
    "</table>\n"
    "<table width=\"20%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "<tr>\n"
    "<td align=\"left\"><input type=\"button\" onclick=\"location.href='/masterCfgList';\" value=\"Back to Listings\"/></td>\n"
    "<td align=\"rigth\"><input type=\"submit\" name=\"x-action\" value=\"Save Changes\"></td>\n"
    "</tr>\n"
    "</table>\n"
    "</form>\n" ;
    return pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
hzEcode _masterCfgEditHdl   (hzHttpEvent* pE)
{
    //  Handle submission to the master 'stage 1' edit config file form.
    //
    //  Begins with producing an MD5 from the text-box content. If this differs from the previous MD5 for 'pathname.new' or just 'pathname' if the .new does not
    //  yet exist, write out the contents to 'pathname.new'
    //
    //  Config editing involves a sequence of events. The submit button in the stage 1 form will cause a HTTP POST event which will be processed by form-handler
    //  _masterCfgEditHdl1(). This takes the text-box content and produces an MD5. If the config file existed beforehand and the MD5 of the text-box content is a
    //  match to that of the original config file, _masterCfgEditHdl1() will redisplay the stage 1 config editor form along with a message "No changes detected".
    //  Where the MD5 is not a match, either because the origional config has been changed or is new, the text-box content is committed to disk and the stage 2
    //  config editor form is displayed. Note that the disk commit will copy the original file if it exists from 'pathname' to 'pathname.X' where X is the last
    //  modified date of the original expressed as YYYYMMDD-hhmmss. The content be it new or changed, is then written to pathname.new.
    //
    //  The stage 2 form contains a test button which causes a HTTP POST event which will be processes by form-handler _masterCfgEditHd2(). This loads the config
    //  as an XML document and parses it using the same set of config read functions used at startup. If either the load or the parse fails, _masterCfgEditHd2()
    //  redisplays the stage 2 form with the error messages. If the load and the parse succeed _masterCfgEditHd2() redisplays the stage 2 form but this time with
    //  an 'adopt change' button. At this point the updated or new resources specified in the updated or new config are quarentined. Pressing the 'adopt change'
    //  button causes another HTTP POST event which will be processed by form-handler _masterCfgEditHd3(). This overwrites the config held at 'pathname' with the
    //  latest 'pathname.new' and moves the resources out of quarentine into mainstream.
    hdsApp*         pApp ;      //  Application
    ofstream        os ;        //  Output stream (to save file)
    hzMD5           md5 ;       //  MD5 for the new content
    hzDirent        de ;        //  Config file info
    hzChain         C ;         //  Page output
    hzChain         Z ;         //  The XML to be tested
    hzDocXml        doc ;       //  The XML document for the resource
    hzString        S ;         //  Arg as string
    hzString        val ;       //  The submitted textarea
    hzString        fnew ;      //  Config file full pathname
    hzEcode         rc ;        //  Return code
    _hzfunc("hdsApp::_masterCfgEditHdl") ;
    pApp = (hdsApp*) pE->m_pContextApp ;
    //  Get file name
    S = "fname" ;
    fnew = pE->m_mapStrings[S] ;
    //  Get new file data
    S = "pgData" ;
    if (pE->m_mapChains.Exists(S))
        Z = pE->m_mapChains[S] ;
    else
    {
        val = pE->m_mapStrings[S] ;
        Z = val ;
    }
    //  Write data to file.new
    fnew += ".new" ;
    os.open(*fnew) ;
    if (os.fail())
        threadLog("Open file failed (%s)\n", *fnew) ;
    else
    {
        os << Z ;
        os.close() ;
    }
    if (!pApp->m_CfgEdits.Exists(de.strName()))
    {
        //  We have not sought to edit this file before
        pApp->m_CfgEdits.Insert(de.strName()) ;
    }
    //  Compile HTML output
    //C << s_master_head_std ;
    C << "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n" ;
    if (!fnew)
        C << "\t<tr height=\"50\"><td align=\"center\" class=\"title\">Dissemino Config Editor: Untitled</td></tr>\n" ;
    else
        C.Printf("\t<tr height=\"50\"><td align=\"center\" class=\"title\">Dissemino Config Editor: Page %s</td></tr>\n", *fnew) ;
    C << "</table>\n" ;
    C << "<form method=\"POST\">\n" ;
    C.Printf("<p>Processing config file %s of %d bytes</p>\n", *fnew, Z.Size()) ;
    C << "<textarea name=\"pgData\" rows=\"36\" cols=\"200\" class=\"main\">" ;
    C << Z ;
    C << "</textarea>\n" ;
    C <<
    "\t</td>\n"
    "</tr>\n"
    "</table>\n"
    "<table width=\"20%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n"
    "<tr>\n"
    "<td align=\"left\"><input type=\"button\" onclick=\"location.href='/masterCfgList';\" value=\"Back to Listings\"/></td>\n"
    "<td align=\"rigth\"><input type=\"submit\" name=\"x-action\" value=\"Save Changes\"></td>\n"
    "</tr>\n"
    "</table>\n"
    "</form>\n" ;
    /*
    rc = doc.Load(Z) ;
    if (rc != E_OK)
    {
        C << "<p>Config did not load</p>\n" ;
        C << doc.m_Error ;
    }
    else
    {
        pRoot = doc.GetRoot() ;
        if (!pRoot)
        {
            C << "<p>Document has no root</p>\n" ;
            C << m_cfgErr ;
            rc = E_NODATA ;
        }
        else
        {
            rc = _readInclFile(pRoot) ;
            if (rc != E_OK)
            {
                C << "<p>Document did not parse</p>\n" ;
                C << m_cfgErr ;
            }
        }
        C <<
        "<table>\n"
        "<tr>\n"
        "\t<td align=\"center\">\n"
        "\t<td><input type=\"button\" onclick=\"location.href='/masterCfgList';\" value=\"Abort Edit\"/></td>\n"
        "\t<td width=\"20\"> </td>\n"
        "\t<input type=\"submit\" formaction=\"masterCfgEditHdl1\" value=\"Submit\">\n" ;
        if (rc == E_OK)
        {
            C <<
            "\t<td width=\"20\"> </td>\n"
            "\t<input type=\"submit\" formaction=\"masterCfgEditHdl2\" value=\"Apply\">\n" ;
        }
        C <<
        "\t</td>\n"
        "</tr>\n"
        "</table>\n" ;
    }
    C <<
    "</form>\n"
    "</body>\n"
    "</html>\n" ;
    */
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
hzEcode _masterCfgRestart   (hzHttpEvent* pE)
{
    //  Checks for config updates and if any found, seeks to incorporate the changes into the live application.
    //
    //  MD5 differences 
    hzVect<hzString>    Dirs ;      //  All directories in m_Configdir ending in .new
    hzVect<hzString>    Files ;     //  All files in m_Configdir ending in .new
    hdsApp*         pApp ;          //  Application
    hzDirent        de ;            //  Directory entry
    hzDocXml        doc ;           //  The XML document for the resource
    hzXmlNode*      pRoot ;         //  Root node of document
    hzChain         C ;             //  For HTML output
    hzString        S ;
    hzString        fname ;         //  Name of .new file in config dir
    uint32_t        n ;             //  File iterator
    hzEcode         rc = E_OK ;     //  Return code
    pApp = (hdsApp*) pE->m_pContextApp ;
    ListDir(Dirs, Files, pApp->m_Configdir, "*.new") ;
    //  Compile HTML output
    //C << s_master_head_std ;
    C <<
    "<table width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" bgcolor=\"#b3fff0\">\n"
    "   <tr height=\"50\"><td align=\"center\" class=\"title\">Dissemino Config Change Acceptor</td></tr>\n"
    "</table>\n" ;
    C.Printf("<p>Re-configuring for %d files in dir %s</p>\n", Files.Count(), *pApp->m_Configdir) ;
    for (n = 0 ; rc == E_OK && n < Files.Count() ; n++)
    {
        S = Files[n] ;
        //de = Files[n] ;
        C.Printf("<p>Re-configuring for file %s</p>\n", *S) ;
        if (de.strName().Contains("Main"))
            continue ;
        //fname = m_Configdir + "/" + de.Name() ;
        fname = pApp->m_Configdir + "/" + S ;
        rc = doc.Load(fname) ;
        if (rc != E_OK)
        {
            C.Printf("<p>Config %s did not load</p>\n", *fname) ;
            C << doc.Error() ;
            continue ;
        }
        C.Printf("<p>Loaded Config %s</p>\n", *fname) ;
        pRoot = doc.GetRoot() ;
        if (!pRoot)
        {
            C << "<p>Document has no root</p>\n" ;
            C << pApp->m_cfgErr ;
            //rc = E_NODATA ;
            continue ;
        }
        C.Printf("<p>Parsed Config %s</p>\n", *fname) ;
        /*
 *          FIX
        rc = pApp->_readInclFile(pRoot) ;
        */
        //  Any errors
        if (rc != E_OK)
        {
            C << "<p>Document did not compile</p>\n" ;
            C << pApp->m_cfgErr ;
            continue ;
        }
        C.Printf("<p>Accepted Config %s</p>\n", *fname) ;
    }
    C << "</form>\n" ;
    return pE->SendRawChain(HTTPMSG_OK, HMTYPE_TXT_HTML, C, 0, false) ;
}
#if 0
void    hdsApp::ReportStatus    (hzHttpEvent* pE)
{
    //  Display HTML Memory Usage Report
    //
    //  Arguments:  1)  pE      The HTTP event being responded to
    //
    //  Returns:    None
    hzChain     C ;     //  Working chain for HTML output
    ReportMemoryUsage(C, true) ;
    ReportMutexContention(C, true) ;
    pE->SendAjaxResult(HTTPMSG_OK, C) ;
}
#endif