File: Base\MS\Internal\Security\RightsManagement\IssuanceLicense.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//
// <copyright file="IssuanceLicense.cs" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//   This class wraps the issuance license publishing serveces 
//
// History:
//  06/13/2005: IgorBel:  Initial implementation.
//
//-----------------------------------------------------------------------------
 
 
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Security.RightsManagement;
using SecurityHelper = MS.Internal.WindowsBase.SecurityHelper;
using System.Globalization;             // For CultureInfo
using MS.Internal;                      // for Invariant
using System.Windows;
 
namespace MS.Internal.Security.RightsManagement
{
 
    /// <SecurityNote>
    ///     Critical:    This class exposes access to methods that eventually do one or more of the the following
    ///             1. call into unmanaged code 
    ///             2. affects state/data that will eventually cross over unmanaged code boundary
    ///             3. Return some RM related information which is considered private 
    /// </SecurityNote>
    [SecurityCritical(SecurityCriticalScope.Everything)]
    internal class IssuanceLicense : IDisposable
    {
        internal IssuanceLicense(
                                        DateTime validFrom,
                                        DateTime validUntil,
                                        string referralInfoName,
                                        Uri referralInfoUri,
                                        ContentUser owner,
                                        string issuanceLicense,
                                        SafeRightsManagementHandle boundLicenseHandle,
                                        Guid contentId,
                                        ICollection<ContentGrant> grantCollection,
                                        IDictionary<int, LocalizedNameDescriptionPair> localizedNameDescriptionDictionary,
                                        IDictionary<string, string> applicationSpecificDataDictionary,
                                        int rightValidityIntervalDays,
                                        RevocationPoint revocationPoint)
        {
            Initialize(
                validFrom,
                validUntil,
                referralInfoName,
                referralInfoUri,
                owner,
                issuanceLicense,
                boundLicenseHandle,
                contentId,
                grantCollection,
                localizedNameDescriptionDictionary,
                applicationSpecificDataDictionary,
                rightValidityIntervalDays,
                revocationPoint);
        }
 
        /// <summary>
        /// constructor that buils an issuance license from scratch
        /// </summary>
        private void Initialize(
                                        DateTime validFrom,
                                        DateTime validUntil,
                                        string referralInfoName,
                                        Uri referralInfoUri,
                                        ContentUser owner,
                                        string issuanceLicense,
                                        SafeRightsManagementHandle boundLicenseHandle,
                                        Guid contentId,
                                        ICollection<ContentGrant> grantCollection,
                                        IDictionary<int, LocalizedNameDescriptionPair> localizedNameDescriptionDictionary,
                                        IDictionary<string, string> applicationSpecificDataDictionary,
                                        int rightValidityIntervalDays,
                                        RevocationPoint revocationPoint)
        {
            // according to the unmanaged RM SDK spec only the following scenarios are supported:
            // 1. This can be called to create an issuance license from a template. 
            //       issuanceLicense         An unsigned issuance license from 
            //                                   a file or by passing an issuance license 
            //                                   handle into DRMGetIssuanceLicenseTemplate 
            //       boundLicenseHandle   NULL
            //
            // 2. This allows you to reuse rights information (the list follows this table).
            //       issuance license        A signed issuance license
            //       boundLicenseHandle   Handle to license bound by OWNER or VIEWRIGHTSDATA right
            //
            // 3. This creates an issuance license from scratch. It includes no users, rights, metadata, or policies.
            //       issuance license         NULL
            //       boundLicenseHandle   NULL
 
            Debug.Assert(!boundLicenseHandle.IsClosed); // it must be either present or not
            // closed handle is an indication of some internal error
 
            Invariant.Assert((boundLicenseHandle.IsInvalid) || (issuanceLicense != null));
 
            SystemTime validFromSysTime = null;
            SystemTime validUntilSysTime = null;
 
            if ((validFrom != DateTime.MinValue) || (validUntil != DateTime.MaxValue))
            {
                // we need to use non null values if at least one of the time boundaries isn't default
                // DRM SDK will not enforce date time unless both timeFrom and timeUnti are set 
                validFromSysTime = new SystemTime((DateTime)validFrom);
                validUntilSysTime = new SystemTime((DateTime)validUntil);
            }
 
            string referralInfoUriStr = null;
            if (referralInfoUri != null)
            {
                referralInfoUriStr = referralInfoUri.ToString();
            }
 
            // input parameter must be initialized to the invalid handle 
            // attempt to pass in a null throws an exception from the Safe 
            // Handle Marshalling code  
            SafeRightsManagementPubHandle ownerHandle;
 
            if (owner != null)
            {
                ownerHandle = GetHandleFromUser(owner);
            }
            else
            {
                ownerHandle = SafeRightsManagementPubHandle.InvalidHandle;
            }
 
            int hr;
 
            _issuanceLicenseHandle = null;
 
            hr = SafeNativeMethods.DRMCreateIssuanceLicense(
                validFromSysTime,
                validUntilSysTime,
                referralInfoName,
                referralInfoUriStr,
                ownerHandle,
                issuanceLicense,
                boundLicenseHandle,
                out _issuanceLicenseHandle);
 
            Errors.ThrowOnErrorCode(hr);
            Invariant.Assert((_issuanceLicenseHandle != null) &&
                                       (!_issuanceLicenseHandle.IsInvalid));
 
            Debug.Assert(rightValidityIntervalDays >= 0); // our internal code makes the guarantee that is is not negative
            if (rightValidityIntervalDays > 0)
            {
                // If it is 0 we shouldn't override the value as it might be coming from a template 
                SafeNativeMethods.DRMSetIntervalTime(_issuanceLicenseHandle, (uint)rightValidityIntervalDays);
            }
 
            if (grantCollection != null)
            {
                foreach (ContentGrant grant in grantCollection)
                {
                    AddGrant(grant);
                }
            }
 
            // Set localized name description info 
            if (localizedNameDescriptionDictionary != null)
            {
                foreach (KeyValuePair<int, LocalizedNameDescriptionPair> nameDescriptionEntry in localizedNameDescriptionDictionary)
                {
                    AddNameDescription(nameDescriptionEntry.Key, nameDescriptionEntry.Value);
                }
            }
 
            // Set application specific data 
            if (applicationSpecificDataDictionary != null)
            {
                foreach (KeyValuePair<string, string> applicationSpecificDataEntry in applicationSpecificDataDictionary)
                {
                    AddApplicationSpecificData(applicationSpecificDataEntry.Key, applicationSpecificDataEntry.Value);
                }
            }
 
            // set metafata as required 
            if (contentId != null)
            {
                hr = SafeNativeMethods.DRMSetMetaData(
                    _issuanceLicenseHandle,
                    contentId.ToString("B"),
                    DefaultContentType,
                    null,
                    null,
                    null,
                    null);
 
                Errors.ThrowOnErrorCode(hr);
            }
 
            // set revocation point if required 
            if (revocationPoint != null)
            {
                SetRevocationPoint(revocationPoint);
            }
        }
 
        internal SafeRightsManagementPubHandle Handle
        {
            get
            {
                CheckDisposed();
                return _issuanceLicenseHandle;
            }
        }
 
        override public string ToString()
        {
            uint issuanceLicenseTemplateLength = 0;
            StringBuilder issuanceLicenseTemplate = null;
 
            int hr = SafeNativeMethods.DRMGetIssuanceLicenseTemplate(
                _issuanceLicenseHandle,
                ref issuanceLicenseTemplateLength,
                null);
 
            Errors.ThrowOnErrorCode(hr);
 
            issuanceLicenseTemplate = new StringBuilder(checked((int)issuanceLicenseTemplateLength));
 
            hr = SafeNativeMethods.DRMGetIssuanceLicenseTemplate(
                _issuanceLicenseHandle,
                ref issuanceLicenseTemplateLength,
                issuanceLicenseTemplate);
 
            Errors.ThrowOnErrorCode(hr);
 
            return issuanceLicenseTemplate.ToString();
        }
 
        internal void UpdateUnsignedPublishLicense(UnsignedPublishLicense unsignedPublishLicense)
        {
            Invariant.Assert(unsignedPublishLicense != null);
 
            DateTime timeFrom;
            DateTime timeUntil;
            DistributionPointInfo distributionPointInfo = DistributionPointInfo.ReferralInfo;
            string distributionPointName;
            string distributionPointUri;
            ContentUser owner;
            bool officialFlag;
 
            GetIssuanceLicenseInfo(out timeFrom,
                                                out timeUntil,
                                                distributionPointInfo,
                                                out distributionPointName,
                                                out distributionPointUri,
                                                out owner,
                                                out officialFlag);
 
            unsignedPublishLicense.ReferralInfoName = distributionPointName;
 
            if (distributionPointUri != null)
            {
                unsignedPublishLicense.ReferralInfoUri = new Uri(distributionPointUri);
            }
            else
            {
                unsignedPublishLicense.ReferralInfoUri = null;
            }
 
            unsignedPublishLicense.Owner = owner;
 
            // Let's get the validity Iterval information (days) and save it in the license 
            uint validityDays = 0;
            int hr = SafeNativeMethods.DRMGetIntervalTime(_issuanceLicenseHandle, ref validityDays);
            Errors.ThrowOnErrorCode(hr);
            checked { unsignedPublishLicense.RightValidityIntervalDays = (int)validityDays; }
 
            // let's get the rights information 
            int userIndex = 0;
            while (true) // in this loop we are enumerating users mentioned in the license 
            {
                SafeRightsManagementPubHandle userHandle = null;
 
                // extract the user based on the index 
                ContentUser user = GetIssuanceLicenseUser(userIndex, out userHandle);
 
                if ((user == null) || (userHandle == null))
                {
                    break;
                }
 
                int rightIndex = 0;
                while (true) // now we can enumerate rights granted to the given user  
                {
                    SafeRightsManagementPubHandle rightHandle = null;
                    DateTime validFrom;
                    DateTime validUntil;
 
                    // extract the right based on the index and the user 
                    Nullable<ContentRight> right = GetIssuanceLicenseUserRight
                                (userHandle, rightIndex, out rightHandle, out validFrom, out validUntil);
 
                    // 0 right handle is an indication of the end of the list 
                    if (rightHandle == null)
                    {
                        break;
                    }
 
                    // right == null is an indication of a right that we didn't recognize 
                    // we should still continue the enumeration 
                    if (right != null)
                    {
                        // Add the grant for the User Right pair here 
                        unsignedPublishLicense.Grants.Add(
                                new ContentGrant(user, right.Value, validFrom, validUntil));
                    }
 
                    rightIndex++;
                }
                userIndex++;
            }
 
            // let's get the localized name description pairs 
            int nameIndex = 0;
            while (true) // in this loop we are enumerating nameDescription pairs mentioned in the license 
            {
                int localeId;
 
                // extract the user based on the index 
                LocalizedNameDescriptionPair nameDescription = GetLocalizedNameDescriptionPair(nameIndex,
                                    out localeId);
                if (nameDescription == null)
                {
                    break;
                }
 
                // Add the name description info to the license 
                unsignedPublishLicense.LocalizedNameDescriptionDictionary.Add(localeId, nameDescription);
                nameIndex++;
            }
 
            // let's get the application specific data 
            int appDataIndex = 0;
            while (true) // in this loop we are enumerating nameDescription pairs mentioned in the license 
            {
                // extract the user based on the index 
 
                Nullable<KeyValuePair<string, string>> appSpecificDataEntry = GetApplicationSpecificData(appDataIndex);
 
                if (appSpecificDataEntry == null)
                {
                    break;
                }
 
                // Add the name description info to the license 
                unsignedPublishLicense.ApplicationSpecificDataDictionary.Add(appSpecificDataEntry.Value.Key, appSpecificDataEntry.Value.Value);
                appDataIndex++;
            }
 
            // Get the revocation Point information, it is optional and can be null 
            unsignedPublishLicense.RevocationPoint = GetRevocationPoint();
        }
 
        ~IssuanceLicense()
        {
            Dispose(false);
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                try
                {
                    if (_issuanceLicenseHandle != null)
                    {
                        _issuanceLicenseHandle.Dispose();
 
                        // After some investigation it was determined that ArrayList 
                        // is a safe member ,and could be used in Finalizers 
                        // so we do not check for disposing flag.  
                        // It seems that Finalizers are called in nondeterministic order 
                        // but if we have a combination of classes with Finalizers and without Finalizers 
                        // referencing each other, the classes with Finalizers wil be Disposed first.
                        foreach (SafeRightsManagementPubHandle handler in _pubHandlesList)
                        {
                            handler.Dispose();
                        }
                    }
                }
                finally
                {
                    _issuanceLicenseHandle = null;
                    _pubHandlesList.Clear();
                }
            }
        }
 
        private void AddGrant(ContentGrant grant)
        {
            Invariant.Assert(grant != null);
            Invariant.Assert(grant.User != null);
 
            SafeRightsManagementPubHandle right = GetRightHandle(grant);
 
            SafeRightsManagementPubHandle user = GetHandleFromUser(grant.User);
 
            int hr = SafeNativeMethods.DRMAddRightWithUser(
                _issuanceLicenseHandle,
                right,
                user);
 
            Errors.ThrowOnErrorCode(hr);
        }
 
        private void AddNameDescription(int localeId, LocalizedNameDescriptionPair nameDescription)
        {
            // the managed APIs treat Locale Id as int and the unmanaged ones as uint 
            // we need to convert it back and force
            uint locId;
            checked { locId = (uint)localeId; }
 
            int hr = SafeNativeMethods.DRMSetNameAndDescription(
                                    _issuanceLicenseHandle,
                                    false, // true - means delete ; false - add
                                    locId,
                                    nameDescription.Name,
                                    nameDescription.Description);
            Errors.ThrowOnErrorCode(hr);
        }
 
        private void AddApplicationSpecificData(string name, string value)
        {
            int hr = SafeNativeMethods.DRMSetApplicationSpecificData(
                                    _issuanceLicenseHandle,
                                    false, // true - means delete ; false - add
                                    name,
                                    value);
            Errors.ThrowOnErrorCode(hr);
        }
 
        private SafeRightsManagementPubHandle GetRightHandle(ContentGrant grant)
        {
            SafeRightsManagementPubHandle rightHandle = null;
 
            // we only need to use date time inteval if at least one of the values isn't default to Min max 
            // If both of the Min Max we can leave tyhem as nulls otherwise (if at least one not default)
            // we need to set both 
            SystemTime systemTimeValidFrom = null;
            SystemTime systemTimeValidUntil = null;
 
            if ((grant.ValidFrom != DateTime.MinValue) || (grant.ValidUntil != DateTime.MaxValue))
            {
                // we need to use non null values if at least one of the time boundaries isn't default
                // DRM SDK will not enforce date time unless both timeFrom and timeUnti are set 
                systemTimeValidFrom = new SystemTime(grant.ValidFrom);
                systemTimeValidUntil = new SystemTime(grant.ValidUntil);
            }
 
            int hr = SafeNativeMethods.DRMCreateRight(
                ClientSession.GetStringFromRight(grant.Right),
                systemTimeValidFrom,    // SystemTime timeFrom, 
                systemTimeValidUntil,    // SystemTime timeUntil,
                0,       // countExtendedInfo,    
                null,    // string [] extendedInfoNames,
                null,    // string [] extendedInfoValues,
                out rightHandle);
 
            Errors.ThrowOnErrorCode(hr);
            Debug.Assert((rightHandle != null) && (!rightHandle.IsInvalid));
 
            _pubHandlesList.Add(rightHandle);
 
            return rightHandle;
        }
 
        private SafeRightsManagementPubHandle GetHandleFromUser(ContentUser user)
        {
            SafeRightsManagementPubHandle userHandle = null;
            int hr;
 
            // We need to create Internal Authnetication type Users differently 
            if (user.GenericEquals(ContentUser.AnyoneUser) || user.GenericEquals(ContentUser.OwnerUser))
            {
                // Anyone user 
                hr = SafeNativeMethods.DRMCreateUser(
                    user.Name, // This is an optional UI Name (some applications use this and do not work well when it is missing)
                    user.Name, // that would be string Anyone or Owner 
                    ConvertAuthenticationTypeToString(user),   // this would be internal  
                    out userHandle);
            }
            else
            {
                // Windws Passport or WindowsPassport authentication type user 
                hr = SafeNativeMethods.DRMCreateUser(
                    user.Name,
                    null,
                    ConvertAuthenticationTypeToString(user),
                    out userHandle);
            }
 
            Errors.ThrowOnErrorCode(hr);
            Debug.Assert((userHandle != null) && (!userHandle.IsInvalid));
 
            _pubHandlesList.Add(userHandle);
            return userHandle;
        }
 
        static private Nullable<ContentRight> GetRightFromHandle(SafeRightsManagementPubHandle rightHandle,
                                                        out DateTime validFrom,
                                                        out DateTime validUntil)
        {
            uint rightNameLength = 0;
            StringBuilder rightName;
 
            int hr = SafeNativeMethods.DRMGetRightInfo(rightHandle,
                                                                                ref rightNameLength,
                                                                                null,
                                                                                null,
                                                                                null);
            Errors.ThrowOnErrorCode(hr);
 
            rightName = new StringBuilder(checked((int)rightNameLength));
            SystemTime validFromSysTime = new SystemTime(DateTime.Now);
            SystemTime validUntilSysTime = new SystemTime(DateTime.Now);
 
            hr = SafeNativeMethods.DRMGetRightInfo(rightHandle,
                                                                                ref rightNameLength,
                                                                                rightName,
                                                                                validFromSysTime,
                                                                                validUntilSysTime);
            Errors.ThrowOnErrorCode(hr);
 
            validFrom = validFromSysTime.GetDateTime(DateTime.MinValue);
            validUntil = validUntilSysTime.GetDateTime(DateTime.MaxValue);
 
            return ClientSession.GetRightFromString(rightName.ToString());
        }
 
        static private ContentUser GetUserFromHandle(SafeRightsManagementPubHandle userHandle)
        {
            uint userNameLength = 0;
            StringBuilder userName = null;
            uint userIdLength = 0;
            StringBuilder userId = null;
            uint userIdTypeLength = 0;
            StringBuilder userIdType = null;
 
            int hr = SafeNativeMethods.DRMGetUserInfo(userHandle,
                                                                                ref userNameLength,
                                                                                null,
                                                                                ref userIdLength,
                                                                                null,
                                                                                ref userIdTypeLength,
                                                                                null);
            Errors.ThrowOnErrorCode(hr);
 
            if (userNameLength > 0)
            {   // only allocate memory if we got a non-zero size back
                userName = new StringBuilder(checked((int)userNameLength));
            }
 
            if (userIdLength > 0)
            {   // only allocate memory if we got a non-zero size back
                userId = new StringBuilder(checked((int)userIdLength));
            }
 
            if (userIdTypeLength > 0)
            {   // only allocate memory if we got a non-zero size back
                userIdType = new StringBuilder(checked((int)userIdTypeLength));
            }
 
            hr = SafeNativeMethods.DRMGetUserInfo(userHandle,
                                                                                ref userNameLength,
                                                                                userName,
                                                                                ref userIdLength,
                                                                                userId,
                                                                                ref userIdTypeLength,
                                                                                userIdType);
            Errors.ThrowOnErrorCode(hr);
 
            // Convert String Builder values to string values 
            string userNameStr = null;
            if (userName != null)
            {
                userNameStr = userName.ToString();
            }
 
            string userIdTypeStr = null;
            if (userIdType != null)
            {
                userIdTypeStr = userIdType.ToString().ToUpperInvariant();
            }
 
            string userIdStr = null;
            if (userId != null)
            {
                userIdStr = userId.ToString().ToUpperInvariant();
            }
 
            // based on the UserTypeId build appropriate instance of User class 
            if (String.CompareOrdinal(userIdTypeStr, AuthenticationType.Windows.ToString().ToUpperInvariant()) == 0)
            {
                return new ContentUser(userNameStr, AuthenticationType.Windows);
            }
            else if (String.CompareOrdinal(userIdTypeStr, AuthenticationType.Passport.ToString().ToUpperInvariant()) == 0)
            {
                return new ContentUser(userNameStr, AuthenticationType.Passport);
            }
            else if (String.CompareOrdinal(userIdTypeStr, AuthenticationType.Internal.ToString().ToUpperInvariant()) == 0)
            {
                // internal anyone user 
                if (ContentUser.CompareToAnyone(userIdStr))
                {
                    return ContentUser.AnyoneUser;
                }
                else if (ContentUser.CompareToOwner(userIdStr))
                {
                    return ContentUser.OwnerUser;
                }
            }
            else if (String.CompareOrdinal(userIdTypeStr, UnspecifiedAuthenticationType.ToUpperInvariant()) == 0)
            {
                return new ContentUser(userNameStr, AuthenticationType.WindowsPassport);
            }
 
            throw new RightsManagementException(RightsManagementFailureCode.InvalidLicense);
        }
 
        private ContentUser GetIssuanceLicenseUser(int index, out SafeRightsManagementPubHandle userHandle)
        {
            Invariant.Assert(index >= 0);
 
            int hr = SafeNativeMethods.DRMGetUsers(
                _issuanceLicenseHandle, (uint)index, out userHandle);
 
            // there is a special code indication end of the enumeration 
            if (hr == (int)RightsManagementFailureCode.NoMoreData)
            {
                userHandle = null;
                return null;
            }
 
            // check for errors
            Errors.ThrowOnErrorCode(hr);
            Debug.Assert((userHandle != null) && (!userHandle.IsInvalid));
 
            // preserve handle so we can destruct it later 
            _pubHandlesList.Add(userHandle);
 
            // now we can build a ContentUser
            return GetUserFromHandle(userHandle);
        }
 
        private LocalizedNameDescriptionPair GetLocalizedNameDescriptionPair
                                                    (int index, out int localeId)
        {
            Invariant.Assert(index >= 0);
            uint locId = 0;
            uint nameLength = 0;
            StringBuilder name = null;
            uint descriptionLength = 0;
            StringBuilder description = null;
 
            int hr = SafeNativeMethods.DRMGetNameAndDescription(
                    _issuanceLicenseHandle,
                    (uint)index,
                    out locId,
                    ref nameLength,
                    name,
                    ref descriptionLength,
                    description);
 
            // there is a special code indication end of the enumeration 
            if (hr == (int)RightsManagementFailureCode.NoMoreData)
            {
                localeId = 0;
                return null;
            }
 
            // check for errors
            Errors.ThrowOnErrorCode(hr);
 
            if (nameLength > 0)
            {   // only allocate memory if we got a non-zero size back
                name = new StringBuilder(checked((int)nameLength));
            }
 
            if (descriptionLength > 0)
            {   // only allocate memory if we got a non-zero size back
                description = new StringBuilder(checked((int)descriptionLength));
            }
 
            hr = SafeNativeMethods.DRMGetNameAndDescription(
                    _issuanceLicenseHandle,
                    (uint)index,
                    out locId,
                    ref nameLength,
                    name,
                    ref descriptionLength,
                    description);
            Errors.ThrowOnErrorCode(hr);
 
            // the managed APIs treat Locale Id as int and the unmanaged ones as uint 
            // we need to convert it back and force
            checked { localeId = (int)locId; }
 
            // now we can build a ContentUser
            return new LocalizedNameDescriptionPair(name == null ? null : name.ToString(),
                                                                          description == null ? null : description.ToString());
        }
 
        private Nullable<KeyValuePair<string, string>> GetApplicationSpecificData(int index)
        {
            Invariant.Assert(index >= 0);
            uint nameLength = 0;
            uint valueLength = 0;
 
            // check whether element with such index is present, 
            // and if it is get the sizes of the name value strings 
            int hr = SafeNativeMethods.DRMGetApplicationSpecificData(
                                                _issuanceLicenseHandle,
                                                (uint)index,    // safe cast as the caller responsible for keeping this postive 
                                                ref nameLength,
                                                null,
                                                ref valueLength,
                                                null);
 
            // there is a special code indicating end of the enumeration 
            if (hr == (int)RightsManagementFailureCode.NoMoreData)
            {
                return null;
            }
 
            // check for errors
            Errors.ThrowOnErrorCode(hr);
 
            StringBuilder tempName = null;
            // allocate memory as necessary, it seems that Unmanaged libraries really do not like
            // getting a non null buffer of size 0 
            if (nameLength > 0)
            {
                tempName = new StringBuilder(checked((int)nameLength));
            }
 
            StringBuilder tempValue = null;
            // allocate memory as necessary, it seems that Unmanaged libraries really do not like
            // getting a non null buffer of size 0 
            if (valueLength > 0)
            {
                tempValue = new StringBuilder(checked((int)valueLength));
            }
 
            // The second call supposed to return the actual string values fcheck whether element with such index is present, 
            // and if it is get the sizes of the name value strings 
            hr = SafeNativeMethods.DRMGetApplicationSpecificData(
                                                _issuanceLicenseHandle,
                                                (uint)index,    // safe cast as the caller responsible for keeping this postive 
                                                ref nameLength,
                                                tempName,
                                                ref valueLength,
                                                tempValue);
            // check for errors
            Errors.ThrowOnErrorCode(hr);
 
            // build strings from the StringBuilder  instances 
            string name = (tempName == null) ? null : tempName.ToString();
            string value = (tempValue == null) ? null : tempValue.ToString();
 
            KeyValuePair<string, string> result = new KeyValuePair<string, string>(name, value);
 
            return result;
        }
 
        private Nullable<ContentRight> GetIssuanceLicenseUserRight
                                                    (SafeRightsManagementPubHandle userHandle,
                                                    int index,
                                                    out SafeRightsManagementPubHandle rightHandle,
                                                    out DateTime validFrom,
                                                    out DateTime validUntil)
        {
            Invariant.Assert(index >= 0);
 
            int hr = SafeNativeMethods.DRMGetUserRights(_issuanceLicenseHandle, userHandle, (uint)index, out rightHandle);
 
            // there is a special code indicating end of the enumeration 
            if (hr == (int)RightsManagementFailureCode.NoMoreData)
            {
                rightHandle = null;
                validFrom = DateTime.MinValue;
                validUntil = DateTime.MaxValue;
 
                return null;       // intentionally we return an invalid value, to make use client 
                // properly verify right handle 
            }
 
            // check for errors
            Errors.ThrowOnErrorCode(hr);
            Debug.Assert((rightHandle != null) && (!rightHandle.IsInvalid));
 
            // preserve handle so we can destruct it later 
            _pubHandlesList.Add(rightHandle);
 
            // now we can build a User
            return GetRightFromHandle(rightHandle, out validFrom, out validUntil);
        }
 
        private void GetIssuanceLicenseInfo(
                                                    out DateTime timeFrom,
                                                    out DateTime timeUntil,
                                                    DistributionPointInfo distributionPointInfo,
                                                    out string distributionPointName,
                                                    out string distributionPointUri,
                                                    out ContentUser owner,
                                                    out bool officialFlag)
        {
            uint distributionPointNameLength = 0;
            uint distributionPointUriLength = 0;
            bool officialFlagTemp = false;
            SafeRightsManagementPubHandle ownerHandleTemp = null;
 
            int hr = SafeNativeMethods.DRMGetIssuanceLicenseInfo(
                                                                        _issuanceLicenseHandle,
                                                                        null,
                                                                        null,
                                                                        (uint)distributionPointInfo,
                                                                        ref distributionPointNameLength,
                                                                        null,
                                                                        ref distributionPointUriLength,
                                                                        null,
                                                                        out ownerHandleTemp,
                                                                        out officialFlagTemp);
            Errors.ThrowOnErrorCode(hr);
 
            if (ownerHandleTemp != null)
            {
                // As a result of calling DRMGetIssuanceLicenseInfo twice,
                // we are getting 2 handles. We are going to dispose the first one 
                // and preserve the second one.
                ownerHandleTemp.Dispose();
                ownerHandleTemp = null;
            }
 
            StringBuilder distributionPointNameTemp = null;
            // allocate memory as necessary, it seems that Unmanaged libraries really do not like
            // getting a non null buffer of size 0 
            if (distributionPointNameLength > 0)
            {
                distributionPointNameTemp = new StringBuilder(checked((int)distributionPointNameLength));
            }
 
            StringBuilder distributionPointUriTemp = null;
            // allocate memory as necessary, it seems that Unmanaged libraries really do not like
            // getting a non null buffer of size 0 
            if (distributionPointUriLength > 0)
            {
                distributionPointUriTemp = new StringBuilder(checked((int)distributionPointUriLength));
            }
 
            SystemTime timeFromTemp = new SystemTime(DateTime.Now);
            SystemTime timeUntilTemp = new SystemTime(DateTime.Now);
 
            hr = SafeNativeMethods.DRMGetIssuanceLicenseInfo(
                                                                        _issuanceLicenseHandle,
                                                                        timeFromTemp,
                                                                        timeUntilTemp,
                                                                        (uint)distributionPointInfo,
                                                                        ref distributionPointNameLength,
                                                                        distributionPointNameTemp,
                                                                        ref distributionPointUriLength,
                                                                        distributionPointUriTemp,
                                                                        out ownerHandleTemp,
                                                                        out officialFlagTemp);
            Errors.ThrowOnErrorCode(hr);
 
            timeFrom = timeFromTemp.GetDateTime(DateTime.MinValue);
            timeUntil = timeUntilTemp.GetDateTime(DateTime.MaxValue);
 
            // only if we got some data back we shall try to process it 
            if (distributionPointNameTemp != null)
            {
                distributionPointName = distributionPointNameTemp.ToString();
            }
            else
            {
                distributionPointName = null;
            }
 
            // only if we got some data back we shall try to process it 
            if (distributionPointUriTemp != null)
            {
                distributionPointUri = distributionPointUriTemp.ToString();
            }
            else
            {
                distributionPointUri = null;
            }
 
            // if we have owner let's convert it to a user and preserve 
            // handler for further destruction
            owner = null;
            if (ownerHandleTemp != null)
            {
                _pubHandlesList.Add(ownerHandleTemp);
 
                if (!ownerHandleTemp.IsInvalid)
                {
                    owner = GetUserFromHandle(ownerHandleTemp);
                }
            }
 
            officialFlag = officialFlagTemp;
        }
 
        private void SetRevocationPoint(RevocationPoint revocationPoint)
        {
            int hr = SafeNativeMethods.DRMSetRevocationPoint(
                                _issuanceLicenseHandle,
                                false, // flagDelete,
                                revocationPoint.Id,
                                revocationPoint.IdType,
                                revocationPoint.Url.AbsoluteUri, // We are using Uri class as a basic validation mechanism. These URIs come from unmanaged 
                // code libraries and go back as parameters into the unmanaged code libraries. 
                // We use AbsoluteUri property as means of verifying that it is actually an absolute and 
                // well formed Uri. If by any chance it happened to be a relative URI, an exception will 
                // be thrown here. This will perform the necessary escaping.
                                revocationPoint.Frequency,
                                revocationPoint.Name,
                                revocationPoint.PublicKey);
 
            Errors.ThrowOnErrorCode(hr);
        }
 
        private RevocationPoint GetRevocationPoint()
        {
            uint idLength = 0;
            uint idTypeLength = 0;
            uint urlLength = 0;
            uint nameLength = 0;
            uint publicKeyLength = 0;
            SystemTime frequency = new SystemTime(DateTime.Now);
 
            int hr = SafeNativeMethods.DRMGetRevocationPoint(
                                _issuanceLicenseHandle,
                                ref idLength,
                                null,
                                ref idTypeLength,
                                null,
                                ref urlLength,
                                null,
                                frequency,
                                ref nameLength,
                                null,
                                ref publicKeyLength,
                                null);
            if (hr == (int)RightsManagementFailureCode.RevocationInfoNotSet)
            {
                return null;
            }
 
            Errors.ThrowOnErrorCode(hr);
 
            // allocate memory as necessary, it seems that Unmanaged libraries really do not like
            // getting a non null buffer of size 0 
            StringBuilder idTemp = null;
            if (idLength > 0)
            {
                idTemp = new StringBuilder(checked((int)idLength));
            }
 
            StringBuilder idTypeTemp = null;
            if (idTypeLength > 0)
            {
                idTypeTemp = new StringBuilder(checked((int)idTypeLength));
            }
 
            StringBuilder urlTemp = null;
            if (urlLength > 0)
            {
                urlTemp = new StringBuilder(checked((int)urlLength));
            }
 
            StringBuilder nameTemp = null;
            if (nameLength > 0)
            {
                nameTemp = new StringBuilder(checked((int)nameLength));
            }
 
            StringBuilder publicKeyTemp = null;
            if (publicKeyLength > 0)
            {
                publicKeyTemp = new StringBuilder(checked((int)publicKeyLength));
            }
 
            hr = SafeNativeMethods.DRMGetRevocationPoint(
                                _issuanceLicenseHandle,
                                ref idLength,
                                idTemp,
                                ref idTypeLength,
                                idTypeTemp,
                                ref urlLength,
                                urlTemp,
                                frequency,
                                ref nameLength,
                                nameTemp,
                                ref publicKeyLength,
                                publicKeyTemp);
            Errors.ThrowOnErrorCode(hr);
 
            RevocationPoint resultRevocationPoint = new RevocationPoint();
 
            resultRevocationPoint.Id = (idTemp == null) ? null : idTemp.ToString();
            resultRevocationPoint.IdType = (idTypeTemp == null) ? null : idTypeTemp.ToString();
            resultRevocationPoint.Url = (urlTemp == null) ? null : new Uri(urlTemp.ToString());
            resultRevocationPoint.Name = (nameTemp == null) ? null : nameTemp.ToString();
            resultRevocationPoint.PublicKey = (publicKeyTemp == null) ? null : publicKeyTemp.ToString();
            resultRevocationPoint.Frequency = frequency;
 
            return resultRevocationPoint;
        }
 
        /// <summary>
        /// Call this before accepting any API call 
        /// </summary>
        private void CheckDisposed()
        {
            //As this class is not public, and the corresponding public class (Unsigned Publish License 
            //that uses this class is not disposable, it means, that the only probable reason for using 
            //Disposed instance of this class is a bug in our internal logic; therefore, Invariant Assert is 
            //more appropriate. Throwing ObjectDisposedException exception would be misleading 
            //as users will not deal with a Disposable class.  
            Invariant.Assert((_issuanceLicenseHandle != null) &&
                                       (!_issuanceLicenseHandle.IsInvalid));
        }
 
        /// <summary>
        /// Converts Authentication type enumeration into a string that can be accepted by the unmanaged code
        /// </summary>
        private string ConvertAuthenticationTypeToString(ContentUser user)
        {
            if (user.AuthenticationType == AuthenticationType.WindowsPassport)
            {
                return UnspecifiedAuthenticationType;
            }
            else
            {
                return user.AuthenticationType.ToString();
            }
        }
 
 
        private List<SafeRightsManagementPubHandle> _pubHandlesList = new List<SafeRightsManagementPubHandle>(50); // initial capacity 
        private SafeRightsManagementPubHandle _issuanceLicenseHandle = null; // if this is null, we are disposed
 
        private const string DefaultContentType = "MS-GUID";
 
        private const string UnspecifiedAuthenticationType = "Unspecified";
    }
}