Logo Search packages:      
Sourcecode: helix-player version File versions

xmlconfig.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: xmlconfig.cpp,v 1.9.22.1 2004/07/19 21:04:07 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */


//  $Id: xmlconfig.cpp,v 1.9.22.1 2004/07/19 21:04:07 hubbe Exp $

#ifdef _UNIX 
#include <sys/stat.h> 
#endif 

#include "hxtypes.h"
#include "hxcom.h"
#include "looseprs.h"
#include "cbbqueue.h"
#include "hxstrutl.h"
#include "ihxpckts.h"
#include "chxpckts.h"
#include "hxmon.h"      // for registry property type enums
#include "hxerror.h"
#include "xmlconfig.h"
#include "errmsg_macros.h"

#define MAX_TAG_SIZE    32768
#define MAX_ERROR_CHARS 20    // Number of chars to print if an error occurs

static const XMLConfigAlias aliases[] = {
    //Note: Attribute specs here should not have quotes in them!
    //      %0 is replaced with the name of the alias (leftmost column)
    //      %n with the value of the nth attribute
    //      %  args can only appear in the values of attributes
    
    //Alias This Tag    To This         With these 3 args      and these flags
    {"FSMount",         "List",     "Name=FSMount", NULL, NULL,         0},
    {"Plugin",          "List", "Name=%1",      NULL, NULL,         0},
    {"MimeTypes",       "List", "Name=MimeTypes", NULL, NULL,       0},
    {"Type",            "List", "Name=%1",      NULL, NULL,         0},
    {"Ext",             "Var",  NULL,           NULL, NULL,         0},
    {"IPBindings",      "List", "Name=IPBindings", NULL, NULL,      0},
    {"HTTPDeliverable", "List", "Name=HTTPDeliverable", NULL, NULL,      0},
    {"Path",      "Var",      NULL, NULL, NULL,     0},
    {"ConnectControl",  "List", "Name=ConnectControl", NULL, NULL,  0},
    {"Multicast", "List", "Name=Multicast", NULL, NULL,0},
    {"Address",         "Var",  NULL,           NULL, NULL,         0},
    {"NetMask",         "Var",  NULL,           NULL, NULL,         0},
    {"AccessLogging",   "List", "Name=AccessLogging", NULL, NULL,   0},
    {"LiveArchive",     "List", "Name=LiveArchive", NULL, NULL,     0},
    {"FarmSplit",       "List", "Name=FarmSplit", NULL, NULL,     0},
    {"Directory",       "List", "Name=%1",      NULL, NULL,         0},
    {"AccessControl",   "List", "Name=AccessControl", NULL, NULL,     0},
    {"Rule",      "List", "Name=%1", NULL, NULL,     0},
    {"Access",          "Var",      NULL, NULL, NULL,     0},
    {"Transmission",    "Var",      NULL, NULL, NULL,     0},
    {"To",              "Var",      NULL, NULL, NULL,     0},
    {"From",      "Var",      NULL, NULL, NULL,     0},
    {"Ports",           "List", "Name=Ports", NULL, NULL,     0},
    {"Num",       "Var", NULL, NULL, NULL,     0},
    {"Proxy",           "List", "Name=Proxy", NULL, NULL,     0},
    {NULL, NULL, NULL, NULL, NULL, 0}
};

#if defined (_WINDOWS ) || defined (_WIN32) || defined(_SYMBIAN)
#define OS_SEPARATOR_CHAR     '\\'
#define OS_SEPARATOR_STRING   "\\"
#elif defined (_UNIX)
#define OS_SEPARATOR_CHAR     '/'
#define OS_SEPARATOR_STRING   "/"
#elif defined (_MACINTOSH) 
#ifdef _MAC_MACHO
#define OS_SEPARATOR_CHAR       '/' 
#define OS_SEPARATOR_STRING     "/" 
#else
#define OS_SEPARATOR_CHAR       ':' 
#define OS_SEPARATOR_STRING     ":" 
#endif
#endif // defined (_WINDOWS ) || defined (_WIN32)

XMLConfig::XMLConfigListNode::~XMLConfigListNode()
{
    if(m_pList)
    {
      delete m_pList;
    }
    if(m_name)
      delete [] m_name;
    if(m_value)
      delete [] m_value;
}

XMLConfig::XMLConfigList::~XMLConfigList()
{
    CHXSimpleList::Iterator i;

    for(i = Begin(); i != End(); ++i)
    {
      XMLConfigListNode* node = (XMLConfigListNode*)(*i);
      delete node;
    }
}

XMLConfig::XMLConfig():m_filename(NULL), m_lRefCount(0), m_pList(NULL),
                       m_ulMajor(0), m_ulMinor(0), m_ActiveSetsOutstanding(0),
                       m_pRegistry(NULL), m_pMessages(NULL), m_szServerversion(NULL),
                       m_pReconfigureResponse(NULL)
{
}

XMLConfig::XMLConfig(IHXRegistry2* pRegistry, IHXErrorMessages* pMessages, const char* szserverversion,
                     UINT32 dwMajor, UINT32 dwMinor)
     : m_filename(0), m_lRefCount(0), m_pList(NULL), m_ulMajor(dwMajor), m_ulMinor(dwMinor)
       , m_ActiveSetsOutstanding(0), m_pReconfigureResponse(NULL)
{
    if (pRegistry)
    {
        m_pRegistry = pRegistry;
        m_pRegistry->AddRef();
    }

    if (pMessages)
    {
        m_pMessages = pMessages;
        m_pMessages->AddRef(); 
    }

    m_szServerversion = new_string(szserverversion);
    
    XMLConfigAlias* alias = (XMLConfigAlias*) &aliases[0];
    char lwr[256]; /* Flawfinder: ignore */
    m_vserver = -1;

    while(alias->from)
    {
      SafeStrCpy(lwr, alias->from, 256);
      strlwr(lwr);
      m_alias_dict.enter(lwr, alias);
      alias++;
    }
}
XMLConfig::~XMLConfig()
{
    delete[] m_szServerversion; 
    HX_RELEASE(m_pReconfigureResponse);
    HX_RELEASE(m_pRegistry);
    HX_RELEASE(m_pMessages); 
}

HX_RESULT
XMLConfig::init(IHXRegistry2* pRegistry, IHXErrorMessages* pMessages, const char* szserverversion,
                     UINT32 dwMajor, UINT32 dwMinor)
{
    m_ulMajor = dwMajor;
    m_ulMinor = dwMinor;
    if (pRegistry)
    {
        m_pRegistry = pRegistry;
        m_pRegistry->AddRef();
    }

    if (pMessages)
    {
        m_pMessages = pMessages;
        m_pMessages->AddRef(); 
    }

    m_szServerversion = new_string(szserverversion);
    
    XMLConfigAlias* alias = (XMLConfigAlias*) &aliases[0];
    char lwr[256]; /* Flawfinder: ignore */
    m_vserver = -1;

    while(alias->from)
    {
      SafeStrCpy(lwr, alias->from, 256);
      strlwr(lwr);
      m_alias_dict.enter(lwr, alias);
      alias++;
    }
    return HXR_OK;
}

STDMETHODIMP
XMLConfig::QueryInterface(REFIID riid, void** ppvObj)
{
    if(IsEqualIID(riid, IID_IUnknown))
    {
        AddRef();
        *ppvObj = (IUnknown*)(IHXRegConfig*)this;
        return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXRegConfig))
    {
      AddRef();
      *ppvObj = (IHXRegConfig*)this;
      return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

STDMETHODIMP_(UINT32)
XMLConfig::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);    
}

STDMETHODIMP_(UINT32)
XMLConfig::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

HX_RESULT
XMLConfig::Write(const char* name, const char* filename)
{
    FILE* outfile = fopen(filename, "w");
    if(!outfile)
    {
      return HXR_INVALID_FILE;
    }

    fprintf(outfile, "<?XML Version=\"1.0\" ?>\n");
    fprintf(outfile, "<!-- Auto generated by %s %d.%d -->\n\n",
            m_szServerversion, m_ulMajor, m_ulMinor);

#ifdef _UNIX
    chmod(filename, 0700);
#endif //UNIX

    return DumpConfig(name, 0, outfile, m_pRegistry);
}

HX_RESULT
XMLConfig::DumpConfig(const char* name, int indent, FILE* outfile,
                  IHXRegistry2* pRegistry)
{
    IHXValues* pValues;
    const char* propName;
    HX_RESULT res;
    UINT32 prop_id;

    if(HXR_OK != pRegistry->GetPropListByName(name,
                                pValues))
      return HXR_FAIL;

    char* point = (char*)strrchr(name, '.');
    if(point)
    {
      if(strcasecmp(point + 1, "XXXBADLIST") == 0)
          return HXR_FAIL;

      for(int j = 0; j < indent - 1; j++)
          fprintf(outfile, "  ");
      fprintf(outfile, "<List %s>\n", point + 1);
    }
    
    res = pValues->GetFirstPropertyULONG32(propName, prop_id);
    while(res == HXR_OK)
    {
      HXPropType type = pRegistry->GetTypeById(prop_id);
      switch(type)
      {
          case PT_COMPOSITE:
            DumpConfig(propName, indent+1, outfile, pRegistry);
            break;
          case PT_INTEGER:
          {
            INT32 val;
            if(HXR_OK == pRegistry->GetIntById(prop_id, val))
            {
                
                fprintf(outfile, "%*.*s<Var %s=\"%ld\"/>\n",
                      indent * 2, indent * 2, "  ",
                      strrchr(propName, '.') + 1,
                      val);
            }
            break;
          }
          case PT_INTREF:
            break;
          case PT_STRING:
          {
            IHXBuffer* pBuffer;
            if(HXR_OK == pRegistry->GetStrById(prop_id, pBuffer) && pBuffer)
            {
                fprintf(outfile, "%*.*s<Var %s=\"%s\"/>\n",
                      indent * 2, indent * 2, "  ",
                      strrchr(propName, '.') + 1,
                      pBuffer->GetBuffer());
                pBuffer->Release();
            }
            break;
          }
          case PT_BUFFER:
          {
            IHXBuffer* pBuffer;
            pRegistry->GetBufById(prop_id, pBuffer);
            if(pBuffer)
            {
                fprintf(outfile, "%*.*s<Var %s=\"%s\"/>\n",
                      indent * 2, indent * 2, "  ",
                      strrchr(propName, '.') + 1,
                      pBuffer->GetBuffer());
                pBuffer->Release();
            }
            break;
          }
          case PT_UNKNOWN:
          default:
            break;
      }

      res = pValues->GetNextPropertyULONG32(propName, prop_id);
    }
    if(point)
      fprintf(outfile, "%*.*s</List>\n", (indent - 1) * 2, (indent-1)*2, "  ");
    fflush(outfile);
    pValues->Release();
    return HXR_OK;
}

void
XMLConfig::ExpandAttribute(XMLTag* tag, const char* attribute)
{
    const char* equals;
    char subst[2048]; /* Flawfinder: ignore */
    const char* a;
    UINT32 arg;
    UINT32 i;

    equals = strchr(attribute, '=');
    // Aliases are hardcoded.  Don't enter wrong ones, dumbass.
    HX_ASSERT(equals);

    tag->new_attribute()->name = new char[equals - attribute + 1];
    strncpy(tag->m_cur_attribute->name, attribute, /* Flawfinder: ignore */
          equals - attribute);
    tag->m_cur_attribute->name[equals - attribute] = 0;

    // Attributes already present in the tag with the same name
    // as an aliased attribute override the alias.
    for(i = 0; i < tag->m_numAttributes - 1; i++)
    {
      if(tag->attribute(i)->name && 
         (strcasecmp(tag->attribute(i)->name, 
                   tag->m_cur_attribute->name) == 0))
      {
          break;
      }
    }
    if(i < tag->m_numAttributes - 1)
    {
      // Found a duplicate attribute with this name, throw out the
      // alias version.
      delete tag->m_cur_attribute;
      tag->m_numAttributes--;
    }
    else if(!strchr(equals, '%'))
    {
      tag->m_cur_attribute->value = new_string(equals+1);
    }
    else
    {
      for(a = equals, i = 0; *a; a++, i++)
      {
          switch(*a)
          {
            case '%':
                arg = *(a+1) - '0';
                if(arg > 0)
                {
                  if(arg - 1 < tag->m_numAttributes)
                  {
                      SafeStrCpy(&subst[i], tag->attribute(arg - 1)->value, 2048-i);
                      i+= strlen(tag->attribute(arg - 1)->value);
                  }
                  else
                  {
                      ERRMSG(m_pMessages,
                           "%s: Alias %s requires at least %d arguments",
                           m_filename,
                           tag->m_name, arg);
                      return;
                  }
                }
                else
                {
                  SafeStrCpy(&subst[i], tag->m_name, 2048-i);
                  i += strlen(tag->m_name);
                }
                break;
            default:
                subst[i] = *a;
          }
      }
      subst[i] = 0;
      tag->m_cur_attribute->value = new_string(subst);
    }
}

// XMLConfig::Expand
//
// Do alias and loose syntax expansion
BOOL
XMLConfig::Expand(XMLTag* tag, CBigByteQueue* queue)
{
    UINT32 i;

    Dict_entry* ent;
    XMLConfigAlias* alias;
    char* lwr = new char[strlen(tag->m_name) + 1];
    BOOL ignore = FALSE;

    strcpy(lwr, tag->m_name); /* Flawfinder: ignore */
    strlwr(lwr);
    if((ent = m_alias_dict.find(lwr)) != NULL)
    {
      alias = (XMLConfigAlias*)ent->obj;
      delete [] tag->m_name;
      tag->m_name = new_string(alias->to);
      if(tag->m_type == XMLPlainTag)
      {
          if(alias->attr1)
          {
            ExpandAttribute(tag, alias->attr1);
          }
          if(alias->attr2)
          {
            ExpandAttribute(tag, alias->attr2);
          }
          if(alias->attr3)
          {
            ExpandAttribute(tag, alias->attr3);
          }
      }
      if((alias->flags & AL_TAGIFY) && (tag->m_type != XMLEndTag))
      {
          UINT32 bytesAvail = queue->GetQueuedItemCount();
          BYTE* buf = new BYTE[bytesAvail];
          queue->DeQueue(buf, bytesAvail);
          BOOL isList = FALSE;
          char ibuf[1024]; /* Flawfinder: ignore */

          if(!tag->m_need_close && strcasecmp(alias->to, "list") == 0)
          {
            isList = TRUE;
            ignore = TRUE;
            if(tag->get_attribute("name"))
                SafeSprintf(ibuf, 1024, "<List Name=\"%s\">",
                      tag->get_attribute("name"));
            else
                SafeSprintf(ibuf, 1024, "<List Name=\"%s\">",
                      alias->from);
            queue->EnQueue(ibuf, strlen(ibuf));
          }
          UINT32 i;
          for(i = 0; i < tag->m_numAttributes; i++)
          {
            if (tag->attribute(i)->name)
            {
                SafeSprintf(ibuf, 1024, "<Var Name=\"%s\" Value=\"%s\"/>",
                     (tag->attribute(i)->name ? tag->attribute(i)->name : ""),
                      tag->attribute(i)->value);
                queue->EnQueue(ibuf, strlen(ibuf));
            }
          }
          if(isList)
          {
            sprintf(ibuf, "</List>"); /* Flawfinder: ignore */
            queue->EnQueue(ibuf, strlen(ibuf));
          }
          queue->EnQueue(buf, bytesAvail);
      }
    }
    delete [] lwr;

    if(tag->m_type != XMLPlainTag || ignore)
      return ignore;

    if(strcasecmp(tag->m_name, "list") == 0)
    {
      if(tag->m_numAttributes < 1)
      {
          return ignore;
      }
      
      for(i = 0; i < tag->m_numAttributes; i++)
      {
          if(!tag->attribute(i)->name)
          {
            tag->attribute(i)->name = new_string("name");
          }
          else if(strcasecmp(tag->attribute(i)->name, "name") != 0)
          {
            ERRMSG(m_pMessages,
                   "%s: Unknown List Attribute %s",
                   m_filename,
                   tag->attribute(i)->name);
            return ignore;
          }
      }
    }
    else if(strcasecmp(tag->m_name, "var") == 0)
    {
      if(tag->m_numAttributes < 1)
      {
          ERRMSG(m_pMessages,
               "%s: Var tag requires at least one attribute",
               m_filename);
          return ignore;
      }

      if(tag->m_numAttributes == 1)
      {
          // Special case, <Var x=y> converts to <Var name=x value=y>
          tag->new_attribute()->name = new_string("value");
          tag->attribute(1)->value = tag->attribute(0)->value;
          tag->attribute(0)->value = tag->attribute(0)->name;
          if(!tag->attribute(0)->value)
          {
            char* val = new char[20];
            sprintf(val, "elem%ld", tag->elem); /* Flawfinder: ignore */
            tag->attribute(0)->value = val;
          }
          tag->attribute(0)->name = new_string("name");
      }
    }
    else if(strcasecmp(tag->m_name, "server") == 0)
    {
      if(tag->m_numAttributes < 1)
      {
          ERRMSG(m_pMessages,
               "%s: Server tag requires at least one attribute",
               m_filename);
          return ignore;
      }
      if(!tag->get_attribute("number"))
      {
          tag->attribute(0)->name = new_string("number");
      }
    }
    return ignore;
}

XMLConfigVarType
XMLConfig::GetVarType(XMLConfigListNode* node)
{
    char* pos = node->m_value;

    if (0 == *pos)
    {
         return CfgVarString;
    }
    for(; *pos && isdigit(*pos); pos++)
      ;
    if(*pos == 0 && *(node->m_value) != 0)
    {
      node->m_int = atol(node->m_value);
      return CfgVarInt;
    }
    
    if(strcasecmp(node->m_value, "true") == 0)
    {
      node->m_bool = TRUE;
      return CfgVarBool;
    }
    else if(strcasecmp(node->m_value, "false") == 0)
    {
      node->m_bool = FALSE;
      return CfgVarBool;
    }
    return CfgVarString;
}

void
XMLConfig::StuffRegistry(XMLConfigList* list)
{
    CHXSimpleList::Iterator i;

    char* regname = list->m_parentnode->get_registry_name();
    if(m_pRegistry->GetTypeByName(regname) == PT_UNKNOWN)
    { 
      m_pRegistry->AddComp(regname);
    }
    delete [] regname;

    for(i = list->Begin(); i != list->End(); ++i)
    {
      XMLConfigListNode* node = (XMLConfigListNode*)(*i);
      switch(node->m_type)
      {
          case CfgVar:
            regname = node->get_registry_name();
            switch(GetVarType(node))
            {
                case CfgVarInt:
                  // printf("%s = %ld\n", regname, node->m_int);
                        if(m_pRegistry->GetTypeByName(regname) == PT_UNKNOWN)
                  {
                      m_pRegistry->AddInt(regname, node->m_int);
                  }
                  else
                  {
                      m_pRegistry->SetIntByName(regname, node->m_int);
                  }
                  break;
                case CfgVarString:
                {
                  CHXBuffer* pBuffer = new CHXBuffer;
                  pBuffer->AddRef();
                  pBuffer->Set((UINT8*)node->m_value,
                             strlen(node->m_value) + 1);
                  // printf("%s = %s\n", regname, (const char *)pBuffer->GetBuffer());
                  if(m_pRegistry->GetTypeByName(regname) == PT_UNKNOWN)
                  {
                      m_pRegistry->AddStr(regname, pBuffer);
                  }
                  else
                  {
                      m_pRegistry->SetStrByName(regname, pBuffer);
                  }
                  pBuffer->Release();
                  break;
                }
                case CfgVarBool:
                  // printf("%s = %ld\n", regname, node->m_bool);
                  if(m_pRegistry->GetTypeByName(regname) == PT_UNKNOWN)
                  {
                      m_pRegistry->AddInt(regname, node->m_bool);
                  }
                  else
                  {
                      m_pRegistry->SetIntByName(regname, node->m_bool);
                  }
                  break;
            }
            delete [] regname;
            break;
          case CfgList:
            StuffRegistry(node->m_pList);
            break;
      }
    }
}


// 
// Method: Read
//
// Parameters:
//
//    filename:    The file to be read.
//    pWinRegKey:  The Windows registry key to be filled in (can be
//            NULL if no registry writing is needed).
//    pServRegKey: The Server registry key to be filled in.
// 
// Notes: the bIncludedFile is for internal recursive use only.  Do not 
// set it when calling from outside this object.
HX_RESULT
XMLConfig::Read(char* filename, 
            char* pServRegKey, 
            BOOL  bIncludedFile)
{
    HX_RESULT hResult = HXR_OK;
    FILE* fp=0;
    XMLParser parser;
    XMLTag*   tag = 0;
    CBigByteQueue queue(MAX_TAG_SIZE);
    int l=0;
    BOOL filedone = FALSE;
    UINT32 indent = 0;

    HX_ASSERT(filename && strlen(filename));

    HX_VECTOR_DELETE(m_filename);
    m_filename = new_string(filename);

    fp = fopen(filename, "r");
    if (!fp)
    {
      ERRMSG(m_pMessages, "%s: file not found", filename);
      return HXR_FILE_NOT_FOUND;
    }

    if(!m_pList)
    {
      m_pList = new XMLConfigList;
      m_pList->m_parentnode = new XMLConfigListNode;
      m_pList->m_parentnode->m_name = new_string(pServRegKey);
      m_pList->m_parentnode->m_parent = NULL;
    }

    // read the config file into the byte queue
    INT32 nBufSize = 16384;
    BYTE* pBuf = new BYTE[nBufSize];
    while (!filedone && hResult == HXR_OK)
    {
        l = fread(pBuf, 1, nBufSize, fp);
        if (l > 0)
        {
          if (!queue.EnQueue(pBuf, l))
            {
                if (!queue.Grow(l))
                {
                    ERRMSG(m_pMessages,
                           "error expanding data structure while parsing %s",
                           filename);
                    hResult = HXR_FAIL;
                }
                if (!queue.EnQueue(pBuf, l))
                {
                    ERRMSG(m_pMessages,
                           "unknown error while parsing %s",
                           filename);
                    hResult = HXR_FAIL;
                }
            }
        }
        else
        {
          filedone = TRUE;
        }
    }

    UINT32 bytesUsed=0;
    UINT32 bytesAvail=0;
    BYTE* p=0;
    while (hResult == HXR_OK && (bytesAvail = queue.GetQueuedItemCount()) > 0)
    {
      if (bytesAvail > nBufSize)
        {
            HX_VECTOR_DELETE(pBuf);
            pBuf = new BYTE[bytesAvail];
            nBufSize = bytesAvail;
        }
      p = pBuf;

      queue.DeQueue(pBuf, bytesAvail);
      bytesUsed = bytesAvail;

        HX_DELETE(tag);
      XMLParseResult res = parser.Parse((const char*&)p, bytesAvail, tag);
      queue.EnQueue(p, bytesAvail - (p - pBuf));

      switch(res)
      {
          case XMLPNoClose:
            // A tag opener was found, but no closer yet.
            if (filedone)
            {
                pBuf[bytesAvail - 1] = '\0';
                if (bytesAvail > MAX_ERROR_CHARS)
                {
                  pBuf[MAX_ERROR_CHARS - 1] = '\0';
                }
                ERRMSG(m_pMessages,
                "%s: Missing close for tag '%s'...", m_filename, pBuf);
                hResult = HXR_FAIL;
            }
            break;
          case XMLPNoTagType:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Badly formed tag '%s'", m_filename, pBuf);
            break;
          case XMLPBadAttribute:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Badly formed attribute in '%s'", m_filename,
                   pBuf);
            break;
          case XMLPPlainText:
            //Some text.  What do we care?
            break;
          case XMLPBadEndTag:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Unexpected end tag '</%s>'", m_filename, tag->m_name);
            hResult = HXR_FAIL;
            break;
          case XMLPComment:
            // A comment, fine.
            if(tag != NULL)
            {
                //Some kind of comment directive
                XMLAttribute* pAttr = tag->attribute(0);
                if(pAttr &&
                  pAttr->name)
                {
                  if(strcasecmp(pAttr->name, "include") == 0)
                  {
                      char* path;
                      char* newfile = new char[1024];
                      if(!strchr(tag->attribute(0)->value, 
                               OS_SEPARATOR_CHAR) &&
                         ((path = strrchr(m_filename, OS_SEPARATOR_CHAR))
                        != NULL))
                      {
                        path++;
                                if (path - m_filename < 1024)
                                {
                                    strncpy(newfile, m_filename, path - m_filename);
                                    newfile[path - m_filename] = '\0';
                                }
                        SafeStrCat(newfile, tag->attribute(0)->value, 1024);
                      }
                      else
                      {
                        SafeStrCpy(newfile, tag->attribute(0)->value, 1024);
                      }
                      char* filename = m_filename;
                      m_filename = NULL;
                      Read(newfile, pServRegKey, TRUE);
                      delete[] m_filename;
                      m_filename = filename;
                            delete[] newfile;
                  }
                }
            }
            break;
          case XMLPDirective:
            // A directive.  We don't handle any yet.
            break;
          case XMLPProcInst:
            // A processing Instruction, whee!
            break;
          case XMLPTag:
          {
            if(Expand(tag, &queue))
                break;

            XMLConfigListNode* node = new XMLConfigListNode;
            node->m_parent = m_pList->m_parentnode;
            node->m_vserver = m_vserver;

            if(tag->m_need_close)
            {
                if(strcasecmp(tag->m_name, "list") == 0)
                {
                  const char* name = tag->get_attribute("name");
                  if(!name)
                  {
                      ERRMSG(m_pMessages,
                           "%s: List tag requires a name attribute",
                           m_filename);
                      // Process anyway, give it a dummy name
                      name = "XXXBADLIST";
                  }
                  node->m_type = CfgList;
                  node->m_name = new_string(name);
                  node->m_num = tag->elem;
                  m_pList->AddTail(node);

                  m_pListStack.Push(m_pList);
                  node->m_pList = new XMLConfigList;
                  m_pList = node->m_pList;
                  m_pList->m_parentnode = node;
                }
                else if(strcasecmp(tag->m_name, "server") == 0)
                {
                  const char* server = tag->get_attribute("number");
                  if(!server)
                  {
                      ERRMSG(m_pMessages,
                           "%s: Server tag requires a number, defaulting to 0",
                           m_filename);
                      m_vserver = 0;
                  }
                  else
                  {
                      char vserver_str[64]; /* Flawfinder: ignore */
                      m_vserver = atol(server);
                      sprintf(vserver_str, "server%ld", m_vserver); /* Flawfinder: ignore */
                      if(m_pRegistry->GetTypeByName(vserver_str) == PT_UNKNOWN)
                      {
                        m_pRegistry->AddComp(vserver_str);
                        sprintf(vserver_str, "server%ld.config", /* Flawfinder: ignore */
                              m_vserver);
                        m_pRegistry->AddComp(vserver_str);
                      }
                  }
                }
            }
            else if(tag->m_type != XMLEndTag)
            {
                const char* name = tag->get_attribute("name");
                const char* value = tag->get_attribute("value");
                if(name && value)
                {
                  node->m_type  = CfgVar;
                  node->m_name  = new_string(name);
                  node->m_value = new_string(value);
                  node->m_num   = tag->elem;
                  m_pList->AddTail(node);
                }
            }
            else
            {
                // An End Tag
                if(strcasecmp(tag->m_name, "list") == 0)
                {
                  m_pList = (XMLConfigList*)m_pListStack.Pop();
                }
                else if(strcasecmp(tag->m_name, "server") == 0)
                {
                  if(m_vserver < 0)
                  {
                      ERRMSG(m_pMessages,
                           "%s: </Server> Tag with no matching <Server>",
                           m_filename);
                  }
                  else
                  {
                      m_vserver = -1;
                  }
                }
            }
            break;
          }
      }
    }

    if(hResult == HXR_OK && !bIncludedFile)
    {
      StuffRegistry(m_pList);
      delete m_pList->m_parentnode;
    }

    if (hResult == HXR_OK && !m_pListStack.IsEmpty())
    {
        ERRMSG(m_pMessages,
         "%s: <List> tag with no matching </List>",
         m_filename);
      hResult = HXR_FAIL;
    }

    // cleanup
    if (fp) fclose(fp);
    HX_VECTOR_DELETE(pBuf);
    HX_DELETE(tag);

    return hResult;
}

char*
XMLConfig::XMLConfigListNode::get_registry_name(INT32 vserver)
{
    char* name;
    if(m_parent)
    {
      char* parentname = m_parent->get_registry_name(
          vserver < 0 ? m_vserver : vserver);
      name = new char[strlen(parentname) + strlen(m_name) + 2];
      sprintf(name, "%s.%s", parentname, m_name); /* Flawfinder: ignore */
      delete [] parentname;
      return name;
    }
    else
    {
      if(vserver >= 0)
      {
          name = new char[26 + strlen(m_name) + 2];
          sprintf(name, "server%ld.%s", vserver, m_name); /* Flawfinder: ignore */
      }
      else
      {
          name = new_string(m_name);
      }
      return name;
    }
}

///XXXPM This func does not correctly handle the original file not
///being there.
HX_RESULT
XMLConfig::WriteToFile(const char* pKeyName, const char* pFileName)
{
    UINT32 ulListsToIgnore = 0;
    /*
     * Make sure we have a valid file name to use.
     */
    if (!pFileName)
    {
      return HXR_FAIL;
    }

    /*
     * Copy filename to filename.bak.
     */
    char *pNewFileName = new char[strlen(pFileName) + strlen(".bak") + 1];
    sprintf(pNewFileName, "%s.bak", pFileName); /* Flawfinder: ignore */
    FILE* fpOld = fopen(pFileName, "r");
    FILE* fpNew = fopen(pNewFileName, "w");
    if (!fpNew || !fpOld)
    {
      HX_VECTOR_DELETE(pNewFileName);
      if (fpNew) fclose(fpNew);
      if (fpOld) fclose(fpOld);
      return HXR_FAIL;
    }
    INT32 nBufSize=16384;
    BYTE* pBuf = new BYTE[nBufSize];
    int iGot = 0;
    while (!feof(fpOld))
    {
      iGot = fread(pBuf, sizeof(char), nBufSize, fpOld);
      if (iGot)
      {
          fwrite(pBuf, sizeof(char), iGot, fpNew);
      }
    }
    fclose(fpNew);
    fclose(fpOld);

#ifdef _UNIX
    chmod(pNewFileName, 0700);
#endif //UNIX

    fpNew = fopen(pFileName, "w");
    fpOld = fopen(pNewFileName, "r");
    HX_VECTOR_DELETE(pNewFileName);
    if (!fpNew || !fpOld)
    {
      if (fpNew) fclose(fpNew);
      if (fpOld) fclose(fpOld);
      return HXR_FAIL;
    }

    int l;
    BOOL filedone = 0;
    XMLTag*   tag = 0;
    XMLTag*   wspacetag = 0;
    CBigByteQueue queue(MAX_TAG_SIZE);
    XMLParser parser;
    HX_RESULT hResult = HXR_OK;

    /*
     * Files are all aready.  Now we need to build our config levels list.
     */
    CHXSimpleList levlist;
    /*
     *  Add for level 0.
     */
    CHXSimpleList* pNewList = new CHXSimpleList;
    IHXValues* pValues = 0;

    _AddPropsToList(pNewList, pKeyName, m_pRegistry);
    XMLConfigString curr_level;
    curr_level.AddLevel(pKeyName);

    while (!filedone && hResult == HXR_OK)
    {
        l = fread(pBuf, 1, nBufSize, fpOld);
        if (l > 0)
        {
            if (!queue.EnQueue(pBuf, l))
            {
                if (!queue.Grow(l))
                {
                    ERRMSG(m_pMessages,
                           "error expanding data structure while parsing");
                    hResult = HXR_FAIL;
                }
                if (!queue.EnQueue(pBuf, l))
                {
                    ERRMSG(m_pMessages,
                           "unknown error while parsing");
                    hResult = HXR_FAIL;
                }
            }
        }
        else
        {
            filedone = TRUE;
        }
    }

    UINT32 bytesUsed=0;
    UINT32 bytesAvail=0;
    BYTE* p=0;

    while (hResult == HXR_OK && (bytesAvail = queue.GetQueuedItemCount()) > 0)
    {
      if (bytesAvail > nBufSize)
        {
            HX_VECTOR_DELETE(pBuf);
            pBuf = new BYTE[bytesAvail];
            nBufSize = bytesAvail;
        }
      p = pBuf;

      queue.DeQueue(pBuf, bytesAvail);
      bytesUsed = bytesAvail;

        HX_DELETE(tag);
      XMLParseResult res = parser.Parse((const char*&)p, bytesAvail, tag);
      queue.EnQueue(p, bytesAvail - (p - pBuf));

      switch(res)
      {
          case XMLPNoClose:
            // A tag opener was found, but no closer yet.
            if (filedone)
            {
                pBuf[bytesAvail - 1] = '\0';
                if (bytesAvail > MAX_ERROR_CHARS)
                {
                  pBuf[MAX_ERROR_CHARS - 1] = '\0';
                }
                ERRMSG(m_pMessages,
                "%s: Missing close for tag %s...", m_filename, pBuf);
                hResult = HXR_FAIL;
            }
            break;
          case XMLPNoTagType:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Badly formed tag %s", m_filename, pBuf);
            break;
          case XMLPBadAttribute:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Badly formed attribute in %s", m_filename,
                   pBuf);
            break;
          case XMLPPlainText:
            /* 
             * Just plaintext.  Dump this out directly.
             */
            if (wspacetag)
            {
                delete wspacetag;
            }
            wspacetag = tag;
            tag = 0;
            /*
            fwrite(tag->m_cur_attribute->value, sizeof(char),
                strlen(tag->m_cur_attribute->value), fpNew);
                */
            break;
          case XMLPBadEndTag:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Unexpected tag %s", m_filename, pBuf);
            break;
          case XMLPComment:
            /*
             * Just a comment.  Dump it out.  Comment lives from
             * p to pBuf.
             */
            if (wspacetag)
            {
                fwrite(wspacetag->m_cur_attribute->value, sizeof(char),
                  strlen(wspacetag->m_cur_attribute->value), fpNew);
                delete wspacetag;
                wspacetag = 0;
            }

            fwrite(pBuf, sizeof(char), p - pBuf, fpNew);
            break;
          case XMLPDirective:
            // A directive.  We don't handle any yet.
            break;
          case XMLPProcInst:
                /*
             * Just dump processing instructions as we read them.
             */
            if (wspacetag)
            {
                fwrite(wspacetag->m_cur_attribute->value, sizeof(char),
                  strlen(wspacetag->m_cur_attribute->value), fpNew);
                delete wspacetag;
                wspacetag = 0;
            }

            fwrite(pBuf, sizeof(char), p - pBuf, fpNew);
            break;
          case XMLPTag:
          {
            if(Expand(tag, &queue))
                break;

            if(tag->m_need_close)
            {
                if(strcasecmp(tag->m_name, "list") == 0)
                {
                  if (ulListsToIgnore)
                  {
                      ulListsToIgnore++;
                  }
                  else
                  {
                      const char* name = tag->get_attribute("name");
                      if(!name)
                      {
                        ERRMSG(m_pMessages,
                               "%s: List tag requires a name attribute",
                               m_filename);
                        // Process anyway, give it a dummy name
                        name = "XXXBADLIST";
                      }
                      /*
                       * Remove current tag from list, push current list,
                       * create new list, add next level's props.
                       */
                      _RemovePropFromList(pNewList, name);
                      curr_level.AddLevel(name);
                      if (!_PropExists(&curr_level, m_pRegistry))
                      {
                        curr_level.RemoveLevel();
                        ulListsToIgnore++;
                      }
                      else
                      {
                        levlist.AddHead((void*)pNewList);
                        if (wspacetag)
                        {
                            fwrite(wspacetag->m_cur_attribute->value, sizeof(char),
                              strlen(wspacetag->m_cur_attribute->value), fpNew);
                            delete wspacetag;
                            wspacetag = 0;
                        }

                        fprintf(fpNew, "<List Name=\"%s\">", name);
                        pNewList = new CHXSimpleList;
                        _AddPropsToList(pNewList, curr_level.CharStar(), m_pRegistry);
                      }
                  }
                  
                }
            }
            else if(tag->m_type != XMLEndTag)
            {
                if (!ulListsToIgnore)
                {
                  const char* name = tag->get_attribute("name");
                  const char* value = tag->get_attribute("value");
                  if (name)
                  {
                      curr_level.AddLevel(name);
                      _RemovePropFromList(pNewList, name);
                      /*
                       * Make sure this prop is still set.
                       */
                      if (_PropExists(&curr_level, m_pRegistry))
                      {
                        if (wspacetag)
                        {
                            fwrite(wspacetag->m_cur_attribute->value, sizeof(char),
                              strlen(wspacetag->m_cur_attribute->value), fpNew);
                            delete wspacetag;
                            wspacetag = 0;
                        }

                        char* p = _GetPropValueString(curr_level.CharStar(), m_pRegistry);

                        if (p)
                        {
                            fprintf(fpNew, "<Var %s=\"%s\"/>",
                              name,
                              p);
                            delete[] p;
                        }
                      }
                      curr_level.RemoveLevel();
                  }
                }
            }
            else
            {
                // An End Tag
                if(strcasecmp(tag->m_name, "list") == 0)
                {
                  if (ulListsToIgnore)
                  {
                      ulListsToIgnore --;
                  }
                  else
                  {
                      /*
                       * Need to add the stuff at this level that was
                       * was not in the config file.
                       */
                      if (pNewList->GetCount() == 0)
                      {
                        if (wspacetag)
                        {
                            fwrite(wspacetag->m_cur_attribute->value, sizeof(char),
                              strlen(wspacetag->m_cur_attribute->value), fpNew);
                        }
                      }
                      else
                      {
                        fprintf(fpNew, "\n");
                        AppendPropsToFile(fpNew, curr_level, pNewList,
                            4, m_pRegistry, pKeyName);
                        _IndentFile(fpNew, 4, curr_level, pKeyName);
                      }
                      delete wspacetag;
                      wspacetag = 0;

                      fprintf(fpNew, "</List>");
                      curr_level.RemoveLevel();
                      delete pNewList;
                      pNewList = (CHXSimpleList*)levlist.RemoveHead();
                  }
                }
            }
            break;
          }
      }

      // Give up if we've lost all hope
      if (HXR_OK != hResult)
      {
          break;
      }
    }
    if (hResult == HXR_OK && wspacetag)
    {
      fwrite(wspacetag->m_cur_attribute->value, sizeof(char),
          strlen(wspacetag->m_cur_attribute->value), fpNew);
      delete wspacetag;
      wspacetag = 0;
    }

    /*
     * Ok, done with the whole file. Make sure that all of the base level stuff
     * gets added.
     */
     //XXXPM Calculate this later.
    if (hResult == HXR_OK)
    {
        int indent = 4;
        AppendPropsToFile(fpNew, curr_level, pNewList, indent, m_pRegistry, pKeyName);
    }

    // cleanup
    if (fpNew) fclose(fpNew);
    if (fpOld) fclose(fpOld);
    HX_DELETE(pNewList);
    HX_VECTOR_DELETE(pBuf);
    HX_DELETE(tag);

    return hResult;
}


void
XMLConfig::_AddPropsToList(CHXSimpleList* pList, const char* pName,
                    IHXRegistry2* preg)
{
    IHXValues* pValues = 0;
    UINT32 ul;
    const char* pPropName;
    preg->GetPropListByName(pName, pValues);

    if (pValues)
    {
      if (HXR_OK == pValues->GetFirstPropertyULONG32(pPropName, ul))
      {
          XMLPropInfo* pInfo = new XMLPropInfo;
          pInfo->m_pName = new char[strlen(pPropName) + 1 - strlen(pName)];
          strcpy(pInfo->m_pName, &(pPropName[strlen(pName) + 1])); /* Flawfinder: ignore */
          pInfo->m_Type = preg->GetTypeByName(pPropName);
          pList->AddHead((void*)pInfo);

          while (HXR_OK == pValues->GetNextPropertyULONG32(pPropName, ul))
          {
            XMLPropInfo* pInfo = new XMLPropInfo;
            pInfo->m_pName = new char[strlen(pPropName) + 1 - strlen(pName)];
            strcpy(pInfo->m_pName, &(pPropName[strlen(pName) + 1]));
            pInfo->m_Type = preg->GetTypeByName(pPropName);
            pList->AddHead((void*)pInfo);
          }
      }
      pValues->Release();
    }
}

void
XMLConfig::_RemovePropFromList(CHXSimpleList* pList, const char* pName)
{
    LISTPOSITION pos;
    pos = pList->GetHeadPosition();

    XMLPropInfo* pProp;

    
    while (pos)
    {
      pProp = (XMLPropInfo*)pList->GetAt(pos);
      if (!pProp)
      {
          return;
      }
      if (!strcasecmp(pProp->m_pName, pName))
      {
          pList->RemoveAt(pos);
          delete pProp;
          return;
      }
      pList->GetNext(pos);

    }
}

char*
XMLConfig::_GetPropValueString(const char* pName, IHXRegistry2* pReg)
{
    int vartype;
    IHXBuffer* pBuffer;
    INT32 l;
    char* ret;

    vartype = pReg->GetTypeByName(pName);
    if(vartype == PT_INTEGER)
    {
      if(HXR_OK == pReg->GetIntByName(pName, l))
      {
          char num[32]; /* Flawfinder: ignore */
          sprintf(num, "%d", l); /* Flawfinder: ignore */
          ret = new char[strlen(num) + 1];
          strcpy(ret, num); /* Flawfinder: ignore */
          return ret;
      }
      else
      {
          return 0;
      }
    }
    else if(vartype == PT_STRING)
    {
      if(HXR_OK == pReg->GetStrByName(pName,
          pBuffer) && pBuffer)
      {
          ret = new char[pBuffer->GetSize()+1];
          strcpy(ret, (const char*)pBuffer->GetBuffer()); /* Flawfinder: ignore */
          pBuffer->Release();
          return ret;
      }
      else
      {
          return 0;
      }
    }
    return 0;
}

void
XMLConfig::_CleanList(CHXSimpleList* pList)
{
    XMLPropInfo* pInfo;
    while (!pList->IsEmpty())
    {
      pInfo = (XMLPropInfo*)pList->RemoveHead();
      delete pInfo;
    }
}

void
XMLConfig::AppendPropsToFile(FILE* fp, XMLConfigString level,
    CHXSimpleList* pList, int indent_per_level,
    IHXRegistry2* hxreg, const char* pBase)
{
    XMLPropInfo* pInfo = 0;
    while (!pList->IsEmpty())
    {
      pInfo = (XMLPropInfo*)pList->RemoveHead();
      level.AddLevel(pInfo->m_pName);
      _AppendPropToFile(fp, level, indent_per_level, hxreg, pBase);
      level.RemoveLevel();
      delete pInfo;
    }
}

void
XMLConfig::_IndentFile(FILE* fp, int indent_per_level,
                   XMLConfigString level,
                   const char* pBase)
{
    int dots_to_ignore = 1;
    INT32 i;
    const char* pc;
    pc = pBase;
    while (*pc)
    {
      if (*pc == '.')
      {
          dots_to_ignore ++;
      }
      pc++;
    }
    pc = level.CharStar();
    while (*pc)
    {
      if (*pc == '.')
      {
          if (dots_to_ignore)
          {
            dots_to_ignore--;
          }
          else
          {
            for (i = 0; i < indent_per_level; i++)
            {
                fprintf(fp, " ");
            }
          }
      }
      pc ++;
    }
}

void
XMLConfig::_AppendPropToFile(FILE* fp, XMLConfigString level,
                       int indent_per_level,
                       IHXRegistry2* hxreg,
                       const char* pBase)
{
    /*
     * Indent a proper amount.
     */
    INT32 i;
    IHXBuffer* pBuf = 0;

    _IndentFile(fp, indent_per_level, level, pBase);

    HXPropType type = hxreg->GetTypeByName(level.CharStar());
    switch (type)
    {
    case PT_COMPOSITE:
      fprintf(fp, "<List Name=\"%s\">\n", level.Top());
      IHXValues* pValues;
      if (HXR_OK == hxreg->GetPropListByName(level.CharStar(),
          pValues) && pValues)
      {
          HX_RESULT res;
          UINT32 ul;
          const char* pName;
          res = pValues->GetFirstPropertyULONG32(pName, ul);
          while (res == HXR_OK)
          {
            const char* pc = pName + strlen(pName);
            while (pc > pName)
            {
                pc--;
                if (*pc == '.')
                {
                  pc++;
                  level.AddLevel(pc);
                  _AppendPropToFile(fp, level, indent_per_level, hxreg, pBase);
                  level.RemoveLevel();
                  res = pValues->GetNextPropertyULONG32(pName, ul);
                  break;
                }
            }
          }
          pValues->Release();
      }
      _IndentFile(fp, indent_per_level, level, pBase);
      fprintf(fp, "</List>\n");
      break;

    case PT_INTEGER:
      if (HXR_OK == hxreg->GetIntByName(level.CharStar(), i))
      {
          fprintf(fp, "<Var %s=\"%ld\"/>\n", level.Top(),
            i);
      }
      break;

    case PT_STRING:
      if (HXR_OK == hxreg->GetStrByName(level.CharStar(), pBuf)
          && pBuf)
      {
          fprintf(fp, "<Var %s=\"%s\"/>\n", level.Top(),
            (const char*)pBuf->GetBuffer());
          pBuf->Release();
          pBuf = 0;
      }
      break;
    }
}

int
XMLConfig::_PropExists(XMLConfigString* p, IHXRegistry2* preg)
{
    return (preg->GetPropStatusByName(p->CharStar()) == HXR_OK) ? TRUE : FALSE;
}

/*
 * IHXRegConfig::WriteKey
 */
STDMETHODIMP
XMLConfig::WriteKey(const char* pKeyName)
{
    /*
     * If there is a filename then we got it from a file.
     */
    if (m_filename)
    {
      WriteToFile(pKeyName, m_filename);
      return HXR_OK;
    }

    HX_ASSERT(0);
    return HXR_FAIL;
}

HX_RESULT
XMLConfig::Reconfigure(IHXReconfigServerResponse* pResp)
{
    if (m_pReconfigureResponse)
    {
      return HXR_UNEXPECTED;
    }
    m_pReconfigureResponse = pResp;
    m_pReconfigureResponse->AddRef();

    /*
     * If they started from a config file, then reload
     * from the config file.
     */
    if (m_filename)
    {
      return Reconfigure(m_filename);
    }

    /*
     * If we got here there was a booboo
     */
    m_pReconfigureResponse->ReconfigServerDone(HXR_OK, 0, 0);
    m_pReconfigureResponse->Release();
    m_pReconfigureResponse = 0;

    return HXR_OK;
}

HX_RESULT
XMLConfig::Reconfigure(const char* pFileName)
{
    if (!pFileName)
    {
      return HXR_FAIL;
    }

    FILE* fp = fopen(pFileName, "r");
    if (!fp)
    {
      return HXR_FAIL;
    }

    /*
     * Pretend that there is one outstanding just to keep
     * the done methods from getting called, seeing a 0,
     * and sending the response until the end of this 
     * func.
     */
    m_ActiveSetsOutstanding++;

    const char* pKeyName = "config";
    int l = 0;
    BOOL filedone = 0;
    XMLTag*   tag = 0;
    CBigByteQueue queue(MAX_TAG_SIZE);
    XMLParser parser;
    HX_RESULT hResult = HXR_OK;

    /*
     * Files are all aready.  Now we need to build our config levels list.
     */
    CHXSimpleList levlist;
    /*
     *  Add for level 0.
     */
    CHXSimpleList* pNewList = new CHXSimpleList;
    IHXValues* pValues = 0;

    _AddPropsToList(pNewList, pKeyName, m_pRegistry);
    XMLConfigString curr_level;
    curr_level.AddLevel(pKeyName);

    // read the config file into the byte queue
    INT32 nBufSize = 16384;
    BYTE* pBuf = new BYTE[nBufSize];
    while (!filedone && hResult == HXR_OK)
    {
        l = fread(pBuf, 1, nBufSize, fp);
        if (l > 0)
        {
            if (!queue.EnQueue(pBuf, l))
            {
                if (!queue.Grow(l))
                {
                    ERRMSG(m_pMessages,
                           "error expanding data structure while parsing %s",
                           pFileName);
                    hResult = HXR_FAIL;
                }
                if (!queue.EnQueue(pBuf, l))
                {
                    ERRMSG(m_pMessages,
                           "unknown error while parsing %s",
                           pFileName);
                    hResult = HXR_FAIL;
                }
            }
      }
      else
      {
            filedone = TRUE;
      }
    }

    UINT32 bytesUsed=0;
    UINT32 bytesAvail=0;
    BYTE* p=0;
    while (hResult == HXR_OK && (bytesAvail = queue.GetQueuedItemCount()) > 0)
    {
      if (bytesAvail > nBufSize)
        {
            HX_VECTOR_DELETE(pBuf);
            pBuf = new BYTE[bytesAvail];
            nBufSize = bytesAvail;
        }
      p = pBuf;

      queue.DeQueue(pBuf, bytesAvail);
      bytesUsed = bytesAvail;

        HX_DELETE(tag);
      XMLParseResult res = parser.Parse((const char*&)p, bytesAvail, tag);
      queue.EnQueue(p, bytesAvail - (p - pBuf));

      switch(res)
      {
          case XMLPNoClose:
            // A tag opener was found, but no closer yet.
            if (filedone)
            {
                pBuf[bytesAvail - 1] = '\0';
                if (bytesAvail > MAX_ERROR_CHARS)
                {
                  pBuf[MAX_ERROR_CHARS - 1] = '\0';
                }
                ERRMSG(m_pMessages,
                "%s: Missing close for tag %s...", m_filename, pBuf);
                hResult = HXR_FAIL;
            }
            break;
          case XMLPNoTagType:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Badly formed tag %s", m_filename, pBuf);
            break;
          case XMLPBadAttribute:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Badly formed attribute in %s", m_filename,
                   pBuf);
            break;
          case XMLPPlainText:
            break;
          case XMLPBadEndTag:
            *p = 0;
            ERRMSG(m_pMessages,
                   "%s: Unexpected tag %s", m_filename, pBuf);
            break;
          case XMLPComment:
            break;
          case XMLPDirective:
            // A directive.  We don't handle any yet.
            break;
          case XMLPProcInst:
            // A processing Instruction, whee!
            break;
          case XMLPTag:
          {
            if(Expand(tag, &queue))
                break;

            if(tag->m_need_close)
            {
                if(strcasecmp(tag->m_name, "list") == 0)
                {
                  const char* name = tag->get_attribute("name");
                  if(!name)
                  {
                      ERRMSG(m_pMessages,
                           "%s: List tag requires a name attribute",
                           m_filename);
                      // Process anyway, give it a dummy name
                      name = "XXXBADLIST";
                  }
                  /*
                   * Remove current tag from list, push current list,
                   * create new list, add next level's props.
                   */
                  _RemovePropFromList(pNewList, name);
                  curr_level.AddLevel(name);
                  levlist.AddHead((void*)pNewList);
                  pNewList = new CHXSimpleList;
                  _AddPropsToList(pNewList, curr_level.CharStar(), m_pRegistry);
                  
                }
            }
            else if(tag->m_type != XMLEndTag)
            {
                const char* name = tag->get_attribute("name");
                const char* value = tag->get_attribute("value");
                if (name)
                {
                  curr_level.AddLevel(name);
                  _RemovePropFromList(pNewList, name);
                  /*
                   * Here we need to try to set that value.
                   */
                  _ResetProp(&curr_level, value, m_pRegistry);
                  curr_level.RemoveLevel();
                }
            }
            else
            {
                // An End Tag

                /*
                 * Here we need to try to delete everything that
                 * was not in the file.
                 */
                if(strcasecmp(tag->m_name, "list") == 0)
                {
                  _HandlePropsRemovedFromFile(
                      &curr_level, pNewList,
                      m_pRegistry);
                  curr_level.RemoveLevel();
                  delete pNewList;
                  pNewList = (CHXSimpleList*)levlist.RemoveHead();
                }
            }
            break;
          }
      }
    }
    
    _HandlePropsRemovedFromFile(&curr_level, pNewList, m_pRegistry);

    // cleanup
    if (fp) fclose(fp);
    HX_DELETE(tag);
    HX_DELETE(pNewList);
    HX_VECTOR_DELETE(pBuf);

    // send reconfig response
    m_ActiveSetsOutstanding--;
    MaybeSendReconfigResponse();

    return hResult;
}

#ifdef _WIN32
HX_RESULT
XMLConfig::ReconfigureFromReg(const char* pKeyname)
{
    HX_ASSERT(pKeyname && strlen(pKeyname));

    /* 
     * This should be a duplication of the above loop just using
     * the registry instead of a file.
     */

    /*
     * Pretend that there is one outstanding just to keep
     * the done methods from getting called, seeing a 0,
     * and sending the response until the end of this 
     * func.
     */
    m_ActiveSetsOutstanding++;

    CHXSimpleList levlist;
    CHXSimpleList indexlist;
    int curr_index = 0;
    UINT32 ulBufLen = 512;
    UINT32 ulRealBufLen = 512;
    char* pRegBuf = new char[512];

    UINT32 ulBufLen2 = 256;
    UINT32 ulRealBufLen2 = 256;
    char* pRegBuf2 = new char[256];

    UINT32 ulBufLen3 = 256;
    UINT32 ulRealBufLen3 = 256;
    char* pRegBuf3 = new char[256];
    FILETIME msbs;
    /*
     *  Add for level 0.
     */
    CHXSimpleList* pNewList = new CHXSimpleList;
    IHXValues* pValues = 0;

    _AddPropsToList(pNewList, "Config", m_pRegistry);
    XMLConfigString curr_level;
    curr_level.AddLevel("Config");

    char* pRegBase = new char[512];
    memset(pRegBase, 0, 512);
    SafeSprintf(pRegBase, 512, "Software\\RealNetworks\\%s\\%d.%d\\",
            m_szServerversion, m_ulMajor, m_ulMinor);
    UINT32 ulRegBase = strlen(pRegBase) + 1;

    HKEY hKey=0;
    LONG ret=0;
    BOOL bDone = 0;
    char* pc=0;
    /*
     * While we are not done...
     */
    while (!bDone)
    {
      pc = pRegBase + ulRegBase;
      SafeStrCpy(pc, curr_level.CharStar(), 512-ulRegBase);
      while (pc < pRegBase + 512 && *pc)
      {
          if (*pc == '.')
          {
            *pc = '\\';
          }
          pc++;
      }
      ret = RegOpenKeyEx(HKEY_CLASSES_ROOT, pRegBase, 0, KEY_READ, &hKey);
      if (ret != ERROR_SUCCESS)
      {
          //XXXPM fix this
          HX_ASSERT(0);
      }

      ulBufLen = ulRealBufLen;
      //XXXPM handle buffer too small
      ret = RegEnumKeyEx(hKey, curr_index,
          pRegBuf, &ulBufLen, 0, NULL, NULL, &msbs);
      curr_index++;
      /*
       * If it is a composite...
       */
      if (ret == ERROR_SUCCESS)
      {
          const char* name = pRegBuf;
          _RemovePropFromList(pNewList, name);
          curr_level.AddLevel(name);
          levlist.AddHead((void*)pNewList);
          indexlist.AddHead((void*)curr_index);
          curr_index = 0;
          pNewList = new CHXSimpleList;
          _AddPropsToList(pNewList, curr_level.CharStar(), m_pRegistry);
      }
      /*
       * It is a non composite value, so we need to get that value.
       */
      else
      {
          const char* name = pRegBuf;
          const char* value = 0;
          ulBufLen2 = ulRealBufLen2;
          ulBufLen3 = ulRealBufLen3;
          DWORD dwType;
          //XXXPM handle buffer too small

          ret = RegEnumValue(hKey, 0, pRegBuf2, &ulBufLen2, 0, &dwType, 
                           (unsigned char*)pRegBuf3, &ulBufLen3);
          if (ret == ERROR_SUCCESS)
          {
            value = pRegBuf3;
            _ResetProp(&curr_level, value, m_pRegistry);
            curr_level.RemoveLevel();
            delete pNewList;
            pNewList = (CHXSimpleList*)levlist.RemoveHead();
            curr_index = (int)indexlist.RemoveHead();
          }
          else
          {
            /*
             * Closing a composite.
             */
            _HandlePropsRemovedFromFile(&curr_level, pNewList, m_pRegistry);
            curr_level.RemoveLevel();
            delete pNewList;
            if (!levlist.IsEmpty())
            {
                pNewList = (CHXSimpleList*)levlist.RemoveHead();
                curr_index = (int)indexlist.RemoveHead();
            }
            else
            {
                pNewList = 0;
                bDone = 1;
            }
          }
      }
      RegCloseKey(hKey);

    }

    delete[] pRegBase;
    delete[] pRegBuf;
    delete[] pRegBuf2;
    delete[] pRegBuf3;

    m_ActiveSetsOutstanding--;
    MaybeSendReconfigResponse();

    return HXR_OK;
}
#endif

HX_RESULT
XMLConfig::_ResetProp(XMLConfigString* plevel, const char* value, IHXRegistry2* preg)
{
    HX_RESULT res = HXR_FAIL;
    IHXActiveRegistry* pActiveReg = 0;
    LONG32 ulCurrent;
    LONG32 ulNew;
    preg->QueryInterface(IID_IHXActiveRegistry,
      (void**)&pActiveReg);
    if (pActiveReg == 0)
    {
      return HXR_FAIL;
    }

    /*
     * If the prop exists, then figure out the type.
     */
    if (_PropExists(plevel, preg))
    {
      HXPropType type;
      type = preg->GetTypeByName(plevel->CharStar());
      switch (type)
      {
      case PT_INTEGER:
          ulCurrent = 0;
          ulNew = atoi(value);

          /*
           * If it is an int and we are not changing its value, then say ok and
           * exit.
           */
          preg->GetIntByName(plevel->CharStar(),
            ulCurrent);
          if (ulCurrent == ulNew)
          {
            res = HXR_OK;
            goto done;
          }

          /*
           * Try to set it by hand first.
           */
          res = preg->SetIntByName(plevel->CharStar(),
            atoi(value));

          /*
           * If it failed because it was active, then use the active reg
           * to set it and add it to our outstanding actives count.
           */
          if (res == HXR_PROP_ACTIVE)
          {
            m_ActiveSetsOutstanding ++;
            res = pActiveReg->SetActiveInt(plevel->CharStar(),
                      ulNew, this);
            if (res != HXR_OK)
            {
                m_ActiveSetsOutstanding --;
            }
          }
          /*
           * res should be set for good by now.
           */
          goto done;
          
      case PT_STRING:
          IHXBuffer* pCurrent = 0;

          /*
           * See if we are changing this string value at all.
           */
          preg->GetStrByName(plevel->CharStar(),
            pCurrent);
          if (pCurrent)
          {
            /*
             * If we are not changing it at all then say ok and exit.
             */
            if (!strcmp((const char*)pCurrent->GetBuffer(),
                value))
            {
                pCurrent->Release();
                res = HXR_OK;
                goto done;
            }
            pCurrent->Release();
          }

          /*
           * We are changing it so get a new buffer and set it.
           */
          IHXBuffer* pBuffer = new CHXBuffer();
            pBuffer->AddRef();
          pBuffer->Set((const unsigned char*)value, strlen(value) + 1);

          res = preg->SetStrByName(plevel->CharStar(),
            pBuffer);

          /*
           * If we failed to set it because it is active, then use the
           * active registry and add to our outstanding actives count.
           */
          if (res == HXR_PROP_ACTIVE)
          {
            m_ActiveSetsOutstanding++;
            res = pActiveReg->SetActiveStr(plevel->CharStar(),
                      pBuffer, this);
            if (res != HXR_OK)
            {
                m_ActiveSetsOutstanding --;
            }
          }
          pBuffer->Release();
          goto done;

      }
      
    }
    else
    {
      /*
       * This is the case where this prop was not already there,
       * so figure out the type from the data.
       */
      BOOL bIsNum = TRUE;
      const char* pc = value;
      while(*pc)
      {
          if (*pc < '0' || *pc > '9')
          {
            bIsNum = FALSE;
            break;
          }
          pc++;
      }
      if (pc == value)
      {
          bIsNum = FALSE;
      }

      if (bIsNum)
      {
          /*
           * It should be an Integer type so attempt to Set it through the
           * active reg.  (This acts like an add through the active reg.  Could
           * have tried to add it by hand first, but didn't.
           */
          m_ActiveSetsOutstanding++;
          res = pActiveReg->SetActiveInt(plevel->CharStar(),
            atoi(value), this);
          if (res != HXR_OK)
          {
            m_ActiveSetsOutstanding--;
          }

      }
      else
      {
          /*
           * It should be a string type.  We will just try to do it through active.
           * (see last comment).
           */
          IHXBuffer* pBuffer = new CHXBuffer();
            pBuffer->AddRef();
          pBuffer->Set((const unsigned char*)value,
            strlen(value) + 1);
          m_ActiveSetsOutstanding++;
          res = pActiveReg->SetActiveStr(plevel->CharStar(),
            pBuffer, this);
          if (res != HXR_OK)
          {
            m_ActiveSetsOutstanding--;
          }
          pBuffer->Release();
      }
    }
done:;
    pActiveReg->Release();
    return res;
}

/************************************************************************
 * IHXActivePropUserResponse stuff.
 *
 * Called with status result on completion of set request.
 */
STDMETHODIMP
XMLConfig::SetActiveIntDone(HX_RESULT res,
                        const char* pName,
                        UINT32 ul,
                        IHXBuffer* pInfo[],
                        UINT32 ulNumInfo)
{
    m_ActiveSetsOutstanding--;
    MaybeSendReconfigResponse();
    return HXR_OK;
}

STDMETHODIMP
XMLConfig::SetActiveStrDone(HX_RESULT res,
                        const char* pName,
                        IHXBuffer* pBuffer,
                        IHXBuffer* pInfo[],
                        UINT32 ulNumInfo)
{
    m_ActiveSetsOutstanding--;
    MaybeSendReconfigResponse();
    return HXR_OK;
}

STDMETHODIMP
XMLConfig::SetActiveBufDone(HX_RESULT res,
                        const char* pName,
                        IHXBuffer* pBuffer,
                        IHXBuffer* pInfo[],
                        UINT32 ulNumInfo)
{
    m_ActiveSetsOutstanding--;
    MaybeSendReconfigResponse();
    return HXR_OK;
}

STDMETHODIMP
XMLConfig::DeleteActivePropDone(HX_RESULT res,
                        const char* pName,
                        IHXBuffer* pInfo[],
                        UINT32 ulNumInfo)
{
    m_ActiveSetsOutstanding--;
    MaybeSendReconfigResponse();
    return HXR_OK;
}

void
XMLConfig::MaybeSendReconfigResponse()
{
    /*
     * If everything has not happened yet,
     * then bail.
     */
    if (m_ActiveSetsOutstanding)
    {
      return;
    }

    m_pReconfigureResponse->ReconfigServerDone(HXR_OK,
      0, 0);
    m_pReconfigureResponse->Release();
    m_pReconfigureResponse = 0;
}

void
XMLConfig::_HandlePropsRemovedFromFile(XMLConfigString* plevel,
                               CHXSimpleList* pList,
                               IHXRegistry2* preg)
{
    XMLPropInfo* pInfo;
    while (!pList->IsEmpty())
    {
      pInfo = (XMLPropInfo*)pList->RemoveHead();
      plevel->AddLevel(pInfo->m_pName);
      switch (pInfo->m_Type)
      {
      case PT_STRING:
          {
            IHXBuffer* pval;
            pval = _GetDefaultValString(
                plevel->CharStar(), preg);
            if (!pval)
            {
                if (!preg->DeleteByName(
                  plevel->CharStar()))
                {
                  IHXActiveRegistry* pActiveReg = 0;
                  preg->QueryInterface(IID_IHXActiveRegistry,
                      (void**)&pActiveReg);
                  if (pActiveReg)
                  {
                      m_ActiveSetsOutstanding++;
                      if (HXR_OK != pActiveReg->DeleteActiveProp(
                        plevel->CharStar(),
                        this))
                      {
                        m_ActiveSetsOutstanding--;
                      }
                      pActiveReg->Release();
                  }
                }
            }
            else
            {
                if (HXR_PROP_ACTIVE == preg->SetStrByName(plevel->CharStar(),
                  pval))
                {
                  IHXActiveRegistry* pActiveReg = 0;
                  preg->QueryInterface(IID_IHXActiveRegistry,
                      (void**)&pActiveReg);
                  if (pActiveReg)
                  {
                      m_ActiveSetsOutstanding++;
                      if (HXR_OK != pActiveReg->SetActiveStr(
                        plevel->CharStar(), pval,
                        this))
                      {
                        m_ActiveSetsOutstanding--;
                      }
                      pActiveReg->Release();
                  }
                }
                pval->Release();
            }
          }
          break;

      case PT_INTEGER:
          {
            INT32 i;
            if (!_GetDefaultValInt(
                plevel->CharStar(),
                &i, preg))
            {
                if (!preg->DeleteByName(
                  plevel->CharStar()))
                {
                  IHXActiveRegistry* pActiveReg = 0;
                  preg->QueryInterface(IID_IHXActiveRegistry,
                      (void**)&pActiveReg);
                  if (pActiveReg)
                  {
                      m_ActiveSetsOutstanding++;
                      if (HXR_OK != pActiveReg->DeleteActiveProp(
                        plevel->CharStar(),
                        this))
                      {
                        m_ActiveSetsOutstanding--;
                      }
                      pActiveReg->Release();
                  }
                }
            }
            else
            {
                if (HXR_PROP_ACTIVE == preg->SetIntByName(plevel->CharStar(),
                  i))
                {
                  IHXActiveRegistry* pActiveReg = 0;
                  preg->QueryInterface(IID_IHXActiveRegistry,
                      (void**)&pActiveReg);
                  if (pActiveReg)
                  {
                      m_ActiveSetsOutstanding++;
                      if (HXR_OK != pActiveReg->SetActiveInt(
                        plevel->CharStar(),
                        i, this))
                      {
                        m_ActiveSetsOutstanding--;
                      }
                      pActiveReg->Release();
                  }
                }
            }
          }
          break;

      case PT_COMPOSITE:
          ///XXXPM there will be a bug here if we have default vars in sub lists and 
          //we try and delete the list.  The default vals will not be found here.
          IHXActiveRegistry* pActiveReg = 0;
          preg->QueryInterface(IID_IHXActiveRegistry,
            (void**)&pActiveReg);
          if (pActiveReg)
          {
            m_ActiveSetsOutstanding++;
            HX_RESULT res;
            res = pActiveReg->DeleteActiveProp(plevel->CharStar(),
                this);
            if (res != HXR_OK)
            {
                m_ActiveSetsOutstanding--;
            }
            pActiveReg->Release();
          }
          break;

      }

      plevel->RemoveLevel();
      delete pInfo;
    }
      
}

IHXBuffer*
XMLConfig::_GetDefaultValString(const char* pProp,
                        IHXRegistry2* preg)
{
    IHXBuffer* pBuffer = 0;
    char configdefaults[256]; /* Flawfinder: ignore */
    HX_ASSERT(strlen(pProp) < 220);
    SafeSprintf(configdefaults, 256, "configdefaults%s", strchr(pProp, '.'));
    preg->GetStrByName(configdefaults, pBuffer);
    return pBuffer;
}

int
XMLConfig::_GetDefaultValInt(const char* pProp, INT32* pOut,
                       IHXRegistry2* preg)
{
    char configdefaults[256]; /* Flawfinder: ignore */
    HX_ASSERT(strlen(pProp) < 220);
    SafeSprintf(configdefaults, 256, "configdefaults%s", strchr(pProp, '.'));
    if (HXR_OK != preg->GetIntByName(configdefaults, *pOut))
    {
      return 0;
    }
    return 1;
}

Generated by  Doxygen 1.6.0   Back to index