//adriaanc
//#include "stdinc.h"
//#include <setupapi.h>
#include <sxsapi.h>

//adriaanc
//#include <stdlib.h>
//#include <search.h>
#include "idp.h"
#include "sxsapi.h"
#include "sxsid.h"

// adriaanc
#include "idaux.h"


ASSEMBLY_IDENTITY_ATTRIBUTE  
SxsComposeAssemblyIdentityAttribute(
    PCWSTR pszNamespace,    SIZE_T cchNamespace, 
    PCWSTR pszName,         SIZE_T cchName, 
    PCWSTR pszValue,        SIZE_T cchValue)
{    
    ASSEMBLY_IDENTITY_ATTRIBUTE anattribute;

    anattribute.Flags         = 0; // reserved flags : must be 0; 
    anattribute.NamespaceCch  = cchNamespace;
    anattribute.NameCch       = cchName;
    anattribute.ValueCch      = cchValue;
    anattribute.Namespace     = pszNamespace; 
    anattribute.Name          = pszName; 
    anattribute.Value         = pszValue; 
    
    return anattribute; 
}

BOOL
SxsAssemblyIdentityIsAttributePresent(
    PCASSEMBLY_IDENTITY pAssemblyIdentity,
    PCWSTR pszNamespace, 
    SIZE_T cchNamespace, 
    PCWSTR pszName, 
    SIZE_T cchName,         
    BOOL & rfFound)
{
    BOOL fSuccess = FALSE; 
    FN_TRACE_WIN32(fSuccess); 
    ULONG Count = 0;  
    ASSEMBLY_IDENTITY_ATTRIBUTE Attribute;            
    DWORD dwFindFlags;
        
    PARAMETER_CHECK(pszName != NULL); 
    rfFound = FALSE;
    if ( pAssemblyIdentity == NULL) 
    { 
        goto Done; 
    }
    // in the case of a NULL namespace, we must set the flag, too ? xiaoyuw@09/11/00 
    dwFindFlags = SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAMESPACE | SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAME;
    Attribute = SxsComposeAssemblyIdentityAttribute(pszNamespace, cchNamespace, pszName, cchName, NULL, 0);
    
    if (pAssemblyIdentity){
        IFFALSE_EXIT(SxsFindAssemblyIdentityAttribute( // find attribute by "namespace" and "name"
                SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAMESPACE | 
                    SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAME |
                    SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_NOT_FOUND_SUCCEEDS,
                pAssemblyIdentity,
                &Attribute,
                NULL,
                &Count)); 
        if ( Count >0 ) { // found
            rfFound = TRUE;
        }
    }
Done:
    fSuccess = TRUE; 
Exit: 
    return fSuccess;
}

BOOL
SxspSetAssemblyIdentityAttributeValue(
    DWORD Flags,
    PASSEMBLY_IDENTITY AssemblyIdentity,
    PCSXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE AttributeReference,
    const WCHAR *Value,
    SIZE_T ValueCch
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    ASSEMBLY_IDENTITY_ATTRIBUTE Attribute;
    DWORD FlagsToRealInsert = 0;

    PARAMETER_CHECK((Flags & ~(SXSP_SET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_OVERWRITE_EXISTING)) == 0);
    PARAMETER_CHECK(AssemblyIdentity != NULL);
    PARAMETER_CHECK(AttributeReference != NULL);
    PARAMETER_CHECK(Value != NULL || ValueCch == 0);

    Attribute.Flags = 0;
    Attribute.Namespace = AttributeReference->Namespace;
    Attribute.NamespaceCch = AttributeReference->NamespaceCch;
    Attribute.Name = AttributeReference->Name;
    Attribute.NameCch = AttributeReference->NameCch;
    Attribute.Value = Value;
    Attribute.ValueCch = ValueCch;

    if (Flags & SXSP_SET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_OVERWRITE_EXISTING)
        FlagsToRealInsert |= SXS_INSERT_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_OVERWRITE_EXISTING;

    IFFALSE_EXIT(::SxsInsertAssemblyIdentityAttribute(FlagsToRealInsert, AssemblyIdentity, &Attribute));
    fSuccess = TRUE;
Exit:
    return fSuccess;
}

// adriaanc
/*
BOOL
SxspSetAssemblyIdentityAttributeValue(
    DWORD Flags,
    PASSEMBLY_IDENTITY AssemblyIdentity,
    PCSXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE AttributeReference,
    const CBaseStringBuffer &Value
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);

    IFFALSE_EXIT(
        ::SxspSetAssemblyIdentityAttributeValue(
            Flags,
            AssemblyIdentity,
            AttributeReference,
            static_cast<PCWSTR>(Value),
            Value.Cch()));

    fSuccess = TRUE;
Exit:
    return fSuccess;
}
*/

/////////////////////////////////////////////////////////////////////////////
// Action : 
// 1. if (namespace, name) is provided, remove all attributes with such (namespace, name)
// 2. if (namespace, name, value), remove at most 1 attribute from assembly-identity 
///////////////////////////////////////////////////////////////////////////////
BOOL
SxspRemoveAssemblyIdentityAttribute(
    DWORD Flags,
    PASSEMBLY_IDENTITY pAssemblyIdentity,
    PCSXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE AttributeReference
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    ASSEMBLY_IDENTITY_ATTRIBUTE Attribute;
    ULONG Ordinal; 
    ULONG Count;
    DWORD dwFindAttributeFlags = 0;

    PARAMETER_CHECK((Flags & ~(SXSP_REMOVE_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_NOT_FOUND_SUCCEEDS)) == 0);
    PARAMETER_CHECK(pAssemblyIdentity != NULL);
    PARAMETER_CHECK(AttributeReference != NULL);

    Attribute.Flags = 0;
    Attribute.Namespace = AttributeReference->Namespace;
    Attribute.NamespaceCch = AttributeReference->NamespaceCch;
    Attribute.Name = AttributeReference->Name;
    Attribute.NameCch = AttributeReference->NameCch;

    dwFindAttributeFlags = SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAMESPACE | SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAME;

    // If it's OK for the attribute not to exist, set the flag in the call to find it.
    if (Flags & SXSP_REMOVE_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_NOT_FOUND_SUCCEEDS)
        dwFindAttributeFlags |= SXS_FIND_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_NOT_FOUND_SUCCEEDS;

    IFFALSE_EXIT(
        ::SxsFindAssemblyIdentityAttribute(
            dwFindAttributeFlags,
            pAssemblyIdentity,
            &Attribute,
            &Ordinal,
            &Count));

//     INTERNAL_ERROR_CHECK(Count <= 1);
    do
    {
        if (!(Count <= 1))
        {
            for (;;)
            {
                HARD_ASSERT_ACTION(_e);
                SetLastError(ERROR_INTERNAL_ERROR);
                goto Exit;
            }
        }
    } while (0);

    if (Count > 0)
    {
        IFFALSE_EXIT(
            ::SxsRemoveAssemblyIdentityAttributesByOrdinal(
                0,                  //  DWORD Flags,
                pAssemblyIdentity,
                Ordinal,
                Count));
    }

    fSuccess = TRUE; 
Exit: 
    return fSuccess; 
}
/////////////////////////////////////////////////////////////////////////////
// if no such attribure with such (namespace and name), return FALSE with 
// ::SetLastError(ERROR_NOT_FOUND); 
///////////////////////////////////////////////////////////////////////////////
BOOL
SxspGetAssemblyIdentityAttributeValue(
    DWORD Flags,
    PCASSEMBLY_IDENTITY AssemblyIdentity,
    PCSXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE AttributeReference,
    OUT PCWSTR *StringOut,
    OUT SIZE_T *CchOut OPTIONAL
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    PCINTERNAL_ASSEMBLY_IDENTITY_ATTRIBUTE InternalAttribute = NULL;
    ASSEMBLY_IDENTITY_ATTRIBUTE Attribute;
    DWORD dwLocateFlags = SXSP_LOCATE_INTERNAL_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAMESPACE | SXSP_LOCATE_INTERNAL_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_MATCH_NAME;

    if (StringOut != NULL)
        *StringOut = NULL;

    if (CchOut != NULL)
        *CchOut = 0;

    PARAMETER_CHECK((Flags & ~(SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL)) == 0);
    PARAMETER_CHECK(AssemblyIdentity != NULL);
    PARAMETER_CHECK(AttributeReference != NULL);

    Attribute.Flags = 0;
    Attribute.Namespace = AttributeReference->Namespace;
    Attribute.NamespaceCch = AttributeReference->NamespaceCch;
    Attribute.Name = AttributeReference->Name;
    Attribute.NameCch = AttributeReference->NameCch;

    if (Flags & SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL)
        dwLocateFlags |= SXSP_LOCATE_INTERNAL_ASSEMBLY_IDENTITY_ATTRIBUTE_FLAG_NOT_FOUND_RETURNS_NULL;

    IFFALSE_EXIT(
        ::SxspLocateInternalAssemblyIdentityAttribute(
            dwLocateFlags,
            AssemblyIdentity,
            &Attribute,
            &InternalAttribute,
            NULL));

    if (InternalAttribute != NULL)
    {
        if (StringOut != NULL)
            *StringOut = InternalAttribute->Attribute.Value;

        if (CchOut != NULL)
            *CchOut = InternalAttribute->Attribute.ValueCch;
    }

    fSuccess = TRUE;
Exit:
    return fSuccess;
}

//adriaanc
/*
BOOL
SxspGetAssemblyIdentityAttributeValue(
    IN DWORD Flags,
    IN PCASSEMBLY_IDENTITY AssemblyIdentity,
    PCSXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE AttributeReference,
    OUT CBaseStringBuffer &Value
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    PCWSTR String = NULL;
    SIZE_T Cch = 0;

    IFFALSE_EXIT(
        ::SxspGetAssemblyIdentityAttributeValue(
            Flags,
            AssemblyIdentity,
            AttributeReference,
            &String,
            &Cch));

    IFFALSE_EXIT(Value.Win32Assign(String, Cch));
    
    fSuccess = TRUE;
Exit:
    return fSuccess;
}
*/
BOOL
SxspUpdateAssemblyIdentityHash(
    DWORD dwFlags,
    PASSEMBLY_IDENTITY AssemblyIdentity
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);

    PARAMETER_CHECK(dwFlags == 0);
    PARAMETER_CHECK(AssemblyIdentity != NULL);

    if (AssemblyIdentity->HashDirty)
    {
        IFFALSE_EXIT(::SxspHashInternalAssemblyIdentityAttributes(
                            0,
                            AssemblyIdentity->AttributeCount, 
                            AssemblyIdentity->AttributePointerArray,
                            &AssemblyIdentity->Hash));

        AssemblyIdentity->HashDirty = FALSE;
    }

    fSuccess = TRUE;
Exit:
    return fSuccess;
}

BOOL
SxspEnsureAssemblyIdentityHashIsUpToDate(
    DWORD dwFlags,
    PCASSEMBLY_IDENTITY AssemblyIdentity
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);

    PARAMETER_CHECK(dwFlags == 0);
    PARAMETER_CHECK(AssemblyIdentity != NULL);

    if (AssemblyIdentity->HashDirty)
        IFFALSE_EXIT(SxspUpdateAssemblyIdentityHash(0, const_cast<PASSEMBLY_IDENTITY>(AssemblyIdentity)));

    fSuccess = TRUE;
Exit:
    return fSuccess;
}


BOOL
SxsHashAssemblyIdentity(
    DWORD dwFlags, 
    PCASSEMBLY_IDENTITY pAssemblyIdentity,
    ULONG * pulPseudoKey
)
{
    BOOL fSuccess = FALSE; 
    FN_TRACE_WIN32(fSuccess); 
    ULONG ulPseudoKey;     

    if (pulPseudoKey) 
        *pulPseudoKey = 0;

    PARAMETER_CHECK(dwFlags == 0);

    if (pAssemblyIdentity == NULL) 
        ulPseudoKey = 0; 
    else
    {
        IFFALSE_EXIT(SxspEnsureAssemblyIdentityHashIsUpToDate(0, pAssemblyIdentity));
        ulPseudoKey = pAssemblyIdentity->Hash;
    }

    if (pulPseudoKey != NULL)
        *pulPseudoKey = ulPseudoKey; 

    fSuccess = TRUE;

Exit:
    return fSuccess; 
}

// just to find whether Equal or Not
BOOL
SxsAreAssemblyIdentitiesEqual(
    DWORD dwFlags, 
    PCASSEMBLY_IDENTITY pAssemblyIdentity1,
    PCASSEMBLY_IDENTITY pAssemblyIdentity2,
    BOOL *EqualOut
    )
{
    BOOL fSuccess = FALSE; 
    FN_TRACE_WIN32(fSuccess);
    BOOL Equal = FALSE;

    if (EqualOut != NULL)
        *EqualOut = FALSE;

    PARAMETER_CHECK((dwFlags & ~(SXS_ARE_ASSEMBLY_IDENTITIES_EQUAL_FLAG_ALLOW_REF_TO_MATCH_DEF)) == 0);
    PARAMETER_CHECK(pAssemblyIdentity1 != NULL);
    PARAMETER_CHECK(pAssemblyIdentity2 != NULL);
    PARAMETER_CHECK(EqualOut != NULL);
    
    // get hash for each assembly identity
    IFFALSE_EXIT(SxspEnsureAssemblyIdentityHashIsUpToDate(0, pAssemblyIdentity1));
    IFFALSE_EXIT(SxspEnsureAssemblyIdentityHashIsUpToDate(0, pAssemblyIdentity2));

    // compare hash value of two identity; it's a quick way to determine they're not equal.
    if (pAssemblyIdentity2->Hash == pAssemblyIdentity1->Hash)
    {
        // Note that two identities which differ only in their internal flags are still semantically
        // equal.
        if ((pAssemblyIdentity1->Flags ==  pAssemblyIdentity2->Flags) &&
            (pAssemblyIdentity1->Hash ==  pAssemblyIdentity2->Hash) &&
            (pAssemblyIdentity1->NamespaceCount ==  pAssemblyIdentity2->NamespaceCount) &&
            (pAssemblyIdentity1->AttributeCount ==  pAssemblyIdentity2->AttributeCount))
        {
            if (dwFlags & SXS_ARE_ASSEMBLY_IDENTITIES_EQUAL_FLAG_ALLOW_REF_TO_MATCH_DEF)
            {
                if (((pAssemblyIdentity1->Type == ASSEMBLY_IDENTITY_TYPE_DEFINITION) ||
                     (pAssemblyIdentity1->Type == ASSEMBLY_IDENTITY_TYPE_REFERENCE)) &&
                    ((pAssemblyIdentity2->Type == ASSEMBLY_IDENTITY_TYPE_DEFINITION) ||
                     (pAssemblyIdentity2->Type == ASSEMBLY_IDENTITY_TYPE_REFERENCE)))
                {
                    // They match sufficiently...
                    Equal = TRUE;
                }
            }
            else
                Equal = (pAssemblyIdentity1->Type == pAssemblyIdentity2->Type);

            if (Equal)
            {
                ULONG ComparisonResult = SXS_COMPARE_ASSEMBLY_IDENTITY_ATTRIBUTES_COMPARISON_RESULT_INVALID;

                // Reset our assumption...
                Equal = FALSE;

                IFFALSE_EXIT(SxspCompareAssemblyIdentityAttributeLists(
                    0,
                    pAssemblyIdentity1->AttributeCount,
                    pAssemblyIdentity1->AttributePointerArray,
                    pAssemblyIdentity2->AttributePointerArray,
                    &ComparisonResult));

                INTERNAL_ERROR_CHECK(
                    (ComparisonResult == SXS_COMPARE_ASSEMBLY_IDENTITY_ATTRIBUTES_COMPARISON_RESULT_LESS_THAN) ||
                    (ComparisonResult == SXS_COMPARE_ASSEMBLY_IDENTITY_ATTRIBUTES_COMPARISON_RESULT_EQUAL) ||
                    (ComparisonResult == SXS_COMPARE_ASSEMBLY_IDENTITY_ATTRIBUTES_COMPARISON_RESULT_GREATER_THAN));

                if (ComparisonResult == SXS_COMPARE_ASSEMBLY_IDENTITY_ATTRIBUTES_COMPARISON_RESULT_EQUAL)
                    Equal = TRUE;
            }
        }
    }

    *EqualOut = Equal;
    fSuccess = TRUE;

Exit:
    return fSuccess;
}