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

smplfsys.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: smplfsys.cpp,v 1.3.8.5 2004/07/09 02:04:08 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 ***** */

/////////////////////////////////////////////////////////////////////////////
//
//  Simple File System for simple synchronous local files
//
//  This is a very simple file system, it just calls basic standard lib
//  calls for open, seek, read, etc...
//

#define INITGUID    1

#include "hxcom.h"
#include "hxtypes.h"
#include "../smplfsys.ver"

#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxplugn.h"
#include "hxengin.h"
#include "hxcore.h"
#include "hxprefs.h"
#include "hxrendr.h"
#include "hxmon.h"
#include "hxauth.h"
#include "hxauthn.h"
#include "hxplgns.h"
#include "hxdataf.h"
#include "hxtick.h"

#include "debug.h"

#undef INITGUID

#include "hxathsp.h"
#include "hxcorsp.h"
#include "hxpktsp.h"
#include "hxcomsp.h"
#include "hxplnsp.h"
#include "hxspriv.h"
#include "hlxosstr.h"

#include "timeval.h"
#include "tparse.h"
#include "dbcs.h" // for HXCompareStrings
#include "hxstring.h"   // for CHXString
#include "hxxfile.h"    // for HXXFile::GetReasonableLocalFileName()
#include "hxstrutl.h"
#include "hxver.h"
#include "chxpckts.h"
#include "hxurl.h"
#include "hxperf.h"
#include "hxcbobj.h"

#include "hxdir.h"

#include <stdio.h>
#include <string.h>

#ifdef _MACINTOSH
#include <fcntl.h>
#include "chxdataf.h"   // Macintosh file i/o
#include "macasyncfile.h"  // Macintosh interrupt file i/o
#ifdef _MAC_MACHO
#include <sys/stat.h>
#include "hlxclib/fcntl.h"
#include <unistd.h> // for unlink
#else
#include <unix.h> // for unlink
#endif
#ifdef _UNIX /* including unix.h defines _UNIX */
#undef _UNIX
#endif
#elif (defined (_WINDOWS ) || defined (_WIN32)) && !defined(WIN32_PLATFORM_PSPC)
#include <direct.h>
#include "datffact.h"
#else
#include "hlxclib/sys/types.h"
#include "hlxclib/sys/stat.h"
#include "hlxclib/fcntl.h"
#include "datffact.h"
#endif
#include "findfile.h"
#include "smplmlog.h"
#include "baseobj.h"
#if defined(HELIX_FEATURE_PROGDOWN)
#include "progdown.h"
#include "microsleep.h"
#endif
#include "smplfsys.h"
#include "hxperf.h"

#ifdef _AIX
#include "hxtbuf.h"
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(Smplfsys);
#endif

// same for all the platforms...may need to tweak it, if necessary
#define MAX_ITERATION_COUNT    200

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif

#ifndef HELIX_CONFIG_NOSTATICS
INT32 smpl_nRefCount = 0;
#endif      // HELIX_CONFIG_NOSTATICS

HX_ENABLE_CHECKPOINTS_FOR_MODULE( "SmplFsys", "SmplFsysPerf.log" )

const char* const CSimpleFileSystem::zm_pDescription  = "RealNetworks Local File System";
const char* const CSimpleFileSystem::zm_pCopyright    = HXVER_COPYRIGHT;
const char* const CSimpleFileSystem::zm_pMoreInfoURL  = HXVER_MOREINFO;
const char* const CSimpleFileSystem::zm_pShortName    = "pn-local";
const char* const CSimpleFileSystem::zm_pProtocol     = "file";

/****************************************************************************
 *
 *  Function:
 *
 *    HXCreateInstance()
 *
 *  Purpose:
 *
 *    Function implemented by all plugin DLL's to create an instance of
 *    any of the objects supported by the DLL. This method is similar to
 *    Window's CoCreateInstance() in its purpose, except that it only
 *    creates objects from this plugin DLL.
 *
 *    NOTE: Aggregation is never used. Therefore and outer unknown is
 *    not passed to this function, and you do not need to code for this
 *    situation.
 *
 */
STDAPI ENTRYPOINT(HXCREATEINSTANCE)
(
    IUnknown**  /*OUT*/ ppIUnknown
)
{
    // Do NOT check for expiration.  Needed for Auto Upgrade.

    *ppIUnknown = (IUnknown*)(IHXPlugin*)new CSimpleFileSystem();
    if (*ppIUnknown)
    {
      (*ppIUnknown)->AddRef();
      return HXR_OK;
    }
    return HXR_OUTOFMEMORY;
}

/****************************************************************************
 *
 *  Function:
 *
 *    CanUnload()
 *
 *  Purpose:
 *
 *    Function implemented by all plugin DLL's if it returns HXR_OK
 *    then the pluginhandler can unload the DLL
 *
 */
STDAPI ENTRYPOINT(CanUnload)(void)
{
#ifdef HELIX_CONFIG_NOSTATICS
    return HXR_FAIL;
#else // HELIX_CONFIG_NOSTATICS
    return (smpl_nRefCount ? HXR_FAIL : HXR_OK);
#endif      // HELIX_CONFIG_NOSTATICS
}


/****************************************************************************
 *
 *  Function:
 *
 *    HXShutdown()
 *
 *  Purpose:
 *
 *    Function implemented by all plugin DLL's to free any *global*
 *    resources. This method is called just before the DLL is unloaded.
 *
 */
STDAPI ENTRYPOINT(HXSHUTDOWN)(void)
{
    return HXR_OK;
}

CSimpleFileSystem::CSimpleFileSystem()
    : m_lRefCount(0)
    , m_pContext(0)
    , m_options(NULL)
    , m_ulMaxIterationLevel(MAX_ITERATION_COUNT)
    , m_pCommonObj(NULL)
    , m_bDisableMemoryMappedIO(FALSE)
    , m_bEnableFileLocking(FALSE)
    , m_ulChunkSize(0)
{
#ifndef HELIX_CONFIG_NOSTATICS
    smpl_nRefCount++;
#endif      // HELIX_CONFIG_NOSTATICS
}

CSimpleFileSystem::~CSimpleFileSystem()
{
#ifndef HELIX_CONFIG_NOSTATICS
    smpl_nRefCount--;
#endif      // HELIX_CONFIG_NOSTATICS

    if (m_pContext)
    {
      m_pContext->Release();
      m_pContext = 0;
    }

    if(m_options)
    {
      m_options->Release();
      m_options = 0;
    }

    HX_RELEASE(m_pCommonObj);
}

/************************************************************************
 *  Method:
 *    IHXPlugin::InitPlugin
 *  Purpose:
 *    Initializes the plugin for use. This interface must always be
 *    called before any other method is called. This is primarily needed
 *    so that the plugin can have access to the context for creation of
 *    IHXBuffers and IMalloc.
 */
STDMETHODIMP CSimpleFileSystem::InitPlugin(IUnknown* /*IN*/ pContext)
{
    HX_LOG_BLOCK( "CSimpleFileSystem::InitPlugin" );

    HX_RESULT           lResult;
    IHXPreferences*     prefs = 0;
    IHXBuffer*          base_path_buf = 0;

    if (pContext && !m_pContext)
    {
        m_pContext = pContext;
      m_pContext->AddRef();

      IHXRegistry* pReg = NULL;
      if (m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pReg) == HXR_OK)
      {
          INT32 lCS = 0;
          if (HXR_OK == pReg->GetIntByName("config.MMapChunkSize", lCS) && lCS)
          {
            m_ulChunkSize = lCS;
          }
          pReg->Release();
      }

      if(!m_options ||
         (HXR_OK != m_options->GetPropertyBuffer("BasePath", base_path_buf)))
      {
          lResult = pContext->QueryInterface(IID_IHXPreferences,
                                     (void**) &prefs);
          if (lResult == HXR_OK)
          {
            lResult = prefs->ReadPref("BasePath", base_path_buf);
            if (lResult == HXR_OK)
            {
                m_base_path = CHXString((char*)base_path_buf->GetBuffer());
            }
          }
      }
      else
      {
          m_base_path = CHXString((char*)base_path_buf->GetBuffer());
      }
    }

    if (prefs)
    {
      prefs->Release();
      prefs = 0;
    }

    if (base_path_buf)
    {
      base_path_buf->Release();
      base_path_buf = 0;
    }

    IHXGetRecursionLevel* pGet;
    lResult = pContext->QueryInterface(IID_IHXGetRecursionLevel,
                               (void**) &pGet);
    if (lResult == HXR_OK)
    {
      m_ulMaxIterationLevel = pGet->GetRecursionLevel();
      pGet->Release();
    }

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXPlugin::GetPluginInfo
 *  Purpose:
 *    Returns the basic information about this plugin. Including:
 *
 *    unInterfaceCount  the number of standard RMA interfaces
 *                supported by this plugin DLL.
 *    pIIDList          array of IID's for standard RMA interfaces
 *                supported by this plugin DLL.
 *    bLoadMultiple     whether or not this plugin DLL can be loaded
 *                multiple times. All File Formats must set
 *                this value to TRUE.
 *    pDescription      which is used in about UIs (can be NULL)
 *    pCopyright  which is used in about UIs (can be NULL)
 *    pMoreInfoURL      which is used in about UIs (can be NULL)
 */
STDMETHODIMP CSimpleFileSystem::GetPluginInfo
(
    REF(BOOL)        /*OUT*/ bLoadMultiple,
    REF(const char*) /*OUT*/ pDescription,
    REF(const char*) /*OUT*/ pCopyright,
    REF(const char*) /*OUT*/ pMoreInfoURL,
    REF(ULONG32)     /*OUT*/ ulVersionNumber
)
{
    bLoadMultiple = TRUE;

    pDescription    = zm_pDescription;
    pCopyright        = zm_pCopyright;
    pMoreInfoURL    = zm_pMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}


// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::QueryInterface
//  Purpose:
//    Implement this to export the interfaces supported by your
//    object.
//
STDMETHODIMP CSimpleFileSystem::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
    {
      { GET_IIDHANDLE(IID_IUnknown), this },
      { GET_IIDHANDLE(IID_IHXPlugin), (IHXPlugin*) this },
      { GET_IIDHANDLE(IID_IHXFileSystemObject), (IHXFileSystemObject*) this },
    };
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::AddRef
//  Purpose:
//    Everyone usually implements this the same... feel free to use
//    this implementation.
//
STDMETHODIMP_(ULONG32) CSimpleFileSystem::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::Release
//  Purpose:
//    Everyone usually implements this the same... feel free to use
//    this implementation.
//
STDMETHODIMP_(ULONG32) CSimpleFileSystem::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

STDMETHODIMP CSimpleFileSystem::GetFileSystemInfo
(
    REF(const char*) /*OUT*/ pShortName,
    REF(const char*) /*OUT*/ pProtocol
)
{
    pShortName    = zm_pShortName;
    pProtocol     = zm_pProtocol;

    return HXR_OK;
}

STDMETHODIMP
CSimpleFileSystem::InitFileSystem(IHXValues* options)
{
    HX_LOG_BLOCK( "CSimpleFileSystem::InitFileSystem" );

    IHXBuffer*          base_path_buf = 0;

    m_options = options;

    if (m_options)
    {
      m_options->AddRef();

      if (HXR_OK == m_options->GetPropertyBuffer("BasePath", base_path_buf))
      {
          m_base_path = CHXString((char*)base_path_buf->GetBuffer());
      }

      ULONG32 ulTemp = 0;
      BOOL bLog = FALSE;
      /*
         *  XXX PM this is weak.  I need a way to show these options
       *  but only if explicitly asked to do so.  Ideally we would
       *  use the IRMErrorMessages with HXLOG_DEBUG and --sdm for
       *  server but I can't here because no context gets passed
       *  to file system object until its first connection.
       *  Can you believe that?
       */
      m_options->GetPropertyULONG32("LogOptionalParams",
            ulTemp);
      bLog = ulTemp ? TRUE : FALSE;

      ulTemp = 0;
      m_options->GetPropertyULONG32("DisableMemoryMappedIO",
          ulTemp);
      m_bDisableMemoryMappedIO = ulTemp ? TRUE : FALSE;

      ulTemp = 0;
      m_options->GetPropertyULONG32("EnableFileLocking",
            ulTemp);
      m_bEnableFileLocking = ulTemp ? TRUE : FALSE;

      ulTemp = 0;
      m_options->GetPropertyULONG32("MaxIterationLevel", ulTemp);
      if (ulTemp)
      {
            m_ulMaxIterationLevel = ulTemp;
      }
      if (bLog)
      {
          char pNumericMount[50]; /* Flawfinder: ignore */
          IHXBuffer* pBuffer = 0;
          const char* pMount;
          m_options->GetPropertyCString("MountPoint", pBuffer);
          if (!pBuffer)
          {
            m_options->GetPropertyBuffer("MountPoint", pBuffer);
            if (!pBuffer)
            {
                m_options->GetPropertyULONG32("MountPount", ulTemp);
            }
          }
          if (pBuffer)
          {
            pMount = (const char*)pBuffer->GetBuffer();
          }
          else
          {
            pMount = pNumericMount;
            sprintf(pNumericMount, "%lu", ulTemp); /* Flawfinder: ignore */
          }

          printf("Optional smplfsys (pn-local) parameters for"
                " MountPoint: %s\n", pMount);
          HX_RELEASE(pBuffer);
          printf("DisableMemoryMappedIO: %s\n",
                m_bDisableMemoryMappedIO ? "TRUE" : "FALSE");
          printf("EnableFileLocking: %s\n",
                m_bEnableFileLocking ? "TRUE" : "FALSE");
          printf("MaxIterationLevel: %lu\n",
                m_ulMaxIterationLevel);
          ulTemp = 0;
          m_options->GetPropertyULONG32("MMapChunkSize", ulTemp);
          if (ulTemp)
          {
            printf("MMapChunkSize: %lu\n", ulTemp);
          }
      }
    }

    if (base_path_buf)
    {
      base_path_buf->Release();
    }

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileSystemObject::CreateFile
//  Purpose:
//    TBD
//
STDMETHODIMP CSimpleFileSystem::CreateFile
(
    IUnknown**    /*OUT*/     ppFileObject
)
{
    HX_LOG_BLOCK( "CSimpleFileSystem::CreateFile" );

    CSimpleFileObject* pFileObj =
      new CSimpleFileObject(m_base_path,
                        this,
                        m_pContext,
                        m_ulMaxIterationLevel);
    if (pFileObj)
    {
      if (HXR_OK == pFileObj->QueryInterface(IID_IUnknown,
                                  (void**)ppFileObject))
      {
          return HXR_OK;
      }
      return HXR_UNEXPECTED;
    }
    return HXR_OUTOFMEMORY;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    CSimpleFileSystem::CreateDir
//  Purpose:
//    TBD
//
STDMETHODIMP CSimpleFileSystem::CreateDir
(
    IUnknown**    /*OUT*/     ppDirObject
)
{
    return HXR_NOTIMPL;
}

CSimpleFileObject::CSimpleFileObject(CHXString& base_path,
                             CSimpleFileSystem *pFS,
                             IUnknown* pContext,
                             UINT32 ulMaxIterationLevel)
    : m_lRefCount(0)
    , m_ulFlags(0)
    , m_bLocalClose(FALSE)
    , m_pContext(pContext)
    , m_pCommonClassFactory(NULL)
    , m_pFileResponse(NULL)
    , m_pFileSystem(pFS)
    , m_pFilename(NULL)
    , m_pRequest(0)
    , m_pDescriptorReg(0)
    , m_pDirResponse(NULL)
    , m_pDirList(NULL)
    , m_pScheduler(NULL)
    , m_pStackCallback(NULL)
    , m_bInRead(FALSE)
    , m_bReadPending(FALSE)
    , m_bAsyncReadPending(FALSE)
    , m_ulPendingReadCount(0)
    , m_ulSize(0)
    , m_ulPos(0)
    , m_bAsyncAccess(TRUE)
    , m_bCanBeReOpened(0)
#ifdef _MACINTOSH
    , m_pDataFile(NULL)
    , m_pAsyncFileResponse(NULL)
    , m_pInterruptState(NULL)
    , m_pFileStatResponse(NULL)
    , m_eSeekReason(EXTERNAL_SEEK)
    , m_ulPreSeekPosition(0)
    , m_bFileToBeClosed(FALSE)
#endif
    , m_nFd(-1)
    , m_ulMaxIterationLevel(ulMaxIterationLevel)
    , m_pUnknownUserContext(NULL)
    , m_ulPendingSeekOffset(0)
    , m_usPendingSeekWhence(SEEK_SET)
#if defined(HELIX_FEATURE_PROGDOWN)
    , m_pProgDownMon(NULL)
    , m_ulCallbackState(CallbackStateUnknown)
    , m_bProgDownEnabled(TRUE)
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */
{
    MLOG_LEAK("CON CSimpleFileObject this=0x%08x\n", this);
#ifndef HELIX_CONFIG_NOSTATICS
    smpl_nRefCount++;
#endif      //  HELIX_CONFIG_NOSTATICS
    m_base_path = base_path;

    if (m_pFileSystem)
    {
      m_pFileSystem->AddRef();
    }

    if (m_pContext)
    {
      m_pContext->AddRef();
#ifdef USE_THREADSAFE_SCHEDULER
      m_pContext->QueryInterface(IID_IHXThreadSafeScheduler, (void**) &m_pScheduler);
      if (!m_pScheduler)
      {
          m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler);
      }
#else
      m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler);
#endif //USE_THREADSAFE_SCHEDULER

#if !defined _MACINTOSH
      IHXDataFileFactory* pDFFact = new HXDataFileFactory;;
      pDFFact->AddRef();
      {
          DPRINTF(0x5d000000, ("CSFO::CSFO() -- after QI\n"));
          {
            pDFFact->CreateFile(m_pDataFile, m_pContext,
                pFS->m_pCommonObj, pFS->m_bDisableMemoryMappedIO,
                pFS->m_ulChunkSize, pFS->m_bEnableFileLocking,
                TRUE);  // Always prefer async I/O
            if (!m_pDataFile)
            {
#ifndef _WIN16
                DPRINTF(0x5d000000, ("Internal Error s/546\n"));
#endif
                pDFFact->Release();
                return;
            }
          }
      }
      pDFFact->Release();
#endif /* !defined _MACINTOSH */

      m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                           (void **)&m_pCommonClassFactory);
    }

    m_pStackCallback = new CHXGenericCallback(this, CSimpleFileObject::StackCallback);
    if (m_pStackCallback)
    {
        m_pStackCallback->AddRef();
    }

#if defined (_MACINTOSH)
    m_pAsyncFileResponse = new SMPLAsyncResponse(this);
    if (m_pContext)
    {
      m_pContext->QueryInterface(IID_IHXInterruptState, (void**) &m_pInterruptState);
    }
#endif

#if defined(HELIX_FEATURE_PROGDOWN)
    m_pProgDownMon = new CProgressiveDownloadMonitor();
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */
};

CSimpleFileObject::~CSimpleFileObject()
{
    MLOG_LEAK("DES CSimpleFileObject this=0x%08x\n", this);
#ifndef HELIX_CONFIG_NOSTATICS
    smpl_nRefCount--;
#endif      // HELIX_CONFIG_NOSTATICS
    m_bLocalClose = TRUE;
    Close();
};


// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::QueryInterface
//  Purpose:
//    Implement this to export the interfaces supported by your
//    object.
//
STDMETHODIMP CSimpleFileObject::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
    {
      { GET_IIDHANDLE(IID_IUnknown), this },
      { GET_IIDHANDLE(IID_IHXFileObject), (IHXFileObject*) this },
      { GET_IIDHANDLE(IID_IHXDirHandler), (IHXDirHandler*) this },
      { GET_IIDHANDLE(IID_IHXFileStat), (IHXFileStat*) this },
      { GET_IIDHANDLE(IID_IHXFileExists), (IHXFileExists*) this },
      { GET_IIDHANDLE(IID_IHXGetFileFromSamePool), (IHXGetFileFromSamePool*) this },
      { GET_IIDHANDLE(IID_IHXRequestHandler), (IHXRequestHandler*) this },
      { GET_IIDHANDLE(IID_IHXFileRename), (IHXFileRename*) this },
      { GET_IIDHANDLE(IID_IHXFileRemove), (IHXFileRemove*) this },
      { GET_IIDHANDLE(IID_IHXFileMove), (IHXFileMove*) this },
      { GET_IIDHANDLE(IID_IHXThreadSafeMethods), (IHXThreadSafeMethods*) this },
    };
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::AddRef
//  Purpose:
//    Everyone usually implements this the same... feel free to use
//    this implementation.
//
STDMETHODIMP_(ULONG32) CSimpleFileObject::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::Release
//  Purpose:
//    Everyone usually implements this the same... feel free to use
//    this implementation.
//
STDMETHODIMP_(ULONG32) CSimpleFileObject::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
      return m_lRefCount;
    }

    delete this;
    return 0;
}

/************************************************************************
 *  Method:
 *    IHXFileObject::Init
 *  Purpose:
 *    Associates a file object with the file response object it should
 *    notify of operation completness. This method should also check
 *    for validity of the object (for example by opening it if it is
 *    a local file).
 */
STDMETHODIMP
CSimpleFileObject::Init
(
    ULONG32       /*IN*/      ulFlags,
    IHXFileResponse*   /*IN*/ pFileResponse
)
{
    MLOG_GEN(NULL, "CSimpleFileObject::Init(0x%08x,0x%08x) this=0x%08x\n",
             ulFlags, pFileResponse, this);
    HX_LOG_BLOCK( "CSimpleFileObject::Init" );

    DPRINTF(0x5d000000, ("CSFO::Init(flags(0x%x), pFileResponse(%p)) "
          "-- fd(%d)\n", ulFlags, pFileResponse, m_nFd));

    HX_RESULT lReturnVal = HXR_OK;
    HX_RESULT resultInitDone = HXR_OK;
    IHXRequestContext* pIHXRequestContextCurrent = NULL;

    if (!pFileResponse) return HXR_INVALID_PARAMETER;
    if (!m_pRequest) return HXR_INVALID_PARAMETER;

    /* Release any previous reponses */
    if (m_pFileResponse)
    {
      m_pFileResponse->Release();
    }

    m_pFileResponse = pFileResponse;
    m_pFileResponse->AddRef();

    /* have we already opened/created a file */
    if (m_nFd != -1)
    {
      m_bReadPending         = FALSE;
      m_ulPendingReadCount = 0;

      /* remove any pending callbacks */
        if (m_pStackCallback) m_pStackCallback->Cancel(m_pScheduler);

      /* if flags are same, then we are all set and there
       * is no need to do anything further
       */
      if (m_ulFlags == ulFlags || ulFlags == 0)
      {
          /* If we have already opened a file, then seek back
           * to zero during re-initialization
           */
#ifndef _MACINTOSH
          HX_RESULT result = m_pDataFile->Seek(0, SEEK_SET);
          m_pFileResponse->InitDone(result);
          return result;
#else
          BOOL bAtInterrupt = FALSE;
          if (m_pInterruptState)
          {
            bAtInterrupt = m_pInterruptState->AtInterruptTime();
          }

          /* I want to know if we ever reach here at interrupt time
           * Please e-mail me a repro case if you hit this assert
           * - RA
           */
          HX_ASSERT(bAtInterrupt == FALSE);

          m_eSeekReason = REINIT_SEEK;
          HX_RESULT theResult = m_pDataFile->SafeSeek(0, SEEK_SET, bAtInterrupt);
          if (theResult != HXR_OK)
          {
            m_pFileResponse->InitDone(HXR_FAIL);
          }

          return theResult;
#endif
          /*
          m_pFileResponse->InitDone(HXR_OK);
          return HXR_OK;
          */
      }

#ifdef _MACINTOSH
      HX_RELEASE(m_pDataFile);
      m_pDataFile = NULL;
#else
      DPRINTF(0x5d000000, ("CSFO::Init() -- m_pDataFile->Close()\n"));

        if (m_pDescriptorReg)
      {
          m_pDescriptorReg->UnRegisterDescriptors(1);
      }

        m_pDataFile->Close();
      m_nFd = -1;
#endif
    }

    m_ulFlags = ulFlags;

    if (!m_pCommonClassFactory)
    {
      m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                          (void **)&m_pCommonClassFactory);
    }

    HX_RELEASE(m_pUnknownUserContext);

    if (m_pRequest && SUCCEEDED(m_pRequest->QueryInterface(
      IID_IHXRequestContext, (void**)&pIHXRequestContextCurrent)))
    {
      pIHXRequestContextCurrent->GetUserContext(m_pUnknownUserContext);
      pIHXRequestContextCurrent->Release();
    }

    DPRINTF(0x5d000000, ("CSFO::Init() -- (2) _OpenFile()\n"));
    lReturnVal = _OpenFile(ulFlags);
    DPRINTF(0x5d000000, ("CSFO::Init(flags(0x%x), pFileResponse(%p)) "
          "-- result(0x%x), m_nFd(%d)\n",
          ulFlags, pFileResponse, lReturnVal, m_nFd));

    if ((m_nFd == -1 || FAILED(lReturnVal)))
    {
      // XXXSSH -- probably obsolete
      if (lReturnVal != HXR_NOT_AUTHORIZED)
      {
          lReturnVal = HXR_DOC_MISSING;
      }
    }
    else
    {
      lReturnVal = HXR_OK;
    }

#if defined(HELIX_FEATURE_PROGDOWN)
    // If we are writing to the file, then disable
    // the progressive download features
    m_bProgDownEnabled = (m_ulFlags & HX_FILE_WRITE ? FALSE : TRUE);
    if (m_pProgDownMon && m_bProgDownEnabled)
    {
        m_pProgDownMon->Init(m_pContext, m_pDataFile, this);
    }
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */

    resultInitDone =
      m_pFileResponse->InitDone(lReturnVal);
    lReturnVal = resultInitDone;

    DPRINTF(0x5d000000, ("CSFO::Init() "
          "-- resultInitDone(0x%x), m_nFd(%d)\n",
          resultInitDone, m_nFd));

    return (lReturnVal);
}

/************************************************************************
 *  Method:
 *      IHXFileObject::GetFilename
 *  Purpose:
 *      Returns the filename (without any path information) associated
 *      with a file object.
 */
STDMETHODIMP CSimpleFileObject::GetFilename
(
    REF(const char*) /*OUT*/ pFilename
)
{
    UpdateFileNameMember();
    // Find the separator character before the file name
    pFilename = ::strrchr(m_pFilename, OS_SEPARATOR_CHAR);

    if (pFilename != NULL) // Found
    {
      // File name starts after the separator charactor
      pFilename++;
    }
    else // Not found
    {
      pFilename = m_pFilename;
    }

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXFileObject::Close
 *  Purpose:
 *    Closes the file resource and releases all resources associated
 *    with the object.
 */
STDMETHODIMP CSimpleFileObject::Close()
{
    MLOG_GEN(NULL, "CSimpleFileObject::Close() this=0x%08x\n", this);
    HX_LOG_BLOCK( "CSimpleFileObject::Close" );

    // If there is a pending callback, be sure to remove it!
    if (m_pStackCallback) m_pStackCallback->Cancel(m_pScheduler);

    HX_RELEASE(m_pStackCallback);
    HX_RELEASE(m_pScheduler);

    HX_RELEASE(m_pUnknownUserContext);

    if (m_pContext)
    {
      m_pContext->Release();
      m_pContext = NULL;
    }

    if (m_pCommonClassFactory)
    {
      m_pCommonClassFactory->Release();
      m_pCommonClassFactory = NULL;
    }

    if (m_pFileSystem)
    {
      m_pFileSystem->Release();
      m_pFileSystem = NULL;
    }

    if (m_pRequest)
    {
      m_pRequest->Release();
      m_pRequest = NULL;
    }

    if (m_pDescriptorReg && (m_nFd != -1))
    {
      m_pDescriptorReg->UnRegisterDescriptors(1);
      m_pDescriptorReg->Release();
      m_pDescriptorReg = 0;
    }
#if defined(HELIX_FEATURE_PROGDOWN)
    // XXXMEH
    // IMPORTANT!!
    // Since the progressive download monitor does now not
    // hold a ref on m_pDataFile, then we MUST make sure
    // and call m_pProgDownMon->Close() BEFORE we close
    // and release m_pDataFile. This is because there could
    // be pending callbacks which will attempt to use
    // the IHXDataFile. Callling m_pProgDownMon->Close()
    // will cancel these pending callbacks.
    if (m_pProgDownMon)
    {
        // Close the progressive download monitor. Closing
        // also cancels any pending callbacks
        m_pProgDownMon->Close();
    }
    HX_DELETE(m_pProgDownMon);
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */

    if (m_pDataFile)
    {
#if defined _MACINTOSH
    HX_RELEASE(m_pDataFile);
    HX_DELETE(m_pAsyncFileResponse);
    HX_RELEASE(m_pInterruptState);
    HX_RELEASE(m_pFileStatResponse);
#else
      DPRINTF(0x5d000000, ("CSFO::Close() -- m_pDataFile->Close()\n"));
      m_pDataFile->Close();
      HX_RELEASE(m_pDataFile);
#endif
    }
    m_nFd = -1;

    if(m_pFilename)
    {
      HX_VECTOR_DELETE(m_pFilename);
      m_pFilename = NULL;
    }

    if (m_pDirResponse)
    {
      m_pDirResponse->Release();
      m_pDirResponse = NULL;
    }

    if (m_pDirList)
    {
      delete m_pDirList;
      m_pDirList = NULL;
    }

    // Make sure we do not attempt to schedule any more replies which
    // would result in a crash after this point
    m_bReadPending = FALSE;

      // It is vitally important that the CloseDone be the last step in
    // this method, as the last reference to this object may be
    // released inside CloseDone!  Do not place code after this if block!
    // MBO: Access to stack variables after CloseDone is OK.

    if (!m_bLocalClose)
    {
      if(m_pFileResponse)
      {
          // This keeps us from getting into an infinite loop when
          // CSimpleFileObject::Close() calls m_pFileResponse->Release()
          // which calls ~CRIFFReader() which calls Close() and so on,
          // never hitting the line that sets m_pFileResponse to NULL...
          IHXFileResponse* pTempFileResponse = m_pFileResponse;
          m_pFileResponse = NULL;

          pTempFileResponse->CloseDone(HXR_OK);

          pTempFileResponse->Release();
        }
    }
    else if (m_pFileResponse)
    {
      m_pFileResponse->Release();
      m_pFileResponse = NULL;
    }



    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXFileObject::Read
 *  Purpose:
 *    Reads a buffer of data of the specified length from the file
 *    and asynchronously returns it to the caller via the
 *    IHXFileResponse interface passed in to Init.
 */
STDMETHODIMP CSimpleFileObject::Read(ULONG32 ulCount)
{
    MLOG_GEN(NULL, "CSimpleFileObject::Read(%lu) this=0x%08x tick=%lu\n",
             ulCount, this, HX_GET_BETTERTICKCOUNT());
    HX_LOG_BLOCK( "CSimpleFileObject::Read" );

    // XXXBHG, For now, you cant read more than 1MB at a time!
    if (ulCount > 0x000FFFFF)
    {
      //Force the system to recognize a failed Read so infinite
      // buffering does not occur with the core waiting for EOF:
      ActualAsyncReadDone(HXR_FAIL, NULL);

      return HXR_INVALID_PARAMETER;
    }

    if((m_nFd == -1) && m_bCanBeReOpened)
    {
      DPRINTF(0x5d000000, ("CSFO::Read() -- _OpenFile()\n"));
      _OpenFile(m_ulFlags);
      m_bCanBeReOpened = FALSE;
#ifndef _MACINTOSH
      m_pDataFile->Seek(m_ulPos, SEEK_SET);
#endif
    }
    if (m_nFd != -1)
    {
      if(!(m_ulFlags & HX_FILE_READ))
          return HXR_UNEXPECTED;

      if (m_bReadPending)
      {
          return HXR_UNEXPECTED;
      }

      m_bReadPending = TRUE;
      m_ulPendingReadCount = ulCount;

      if (m_bInRead && m_bAsyncAccess)
      {
          return HXR_OK;
      }

      m_bInRead = TRUE;

      HX_RESULT theErr = HXR_OK;
      UINT16 uIterationCount;
      uIterationCount = 0;

      AddRef(); // Make sure we do not destruct in ReadDone()

        BOOL bProgFail = FALSE;
      do
      {
            bProgFail = FALSE;
          theErr = DoRead(bProgFail);
          uIterationCount++;
      } while (m_bReadPending && !m_bAsyncReadPending && !theErr && uIterationCount < m_ulMaxIterationLevel && !bProgFail);

      /* have we exceeded our iteration count? */
      if (m_bReadPending && !m_bAsyncReadPending && !theErr && m_bAsyncAccess && !bProgFail)
      {
            MLOG_PD(NULL, "\tScheduling stack callback\n");
          HX_ASSERT(!m_pStackCallback->IsCallbackPending() &&
                 uIterationCount >= m_ulMaxIterationLevel);
            // Schedule a callback if there is not one already scheduled
            m_pStackCallback->ScheduleRelative(m_pScheduler, 0);
      }

      m_bInRead = FALSE;

      Release();

      return theErr;

    }

    return HXR_UNEXPECTED;
}

HX_RESULT
CSimpleFileObject::DoRead(REF(BOOL) rbProgFail)
{
    MLOG_PD(NULL, "CSimpleFileObject::DoRead() this=0x%08x tick=%lu curoffset=%lu\n",
            this, HX_GET_BETTERTICKCOUNT(), m_ulPos);
    HX_LOG_BLOCK( "CSimpleFileObject::DoRead" );

    HX_ASSERT(m_bReadPending);

    HX_RESULT     theErr      = HXR_OK;
    UINT32  ulCount = m_ulPendingReadCount;

#ifndef _MACINTOSH
    // Create buffer object here, notice that we call the
    // CreateInstance method of the controller, but we could
    // have implemented our own object that exposed the IHXBuffer
    // interface.
    IHXBuffer* pBuffer = NULL;
    ULONG32 actual = m_pDataFile->Read(pBuffer, ulCount);

#if defined(HELIX_FEATURE_PROGDOWN)
    MLOG_PD(NULL, "\tulCount = %lu actual = %lu\n", ulCount, actual);
    // Sanity check. Unlikely we'd even make it here
    // if we failed to allocate m_pProgDownMon, but 
    // if so, then just behave like HELIX_FEATURE_PROGDOWN
    // is not defined
    if (m_pProgDownMon && m_bProgDownEnabled)
    {
        // Did we read all the bytes we were asked for?
        if (actual < ulCount)
        {
            MLOG_PD(NULL, "\t\t******** FAILED to read requested bytes\n");
            // The read failed to read all the requested bytes
            //
            // Do we have a history of progressive download
            // with this file?
            if (m_pProgDownMon->HasBeenProgressive())
            {
                MLOG_PD(NULL, "\t\t\tFile HAS been progressive\n");
                // This file has been progressive sometime in
                // the past. However, we need to
                // check the *current* state.
                if (m_pProgDownMon->IsProgressive())
                {
                    MLOG_PD(NULL, "\t\t\t\tFile currently IS progressive\n");
                    // The file is still currently downloading.
                    //
                    // Can we callback asynchronously?
                    if (m_bAsyncAccess)
                    {
                        // Set the flag saying we failed due to
                        // progressive download
                        rbProgFail = TRUE;
                        // Schedule the callback
                        return FinishDoReadWithCallback(actual);
                    }
                    else
                    {
                        // Try synchronously
                        return FinishDoReadWithoutCallback(actual);
                    }
                }
                else
                {
                    MLOG_PD(NULL, "\t\t\t\tFile currently is NOT progressive\n");
                    // The file has been progressive in the past
                    // but the current state is NOT progressive.
                    // That could mean that either the download has
                    // finished or that the download agent is paused.
                    // With our current implementation we can't tell
                    // the difference (we would need to know the "true"
                    // file size to know for sure). Therefore, we will
                    // ask the fileformat whether or not it would "expect"
                    // a failure at this point. The fileformat should have
                    // knowledge to know whether or not a failure of this
                    // read would indicate failure or not. Sometimes fileformats
                    // read in large chunks and *expect* to get a failure.
                    // On the other hand, file formats sometimes do a read
                    // and are NOT expecting it to fail.
                    if (RequireFullRead())
                    {
                        MLOG_PD(NULL, "\t\t\t\tFileResponse requires full reads\n");
                        // The response says it requires full reads
                        //
                        // Can we callback asynchronously?
                        if (m_bAsyncAccess)
                        {
                            // Set the flag saying we failed due to
                            // progressive download
                            rbProgFail = TRUE;
                            // Schedule the callback
                            return FinishDoReadWithCallback(actual);
                        }
                        else
                        {
                            // Try synchronously
                            return FinishDoReadWithoutCallback(actual);
                        }
                    }
                    else if (m_pProgDownMon->GetFormerProgressiveRetryCount() > 0)
                    {
                        MLOG_PD(NULL, "\t\t\t\tRetrying former progressive, count = %lu\n",
                                m_pProgDownMon->GetFormerProgressiveRetryCount());
                        // We do not require a full read, but we are going
                        // to retry a certain number of times before calling
                        // back with ReadDone().
                        //
                        // Decrement the retry count. If the file becomes
                        // progressive again, then this count gets reset.
                        m_pProgDownMon->DecrementFormerProgressiveRetryCount();
                        // Can we callback asynchronously?
                        if (m_bAsyncAccess)
                        {
                            // Set the flag saying we failed due to
                            // progressive download
                            rbProgFail = TRUE;
                            // Schedule the callback
                            return FinishDoReadWithCallback(actual);
                        }
                        else
                        {
                            // Try synchronously
                            return FinishDoReadWithoutCallback(actual);
                        }
                    }
                    else
                    {
                        MLOG_PD(NULL, "\t\t\t\tCannot retry former progressive any more\n");
                        // We have retried on this formerly progressive
                        // file as long as we can, so just call back
                        // with ReadDone().
                        //
                        // Clear the progressive download failure flag
                        rbProgFail = FALSE;
                        // Finish the DoRead
                        return FinishDoRead(actual, pBuffer);
                    }
                }
            }
            else /* if (m_pProgDownMon->HasBeenProgressive()) */
            {
                MLOG_PD(NULL, "\t\t\tFile has NOT been progressive, checking file size\n");
                // This file has never yet been progressive
                //
                // Do a filesize check. This will check
                // the filesize against the filesize at
                // the time that m_pProgDownMon->Init()
                // was called. If the filesize has changed,
                // then the m_pProgDownMon->HasBeenProgressive()
                // flag will change.
                m_pProgDownMon->MonitorFileSize();
                // Has the filesize changed?
                if (m_pProgDownMon->HasBeenProgressive())
                {
                    MLOG_PD(NULL, "\t\t\t\tFile size HAS changed, beginning size monitoring and retrying\n");
                    // The filesize has indeed changed.
                    //
                    // Begin the monitoring of the file size
                    m_pProgDownMon->BeginSizeMonitoring();
                    // Can we callback asynchronously?
                    if (m_bAsyncAccess)
                    {
                        // Set the flag saying we failed due to
                        // progressive download
                        rbProgFail = TRUE;
                        // Schedule the callback
                        return FinishDoReadWithCallback(actual);
                    }
                    else
                    {
                        // Try synchronously
                        return FinishDoReadWithoutCallback(actual);
                    }
                }
                else
                {
                    MLOG_PD(NULL, "\t\t\t\tFile size has NOT changed.\n");
                    // The filesize has NOT changed. This is most likely
                    // just a normal file and the fileformat has attempted
                    // to read past the end of the file. However, we will
                    // first ask the fileformat if it expected a failure.
                    if (RequireFullRead() &&
                        m_pProgDownMon->GetNotProgressiveRetryCount() > 0)
                    {
                        MLOG_PD(NULL, "\t\t\t\t\tRetrying not progressive, count = %lu\n",
                                m_pProgDownMon->GetNotProgressiveRetryCount());
                        // Decrement the retry count. If the file becomes
                        // progressive, then this count gets reset.
                        m_pProgDownMon->DecrementNotProgressiveRetryCount();
                        // The fileformat had the Advise interface and 
                        // said it did not expect a failure here. Also,
                        // we have not retried our maximum number of attempts
                        // yet, so we will retry the read.
                        //
                        // Can we callback asynchronously?
                        if (m_bAsyncAccess)
                        {
                            // Set the flag saying we failed due to
                            // progressive download
                            rbProgFail = TRUE;
                            // Schedule the callback
                            return FinishDoReadWithCallback(actual);
                        }
                        else
                        {
                            // Try synchronously
                            return FinishDoReadWithoutCallback(actual);
                        }
                    }
                    else
                    {
                        // Either:
                        // a) the fileformat either didn't have the Advise interface;
                        // b) the fileformat did have the interface and said it
                        //    was expecting a failure; or
                        // c) the fileformat had the Advise interface and said it
                        //    wasn't expecting a failure, but we've retried all the
                        //    times we can.
                        //
                        // Clear the progressive download failure flag
                        rbProgFail = FALSE;
                        // Finish the DoRead
                        return FinishDoRead(actual, pBuffer);
                    }
                }
            } /* if (m_pProgDownMon->HasBeenProgressive()) else */
        }
        else /* if (actual < ulCount) */
        {
            // The read succeeded
            //
            // Reset the not progressive retry count
            m_pProgDownMon->ResetNotProgressiveRetryCount();
            // Clear the progressive download failure flag
            rbProgFail = FALSE;
            // Finish the DoRead
            return FinishDoRead(actual, pBuffer);
        }
    }
    else /* if (m_pProgDownMon) */
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */
    {
        // Clear the progressive download failure flag
        rbProgFail = FALSE;
        // Finish the DoRead
        return FinishDoRead(actual, pBuffer);
    }

#else /* #ifndef _MACINTOSH */
    BOOL bAtInterrupt = FALSE;
    if (m_pInterruptState)
    {
      bAtInterrupt = m_pInterruptState->AtInterruptTime();
    }

    m_bAsyncReadPending = TRUE;
    theErr = m_pDataFile->SafeRead(ulCount, bAtInterrupt);
#endif /* #ifndef _MACINTOSH #else */

    return theErr;
}

/************************************************************************
 *  Method:
 *    IHXFileObject::Write
 *  Purpose:
 *    Writes a buffer of data to the file and asynchronously notifies
 *    the caller via the IHXFileResponse interface passed in to Init,
 *    of the completeness of the operation.
 */
STDMETHODIMP CSimpleFileObject::Write(IHXBuffer* pBuffer)
{
    if (m_nFd == -1 || !(m_ulFlags & HX_FILE_WRITE))
    {
      return HXR_UNEXPECTED;
    }

    pBuffer->AddRef();
#ifdef _MACINTOSH
      BOOL bAtInterrupt = FALSE;
      if (m_pInterruptState)
      {
          bAtInterrupt = m_pInterruptState->AtInterruptTime();
      }
    UINT32 actual = m_pDataFile->SafeWrite(pBuffer, bAtInterrupt);
#else
    UINT32 actual = m_pDataFile->Write(pBuffer);
#endif
    pBuffer->Release();

    if (actual > 0)
    {
      m_ulPos += actual;
    }

    if(actual == pBuffer->GetSize())
      m_pFileResponse->WriteDone(HXR_OK);
    else
      m_pFileResponse->WriteDone(HXR_FAILED);
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXFileObject::Seek
 *  Purpose:
 *    Seeks to an offset in the file and asynchronously notifies
 *    the caller via the IHXFileResponse interface passed in to Init,
 *    of the completeness of the operation.
 */
STDMETHODIMP CSimpleFileObject::Seek(ULONG32 ulOffset, BOOL bRelative)
{
    MLOG_GEN(NULL, "CSimpleFileObject::Seek(%lu,%lu) this=0x%08x tick=%lu\n",
             ulOffset, bRelative, this, HX_GET_BETTERTICKCOUNT());
    HX_LOG_BLOCK( "CSimpleFileObject::Seek" );

    if((m_nFd == -1) && m_bCanBeReOpened)
    {
      DPRINTF(0x5d000000, ("CSFO::Seek() -- _OpenFile()\n"));
      _OpenFile(m_ulFlags);
    }
    if (m_nFd != -1)
    {
      /* remove any pending callbacks */
        if (m_pStackCallback) m_pStackCallback->Cancel(m_pScheduler);

#if defined(HELIX_FEATURE_PROGDOWN)
        // Seeks can interrupt other seeks, and cancel them
        // out. Therefore, if we had scheduled a callback to
        // handle a failed seek or a failed read, then we need
        // to cancel that callback.
        if (m_pProgDownMon && m_pProgDownMon->IsCallbackPending())
        {
            m_pProgDownMon->CancelCallback();
        }
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */

      AddRef(); // Make sure we do not destruct on possible ReadDone

      /*
       *  Seek cancels pending Reads.
       */
      if (m_bReadPending)
      {
          ActualAsyncReadDone(HXR_CANCELLED, NULL);
      }

      int whence = SEEK_SET;

      if(bRelative)
          whence = SEEK_CUR;

#ifndef _MACINTOSH
        // Save the pending seek parameters
        m_ulPendingSeekOffset = ulOffset;
        m_usPendingSeekWhence = (UINT16) whence;

        // Do the seek
        HX_RESULT seekDoneResult = HXR_OK;
        HX_RESULT result = DoSeek(seekDoneResult);

        // Undo the AddRef();
        Release();

      return ((result == HXR_OK) ? seekDoneResult : result);
#else
      BOOL bAtInterrupt = FALSE;
      if (m_pInterruptState)
      {
          bAtInterrupt = m_pInterruptState->AtInterruptTime();
      }

      m_eSeekReason = EXTERNAL_SEEK;
      HX_RESULT seekDoneResult = m_pDataFile->SafeSeek(ulOffset, whence, bAtInterrupt);

      Release();

      return seekDoneResult;
#endif
    }
    return HXR_UNEXPECTED;
}

HX_RESULT CSimpleFileObject::DoSeek(REF(HX_RESULT) rSeekDoneResult)
{
    MLOG_PD(NULL, "CSimpleFileObject::DoSeek() this=0x%08x tick=%lu offset=%lu whence=%u\n",
            this, HX_GET_BETTERTICKCOUNT(), m_ulPendingSeekOffset, m_usPendingSeekWhence);
    HX_RESULT result = m_pDataFile->Seek(m_ulPendingSeekOffset,
                                         m_usPendingSeekWhence);

    if (result == HXR_OK)
    {
      if (m_usPendingSeekWhence == SEEK_SET)
      {
          m_ulPos = m_ulPendingSeekOffset;
      }
      else
      {
          m_ulPos += m_ulPendingSeekOffset;
      }
    }
#if defined(HELIX_FEATURE_PROGDOWN)
    // For the purposes of progressive download, we
    // will assume that seeking never fails. We will
    // add the assert below to catch anytime it does.
    // If we are finding that this assert is tripping,
    // then we need to add equivalent support in DoSeek()
    // for progressive download that we have in DoRead().
    HX_ASSERT(SUCCEEDED(result));
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */

    rSeekDoneResult = ActualAsyncSeekDone(result);

    return result;
}

void CSimpleFileObject::SeekBackwards(UINT32 ulNumBytes)
{
    if (m_pDataFile && ulNumBytes)
    {
        // Get the current offset
        UINT32 ulCurOffset = m_pDataFile->Tell();
        // Make sure the number of bytes we 
        // are supposed to back up is not greater
        // than the current offset
        if (ulNumBytes > ulCurOffset) ulNumBytes = ulCurOffset;
        // Compute the new absolute offset
        UINT32 ulNewOffset = ulCurOffset - ulNumBytes;
        // Seek the data file to this offset
        m_pDataFile->Seek(ulNewOffset, SEEK_SET);
    }
}

HX_RESULT CSimpleFileObject::FinishDoRead(UINT32 actual, REF(IHXBuffer*) pBuffer)
{
    /*
     * If they get to the end of the file, then close it and reset.
     */
    if (actual > 0 && pBuffer)
    {
        m_ulPos += actual;
    }

#if defined(HELIX_FEATURE_PROGDOWN)
    if (m_ulSize && (!m_bProgDownEnabled || (m_pProgDownMon && !m_pProgDownMon->HasBeenProgressive())))
#else
    if (m_ulSize)
#endif
    {
        if(m_ulPos >= m_ulSize)
        {
          DPRINTF(0x5d000000, ("CSFO::Read() -- m_pDataFile->Close()\n"));
          m_pDataFile->Close();
          m_nFd = -1;
          if (m_pDescriptorReg)
          {
              m_pDescriptorReg->UnRegisterDescriptors(1);
          }
          m_bCanBeReOpened = 1;
        }
    }

    /* ignore return value from readdone! */
    ActualAsyncReadDone((actual > 0 && pBuffer) ? HXR_OK : HXR_FAILED, pBuffer);
    // Release our reference on the buffer!
    HX_RELEASE(pBuffer);

    return HXR_OK;
}

STDMETHODIMP CSimpleFileObject::InitDirHandler
(
    IHXDirHandlerResponse*    /*IN*/  pDirResponse
)
{
    m_pDirResponse = pDirResponse;
    m_pDirResponse->AddRef();
    m_pDirResponse->InitDirHandlerDone(HXR_OK);

    return HXR_OK;
}

STDMETHODIMP CSimpleFileObject::CloseDirHandler()
{
    // Members must not be accessed after async ...Done() response
    if (m_pDirResponse)
    {
      IHXDirHandlerResponse *pTmpDirResponse = m_pDirResponse;

      m_pDirResponse = NULL;
      pTmpDirResponse->CloseDirHandlerDone(HXR_OK);
      pTmpDirResponse->Release();
    }

    return HXR_OK;
}

STDMETHODIMP CSimpleFileObject::MakeDir()
{
    CHXString strFileName;
    HX_RESULT retVal = HXR_OK;

    UpdateFileNameMember();

    GetFullPathname(m_pFilename, &strFileName);

#if defined (_WINDOWS ) || defined (_WIN32)
    if (CreateDirectory(OS_STRING((const char*) strFileName), 0) == 0)
    {
      retVal = HXR_FAIL;
    }
#elif defined (_UNIX)
    if (mkdir((const char*) strFileName, 0755) < 0)
    {
      retVal = HXR_FAIL;
    }
#elif defined (_MACINTOSH)
    //XXXGH...don't know how to do this on Mac
    retVal = HXR_FAIL;
#else
    XHXDirectory* pHXDirectory = XHXDirectory::Construct(&m_pFileSystem->m_pCommonObj);

    retVal = HXR_OUTOFMEMORY;
    if (pHXDirectory)
    {
      retVal = HXR_FAIL;

      pHXDirectory->SetPath((const char*) strFileName);
      if (pHXDirectory->Create())
      {
          retVal = HXR_OK;
      }

      delete pHXDirectory;
    }
#endif

    m_pDirResponse->MakeDirDone(retVal);

    return HXR_OK;
}

STDMETHODIMP CSimpleFileObject::ReadDir()
{
    const char* pDirname = 0;

    if (!m_pDirList)
    {
      CHXString strFileName;

      UpdateFileNameMember();

      GetFullPathname(m_pFilename, &strFileName);

      m_pDirList =
          CFindFile::CreateFindFile((const char*)strFileName, 0, "*");

      if (!m_pDirList)
      {
          m_pDirResponse->ReadDirDone(HXR_FAIL, 0);
          return HXR_OK;
      }

      pDirname = m_pDirList->FindFirst();
    }
    else
    {
      pDirname = m_pDirList->FindNext();
    }

    if (!pDirname)
    {
      delete m_pDirList;
      m_pDirList = 0;
      m_pDirResponse->ReadDirDone(HXR_FILE_NOT_FOUND, 0);

      return HXR_OK;
    }

    HX_RESULT result;

    if (!m_pCommonClassFactory)
    {
      result = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                          (void **)&m_pCommonClassFactory);

      if (HXR_OK != result)
      {
          return result;
      }
    }

    IHXBuffer* pBuffer = 0;

    result = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                   (void**)&pBuffer);

    if (HXR_OK != result)
    {
      return result;
    }

    pBuffer->Set((Byte*)pDirname, strlen(pDirname)+1);
    m_pDirResponse->ReadDirDone(HXR_OK, pBuffer);
    pBuffer->Release();

    return HXR_OK;
}

/************************************************************************
 * Method:
 *    IHXFileObject::Stat
 * Purpose:
 *    Collects information about the file that is returned to the
 *    caller in an IHXStat object
 */
STDMETHODIMP CSimpleFileObject::Stat(IHXFileStatResponse* pFileStatResponse)
{
    MLOG_GEN(NULL, "CSimpleFileObject::Stat(0x%08x) this=0x%08x\n", pFileStatResponse, this);
    HX_LOG_BLOCK( "CSimpleFileObject::Stat" );

    struct stat StatBuffer;
    CHXString     strFileName;

#ifdef _MACINTOSH

        // if fstat fails
#ifdef _MAC_MACHO
      GetFullPathname(m_pFilename, &strFileName);
        int res = stat(strFileName, &StatBuffer);
#else
        int res = stat(m_pFilename, &StatBuffer);
#endif
      if ( res != 0 )
      {
            // return failure code
            return HXR_FAIL;
      }

#else
    if(m_nFd == -1)
    {
      CHXString   strURL;

      UpdateFileNameMember();
      strURL = m_pFilename;

      GetFullPathname(strURL, &strFileName);

      m_pDataFile->Bind((const char *)strFileName);
    }
    if (m_pDataFile->Stat(&StatBuffer) != HXR_OK)
    {
      pFileStatResponse->StatDone(HXR_FAIL,
          0,
          0,
          0,
          0,
          0);
      return HXR_OK;
    }
#endif

    /*
     * XXXSMP:
     *    We we need this because RealPix opens up tons of files
     *    just to stat them.  If you are reading this comment
     *    and the date is past July 31, then please yell at
     *    Sujal.
     */
#if !defined(_MACINTOSH)
    if(m_nFd != -1)
    {
      DPRINTF(0x5d000000, ("CSFO::Stat() -- m_pDataFile->Close()\n"));
      if (m_pDescriptorReg)
      {
          m_pDescriptorReg->UnRegisterDescriptors(1);
      }
      m_pDataFile->Close();
      m_nFd = -1;
      m_bCanBeReOpened = 1;
    }
#endif

    m_ulSize = StatBuffer.st_size;
    pFileStatResponse->StatDone(HXR_OK,
                        StatBuffer.st_size,
                        StatBuffer.st_ctime,
                        StatBuffer.st_atime,
                        StatBuffer.st_mtime,
                        StatBuffer.st_mode);
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXFileObject::Advise
 *  Purpose:
 *    To pass information to the File Object
 */
STDMETHODIMP CSimpleFileObject::Advise(ULONG32 ulInfo)
{
    MLOG_GEN(NULL, "CSimpleFileObject::Advise(%s) this=0x%08x\n",
             (ulInfo == HX_FILEADVISE_RANDOMACCESS ? "HX_FILEADVISE_RANDOMACCESS" :
                (ulInfo == HX_FILEADVISE_SYNCACCESS ? "HX_FILEADVISE_SYNCACCESS" :
                   (ulInfo == HX_FILEADVISE_ASYNCACCESS ? "HX_FILEADVISE_ASYNCACCESS" :
                      (ulInfo == HX_FILEADVISE_RANDOMACCESSONLY ? "HX_FILEADVISE_RANDOMACCESSONLY" :
                         (ulInfo == HX_FILEADVISE_ANYACCESS ? "HX_FILEADVISE_ANYACCESS" : "Unknown"))))),
             this);
    HX_RESULT retVal = HXR_FAIL;

#ifndef _MACINTOSH
    if (ulInfo == HX_FILEADVISE_SYNCACCESS)
    {
      m_bAsyncAccess = FALSE;
      retVal = HXR_OK;
    }
    else if (ulInfo == HX_FILEADVISE_ASYNCACCESS)
    {
      m_bAsyncAccess = TRUE;
      retVal = HXR_OK;
    }
#if defined(HELIX_FEATURE_PROGDOWN)
    else if (ulInfo == HX_FILEADVISE_RANDOMACCESS)
    {
        retVal = HXR_OK;
        if (m_pProgDownMon && m_bProgDownEnabled)
        {
            // Has this file ever been progressive?
            if (m_pProgDownMon->HasBeenProgressive())
            {
                // It has been in the past, but
                // is it currently progressive?
                if (m_pProgDownMon->IsProgressive())
                {
                    // Set the linear return value
                    retVal = HXR_ADVISE_PREFER_LINEAR;
                }
            }
            else
            {
                // Manually monitor the file size. This
                // will trigger HasBeenProgressive() to 
                // be true if the file is progressive
                m_pProgDownMon->MonitorFileSize();
                // Are we now progressive?
                if (m_pProgDownMon->HasBeenProgressive())
                {
                    // Set the linear return value
                    retVal = HXR_ADVISE_PREFER_LINEAR;
                    // Begin the monitoring of the file size
                    m_pProgDownMon->BeginSizeMonitoring();
                }
            }
        }
    }
#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */
#endif      // _MACINTOSH

    MLOG_GEN(NULL, "\treturn %s\n",
             (retVal == HXR_OK ? "HXR_OK" :
                (retVal == HXR_FAIL ? "HXR_FAIL" :
                   (retVal == HXR_ADVISE_PREFER_LINEAR ? "HXR_ADVISE_PREFER_LINEAR" :
                      "Unknown"))));

    return retVal;
}

/************************************************************************
 *    Method:
 *        IHXFileObject::GetFileObjectFromPool
 *    Purpose:
 *      To get another FileObject from the same pool.
 */
STDMETHODIMP CSimpleFileObject::GetFileObjectFromPool (
    IHXGetFileFromSamePoolResponse* response
)
{
    HX_RESULT lReturnVal = HXR_FAILED;
    CSimpleFileObject* pFileObject = 0;
    CHXString new_path;
    CHXString strFileName;
    CHXString strURL;
    IUnknown* pUnknown = 0;

    char* pNewPath    = 0;
    char* pSeparator  = 0;

    UpdateFileNameMember();
    if(!m_pFilename)
    {
      pNewPath = new char[strlen(m_base_path) + 1];
      strcpy(pNewPath, m_base_path); /* Flawfinder: ignore */
    }
    else
    {
      strURL = m_pFilename;

      // Make a nice local file name from the URL!
      GetFullPathname(strURL, &strFileName);

      pNewPath = new char[strlen(strFileName) + 1];
      strcpy(pNewPath, (const char*)strFileName); /* Flawfinder: ignore */

      pSeparator = ::strrchr(pNewPath, OS_SEPARATOR_CHAR);
      if(pSeparator)
      {
          /* Separator will be added in seturl */
          *pSeparator = 0;
      }
      else
      {
          // started w/filename. no separator implies no path
          pNewPath[0] = 0;
      }
    }
    new_path = pNewPath;
    if (pNewPath)
    {
      delete [] pNewPath;
      pNewPath = 0;
    }

    pFileObject = new CSimpleFileObject(new_path,
                              m_pFileSystem,
                              m_pContext,
                              m_ulMaxIterationLevel);

    if (!pFileObject)
    {
      return HXR_OUTOFMEMORY;
    }

    lReturnVal = pFileObject->QueryInterface(IID_IUnknown,
                                   (void**)&pUnknown);

    response->FileObjectReady(lReturnVal == HXR_OK ?
                        HXR_OK : HXR_FAILED,
                        pUnknown);
    if(pUnknown)
    {
      pUnknown->Release();
      pUnknown = NULL;
    }

    return lReturnVal;
}

// IHXFileExists interface
/************************************************************************
 *    Method:
 *        IHXFileExists::DoesExist
 *    Purpose:
 */
STDMETHODIMP CSimpleFileObject::DoesExist(const char* /*IN*/  pPath,
                        IHXFileExistsResponse* /*IN*/  pFileResponse)
{
    BOOL bExists = FALSE;
    BOOL bPlusURL = FALSE;
    CHXString   strFileName;
    CHXString     strPath;
    CHXString   plusFileName;
    CHXString   plusPath;

    strPath = pPath;

    bPlusURL = HXXFile::IsPlusURL(pPath);
    if (bPlusURL)
    {
      INT32 index = strPath.ReverseFind('+');

      plusFileName = strPath.Right(strPath.GetLength() - (index+1));
      strPath = strPath.Left(index);
      index = strPath.ReverseFind('/');

      if (index >= 0)
      {
          plusPath = strPath.Left(index+1);
          plusPath = plusPath + plusFileName;
      }
      else
      {
          plusPath = plusFileName;
      }

      HXXFile::GetReasonableLocalFileName(plusPath);
      GetFullPathname(plusPath, &plusFileName);
    }

    // Make a nice local file name from the URL!

    HXXFile::GetReasonableLocalFileName(strPath);
    GetFullPathname(strPath, &strFileName);

#ifdef _MACINTOSH
    CHXDataFile*      pDataFile = 0; /* cross-platform file object */
    pDataFile = CHXDataFile::Construct();
    if (pDataFile->Open((const char*)strFileName,O_RDONLY) == HXR_OK)
    {
      if (bPlusURL)
      {
          if (pDataFile->Open((const char*)plusFileName,O_RDONLY) == HXR_OK)
          {
            bExists = TRUE;
          }
      }
      else
      {
          bExists = TRUE;
      }
    }
    delete pDataFile;
#else
    struct stat statbuf;

    m_pDataFile->Bind((const char *)strFileName);
    if (m_pDataFile->Stat(&statbuf) == 0)
    {
      if (bPlusURL)
      {
          m_pDataFile->Bind((const char *)plusFileName);
          if (m_pDataFile->Stat(&statbuf) == 0)
          {
            DPRINTF(0x5d000000, ("CSFO::DoesExist() -- both r TRUE\n"));
            bExists = TRUE;
          }
      }
      else
      {
          DPRINTF(0x5d000000, ("CSFO::DoesExist() -- TRUE\n"));
          bExists = TRUE;
      }
    }
    else
      DPRINTF(0x5d000000, ("CSFO::DoesExist() -- FALSE\n"));
#endif

    pFileResponse->DoesExistDone(bExists);
    return HXR_OK;
}

/************************************************************************
 *    Method:
 *        Private interface::OpenFile
 *    Purpose:
 *        This common method is used from Init() and GetFileObjectFromPool()
 */
STDMETHODIMP CSimpleFileObject::_OpenFile(ULONG32     ulFlags)
{
    HX_LOG_BLOCK( "CSimpleFileObject::_OpenFile" );

    DPRINTF(0x5d000000, ("CSFO::_OpenFile(%lu)\n", ulFlags));
    HX_RESULT lReturnVal = HXR_OK;
    IHXUserImpersonation* pUserImpersonationThis = NULL;
    CHXString strFileName;
    CHXString strURL;

    m_ulFlags = ulFlags;

    UpdateFileNameMember();
    strURL = m_pFilename;

    // Make a nice local file name from the URL!
    GetFullPathname(strURL, &strFileName);

    if (m_pUnknownUserContext)
    {
      m_pUnknownUserContext->QueryInterface(IID_IHXUserImpersonation,
          (void**)&pUserImpersonationThis);
    }

    if (pUserImpersonationThis)
    {
      // see ntauth plugin
      pUserImpersonationThis->Start();
    }

    UINT16 flags = 0;

#ifdef _MACINTOSH
    // use native modes
    if (ulFlags & HX_FILE_READ)
      flags |= O_RDONLY;
    if (ulFlags & HX_FILE_WRITE)
      flags |= O_WRONLY;
    if (ulFlags & HX_FILE_BINARY)
      flags |= O_BINARY;
    if (!ulFlags)
    {
      flags = O_RDONLY | O_BINARY;
      m_ulFlags = HX_FILE_READ | HX_FILE_BINARY;
    }

    // XXXGH must do HX_FILE_REPLACE for MAC
    m_pDataFile = (CMacAsyncFile*) CMacAsyncFile::Construct();
    m_pDataFile->SetAsyncResponse(m_pAsyncFileResponse);
    BOOL bAtInterrupt = FALSE;
    if (m_pInterruptState)
    {
         bAtInterrupt = m_pInterruptState->AtInterruptTime();
    }

    m_nFd = m_pDataFile->SafeOpen((const char*)strFileName,flags, 0, bAtInterrupt);
#else
    if (ulFlags & HX_FILE_READ)
      flags |= HX_FILEFLAG_READ;
    if (ulFlags & HX_FILE_WRITE)
      flags |= HX_FILEFLAG_WRITE;
    if (ulFlags & HX_FILE_BINARY)
      flags |= HX_FILEFLAG_BINARY;
    if (!ulFlags)
    {
      flags = HX_FILEFLAG_READ | HX_FILEFLAG_BINARY;
      m_ulFlags = HX_FILE_READ | HX_FILE_BINARY;
    }
    DPRINTF(0x5d000000, ("CSFO::_OpenFile() -- flags(%u)\n", flags));

    HX_ASSERT(m_pDataFile);

    MLOG_GEN(NULL, "CSimpleFileObject::_OpenFile() this=0x%08x tick=%lu filename=%s\n",
             this, HX_GET_BETTERTICKCOUNT(), (const char*) strFileName);
    m_pDataFile->Bind((const char *)strFileName);
    if (HXR_OK == m_pDataFile->Open((UINT16)m_ulFlags))
    {
      m_nFd = m_pDataFile->GetFd();
      DPRINTF(0x5d000000, ("CSFO::_OpenFile() "
            "-- fd(%d), filename(%s)\n", m_nFd,
            ((const char *)strFileName) ? (const char *)strFileName : "NULL"));
    }
    else
    {
      m_nFd = -1;
    }

#endif

    if (pUserImpersonationThis)
    {
      pUserImpersonationThis->Stop();
    }

    /* InitDone may result in close/destruction of this object.Mime mapper
     * is only interested in the mimetype. This is the sequence of events:
     * InitDone->FinishSetup->Close MimeMapper.. Release it. load right ff
     * hand over file system to the file format.
     * So by the time control returns back here, this object may already be
     * closed, resulting in m_nFd = -1
     */
    if (m_nFd != -1)
    {
      lReturnVal = HXR_OK;
    }
    else if (pUserImpersonationThis)
    {
      lReturnVal = HXR_NOT_AUTHORIZED;
    }
    else
    {
      DPRINTF(0x5d000000, ("Error: file missing\n"));
      lReturnVal = HXR_DOC_MISSING;
    }

    HX_RELEASE(pUserImpersonationThis);

    if (lReturnVal == HXR_OK)
    {
      if (!m_pDescriptorReg)
      {
          m_pContext->QueryInterface(IID_IHXDescriptorRegistration,
                                    (void **)&m_pDescriptorReg);
      }

      if (m_pDescriptorReg)
      {
          m_pDescriptorReg->RegisterDescriptors(1);
      }
    }

    return lReturnVal;
}

void
CSimpleFileObject::UpdateFileNameMember()
{
    const char* pURL;

    if (m_base_path.GetLength() > 0)
    {
       /* Running on the Server, do this without wasting CPU! */
       if (m_pRequest->GetURL(pURL) != HXR_OK)
       {
         HX_VECTOR_DELETE(m_pFilename);
         return;
       }

       UINT32 ulIndex = 0;
       while (pURL[ulIndex] != 0)
       {

         // HP - allow '$' in directory/file names
         //
         // still need to take care of that obsolete $ sign option:
         // rtsp://moe.cr.prognet.com/ambush.rm$1:00
         // time after the $ is assumed to be the start time.
         //
         // the solution is to compare the string following the $ to
         // a properly formed time. If the string is a time and only
         // a time, then we know its the old-style start-time option
         // otherwise, '$' is part of the directory/file name and we
         // will keep it.
         if (pURL[ulIndex] == '$')
         {
             const char* pszOption = &pURL[ulIndex + 1];
             if (::TimeParse(pszOption))
             {
               goto strip_and_duplicate;
             }
         }
         else
         {

#if !defined(_MACINTOSH) && !defined(_MAC_UNIX) // ? # and + are legal in Mac file names and shouldn't be stripped (yes, we're seeing a file name, not a URL here)

             switch (pURL[ulIndex])
             {


             case '?':
             case '#':
             case '+':
               goto strip_and_duplicate;
             default:
               break;
             }
#endif
         }
         ulIndex++;
       }
       HX_VECTOR_DELETE(m_pFilename);

#if defined(_MACINTOSH) || defined(_MAC_UNIX)
       m_pFilename = new_string(pURL); // on Mac, new_path_string mangles legal filenames which have / or \ in them
#else
       m_pFilename = new_path_string(pURL);
#endif

       return;

strip_and_duplicate:
       HX_VECTOR_DELETE(m_pFilename);

#if defined(_MACINTOSH) || defined(_MAC_UNIX)
       m_pFilename = new_string(pURL); // on Mac, new_path_string mangles legal filenames which have / or \ in them
#else
       m_pFilename = new_path_string(pURL);
#endif

       m_pFilename[ulIndex] = 0;

       return;
    }

    pURL = 0;

    if (m_pRequest)
    {
      if (m_pRequest->GetURL(pURL) != HXR_OK)
      {
          HX_VECTOR_DELETE(m_pFilename);

          m_pFilename = 0;
      }

      IHXValues* pReqHeader = NULL;
      BOOL bVerbatimFileName = FALSE;

      if (SUCCEEDED(m_pRequest->GetRequestHeaders(pReqHeader)) &&
                                        pReqHeader)
      {
          ULONG32 ulVal = 0;
          if (SUCCEEDED(pReqHeader->GetPropertyULONG32("VerbatimFileName",
                                           ulVal)))
          {
            bVerbatimFileName = (ulVal ? TRUE : FALSE);
          }
      }
      HX_RELEASE(pReqHeader);

      CHXString strFilename;

      if (bVerbatimFileName)
      {
          if (pURL)
          {
            strFilename = pURL;
          }
      }
      else
      {
          CHXURL* pCHXURL = new CHXURL(pURL, m_pCommonClassFactory);
          if (pCHXURL)
          {
            IHXValues* pHeader = pCHXURL->GetProperties();
            if(pHeader)
            {
                IHXBuffer* pUrlBuffer = NULL;
                if(HXR_OK == pHeader->GetPropertyBuffer(PROPERTY_URL, pUrlBuffer) &&
                  pUrlBuffer)
                {
                  strFilename = (const char*)pUrlBuffer->GetBuffer();
                  HX_RELEASE(pUrlBuffer);
                }
                HX_RELEASE(pHeader);
            }
            delete pCHXURL;
          }

          /*
          * Strip off the parameters
          */

          INT32 index = strFilename.Find('?');

          if (index >= 0)
          {
            strFilename = strFilename.Left(index);
          }

          if (HXXFile::IsPlusURL(pURL))
          {
            index = strFilename.ReverseFind('+');

            if (index >= 0)
            {
                strFilename = strFilename.Left(index);
            }
          }
      }

      HXXFile::GetReasonableLocalFileName(strFilename);

      HX_VECTOR_DELETE(m_pFilename);

#if defined(_CARBON) || defined(_MAC_UNIX)
      // Under Carbon, GetReasonableLocalFileName returns a proper Mac path, possibly including
      // slashes which shouldn't be mucked with.
      m_pFilename = new_string((const char *) strFilename);
#else
      m_pFilename = new_path_string((const char*)strFilename);
#endif

    }
}

STDMETHODIMP CSimpleFileObject::SetRequest
(
    IHXRequest* pRequest
)
{
    if (!pRequest)
    {
      return HXR_INVALID_PARAMETER;
    }

    HX_RELEASE(m_pRequest);

    m_pRequest = pRequest;
    m_pRequest->AddRef();

    UpdateFileNameMember();

    return HXR_OK;
}

STDMETHODIMP CSimpleFileObject::GetRequest
(
    REF(IHXRequest*) pRequest
)
{
    pRequest = m_pRequest;

    if (pRequest)
    {
      pRequest->AddRef();
    }

    return HXR_OK;
}

static BOOL DoRename(const char* pOldName,
                 const char* pNewName,
                 IUnknown** ppCommonObj)
{
    BOOL ret = FALSE;

#if defined(_MACINTOSH) || defined(_SYMBIAN)
    XHXDirectory* pHXDir =  XHXDirectory::Construct(ppCommonObj);

    if (pHXDir)
    {
      if (SUCCEEDED(pHXDir->Rename(pOldName, pNewName)))
      {
          ret = TRUE;
      }

      delete pHXDir;
    }
#elif defined(WIN32_PLATFORM_PSPC)
    if (MoveFile(OS_STRING(pOldName), OS_STRING(pNewName)) != 0)
    {
      ret = TRUE;
    }
#else
    if (rename(pOldName, pNewName) == 0)
    {
      ret = TRUE;
    }
#endif /* !defined(WIN32_PLATFORM_PSPC) */

    return ret;
}

STDMETHODIMP CSimpleFileObject::Rename
(
    const char* pFilename
)
{
    CHXString strFilename;
    CHXString newFilename;

    UpdateFileNameMember();
    strFilename = m_pFilename;

    INT32 index = strFilename.ReverseFind(OS_SEPARATOR_CHAR);

    if (index != -1)
    {
      newFilename = strFilename.Left(index+1);
      newFilename += pFilename;
    }
    else
    {
      newFilename = pFilename;
    }

    GetFullPathname((const char*)newFilename, &newFilename);
    GetFullPathname((const char*)strFilename, &strFilename);

    if (DoRename(strFilename, newFilename, &m_pFileSystem->m_pCommonObj) == FALSE)
    {
      return HXR_FAIL;
    }

    return HXR_OK;
}

STDMETHODIMP CSimpleFileObject::Move
(
    const char* pFilename
)
{
    CHXString strFilename;
    CHXString newFilename;
    HX_RESULT retVal = HXR_OK;

    UpdateFileNameMember();

    newFilename = pFilename;
    HXXFile::GetReasonableLocalFileName(newFilename);

    GetFullPathname((const char*) newFilename, &newFilename);
    GetFullPathname(m_pFilename, &strFilename);

    if (DoRename(strFilename, newFilename, &m_pFileSystem->m_pCommonObj) == FALSE)
    {
#ifdef _UNIX
      // We'll attempt to move the file manually since on UNIX
      // rename accross devices (file systems) is not supported
      FILE* pSource = fopen((const char*) strFilename, "rb");
      FILE* pTarget = fopen((const char*) newFilename, "rb");

      retVal = HXR_FAIL;

      if (pTarget)
      {
          // Target file already exists -> fail.
          fclose(pTarget);
          pTarget = NULL;
      }
      else
      {
          pTarget = fopen((const char*) newFilename, "wb");
      }

      if (pSource && pTarget)
      {
          retVal = HXR_OK;
      }

      if (retVal == HXR_OK)
      {
          const ULONG32 COPY_BUF_SIZE = 1024;
          UINT8 pBuffer[COPY_BUF_SIZE];
          size_t bufLen;

          do
          {
            bufLen = fread(pBuffer, 1, COPY_BUF_SIZE, pSource);
            if (bufLen > 0)
            {
                if (fwrite(pBuffer, 1, bufLen, pTarget) != bufLen)
                {
                  retVal = HXR_FAIL;
                  break;
                }
            }
          } while (bufLen == COPY_BUF_SIZE);
      }

      // Make sure no erorrs occured
      if (retVal == HXR_OK)
      {
          retVal = HXR_FAIL;

          if (feof(pSource) &&
            (ferror(pSource) == 0) &&
            (ferror(pTarget) == 0))
          {
            retVal = HXR_OK;
          }
      }

      if (pSource)
      {
          fclose(pSource);
          pSource = NULL;
      }

      // Remove the source file - only if copied OK
      if (retVal == HXR_OK)
      {
          if (remove((const char*) strFilename) != 0)
          {
            retVal = HXR_FAIL;
          }
      }

      if (pTarget)
      {
          fclose(pTarget);
          pTarget = NULL;

          // In case of failure, remove the target
          if (retVal != HXR_OK)
          {
            remove((const char*) newFilename);
          }
      }
#else // _UNIX
      retVal = HXR_FAIL;
#endif      // _UNIX
    }

    return retVal;
}

STDMETHODIMP
CSimpleFileObject::Remove()
{
#ifdef _MACINTOSH
    CHXString strFilename;

    UpdateFileNameMember();
    strFilename = m_pFilename;

    GetFullPathname((const char*)strFilename, &strFilename);

    if (unlink((const char*)strFilename) != 0)
    {
      return HXR_FAIL;
    }

    return HXR_OK;
#else
    UpdateFileNameMember();
    CHXString strFileName;
    CHXString strUrl = m_pFilename;
    // Make a nice local file name from the URL!

    HXXFile::GetReasonableLocalFileName(strUrl);
    GetFullPathname(strUrl, &strFileName);

    m_pDataFile->Bind((const char*)strFileName);
    HX_RESULT res = m_pDataFile->Delete();
    if (res == HXR_OK)
    {
      m_nFd = -1;
    }
    return res;
#endif
}

void
CSimpleFileObject::GetFullPathname(const char* pPath, CHXString* pPathname)
{

    if (m_base_path.GetLength() > 0)
    {
      INT32 lLevel = 0;
      for (const char* pTemp = pPath; *pTemp; pTemp++)
      {
          /*
           * Increment directory level if we have a slash, and it's
           * not a leading slash, and it's not right after a slash
           * (doesn't count on most OSes).
           */
          if (((*pTemp == OS_SEPARATOR_CHAR) ||
               (*pTemp == '/')) && (pTemp != pPath) &&
             ((*(pTemp-1)) != OS_SEPARATOR_CHAR) &&
             ((*(pTemp-1)) != '/'))

          {
            lLevel++;
          }

          if ((pTemp != pPath) &&
            (*pTemp == '.') &&
            (*(pTemp - 1) == '.'))

          {
            lLevel--;
            if (((*(pTemp + 1)) == OS_SEPARATOR_CHAR) ||
                ((*(pTemp + 1)) == '/'))
            {
                pTemp++;
            }
            if (lLevel < 0)
            {
                *pPathname = "!$InvalidPath";
                return;
            }
          }
      }

      if (*pPath)
      {
#if defined(_MACINTOSH) && !defined(_CARBON)

          CHXString strPath;
          CHXURL::decodeURL(pPath, strPath);
            pPath = (const char*)strPath;
            UINT32 ulPathLen = strPath.GetLength();
#else
            UINT32 ulPathLen = strlen(pPath);
#endif
            UINT32 ulBaseLen = m_base_path.GetLength();
            char* pStr = new char[ulBaseLen + ulPathLen + 2];
            char* pTmp = pStr;
            if (ulBaseLen)
            {
                memcpy(pTmp, (const char*)m_base_path, ulBaseLen); /* Flawfinder: ignore */
                pTmp += ulBaseLen;
            }
            *pTmp = OS_SEPARATOR_CHAR;
            pTmp++;
            memcpy(pTmp, pPath, ulPathLen + 1); /* Flawfinder: ignore */
            CHXString strResult(pStr, ulBaseLen + ulPathLen + 2);
            *pPathname = strResult;
            delete[] pStr;
      }
      else
      {
          *pPathname = m_base_path;
      }
    }
    else
    {

#if defined (_MACINTOSH) && !defined(_CARBON)

        CHXString strPath;
        CHXURL::decodeURL(pPath, strPath);
#else
        CHXString strPath(pPath, strlen(pPath));
#endif
        *pPathname = strPath;
    }
}

HX_RESULT
CSimpleFileObject::ActualAsyncReadDone(HX_RESULT result, IHXBuffer* pBuffer)
{
    MLOG_PD(NULL, "CSimpleFileObject::ActualAsyncReadDone(0x%08x,0x%08x) tick=%lu\n",
            result, pBuffer, HX_GET_BETTERTICKCOUNT());
    HX_LOG_BLOCK( "CSimpleFileObject::ActualAsyncReadDone" );

    m_bReadPending = FALSE;
    m_bAsyncReadPending = FALSE;

    // Let the file response sink know about the buffer...
    HX_RESULT rv = m_pFileResponse->ReadDone(result, pBuffer);

    MLOG_PD(NULL, "\treturning 0x%08x from ActualAsyncReadDone() tick=%lu\n",
            rv, HX_GET_BETTERTICKCOUNT());

    return rv;
}

HX_RESULT
CSimpleFileObject::ActualAsyncSeekDone(HX_RESULT result)
{
#ifndef _MACINTOSH
    MLOG_PD(NULL, "CSimpleFileObject::ActualAsyncSeekDone(0x%08x) tick=%lu\n",
            result, HX_GET_BETTERTICKCOUNT());
    HX_RESULT rv = m_pFileResponse->SeekDone(result);
    MLOG_PD(NULL, "\treturning 0x%08x from ActualAsyncSeekDone() tick=%lu\n",
            rv, HX_GET_BETTERTICKCOUNT());
    return rv;
#else
    switch (m_eSeekReason)
    {
      case REINIT_SEEK:
          return m_pFileResponse->InitDone(!result ? HXR_OK : HXR_FAIL);
      case EXTERNAL_SEEK:
          return m_pFileResponse->SeekDone(result);
      case PRE_TELL_SEEK:
          {
            m_ulSize = m_pDataFile->Tell();
            BOOL bAtInterrupt = FALSE;
            if (m_pInterruptState)
            {
                bAtInterrupt = m_pInterruptState->AtInterruptTime();
            }

            m_eSeekReason = POST_TELL_SEEK;

            /* If we need to close the file, why waste time in seeking back
             * to the original position
             */
            if (!m_bFileToBeClosed)
            {
                m_pDataFile->SafeSeek(m_ulPreSeekPosition,SEEK_SET);
            }
            else
            {
                ActualAsyncSeekDone(result);
            }
          }
          break;
      case POST_TELL_SEEK:
          {
            if (m_bFileToBeClosed && m_pDataFile)
            {
                m_bFileToBeClosed = FALSE;
                HX_RELEASE(m_pDataFile);
                m_pDataFile = NULL;
                m_nFd = -1;
            }

            struct stat StatBuffer;
            StatBuffer.st_size = m_ulSize;
            StatBuffer.st_ctime = 0;
            StatBuffer.st_atime = 0;
            StatBuffer.st_mtime = 0;
            StatBuffer.st_mode = 0;
            if (m_pFileStatResponse)
            {
                // Access to members after async ..Done() call is invalid
                IHXFileStatResponse *pTmpFileStatResponse = m_pFileStatResponse;

                m_pFileStatResponse = NULL;
                pTmpFileStatResponse->StatDone( HXR_OK,
                                        StatBuffer.st_size,
                                        StatBuffer.st_ctime,
                                        StatBuffer.st_atime,
                                        StatBuffer.st_mtime,
                                        StatBuffer.st_mode);
                pTmpFileStatResponse->Release();
            }
          }
          break;
      default:
          HX_ASSERT(FALSE);
          return HXR_FAIL;
    }

    return HXR_OK;
#endif
}

STDMETHODIMP_(UINT32)
CSimpleFileObject::IsThreadSafe()
{
    return HX_THREADSAFE_METHOD_FS_READ;
}

#ifdef _MACINTOSH
// SMPLAsyncResponse
CSimpleFileObject::SMPLAsyncResponse::SMPLAsyncResponse(CSimpleFileObject* pFileObject)
{
    m_pSMPLFileObject = pFileObject;
}

CSimpleFileObject::SMPLAsyncResponse::~SMPLAsyncResponse()
{
}

HX_RESULT
CSimpleFileObject::SMPLAsyncResponse::AsyncReadDone (HX_RESULT result, IHXBuffer* pBuffer)
{
    m_pSMPLFileObject->ActualAsyncReadDone(result, pBuffer);
    return HXR_OK;
}

HX_RESULT
CSimpleFileObject::SMPLAsyncResponse::AsyncSeekDone (HX_RESULT result)
{
    m_pSMPLFileObject->ActualAsyncSeekDone(result);
    return HXR_OK;
}

#endif /*_MACINTOSH*/

#if defined(HELIX_FEATURE_PROGDOWN)

STDMETHODIMP CSimpleFileObject::ProgressiveCallback()
{
    if (m_ulCallbackState == CallbackStateSeek)
    {
        MLOG_PD(NULL, "CSimpleFileObject::ProgressiveCallback() this=0x%08x tick=%lu state=%s\n",
                this, HX_GET_BETTERTICKCOUNT(), "CallbackStateSeek");
        // Make sure we do not destruct during SeekDone()
        AddRef();
        // Do the seek
        HX_RESULT seekDoneResult = HXR_OK;
        DoSeek(seekDoneResult);
        // Undo the AddRef();
        Release();
    }
    else if (m_ulCallbackState == CallbackStateRead)
    {
        MLOG_PD(NULL, "CSimpleFileObject::ProgressiveCallback() this=0x%08x tick=%lu state=%s\n",
                this, HX_GET_BETTERTICKCOUNT(), "CallbackStateRead");
        // Make sure we do not destruct in ReadDone()
        AddRef();
        // Call DoRead()
        BOOL bProgFail = FALSE;
        DoRead(bProgFail);
        // Undo the AddRef()
        Release();
    }

    return HXR_OK;
}

BOOL CSimpleFileObject::RequireFullRead()
{
    BOOL bRet = FALSE;

    if (m_pFileResponse)
    {
        // QueryInterface for the IHXAdvise interface
        IHXAdvise* pAdvise = NULL;
        m_pFileResponse->QueryInterface(IID_IHXAdvise, (void**) &pAdvise);
        if (pAdvise)
        {
            if (SUCCEEDED(pAdvise->Advise(HX_FILERESPONSEADVISE_REQUIREFULLREAD)))
            {
                bRet = TRUE;
            }
        }
        HX_RELEASE(pAdvise);
    }

    return bRet;
}

HX_RESULT CSimpleFileObject::FinishDoReadWithCallback(UINT32 actual)
{
    // Seek backwards if necessary
    SeekBackwards(actual);
    // Set the callback state
    m_ulCallbackState = CallbackStateRead;
    // Schedule a callback to try the read again
    m_pProgDownMon->ScheduleCallback();

    return HXR_OK;
}

HX_RESULT CSimpleFileObject::FinishDoReadWithoutCallback(UINT32 actual)
{
    // Seek backwards if necessary
    SeekBackwards(actual);
    // Since we can't employ callback when m_bAsyncAccess is FALSE,
    // then we must sleep. We'll sleep for 100ms (or 100000 microsec)
    microsleep(100000);

    return HXR_OK;
}

#endif /* #if defined(HELIX_FEATURE_PROGDOWN) */

void CSimpleFileObject::StackCallback(void* pArg)
{
    MLOG_PD(NULL, "CSimpleFileObject::StackCallback() tick=%lu\n", HX_GET_BETTERTICKCOUNT());
    if (pArg)
    {
        CSimpleFileObject* pFileObject = (CSimpleFileObject*) pArg;
        pFileObject->AddRef();
        BOOL bProgFail = FALSE;
        pFileObject->DoRead(bProgFail);
        pFileObject->Release();
    }
}

Generated by  Doxygen 1.6.0   Back to index