//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*--
Module Name: davxml.h
Abstract: Parses XML in POST data for DAV verbs
--*/


#include "httpd.h"


//
//  Base class that other parsers inherit from
//

// By default any character data sent in an XML tag will be discarded.  Set 
// this flag for any properties that character data needs to be saved for.
#define DAV_STATE_SAVE_CHARACTERS    0x1000

#define DAV_STATE_UNINITAILIZED           0
#define DAV_STATE_UNKNOWN                 1
#define DAV_STATE_UNRECOGNIZED_TAG        2


typedef enum 
{
    NAMESPACE_DAV,
    NAMESPACE_MS,
} DAV_NAMESPACE;

class CDavParse : public SVSSAXContentHandler 
{
protected:
    DWORD            m_State;
    DWORD            m_PreviousState; 
    int              m_iUnknownDepth;  // Number of unknown tags on "stack"
    CWebDav          *m_pWebDav;
    SVSSimpleBuffer  m_Chars;

public:
    CDavParse(CWebDav *pWebDav) 
    { 
        m_pWebDav = pWebDav;
        m_PreviousState = m_State   = DAV_STATE_UNINITAILIZED;
        m_iUnknownDepth = 0;
    }

    ~CDavParse() 
    { 
        ; 
    }

    // Verify data sent to us is as it should be
    BOOL PrepareNonEmptyBuf(void);
    BOOL VerifyNS(DAV_NAMESPACE ns, const WCHAR *szNamespace, DWORD ccNamespace);
    BOOL VerifyRequiredTag(const CHAR *szTokenExpected, DWORD ccExpected, const CHAR *szLocalName, DWORD ccLocalName);

    // SAX implementation.  Leave startElement and endElement to particular search class.
    virtual HRESULT STDMETHODCALLTYPE characters(const wchar_t  *pwchChars, int cchChars) 
    {
        if (! (m_State & DAV_STATE_SAVE_CHARACTERS))
        {
            return S_OK;
        }

        if (m_Chars.Append((PSTR) pwchChars, cchChars*sizeof(WCHAR)))
        {
            return S_OK;
        }

        return E_FAIL;
    }

    // When we get a tag we don't know about, save old state temporarily.
    // 
    void SetUnknownTag(void) 
    {
        if (m_State != DAV_STATE_UNRECOGNIZED_TAG) 
        {
            DEBUGCHK(m_iUnknownDepth == 0);
            m_iUnknownDepth++;
            m_PreviousState = m_State;
            m_State         = DAV_STATE_UNRECOGNIZED_TAG;
        }
        else 
        {
            DEBUGCHK(m_iUnknownDepth);
            m_iUnknownDepth++;
        }
    }

    void RestoreUnknownTagState(void) 
    {
        DEBUGCHK(m_State == DAV_STATE_UNRECOGNIZED_TAG);
        m_iUnknownDepth--;

        if (m_iUnknownDepth == 0)
        {
            m_State = m_PreviousState;
        }
    }
};


// 
// PROPFIND
//

// PROPFIND has the form: 
// <propfind>
//  <propname/>|<allprop/>|<prop>

// <propname> returns only property names (not values) for source URL
// <allprop>  returns all property names and values for source URL
// <prop> is theoretically free-form XML specifying which properties to receive.
//   CE supports only the fixed tags such as <getcontentlength> and <getcontentlength>.

// PropFind states
#define PROPFIND_STATE_PROPFIND             10
#define PROPFIND_STATE_PROP                 11
#define PROPFIND_STATE_ALLPROP              12
#define PROPFIND_STATE_PROPNAME             13
#define PROPFIND_STATE_CONTENT_LEN          14
#define PROPFIND_STATE_DISPLAY_NAME         15
#define PROPFIND_STATE_CREATION_DATE        16
#define PROPFIND_STATE_GET_ETAG             17
#define PROPFIND_STATE_LAST_MODIFIED        18
#define PROPFIND_STATE_RESOURCE_TYPE        19
#define PROPFIND_STATE_IS_HIDDEN            20
#define PROPFIND_STATE_IS_COLLECTION        21
#define PROPFIND_STATE_SUPPORTED_LOCK       22
#define PROPFIND_STATE_LOCK_DISCOVERY       23
#define PROPFIND_STATE_CONTENT_TYPE         24

class CPropFindParse : public CDavParse 
{
    DWORD m_dwDavProperties; // subset of properties to send, mask of DAV_PROP_xxx flags.

public:
    CPropFindParse(CWebDav *pWebDav) : CDavParse(pWebDav) 
    {
        m_dwDavProperties = 0;
    }

    ~CPropFindParse(void) 
    { 
        ; 
    }

    // implemented SAX functions.
    virtual HRESULT STDMETHODCALLTYPE startElement(const wchar_t * pwchNamespaceUri, int cchNamespaceUri, const wchar_t * pwchLocalName, 
                         int cchLocalName, const wchar_t * pwchQName, int cchQName, ISAXAttributes * pAttributes);

    virtual HRESULT STDMETHODCALLTYPE endElement(const wchar_t  *pwchNamespaceUri, int cchNamespaceUri, const wchar_t  *pwchLocalName,
                                                 int cchLocalName, const wchar_t  *pwchQName, int cchQName);
};

//
// PROPPATCH
//

// PROPPATCH has the form:
// <propertyupdate>
//  <set|remove> // one or more such elements.
//   <prop> ... </prop>

// <prop> tag is theoretically free-form XML specifying properties to set,
//   though CE has a limited set of what will be accepted.

// PropPatch states
#define PROPPATCH_STATE_PROPERTYUPDATE      10
#define PROPPATCH_STATE_SET                 11
#define PROPPATCH_STATE_REMOVE              12
#define PROPPATCH_STATE_PROP                13
#define PROPPATCH_STATE_WIN32_LAST_ACCESS   14
#define PROPPATCH_STATE_WIN32_LAST_MODIFIED 15
#define PROPPATCH_STATE_WIN32_CREATION_TIME 16
#define PROPPATCH_STATE_WIN32_FILE_ATTRIBS  (DAV_STATE_SAVE_CHARACTERS+0)

class CPropPatchParse : public CDavParse 
{
public:
    CPropPatchParse(CWebDav *pWebDav) : CDavParse(pWebDav) 
    { 
        ; 
    }

    ~CPropPatchParse(void) 
    { 
        ; 
    }

    // implemented SAX functions.
    virtual HRESULT STDMETHODCALLTYPE startElement(const wchar_t * pwchNamespaceUri, int cchNamespaceUri, const wchar_t * pwchLocalName, 
                         int cchLocalName, const wchar_t * pwchQName, int cchQName, ISAXAttributes * pAttributes);

    virtual HRESULT STDMETHODCALLTYPE endElement(const wchar_t  *pwchNamespaceUri, int cchNamespaceUri, const wchar_t  *pwchLocalName,
                                                 int cchLocalName, const wchar_t  *pwchQName, int cchQName);
};



//
//  LOCK
//

// LOCK has the form:
// <lockinfo>
//  <lockscope>(<exclusive/>|<shared/>)</lockscope>
//  <locktype><write/></locktype>
//  <owner><href></owner>

#define LOCK_STATE_LOCKINFO               10
#define LOCK_STATE_LOCKSCOPE              11
#define LOCK_STATE_EXCLUSIVE              12
#define LOCK_STATE_SHARED                 13
#define LOCK_STATE_LOCKTYPE               14
#define LOCK_STATE_WRITE                  15
#define LOCK_STATE_OWNER                  (DAV_STATE_SAVE_CHARACTERS+0)



// CNameSpaceMap contains mappings between namespace name and the prefix.  However
// it's possible for a namespace prefix to be overriden based on scoping rules of XML
// (i.e <a xmlns:d="Fo"><b xmlns:d="Fo2">...).  So if we get multiple name spaces of 
// same name put additional mappings under m_pChild.
class CNameSpaceMap 
{
private:
    // make these private as this class is only initialized through small block allocator anyway...
    CNameSpaceMap() 
    {
        ; 
    }
    ~CNameSpaceMap() 
    { 
        ; 
    }

public:
    WCHAR *m_szPrefix;
    WCHAR *m_szURI;
    CNameSpaceMap *m_pNext;  // sibling in linked list
    CNameSpaceMap *m_pChild; 

    BOOL Init(const WCHAR *szPrefix, const WCHAR *szURI) 
    {
        m_pChild = m_pNext = NULL;
        m_szPrefix = m_szURI = NULL;

        if (szPrefix) 
        {
            if (NULL == (m_szPrefix = MySzDupW(szPrefix)))
            {
                return FALSE;
            }
        }
        if (NULL == (m_szURI = MySzDupW(szURI)))
        {
            return FALSE;
        }

        return TRUE;
    }

    void DeInit(void) 
    {
        MyFree(m_szPrefix);
        MyFree(m_szURI);
    }
    
};


class CLockParse : public CDavParse 
{ 
private:
    friend class CWebDavFileLockManager;

//    DWORD m_dwLockAccess; // CreateFile for DavLock is always opened with (GENERIC_READ | GENERIC_WRITE).
    DAV_LOCK_SHARE_MODE m_lockShareMode;

    SVSXMLWriter        m_OwnerXML;
    PSTR                m_szLockOwner;

    // keep a mapping between name space prefixes and URI's for <owner> tag printout.
    
    BOOL m_fReadNameSpaceMap;     // Do we have to do NS processing?  We don't once we've handled <owner>
    CNameSpaceMap *m_NSList;      // List of name spaces.
    FixedMemDescr *m_NSDescr;     // Memory for queue

    void SetSharedLock(void)    
    { 
        m_lockShareMode = DAV_LOCK_SHARED;    
    }
    void SetExclusiveLock(void) 
    { 
        m_lockShareMode = DAV_LOCK_EXCLUSIVE; 
    }

    BOOL SetLockOwner(void) 
    {
        MyFreeNZ(m_szLockOwner);

        if (m_OwnerXML.GetNumChars() == 0) 
        {
            // If the owner is empty, then store an empty string.
            if (NULL == (m_szLockOwner = MySzDupA(cszEmpty)))
            {
                return FALSE;
            }

            return TRUE;
        }

        if (FAILED(m_OwnerXML.characters(L"\0",1)))
        {
            return FALSE;
        }

        if (NULL == (m_szLockOwner = m_OwnerXML.ConvertOutput(CP_ACP)))
        {
            return FALSE;
        }

        return TRUE;
    }

    // LOCK accessors, when parsing XML
    // SetWriteLockType does nothing for now, because we only support write locks.  
    void SetWriteLockType(void) 
    { 
        ;
    } 

public:
    CLockParse(CWebDav *pWebDav) : CDavParse(pWebDav) 
    { 
        m_szLockOwner = NULL;
        m_lockShareMode   = DAV_LOCK_UNKNOWN;
        m_fReadNameSpaceMap     = TRUE;

        m_NSDescr = svsutil_AllocFixedMemDescr(sizeof(CNameSpaceMap),10);
        m_NSList  = NULL;
    }

    ~CLockParse(void) 
    {
        MyFreeNZ(m_szLockOwner);    

        while (m_NSList) 
        {
            // For each item in the list
            CNameSpaceMap *pNext  = m_NSList->m_pNext;
            CNameSpaceMap *pChild = m_NSList;

            while (pChild) 
            {
                // For each child element.
                CNameSpaceMap *pNextChild = pChild->m_pChild;

                pChild->DeInit();
                svsutil_FreeFixed(pChild,m_NSDescr);
                pChild = pNextChild;
            }

            m_NSList = pNext;
        }

        svsutil_ReleaseFixedEmpty(m_NSDescr);
        
    }

    // implemented SAX functions.
    virtual HRESULT STDMETHODCALLTYPE startElement(const wchar_t * pwchNamespaceUri, int cchNamespaceUri, const wchar_t * pwchLocalName, 
                         int cchLocalName, const wchar_t * pwchQName, int cchQName, ISAXAttributes * pAttributes);

    virtual HRESULT STDMETHODCALLTYPE endElement(const wchar_t  *pwchNamespaceUri, int cchNamespaceUri, const wchar_t  *pwchLocalName,
                                                 int cchLocalName, const wchar_t  *pwchQName, int cchQName);

    virtual HRESULT STDMETHODCALLTYPE characters(const wchar_t  *pwchChars, int cchChars) 
    {
        // special handeling for LOCK characters.  Currently only data we care
        // about lives inside <owner> tag, but because this could be XML we need to 
        // use 
        if (m_State != LOCK_STATE_OWNER)
        {
            return S_OK;
        }

        return m_OwnerXML.characters(pwchChars,cchChars);
    }

    virtual HRESULT STDMETHODCALLTYPE processingInstruction(const wchar_t __RPC_FAR *pwchTarget,int cchTarget,
                                                            const wchar_t __RPC_FAR *pwchData, int cchData)
    {
        if (m_State != LOCK_STATE_OWNER)
        {
            return S_OK;
        }

        return m_OwnerXML.processingInstruction(pwchTarget,cchTarget,pwchData,cchData);
    }

    // Spit out XML tags into a file stream.
    BOOL WriteXMLTagToBuffer(const wchar_t * pwchNamespaceUri, int cchNamespaceUri, const wchar_t * pwchLocalName, 
                     int cchLocalName, const wchar_t * pwchQName, int cchQName, ISAXAttributes * pAttributes, BOOL fIsStartTag);

    // Keep track of Prefix Mappings because we may need to spit them out again
    // for the owner tag...
    virtual HRESULT STDMETHODCALLTYPE startPrefixMapping(
        /* [in] */ const wchar_t __RPC_FAR *pwchPrefix,
        /* [in] */ int cchPrefix,
        /* [in] */ const wchar_t __RPC_FAR *pwchUri,
        /* [in] */ int cchUri);
    
    virtual HRESULT STDMETHODCALLTYPE endPrefixMapping( 
        /* [in] */ const wchar_t __RPC_FAR *pwchPrefix,
        /* [in] */ int cchPrefix);
};
