//---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1997.
//
//  File:  parse.cxx
//
//  Contents:  IIS Pathname Parser
//
//    The Pathname Parser is a key component in ADs providers. It checks for
//    syntactic validity of an ADs pathname that has been passed to this
//    provider. If the syntax is valid, then an OBJECTINFO structure is
//    constructed. This OBJECTINFO structure contains a componentized version
//    of the ADs pathname for this object.
//
//    Note all that is being done is a syntax check. Rather than special-case
//    every single new nuance to pathnames, all path checking must conform to
//    the grammar rules laid out by the parser.
//
//
//
//  History:
//----------------------------------------------------------------------------
#include "iis.hxx"
#pragma hdrstop

KWDLIST KeywordList[MAX_KEYWORDS] =
{
    { TOKEN_SCHEMA, SCHEMA_CLASS_NAME},
    { TOKEN_CLASS, CLASS_CLASS_NAME },
    { TOKEN_PROPERTY, PROPERTY_CLASS_NAME },
    { TOKEN_SYNTAX, SYNTAX_CLASS_NAME },
};


// Object -> PathName, Type, eos
// Object -> PathName, eos

//+---------------------------------------------------------------------------
//  Function:   ADsObject
//
//  Synopsis:   parses an ADs pathname passed to this provider. This function
//              parses the following grammar rules
//
//              <ADsObject> -> <ProviderName> <IISObject>
//
//
//  Arguments:  [CLexer * pTokenizer] - a lexical analyzer object
//              [POBJECTINFO pObjectInfo] - a pointer to an OBJECTINFO structure
//
//  Returns:    [HRESULT] 0 if successful, error HRESULT if not
//
//  Modifies:   pTokenizer (consumes the input buffer)
//
//----------------------------------------------------------------------------
HRESULT
ADsObject(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = ProviderName(pTokenizer, pObjectInfo);
    BAIL_IF_ERROR(hr);

    pObjectInfo->ObjectType = TOKEN_IISOBJECT;
    pObjectInfo->ComponentArray = NULL;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    switch (dwToken) {

    case TOKEN_END:
        RRETURN(S_OK);

    case TOKEN_COMMA:
        hr = Type(pTokenizer, pObjectInfo);
        BAIL_IF_ERROR(hr);
        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);
        if (dwToken == TOKEN_END) {
            RRETURN(S_OK);
        }else {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

    default:
        hr = pTokenizer->PushBackToken();


        hr = IISObject(pTokenizer, pObjectInfo);
        BAIL_IF_ERROR(hr);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);

        switch (dwToken) {
        case TOKEN_END:
            RRETURN(S_OK);

        case TOKEN_COMMA:
            hr = Type(pTokenizer, pObjectInfo);
            BAIL_IF_ERROR(hr);
            hr = pTokenizer->GetNextToken(szToken, &dwToken);
            BAIL_IF_ERROR(hr);
            if (dwToken == TOKEN_END) {
                RRETURN(S_OK);
            }else {
                RRETURN(E_ADS_BAD_PATHNAME);
            }

        default:
            RRETURN(E_FAIL);

        }
    }

cleanup:
    RRETURN(hr);

}


//+---------------------------------------------------------------------------
//  Function:   IISObject
//
//  Synopsis:   parses an ADs pathname passed to this provider. This function
//              parses the following grammar rules
//
//              <IISObject> -> "\\""identifier""\" <IISObject>
//
//
//  Arguments:  [CLexer * pTokenizer] - a lexical analyzer object
//              [POBJECTINFO pObjectInfo] - a pointer to an OBJECTINFO structure
//
//  Returns:    [HRESULT] 0 if successful, error HRESULT if not
//
//  Modifies:   pTokenizer (consumes the input buffer)
//
//----------------------------------------------------------------------------

HRESULT
IISObject(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if ((dwToken != TOKEN_FSLASH) &&  (dwToken != TOKEN_BSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }


    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if ((dwToken != TOKEN_FSLASH) && (dwToken != TOKEN_BSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }


    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if (dwToken != TOKEN_IDENTIFIER) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = AddTreeName(pObjectInfo, szToken);
    BAIL_IF_ERROR(hr);

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    //
    // If we get an TOKEN_END, then we have a tree name only \\<tree_name>
    //

    if (dwToken == TOKEN_END || dwToken == TOKEN_COMMA) {
        hr = pTokenizer->PushBackToken();
        RRETURN(S_OK);
    }

    if ((dwToken != TOKEN_BSLASH) && (dwToken != TOKEN_FSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = PathName(pTokenizer, pObjectInfo);
    BAIL_IF_ERROR(hr);

cleanup:
    RRETURN(hr);
}

HRESULT
ProviderName(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken == TOKEN_ATSIGN) {

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);

        if (dwToken != TOKEN_IDENTIFIER) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

        hr = AddProviderName(pObjectInfo, szToken);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);


        if (dwToken != TOKEN_EXCLAMATION) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

    }else if (dwToken == TOKEN_IDENTIFIER) {

        hr = AddProviderName(pObjectInfo, szToken);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);


        if (dwToken != TOKEN_COLON) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

    }else {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    //
    // You can now disable the processing for "@" and "!" treat them
    // as ordinary characters.
    //

    pTokenizer->SetAtDisabler(TRUE);


    RRETURN(S_OK);

cleanup:

    RRETURN(hr);
}


//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
HRESULT
PathName(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    HRESULT hr;
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;

    do
    {
        hr = Component(pTokenizer, pObjectInfo);
        BAIL_IF_ERROR(hr);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);
    }
    while( (dwToken == TOKEN_BSLASH) || (dwToken == TOKEN_FSLASH) );

    hr = pTokenizer->PushBackToken();
    BAIL_IF_ERROR(hr);

cleanup:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:  Component -> <identifier>
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
HRESULT
Component(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szValue[MAX_TOKEN_LENGTH];
    WCHAR szComponent[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szComponent, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken != TOKEN_IDENTIFIER) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = AddComponent(pObjectInfo, szComponent, NULL);
    BAIL_IF_ERROR(hr);

cleanup:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
CLexer::CLexer(LPWSTR szBuffer):
                _ptr(NULL),
                _Buffer(NULL),
                _dwLastTokenLength(0),
                _dwLastToken(0),
                _dwEndofString(0),
                _bAtDisabled(FALSE)
{
    if (!szBuffer || !*szBuffer) {
        return;
    }
    _Buffer = AllocADsStr(szBuffer);
    _ptr = _Buffer;
}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History:    08-12-96   t-danal     Created.
//
//----------------------------------------------------------------------------
CLexer::~CLexer()
{
    FreeADsStr(_Buffer);
}

/*++
    This routine assumes that szToken is a buffer of at least MAX_TOKEN_LENGTH
--*/
HRESULT
CLexer::GetNextToken(LPWSTR szToken, LPDWORD pdwToken)
{
    WCHAR c;
    DWORD state = 0;
    LPWSTR pch = szToken;

    memset(szToken, 0, sizeof(WCHAR) * MAX_TOKEN_LENGTH);
    _dwLastTokenLength = 0;
    while (1) {
        c = NextChar();
        switch (state) {
        case  0:
            if (_dwLastTokenLength++ >= MAX_TOKEN_LENGTH) {
                RRETURN(RETURNCODETOHRESULT(ERROR_INVALID_NAME));
            }
            *pch++ = c;

            if (c == L'\\') {
                *pdwToken = TOKEN_BSLASH;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'/') {
                *pdwToken = TOKEN_FSLASH;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L',') {
                *pdwToken = TOKEN_COMMA;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'='){
                *pdwToken = TOKEN_EQUAL;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }
            else if (c == L':'){
                *pdwToken = TOKEN_COLON;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'\0'){
                *pdwToken = TOKEN_END;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'@') {

                if (!_bAtDisabled) {

                    *pdwToken = TOKEN_ATSIGN;
                    _dwLastToken = *pdwToken;
                    RRETURN(S_OK);

                }else {
                    state = 1;
                }

            }else if (c == L'!'){

                if (!_bAtDisabled) {

                    *pdwToken = TOKEN_EXCLAMATION;
                    _dwLastToken = *pdwToken;
                    RRETURN(S_OK);


                }else {
                    state = 1;
                }

            }else {

                state = 1;
            }
            break;


        case 1:
            if (c == L'\\' || c == L'\0' || c == L',' ||
                    c == L'=' ||
                    c == L':' || c == L'/') {
                PushbackChar();

                *pdwToken = TOKEN_IDENTIFIER;
                _dwLastToken = *pdwToken;
                RRETURN (S_OK);

            }else if (c == L'@' || c == L'!') {

                if (!_bAtDisabled) {

                    PushbackChar();

                    *pdwToken = TOKEN_IDENTIFIER;
                    _dwLastToken = *pdwToken;
                    RRETURN(S_OK);


                }else {

                    if (_dwLastTokenLength++ >= MAX_TOKEN_LENGTH) {
                        RRETURN(RETURNCODETOHRESULT(ERROR_INVALID_NAME));
                    }
                    *pch++ = c;
                    state = 1;
                    break;

                }

            }else {
                if (_dwLastTokenLength++ >= MAX_TOKEN_LENGTH) {
                    RRETURN(RETURNCODETOHRESULT(ERROR_INVALID_NAME));
                }
                *pch++ = c;
                state = 1;
                break;
            }

        default:
            RRETURN(RETURNCODETOHRESULT(ERROR_INVALID_NAME));
        }
    }
}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
WCHAR
CLexer::NextChar()
{
    if (_ptr == NULL || *_ptr == L'\0') {
        _dwEndofString = TRUE;
        return(L'\0');
    }
    return(*_ptr++);
}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
HRESULT
CLexer::PushBackToken()
{
    if (_dwLastToken == TOKEN_END) {
        RRETURN(S_OK);
    }
    _ptr -= _dwLastTokenLength;

    RRETURN(S_OK);
}


//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
void
CLexer::PushbackChar()
{
    if (_dwEndofString) {
        return;
    }
    _ptr--;

}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
//----------------------------------------------------------------------------
BOOL
CLexer::IsKeyword(LPWSTR szToken, LPDWORD pdwToken)
{
    DWORD i = 0;

    for (i = 0; i < MAX_KEYWORDS; i++) {
        if (!_wcsicmp(szToken, KeywordList[i].Keyword)) {
            *pdwToken = KeywordList[i].dwTokenId;
            return(TRUE);
        }
    }
    *pdwToken = 0;
    return(FALSE);
}

//+---------------------------------------------------------------------------
//Function:
//
//Synopsis:
//
//Arguments:
//
//Returns:
//
//Modifies:
//
//History:    11-3-95   krishnag     Created.
//
//----------------------------------------------------------------------------
HRESULT
AddComponent(POBJECTINFO pObjectInfo, LPWSTR szComponent, LPWSTR szValue)
{
    if (!szComponent || !*szComponent) {
        RRETURN(E_FAIL);
    }

    if (pObjectInfo->ComponentArray == NULL) {
        pObjectInfo->ComponentArray =
               (PCOMPONENT) AllocADsMem(sizeof(COMPONENT)*MAXCOMPONENTS);
        pObjectInfo->MaxComponents = MAXCOMPONENTS;
    }
    else if (pObjectInfo->NumComponents == pObjectInfo->MaxComponents) {
        pObjectInfo->ComponentArray =
               (PCOMPONENT) ReallocADsMem(
                                  pObjectInfo->ComponentArray,
                                  sizeof(COMPONENT)*pObjectInfo->MaxComponents,
                                  sizeof(COMPONENT)*(
                                    pObjectInfo->MaxComponents+MAXCOMPONENTS)
                                  );
        pObjectInfo->MaxComponents += MAXCOMPONENTS;
    }

    pObjectInfo->ComponentArray[pObjectInfo->NumComponents].szComponent =
                        AllocADsStr(szComponent);

    if (szValue && *szValue) {
        pObjectInfo->ComponentArray[pObjectInfo->NumComponents].szValue =
                        AllocADsStr(szValue);
    }
    else {
        pObjectInfo->ComponentArray[pObjectInfo->NumComponents].szValue = NULL;
    }

    pObjectInfo->NumComponents++;

    RRETURN(S_OK);
}

HRESULT
AddProviderName(POBJECTINFO pObjectInfo, LPWSTR szToken)
{
    if (!szToken || !*szToken) {
        RRETURN(E_FAIL);
    }

    pObjectInfo->ProviderName = AllocADsStr(szToken);

    RRETURN(S_OK);
}


HRESULT
AddTreeName(POBJECTINFO pObjectInfo, LPWSTR szToken)
{
    if (!szToken || !*szToken) {
        RRETURN(E_FAIL);
    }

    pObjectInfo->TreeName = AllocADsStr(szToken);

    RRETURN(S_OK);
}

HRESULT
AddClassName(POBJECTINFO pObjectInfo, LPWSTR szToken)
{
    if (!szToken || !*szToken) {
        RRETURN(E_FAIL);
    }

    wcscpy(pObjectInfo->ClassName, szToken);

    RRETURN(S_OK);
}

//+---------------------------------------------------------------------------
// Function:
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History:    11-3-95   krishnag     Created.
//
//----------------------------------------------------------------------------
HRESULT
SetType(POBJECTINFO pObjectInfo, DWORD dwToken)
{
    pObjectInfo->ObjectType = dwToken;
    RRETURN(S_OK);
}


//+---------------------------------------------------------------------------
// Function:    Type
//
// Synopsis:    Parses Type-> "user" | "group" etc
//
// Arguments:   [CLexer * pTokenizer]
//              [POBJECTINFo pObjectInfo]
//
// Returns:     HRESULT
//
// Modifies:    -
//
// History:    11-3-95   krishnag     Created.
//
//----------------------------------------------------------------------------


HRESULT
Type(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_PATH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken == TOKEN_IDENTIFIER ) {
        if (pTokenizer->IsKeyword(szToken, &dwToken)) {
            hr = SetType(pObjectInfo, dwToken);
            RRETURN(hr);
        }
        hr = AddClassName(pObjectInfo, szToken);
        RRETURN(hr);
    }

    RRETURN(E_FAIL);

cleanup:
    RRETURN(hr);
}


void
CLexer::SetAtDisabler(
    BOOL bFlag
    )
{
    _bAtDisabled = bFlag;
}

BOOL
CLexer::GetAtDisabler()
{
    return(_bAtDisabled);
}