public void MSOXORULE_S01_TC03_AddExtendedRule_WithLargeMessageCondition()
        {
            this.CheckMAPIHTTPTransportSupported();

            #region TestUser1 creates an FAI message.
            RopCreateMessageResponse ropCreateMessageResponse;
            uint extendedRuleMessageHandle = this.OxoruleAdapter.RopCreateMessage(this.InboxFolderHandle, this.InboxFolderID, Convert.ToByte(true), out ropCreateMessageResponse);
            Site.Assert.AreEqual<uint>(0, ropCreateMessageResponse.ReturnValue, "Creating the first FAI message should succeed.");
            #endregion

            #region TestUser1 adds the extended rule with NamedProperty successfully.
            string ruleConditionSubjectName = Constants.RuleConditionSubjectContainString;
            NamedPropertyInfo namedPropertyInfo = new NamedPropertyInfo
            {
                NoOfNamedProps = 2,
                PropId = new uint[2]
                {
                    0x8001, 0x8002
                }
            };

            // 0x01 means the property is identified by the name property.
            PropertyName testPropertyName = new PropertyName
            {
                Guid = System.Guid.NewGuid().ToByteArray(),
                Kind = 0x01,
                Name = Encoding.Unicode.GetBytes(Constants.NameOfPropertyName + "\0")
            };
            testPropertyName.NameSize = (byte)testPropertyName.Name.Length;

            // 0x00 means the property is identified by the LID.
            PropertyName secondPropertyName = new PropertyName
            {
                Guid = System.Guid.NewGuid().ToByteArray(),
                Kind = 0x00,
                LID = 0x88888888
            };
            namedPropertyInfo.NamedProperty = new PropertyName[2] { testPropertyName, secondPropertyName };
            namedPropertyInfo.NamedPropertiesSize = (uint)(testPropertyName.Serialize().Length + secondPropertyName.Serialize().Length);
            string ruleName = Common.GenerateResourceName(this.Site, Constants.ExtendRulename1);
            TaggedPropertyValue[] extendedRuleProperties = AdapterHelper.GenerateExtendedRuleTestData(ruleName, 0, (uint)RuleState.ST_ENABLED, Constants.PidTagRuleProvider, ActionType.OP_MARK_AS_READ, new DeleteMarkReadActionData(), ruleConditionSubjectName, namedPropertyInfo);

            // Set properties for extended rule FAI message.
            RopSetPropertiesResponse ropSetPropertiesResponse = this.OxoruleAdapter.RopSetProperties(extendedRuleMessageHandle, extendedRuleProperties);
            Site.Assert.AreEqual<uint>(0, ropSetPropertiesResponse.ReturnValue, "Setting property for Extended rule FAI message should succeed.");

            // Save changes of message.
            RopSaveChangesMessageResponse ropSaveChangesMessagResponse = this.OxoruleAdapter.RopSaveChangesMessage(extendedRuleMessageHandle);
            Site.Assert.AreEqual<uint>(0, ropSaveChangesMessagResponse.ReturnValue, "Saving Extend rule message should succeed.");

            // Specify the properties to be got. 
            PropertyTag[] propertyTagArray = new PropertyTag[2];

            // PidTagRuleMessageProviderData
            propertyTagArray[0].PropertyId = (ushort)PropertyId.PidTagRuleMessageProvider;
            propertyTagArray[0].PropertyType = (ushort)PropertyType.PtypString;

            // PidTagRuleMessageName
            propertyTagArray[1].PropertyId = (ushort)PropertyId.PidTagRuleMessageName;
            propertyTagArray[1].PropertyType = (ushort)PropertyType.PtypString;

            // Get the specific properties of the extended rule.
            RopGetPropertiesSpecificResponse ropGetPropertiesSpecificResponse = this.OxoruleAdapter.RopGetPropertiesSpecific(extendedRuleMessageHandle, propertyTagArray);
            Site.Assert.AreEqual<uint>(0, ropGetPropertiesSpecificResponse.ReturnValue, "Getting folder id property operation should succeed.");

            #region Capture Code
            // Add the debug information
            Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXORULE_R184");

            // Verify MS-OXORULE requirement: MS-OXORULE_R184
            string pidTagRuleMessageProviderData = AdapterHelper.PropertyValueConvertToString(ropGetPropertiesSpecificResponse.RowData.PropertyValues[0].Value);
            Site.CaptureRequirementIfAreEqual<string>(
                Constants.PidTagRuleProvider,
                pidTagRuleMessageProviderData,
                184,
                @"[In PidTagRuleMessageProvider Property] This property has the same semantics as the PidTagRuleProvider property (section 2.2.1.3.1.5). [The PidTagRuleMessageProvider property identifies the client application that owns the rule (2).]");

            // Add the debug information
            Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXORULE_R164");

            // Verify MS-OXORULE requirement: MS-OXORULE_R164
            // The rule name set by client is "ExtendRulename1".
            string pidTagRuleMessageNameValue = AdapterHelper.PropertyValueConvertToString(ropGetPropertiesSpecificResponse.RowData.PropertyValues[1].Value);
            Site.CaptureRequirementIfAreEqual<string>(
                 ruleName,
                 pidTagRuleMessageNameValue,
                 164,
                 @"[In PidTagRuleMessageName Property] This property has the same semantics as the PidTagRuleName property (section 2.2.1.3.1.4). [The PidTagRuleMessageName property specifies the name of the rule (2).]");
            #endregion
            #endregion

            #region TestUser1 retrieves data of the new extended rule.
            this.OxoruleAdapter.TargetOfRop = TargetOfRop.ForExtendedRules;
            RopGetPropertiesAllResponse ropGetExtendRuleMessageResponse = this.OxoruleAdapter.RopGetPropertiesAll(extendedRuleMessageHandle, this.PropertySizeLimitFlag, (ushort)WantUnicode.Want);
            Site.Assert.AreEqual<uint>(0, ropGetExtendRuleMessageResponse.ReturnValue, "Getting all properties operation should succeed.");
            Site.Assert.IsTrue(ropGetExtendRuleMessageResponse.PropertyValues.Length != 0, "Extended Rule data should be found in related FAI message!");
            this.OxoruleAdapter.TargetOfRop = TargetOfRop.OtherTarget;

            ExtendedRuleActions extendedRuleMessageActions = null;

            // Check the properties set on Extended Rule, and find the Extended Rule Actions.
            for (int i = 0; i < ropGetExtendRuleMessageResponse.PropertyValues.Length; i++)
            {
                // propertyId indicates the Id of a property set on Extended Rule.
                ushort propertyId = ropGetExtendRuleMessageResponse.PropertyValues[i].PropertyTag.PropertyId;
                if (propertyId == (ushort)PropertyId.PidTagExtendedRuleMessageActions)
                {
                    byte[] propertyValue = ropGetExtendRuleMessageResponse.PropertyValues[i].Value;
                    extendedRuleMessageActions = AdapterHelper.PropertyValueConvertToExtendedRuleActions(propertyValue);
                    break;
                }
            }

            Site.Assert.AreNotEqual<ExtendedRuleActions>(null, extendedRuleMessageActions, "extendedRuleMessageActions should not be null.");

            // Get the Property Names saved by server in the extendedRuleMessageActions.
            PropertyName[] propertyNames = extendedRuleMessageActions.NamedPropertyInformation.NamedProperty;
            PropertyName testPropertyNameSavedOnServer = new PropertyName();
            PropertyName secondPropertyNameSavedOnServer = new PropertyName();
            if (propertyNames != null && propertyNames.Length > 0)
            {
                for (int i = 0; i < propertyNames.Length; i++)
                {
                    // If the Kind is 0x01, it means this PropertyName is the testPropertyName.
                    if (propertyNames[i].Kind == 0x01)
                    {
                        testPropertyNameSavedOnServer = propertyNames[i];
                    }
                    else if (propertyNames[i].Kind == 0x00)
                    {
                        // If the Kind is 0x00, it means this PropertyName is the secondPropertyName.
                        secondPropertyNameSavedOnServer = propertyNames[i];
                    }
                }
            }

            Site.Assert.IsNotNull(testPropertyNameSavedOnServer.Guid, "testPropertyNameSavedOnServer should not be null.");
            Site.Assert.IsNotNull(secondPropertyNameSavedOnServer.Guid, "secondPropertyNameSavedOnServer should not be null.");
            #endregion

            #region TestUser1 creates a new FAI message.
            extendedRuleMessageHandle = this.OxoruleAdapter.RopCreateMessage(this.InboxFolderHandle, this.InboxFolderID, Convert.ToByte(true), out ropCreateMessageResponse);
            Site.Assert.AreEqual<uint>(0, ropCreateMessageResponse.ReturnValue, "Creating the second FAI message should succeed.");
            #endregion

            #region TestUser1 creates an extended rule with PidTagExtendedRuleMessageCondition property value size larger than the size set by the server.
            propertyTagArray = new PropertyTag[1];
            propertyTagArray[0].PropertyId = (ushort)PropertyId.PidTagExtendedRuleSizeLimit;
            propertyTagArray[0].PropertyType = (ushort)PropertyType.PtypInteger32;

            ropGetPropertiesSpecificResponse = this.OxoruleAdapter.RopGetPropertiesSpecific(this.LogonHandle, propertyTagArray);
            uint pidTagExtendedRuleSizeLimit = Common.ConvertByteArrayToUint(ropGetPropertiesSpecificResponse.RowData.PropertyValues[0].Value);
            
            // According to MS-OXCRPC, "The server SHOULD fail with the RPC status code of RPC_X_BAD_STUB_DATA (0x000006F7) if the request buffer is larger than 0x00040000 bytes in size."
            if (pidTagExtendedRuleSizeLimit < 0x00040000)
            {
                ruleConditionSubjectName = Constants.RuleConditionSubjectContainString;
                namedPropertyInfo = new NamedPropertyInfo
                {
                    NoOfNamedProps = 2,
                    PropId = new uint[2]
                    {
                        0x8001, 0x8002
                    }
                };

                // Generate a string value whose size lager than the one specified by the PidTagExtendedRuleSizeLimit property.
                StringBuilder stringByteValue = new StringBuilder("ExtentRuleSize");
                stringByteValue.Append('a', (int)pidTagExtendedRuleSizeLimit);

                // If the value of Kind is 0x01, it means that the property is identified by the name property.
                testPropertyName = new PropertyName
                {
                    Guid = System.Guid.NewGuid().ToByteArray(),
                    Kind = 0x01,
                    Name = Encoding.Unicode.GetBytes(stringByteValue + "\0")
                };

                testPropertyName.NameSize = (byte)testPropertyName.Name.Length;

                // If the value of Kind is 0x00, it means that the property is identified by the LID.
                secondPropertyName = new PropertyName
                {
                    Guid = System.Guid.NewGuid().ToByteArray(),
                    Kind = 0x00,
                    LID = 0x88888888
                };

                namedPropertyInfo.NamedProperty = new PropertyName[2] { testPropertyName, secondPropertyName };
                namedPropertyInfo.NamedPropertiesSize = (uint)(testPropertyName.Serialize().Length + secondPropertyName.Serialize().Length);
                extendedRuleProperties = AdapterHelper.GenerateExtendedRuleTestData(Common.GenerateResourceName(this.Site, Constants.ExtendRulename1), 0, (uint)RuleState.ST_ENABLED, Constants.PidTagRuleProvider, ActionType.OP_MARK_AS_READ, new DeleteMarkReadActionData(), ruleConditionSubjectName, namedPropertyInfo);
                TaggedPropertyValue pidTagExtendedRuleMessageCondition = new TaggedPropertyValue();
                foreach (TaggedPropertyValue propertyValue in extendedRuleProperties)
                {
                    if (propertyValue.PropertyTag.PropertyId == (ushort)PropertyId.PidTagExtendedRuleMessageCondition)
                    {
                        pidTagExtendedRuleMessageCondition = propertyValue;
                        break;
                    }
                }

                uint pidTagExtendedRuleMessageConditionSize = uint.Parse(pidTagExtendedRuleMessageCondition.Value.Length.ToString());

                // Set properties for extended rule FAI message.
                ropSetPropertiesResponse = this.OxoruleAdapter.RopSetProperties(extendedRuleMessageHandle, extendedRuleProperties);

                // Save changes of message.
                ropSaveChangesMessagResponse = this.OxoruleAdapter.RopSaveChangesMessage(extendedRuleMessageHandle);

                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXORULE_R1016");

                // Verify MS-OXORULE requirement: MS-OXORULE_R1016.
                if (pidTagExtendedRuleMessageConditionSize > pidTagExtendedRuleSizeLimit)
                {
                    Site.CaptureRequirementIfAreNotEqual<uint>(
                        0x0000,
                        ropSaveChangesMessagResponse.ReturnValue,
                        1016,
                        @"[In Processing Incoming Messages to a Folder] If the PidTagExtendedRuleSizeLimit property is set and the size of the PidTagExtendedRuleMessageCondition property (section 2.2.4.1.10) exceeds the value specified by the PidTagExtendedRuleSizeLimit property, the server MUST return an error.");
                }
                else
                {
                    Site.Assert.Fail("The size of the PidTagExtendedRuleMessageCondition property should exceeds the value specified by the PidTagExtendedRuleSizeLimit property.");
                }
            }
            #endregion
        }
        /// <summary>
        /// Get the named properties value of specified Message object.
        /// </summary>
        /// <param name="longIdProperties">The list of named properties</param>
        /// <param name="messageHandle">The object handle of specified Message object.</param>
        /// <returns>Returns named property values of specified Message object.</returns>
        public Dictionary<PropertyNames, byte[]> GetNamedPropertyValues(List<PropertyNameObject> longIdProperties, uint messageHandle)
        {
            object response = null;
            byte[] rawData = null;

            #region Call RopGetPropertyIdsFromNames to get property ID.
            PropertyName[] propertyNames = new PropertyName[longIdProperties.Count];

            for (int i = 0; i < longIdProperties.Count; i++)
            {
                propertyNames[i] = longIdProperties[i].PropertyName;
            }

            RopGetPropertyIdsFromNamesRequest getPropertyIdsFromNamesRequest;
            RopGetPropertyIdsFromNamesResponse getPropertyIdsFromNamesResponse;
            getPropertyIdsFromNamesRequest.RopId = (byte)RopId.RopGetPropertyIdsFromNames;
            getPropertyIdsFromNamesRequest.LogonId = 0x00;
            getPropertyIdsFromNamesRequest.InputHandleIndex = 0x00;
            getPropertyIdsFromNamesRequest.Flags = (byte)GetPropertyIdsFromNamesFlags.Create;
            getPropertyIdsFromNamesRequest.PropertyNameCount = (ushort)propertyNames.Length;
            getPropertyIdsFromNamesRequest.PropertyNames = propertyNames;

            this.DoRopCall(getPropertyIdsFromNamesRequest, messageHandle, ref response, ref rawData, GetPropertiesFlags.None);
            getPropertyIdsFromNamesResponse = (RopGetPropertyIdsFromNamesResponse)response;
            Site.Assert.AreEqual<uint>(0, getPropertyIdsFromNamesResponse.ReturnValue, "Call RopGetPropertyIdsFromNames should success.");
            #endregion

            #region Call RopGetPropertiesSpecific to get the specific properties of specific message object.
            // Get specific property for created message
            RopGetPropertiesSpecificRequest getPropertiesSpecificRequest = new RopGetPropertiesSpecificRequest();
            RopGetPropertiesSpecificResponse getPropertiesSpecificResponse;
            getPropertiesSpecificRequest.RopId = (byte)RopId.RopGetPropertiesSpecific;
            getPropertiesSpecificRequest.LogonId = 0x00;
            getPropertiesSpecificRequest.InputHandleIndex = 0x00;
            getPropertiesSpecificRequest.PropertySizeLimit = 0xFFFF;

            PropertyTag[] tagArray = new PropertyTag[longIdProperties.Count];
            for (int j = 0; j < getPropertyIdsFromNamesResponse.PropertyIds.Length; j++)
            {
                tagArray[j] = new PropertyTag
                {
                    PropertyId = getPropertyIdsFromNamesResponse.PropertyIds[j].ID,
                    PropertyType = (ushort)longIdProperties[j].PropertyType
                };
            }

            getPropertiesSpecificRequest.PropertyTagCount = (ushort)tagArray.Length;
            getPropertiesSpecificRequest.PropertyTags = tagArray;

            this.DoRopCall(getPropertiesSpecificRequest, messageHandle, ref response, ref rawData, GetPropertiesFlags.None);
            getPropertiesSpecificResponse = (RopGetPropertiesSpecificResponse)response;
            Site.Assert.AreEqual<uint>(0, getPropertiesSpecificResponse.ReturnValue, "Calling RopGetPropertiesSpecific should be successful.");

            Dictionary<PropertyNames, byte[]> propertyList = new Dictionary<PropertyNames, byte[]>();
            PropertyObj propertyObjPidLidCommonStart = null;
            PropertyObj propertyObjPidLidCommonEnd = null;

            for (int i = 0; i < getPropertiesSpecificResponse.RowData.PropertyValues.Count; i++)
            {
                PropertyObj propertyObj = new PropertyObj
                {
                    PropertyName = longIdProperties[i].DisplayName,
                    ValueType = longIdProperties[i].PropertyType
                };
                PropertyHelper.GetPropertyObjFromBuffer(propertyObj, getPropertiesSpecificResponse.RowData.PropertyValues[i].Value);

                // Verify requirements related with named properties PidNameKeywords, PidNameContentBase, PidNameAcceptLanguage and PidNameContentClass.
                this.VerifyMessageSyntaxDataType(propertyObj);

                if (propertyObj.PropertyName == PropertyNames.PidLidCommonStart)
                {
                    propertyObjPidLidCommonStart = propertyObj;
                }

                if (propertyObj.PropertyName == PropertyNames.PidLidCommonEnd)
                {
                    propertyObjPidLidCommonEnd = propertyObj;
                }

                propertyList.Add(longIdProperties[i].DisplayName, getPropertiesSpecificResponse.RowData.PropertyValues[i].Value);
            }

            // Verify the requirements of PidLidCommonStart and PidLidCommonEnd.
            if (PropertyHelper.IsPropertyValid(propertyObjPidLidCommonStart) || PropertyHelper.IsPropertyValid(propertyObjPidLidCommonEnd))
            {
                this.VerifyMessageSyntaxPidLidCommonStartAndPidLidCommonEnd(propertyObjPidLidCommonStart, propertyObjPidLidCommonEnd);
            }
            #endregion

            return propertyList;
        }
        /// <summary>
        /// Get PropertyName from ptfconfig file.
        /// </summary>
        /// <param name="kind">The kind of PropertyName.</param>
        /// <returns>An instance of PropertyName.</returns>
        private PropertyName GetPropertyName(byte kind)
        {
            PropertyName propertyName = new PropertyName
            {
                Kind = kind
            };

            if (propertyName.Kind == (byte)Kind.LidField)
            {
                propertyName.LID = PropertyName00Lid;
                propertyName.Guid = (new Guid(PropertyName00Guid)).ToByteArray();
            }
            else if (propertyName.Kind == (byte)Kind.NameField)
            {
                propertyName.Name = Encoding.Unicode.GetBytes(PropertyName01Name + "\0");
                propertyName.NameSize = (byte)propertyName.Name.Length;
                propertyName.Guid = (new Guid(PropertyName01Guid)).ToByteArray();
            }
            else if (propertyName.Kind == (byte)Kind.NoAssociated)
            {
                propertyName.Guid = (new Guid(PropertyNameFFGuid)).ToByteArray();
            }

            return propertyName;
        }
        /// <summary>
        /// The method is used to query an object for all the named properties. 
        /// </summary>
        /// <param name="queryFlags">Specifies QueryFlags parameter in request.</param>
        /// <param name="hasGuid">Indicates whether HasGuid is zero, 
        /// If the HasGUID field is non-zero then the PropertyGUID field MUST be included in the request. 
        /// If no PropertyGUID is specified, then properties from any GUID MUST be returned in the results.</param>
        /// <param name="isKind0x01Returned">True if the named properties of the response with the Kind field 
        /// ([MS-OXCDATA] section 2.6.1) set to 0x1 was returned.</param>
        /// <param name="isKind0x00Returned">True if the named properties of the response with the Kind field 
        /// ([MS-OXCDATA] section 2.6.1) set to 0x0 was returned.</param>
        /// <param name="isNamedPropertyGuidReturned">True if the named properties  with a GUID field ([MS-OXCDATA] 
        /// section 2.6.1) value that does not match the value of the PropertyGUID field was returned.</param>
        public void RopQueryNamedPropertiesMethod(
            QueryFlags queryFlags,
            bool hasGuid,
            out bool isKind0x01Returned,
            out bool isKind0x00Returned,
            out bool isNamedPropertyGuidReturned)
        {
            #region Initialize parameters property
            // The value of hasGuid in RopQueryNameProperties method.
            byte hasGuidRequest = 0;

            // The value of queryFlags in RopQueryNameProperties method.
            byte queryFlagRequest = Convert.ToByte(queryFlags);

            // The value of propertyGuid in RopQuerynameProperties method.
            byte[] propertyGuid = null;

            // Set default values for three parameters.
            isKind0x01Returned = false;
            isKind0x00Returned = false;
            isNamedPropertyGuidReturned = false;

            // When name property has GUID, set parameters.
            if (hasGuid)
            {
                hasGuidRequest = Convert.ToByte(true);
                PropertyName[] ptyName = new PropertyName[1];
                ptyName[0] = this.GetPropertyName((byte)Kind.NameField);
                propertyGuid = ptyName[0].Guid;

                this.RopGetPropertyIdsFromNames(this.cprptCurrentHandle, (byte)GetPropertyIdsFromNamesFlags.Create, ptyName, true);
                if (this.cprptCurrentType == ServerObjectType.Message)
                {
                    this.RopSaveChangesMessage(this.cprptCurrentHandle, true);
                }
                else if (this.cprptCurrentType == ServerObjectType.Attachment)
                {
                    this.RopSaveChangesAttachment(this.cprptCurrentHandle, true);
                }
            }
            #endregion
            // Call RopQueryNamedProperties.
            RopQueryNamedPropertiesResponse queryNamePtyRes = this.RopQueryNamedProperties(this.cprptCurrentHandle, queryFlagRequest, hasGuidRequest, propertyGuid);

            #region Verify isKind0x00Return and isKind0x00Return
            // Verify if the kind of NamedProperties in RopQueryNamedPropertiesResponse is 0x01.
            for (int i = 0; i < queryNamePtyRes.IdCount; i++)
            {
                if (queryNamePtyRes.PropertyNames[i].Kind == (byte)Kind.NameField)
                {
                    isKind0x01Returned = true;
                    break;
                }
            }

            // Verify if the kind of NamedProperties in RopQueryNamedPropertiesResponse is 0x00.
            for (int i = 0; i < queryNamePtyRes.IdCount; i++)
            {
                if (queryNamePtyRes.PropertyNames[i].Kind == (byte)Kind.LidField)
                {
                    isKind0x00Returned = true;
                    break;
                }
            }
            #endregion

            #region Verify isNamedPropertyGuidReturn
            // If the PropertyGUID field presents, named properties with a GUID field value that does not match the value of the PropertyGUID field MUST NOT be returned.
            for (int i = 0; i < queryNamePtyRes.IdCount; i++)
            {
                if (hasGuid && Common.CompareByteArray(queryNamePtyRes.PropertyNames[i].Guid, propertyGuid))
                {
                    isNamedPropertyGuidReturned = true;
                }
                else
                {
                    isNamedPropertyGuidReturned = false;
                    break;
                }
            }
            #endregion

            // Verify RopQueryNamedProperties.
            this.VerifyRopQueryNamedProperties(queryFlags, propertyGuid, queryNamePtyRes, this.cprptCurrentType, true);
        }
        /// <summary>
        /// Verify PropertyName structure.
        /// </summary>
        /// <param name="propertyName">The property name.</param>
        private void VerifyPropertyName(PropertyName propertyName)
        {
            // Add the debug information.
            Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R17, The actual value of Kind is {0}", propertyName.Kind);

            // Verify MS-OXCDATA requirement: MS-OXCDATA_R17.
            bool isVerifyR17 = propertyName.Kind == 0x00 ||
                               propertyName.Kind == 0x01 ||
                               propertyName.Kind == 0xFF;

            Site.CaptureRequirementIfIsTrue(
                isVerifyR17,
                "MS-OXCDATA",
                17,
                @"[In PropertyName Structure] Kind (1 byte): The possible values [0x00, 0x01, 0xFF] for the Kind field are in the following table.");

            if (propertyName.Kind == 0x00)
            {
                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R25");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R25.
                // LID is not 0 means it is present.
                Site.CaptureRequirementIfIsNotNull(
                    propertyName.LID,
                    "MS-OXCDATA",
                    25,
                    @"[In PropertyName Structure] LID (optional) (4 bytes): This field is present only if the value of the Kind field is equal to 0x00.");

                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R26");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R26.
                Site.CaptureRequirementIfAreEqual<Type>(
                    typeof(uint),
                    propertyName.LID.GetType(),
                    "MS-OXCDATA",
                    26,
                    @"[In PropertyName Structure] LID (optional) (4 bytes):  An unsigned integer that identifies the named property within its property set.");

                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R27");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R27.
                // NameSize!=null means NameSize present.
                Site.CaptureRequirementIfIsNull(
                    propertyName.NameSize,
                    "MS-OXCDATA",
                    27,
                    @"[In PropertyName Structure] NameSize (optional) (1 byte):  This field is present only if the value of the Kind field is equal to 0x01.");

                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R29");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R29.
                // That Name does not equal null means Name presents.
                Site.CaptureRequirementIfIsNull(
                    propertyName.Name,
                    "MS-OXCDATA",
                    29,
                    @"[In PropertyName Structure] Name (optional) (variable): This field is present only if Kind is equal to 0x01.");
            }
            else
            {
                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R25");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R25.
                // LID is not 0 means it is present.
                Site.CaptureRequirementIfIsNull(
                    propertyName.LID,
                    "MS-OXCDATA",
                    25,
                    @"[In PropertyName Structure] LID (optional) (4 bytes): This field is present only if the value of the Kind field is equal to 0x00.");

                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R27");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R27.
                // NameSize!=null means NameSize present.
                Site.CaptureRequirementIfIsNotNull(
                    propertyName.NameSize,
                    "MS-OXCDATA",
                    27,
                    @"[In PropertyName Structure] NameSize (optional) (1 byte):  This field is present only if the value of the Kind field is equal to 0x01.");

                // Add the debug information.
                Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCDATA_R29");

                // Verify MS-OXCDATA requirement: MS-OXCDATA_R29.
                // That Name does not equal null means Name presents.
                Site.CaptureRequirementIfIsNotNull(
                    propertyName.Name,
                    "MS-OXCDATA",
                    29,
                    @"[In PropertyName Structure] Name (optional) (variable): This field is present only if Kind is equal to 0x01.");
            }

            // Add the debug information.
            Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXORULE_R941");

            // Verify MS-OXORULE requirement: MS-OXORULE_R941.
            // The format of the PropertyName structure has been verified by the above capture code, so R941 can be verified directly.
            Site.CaptureRequirement(
                941,
                @"[In NamedPropertyInformation Structure] The format of the PropertyName structure is specified in [MS-OXCDATA] section 2.6.1.");
        }
        /// <summary>
        /// This method is used to map abstract, client-defined named properties to concrete 16-bit property IDs. 
        /// </summary>
        /// <param name="isTestOrder">Indicates whether to test returned PropertyNames order.</param>
        /// <param name="isCreateFlagSet">Indicates whether the "Create" Flags in request parameter is set.</param>
        /// <param name="isPropertyNameExisting">Indicates whether PropertyName is existing in object mapping.</param>
        /// <param name="specialPropertyName">Specifies PropertyName of request parameter</param>
        /// <param name="isCreatedEntryReturned">If Create Flags is set: If set, indicates that the server MUST create new
        /// entries for any name parameters that are not found in the existing mapping set, and return existing entries for any
        /// name parameters that are found in the existing mapping set.</param>
        /// <param name="error">Specifies the ErrorCode when server reached limit.</param>
        public void RopGetPropertyIdsFromNamesMethod(
            bool isTestOrder,
            bool isCreateFlagSet,
            bool isPropertyNameExisting,
            SpecificPropertyName specialPropertyName,
            out bool isCreatedEntryReturned,
            out CPRPTErrorCode error)
        {
            #region Initialize parameters
            // Specify whether the order in response is the same as that in request.
            bool isOrder = true;

            // Specify whether the property names and ids are mapped.
            bool isNamesAndIdsMapping = false;

            // Set default value for the out parameter.
            isCreatedEntryReturned = false;
            error = CPRPTErrorCode.None;

            // Set value for flags in RopGetPropertyIdsFromNames.
            byte flags = (byte)GetPropertyIdsFromNamesFlags.None;
            if (isCreateFlagSet)
            {
                flags = (byte)GetPropertyIdsFromNamesFlags.Create;
            }

            PropertyName[] propertyNames = null;

            if (isTestOrder)
            {
                propertyNames = new PropertyName[ConstValues.TestPropertyCount];
            }
            else
            {
                propertyNames = new PropertyName[1];
            }

            switch (specialPropertyName)
            {
                case SpecificPropertyName.Kind0x01:
                case SpecificPropertyName.NoConstraint:
                    propertyNames[0] = this.GetPropertyName((byte)Kind.NameField);
                    break;
                case SpecificPropertyName.PS_MAPIAndKind0x01:
                    propertyNames[0] = this.GetPropertyName((byte)Kind.NameField);
                    propertyNames[0].Guid = (new Guid(PSMAPI)).ToByteArray();
                    break;
                case SpecificPropertyName.PS_MAPIAndKind0x00:
                    propertyNames[0] = this.GetPropertyName((byte)Kind.LidField);
                    propertyNames[0].Guid = (new Guid(PSMAPI)).ToByteArray();
                    break;
                default:
                    Site.Assert.Fail("Invalid SpecificPropertyName enum value {0}.", specialPropertyName);
                    break;
            }

            if (!isPropertyNameExisting && !isCreateFlagSet)
            {
                propertyNames[0].Kind = (byte)Kind.NameField;
                propertyNames[0].Name = Encoding.Unicode.GetBytes(PropertyNameNotMapped + "\0");
                propertyNames[0].NameSize = (byte)propertyNames[0].Name.Length;
                propertyNames[0].Guid = (new Guid(PropertyNameNotMappedGuid)).ToByteArray();
            }

            // The existing property ID.
            ushort existId = 0;

            // If the property exists, create it.
            if (isPropertyNameExisting)
            {
                PropertyName[] propertyNameTmp = new PropertyName[1];
                propertyNameTmp[0] = propertyNames[0];
                RopGetPropertyIdsFromNamesResponse ptyNameExistingRes = this.RopGetPropertyIdsFromNames(this.cprptCurrentHandle, (byte)GetPropertyIdsFromNamesFlags.Create, propertyNameTmp, true);
                existId = ptyNameExistingRes.PropertyIds[0].ID;
            }

            // The second property ID.
            ushort secondId = 0;

            // The third property ID.
            ushort thirdId = 0;

            // If the PropertyNames order need to verify, create the second and the third properties.
            if (isTestOrder)
            {
                // Add second property.
                PropertyName[] propertyNameTmp = new PropertyName[1];
                propertyNameTmp[0] = this.GetPropertyName((byte)Kind.NameField);
                propertyNameTmp[0].Name[0]++;
                propertyNameTmp[0].Guid[0]++;
                propertyNames[1] = propertyNameTmp[0];
                RopGetPropertyIdsFromNamesResponse ptyNameExistingRes = this.RopGetPropertyIdsFromNames(this.cprptCurrentHandle, (byte)GetPropertyIdsFromNamesFlags.Create, propertyNameTmp, true);
                secondId = ptyNameExistingRes.PropertyIds[0].ID;

                // Add second property.
                propertyNameTmp[0] = this.GetPropertyName((byte)Kind.NameField);
                propertyNameTmp[0].Name[0]--;
                propertyNameTmp[0].Guid[0]--;
                propertyNames[2] = propertyNameTmp[0];
                ptyNameExistingRes = this.RopGetPropertyIdsFromNames(this.cprptCurrentHandle, (byte)GetPropertyIdsFromNamesFlags.Create, propertyNameTmp, true);
                thirdId = ptyNameExistingRes.PropertyIds[0].ID;
            }
            #endregion

            RopGetPropertyIdsFromNamesResponse getPropertyIdsFromNamesResponse = this.RopGetPropertyIdsFromNames(this.cprptCurrentHandle, flags, propertyNames, false);

            #region When property exists, check if names and ids are mapping
            if (isPropertyNameExisting)
            {
                RopGetNamesFromPropertyIdsResponse getPropertyNamesFromPropertyIdsResponse = this.RopGetNamesFromPropertyIds(this.cprptCurrentHandle, getPropertyIdsFromNamesResponse.PropertyIds);

                // Verify whether property name and id is mapping.
                for (int i = 0; i < getPropertyNamesFromPropertyIdsResponse.PropertyNameCount; i++)
                {
                    if (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Kind == (byte)Kind.LidField)
                    {
                        if ((getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Guid.ToString() == propertyNames[i].Guid.ToString()) &&
                            (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Kind == propertyNames[i].Kind) &&
                            (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].LID == propertyNames[i].LID))
                        {
                            isNamesAndIdsMapping = true;
                        }
                        else
                        {
                            isNamesAndIdsMapping = false;
                            break;
                        }
                    }
                    else if (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Kind == (byte)Kind.NameField)
                    {
                        if ((getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Guid.ToString() == propertyNames[i].Guid.ToString()) &&
                            (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Kind == propertyNames[i].Kind) &&
                            (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].NameSize == propertyNames[i].NameSize) &&
                            (getPropertyNamesFromPropertyIdsResponse.PropertyNames[i].Name.ToString() == propertyNames[i].Name.ToString()))
                        {
                            isNamesAndIdsMapping = true;
                        }
                        else
                        {
                            isNamesAndIdsMapping = false;
                            break;
                        }
                    }
                }
            }
            #endregion

            #region Test order
            if (getPropertyIdsFromNamesResponse.PropertyIds != null && propertyNames.Length == ConstValues.TestPropertyCount && getPropertyIdsFromNamesResponse.PropertyIds.Length == ConstValues.TestPropertyCount)
            {
                if (getPropertyIdsFromNamesResponse.PropertyIds[1].ID != secondId || getPropertyIdsFromNamesResponse.PropertyIds[2].ID != thirdId)
                {
                    isOrder = false;
                }
            }
            #endregion

            this.VerifyRopGetPropertyIdsFromNames((ushort)propertyNames.Length, propertyNames, flags, getPropertyIdsFromNamesResponse, isOrder, this.cprptCurrentType, isPropertyNameExisting, isNamesAndIdsMapping);

            #region Check error and isCreateEnrtyReturned

            switch ((CPRPTErrorCode)getPropertyIdsFromNamesResponse.ReturnValue)
            {
                case CPRPTErrorCode.None:

                    // Verify whether the created entry is returned.
                    if (isCreateFlagSet)
                    {
                        if (isPropertyNameExisting)
                        {
                            isCreatedEntryReturned = false;
                        }
                        else if (getPropertyIdsFromNamesResponse.PropertyIds[0].ID != existId)
                        {
                            isCreatedEntryReturned = true;
                        }
                    }

                    break;
                case CPRPTErrorCode.ecWarnWithErrors:
                    error = CPRPTErrorCode.ecWarnWithErrors;
                    break;
                case CPRPTErrorCode.NotEnoughMemory:
                    error = CPRPTErrorCode.NotEnoughMemory;
                    break;
                default:
                    Site.Assert.Fail("Unexpected RopGetPropertyIdsFromNames error code.Error: 0x{0:X8}", getPropertyIdsFromNamesResponse.ReturnValue);
                    break;
            }

            #endregion
        }
        /// <summary>
        /// Verify the RopCopyTo operation related requirements.
        /// </summary>
        /// <param name="propertyNameCount">The PropertyNameCount parameter in the request buffer.</param>
        /// <param name="propertyNames">A list of PropertyName in the request buffer.</param>
        /// <param name="flags">The Flags set by client in the request buffer.</param>
        /// <param name="ropGetPropertyIdsFromNamesResponse">The RopCopyTo response buffer structure.</param>
        /// <param name="isOrdered">A boolean value which indicates whether the order of elements in request/response buffer is right.</param>
        /// <param name="objectType">Indicates which object type the RopGetPropertyIdsFromNames operation is acting on.</param>
        /// <param name="isPropertyNameExisting">A boolean value indicates whether the queried PropertyNames exists on server.</param>
        /// <param name="isNamesAndIdsMapping">A boolean value indicates whether the Names and the returned Ids are mapping.</param>
        private void VerifyRopGetPropertyIdsFromNames(
            ushort propertyNameCount,
            PropertyName[] propertyNames,
            byte flags,
            RopGetPropertyIdsFromNamesResponse ropGetPropertyIdsFromNamesResponse,
            bool isOrdered,
            ServerObjectType objectType,
            bool isPropertyNameExisting,
            bool isNamesAndIdsMapping)
        {
            // Since the RopGetPropertyIdsFromNames ROP response was parsed successfully, MS-OXCPRPT_R52502 can be captured directly.
            Site.CaptureRequirement(
                52502,
                @"[In Processing RopGetPropertyIdsFromNames] The server responds with a RopGetPropertyIdsFromNames ROP response buffer.");

            // Since the RopGetPropertyIdsFromNames ROP response was parsed successfully, MS-OXCPRPT_R41404 can be captured directly.
            Site.CaptureRequirement(
               41404,
               @"[In Getting Property IDs for Named Properties] The client provides a property set and an identifier in either integer or string form, and the server returns the property ID, which the client uses for any operations performed on that property.");

            if (propertyNames[0].Kind == 0x00)
            {
                Site.CaptureRequirementIfAreNotEqual<ushort>(
                    0x0000,
                    ropGetPropertyIdsFromNamesResponse.PropertyIds[0].ID,
                    225,
                    @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIds: Reasons a name couldn't be mapped include: Use of the PS_MAPI namespace and not specifying 0x00 for the Kind field of the PropertyName structure ([MS-OXCDATA] section 2.6.1).");
            }

            if (flags != 0x02 && isPropertyNameExisting)
            {
                Site.CaptureRequirementIfAreNotEqual<ushort>(
                   0x0000,
                   ropGetPropertyIdsFromNamesResponse.PropertyIds[0].ID,
                   226,
                   @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIds: Reasons a name couldn't be mapped include:The name wasn't found in the mapping table and the Flags field of the ROP request buffer was not set to 0x02.");
            }

            if (ropGetPropertyIdsFromNamesResponse.ReturnValue.Equals((uint)CPRPTErrorCode.None))
            {
                if (objectType == ServerObjectType.Message)
                {
                    // If the object type this operation acting on is Message object and this operation is performed successfully, then the following requirement can be captured.
                    Site.CaptureRequirement(
                        20701,
                        @"[In RopGetPropertyIdsFromNames ROP] This operation [RopGetPropertyIdsFromNames ROP] is valid on Message objects.");
                }

                if (objectType == ServerObjectType.Attachment)
                {
                    // If the object type this operation acting on is Folder object and this operation is performed successfully, then the following requirement can be captured.
                    Site.CaptureRequirement(
                        20702,
                        @"[In RopGetPropertyIdsFromNames ROP] This operation [RopGetPropertyIdsFromNames ROP] is valid on Attachment objects.");
                }

                if (objectType == ServerObjectType.Folder)
                {
                    // If the object type this operation acting on is Attachment object and this operation is performed successfully, then the following requirement can be captured.
                    Site.CaptureRequirement(
                        20703,
                        @"[In RopGetPropertyIdsFromNames ROP] This operation [RopGetPropertyIdsFromNames ROP] is valid on Folder objects.");
                }

                if (objectType == ServerObjectType.Logon)
                {
                    // If the object type this operation acting on is Logon object and this operation is performed successfully, then the following requirement can be captured.
                    Site.CaptureRequirement(
                        20704,
                        @"[In RopGetPropertyIdsFromNames ROP] This operation [RopGetPropertyIdsFromNames ROP] is valid on Logon objects.");
                }

                // The parser has ensured the field satisfied the format, otherwise the response cannot be received.
                Site.CaptureRequirement(
                    215,
                    @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIdCount: 2 bytes integer.");

                Site.CaptureRequirementIfAreEqual<ushort>(
                      (ushort)ropGetPropertyIdsFromNamesResponse.PropertyIds.Length,
                      ropGetPropertyIdsFromNamesResponse.PropertyIdCount,
                      21502,
                      @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIdCount: An integer that specifies the number of property IDs contained in the PropertyIds field. ");

                if (propertyNameCount != 0)
                {
                    Site.CaptureRequirementIfAreEqual<ushort>(
                        propertyNameCount,
                        ropGetPropertyIdsFromNamesResponse.PropertyIdCount,
                        216,
                        @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIdCount: The value of this field MUST be equal to the value of the PropertyNameCount field of the ROP request buffer unless the value of the PropertyNameCount field is zero.");
                }
            }

            // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R218
            bool isVerifyR218 = true;
            for (int i = 0; i < ropGetPropertyIdsFromNamesResponse.PropertyIds.Length; i++)
            {
                // Check that every element's size in PropertyIds is 16-bit 
                if (Marshal.SizeOf(ropGetPropertyIdsFromNamesResponse.PropertyIds[i]) != 2)
                {
                    // Add the debug information
                    Site.Log.Add(
                        LogEntryKind.Debug,
                        "Verify MS-OXCPRPT_R218, The actual length of PropertyId is {0}",
                        Marshal.SizeOf(ropGetPropertyIdsFromNamesResponse.PropertyIds[i]));
                    isVerifyR218 = false;
                    break;
                }
            }

            Site.CaptureRequirementIfIsTrue(
                isVerifyR218,
                218,
                @"[In RopGetPropertyIdsFromNames ROP Response Buffer] propertyIds (variable): An array of 16-bit integers. ");

            // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R222
            // The order has been verified when de-serializing the related structures and represented by the variable isOrdered
            Site.CaptureRequirementIfIsTrue(
                isOrdered,
                222,
                @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIds: The order of property IDs in this array MUST match the order of the named properties specified in the PropertyNames field of the ROP request buffer.");

            Site.CaptureRequirementIfIsTrue(
               isOrdered,
               21801,
               @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIds: Each integer is a property ID that is mapped from a named property that is specified in the PropertyNames field of the ROP request buffer");

            if (!((propertyNameCount == 0) && (objectType == ServerObjectType.Logon)))
            {
                // !isPropertyNameExisting means that the propertyNames doesn't exist on server.
                // The property flag doesn't equal 0x02 means the Create bit in the Flags parameter is not set.
                if (!isPropertyNameExisting && (flags != 0x02))
                {
                    for (int i = 0; i < ropGetPropertyIdsFromNamesResponse.PropertyIds.Length; i++)
                    {
                        // Add the debug information
                        Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCPRPT_R532");

                        // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R532
                        // The PropertyNames don't exist on server means the PropertyNames requested are the unfound rows.
                        // The logon user is Administrator and the test suite will not reach the server-imposed limit on property ID 
                        // mappings, so the user always has permission to create new entries and the server-imposed limit is not reached.
                        // Since the Flags parameter doesn't have the Create flag bit set, so the "unless" condition will be still false.
                        Site.CaptureRequirementIfAreEqual<ushort>(
                            0x0000,
                            ropGetPropertyIdsFromNamesResponse.PropertyIds[i].ID,
                            532,
                            @"[In Processing RopGetPropertyIdsFromNames] [If the PropertyNameCount parameter is zero, and the RopGetPropertyIdsFromNames is acting on a Logon object, the server must enumerate all PropertyNames associated with property IDs] Otherwise, the server MUST, for each entry in the PropertyNames field of the ROP request buffer, follow this procedure: 3. For unfound rows, the returned property ID MUST be 0x0000 unless all of the following conditions are true:
                            1.The Flags field of the ROP request buffer is set to 0x02.
                            2. The user has permission to create new entries.
                            3. The server-imposed limit on property ID mappings specified later in this section hasn't yet been reached.");

                        // Add the debug information
                        Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCPRPT_R220");

                        // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R220
                        // The PropertyNames don't exist on server and the Flags parameter doesn't have the Create flag bit set means 
                        // the names could not be mapped.
                        Site.CaptureRequirementIfAreEqual<ushort>(
                            0x0000,
                            ropGetPropertyIdsFromNamesResponse.PropertyIds[i].ID,
                            220,
                            @"[In RopGetPropertyIdsFromNames ROP Response Buffer] PropertyIds: If a named property cannot be mapped, the associated entry in the PropertyIds field MUST be 0x0000.");
                    }
                }

                // !isPropertyNameExisting means the propertyNames don't exist on server
                // flags equals to 0x02 means the Flags parameter has the Create flag bit set.
                // The logon user is Administrator and the test suite will not reach the server-imposed limit on property ID 
                // mappings, so the user always has permission to create new entries and the server-imposed limit is not reached.
                // If all of these above condition are met, it means the returned id is newly assigned.
                if (!isPropertyNameExisting && (flags == 0x02))
                {
                    for (int i = 0; i < ropGetPropertyIdsFromNamesResponse.PropertyIds.Length; i++)
                    {
                        // Add the debug information
                        Site.Log.Add(LogEntryKind.Debug, "Verify MS-OXCPRPT_R534");

                        // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R534
                        Site.CaptureRequirementIfAreNotEqual<ushort>(
                            0xFFFF,
                            ropGetPropertyIdsFromNamesResponse.PropertyIds[i].ID,
                            534,
                            @"[In Processing RopGetPropertyIdsFromNames] [If the PropertyNameCount parameter is zero, and the RopGetPropertyIdsFromNames is acting on a Logon object, the server must enumerate all PropertyNames associated with property IDs] Otherwise, the server MUST, for each entry in the PropertyNames field of the ROP request buffer, follow this procedure: 4. [If the above conditions in step three are all met, a new property ID is registered for the named property.] The newly assigned property ID MUST NOT be equal to 0xFFFF.");

                        // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R535
                        Site.CaptureRequirementIfIsTrue(
                            ropGetPropertyIdsFromNamesResponse.PropertyIds[i].ID > 0x8000,
                            535,
                            @"[In Processing RopGetPropertyIdsFromNames] [If the PropertyNameCount parameter is zero, and the RopGetPropertyIdsFromNames is acting on a Logon object, the server must enumerate all PropertyNames associated with property IDs] Otherwise, the server MUST, for each entry in the PropertyNames field of the ROP request buffer, follow this procedure: 4. [If the above conditions in step three are all met, a new property ID is registered for the named property.]  The newly assigned property ID MUST be greater than 0x8000.");
                    }
                }

                // When the PropertyNames requested exist on the server, then the server can find the property ID associated with the PropertyName.
                if (isPropertyNameExisting)
                {
                    // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R531
                    // Whether the PropertyNames queried and the returned Ids are mapping has been verified and represented as the Boolean value isNameAndIdMapping.
                    Site.CaptureRequirementIfIsTrue(
                        isNamesAndIdsMapping,
                        531,
                        @"[In Processing RopGetPropertyIdsFromNames] [If the PropertyNameCount parameter is zero, and the RopGetPropertyIdsFromNames is acting on a Logon object, the server must enumerate all PropertyNames associated with property IDs] Otherwise, the server MUST, for each entry in the PropertyNames field of the ROP request buffer, follow this procedure: 2. Find the property ID registered for the named property that matches the PropertyName structure as follows: If the Kind field is set to 0x00, the named property has a LID that matches the value of the LID field of the PropertyName structure.");

                    for (int i = 0; i < ropGetPropertyIdsFromNamesResponse.PropertyIds.Length; i++)
                    {
                        if (Common.CompareByteArray(propertyNames[i].Guid, this.valuePSMAPI))
                        {
                            // Verify MS-OXCPRPT requirement: MS-OXCPRPT_R642
                            Site.CaptureRequirementIfAreEqual<ushort>(
                                (ushort)propertyNames[i].LID,
                                ropGetPropertyIdsFromNamesResponse.PropertyIds[i].ID,
                                642,
                                @"[In Processing RopGetPropertyIdsFromNames] [If the PropertyNameCount parameter is zero, and the RopGetPropertyIdsFromNames is acting on a Logon object, the server must enumerate all PropertyNames associated with property IDs] Otherwise, the server MUST, for each entry in the PropertyNames field of the ROP request buffer, follow this procedure: 1.  If the GUID field of the PropertyName structure ([MS-OXCDATA] section 2.6.1) in the ROP request buffer specifies the PS_MAPI property set, the returned property ID is obtained from the LID field.");
                        }
                    }
                }
            }

            // The parser has ensured the field satisfied the format, otherwise the response cannot be received.
            Site.CaptureRequirement(
                198,
                @"[In RopGetPropertyIdsFromNames ROP] The RopGetPropertyIdsFromNames ROP ([MS-OXCROPS] section 2.2.8.1) maps abstract, client-defined named properties to concrete 16-bit property IDs (of which 15 bits are significant).");
        }
        /// <summary>
        /// Set the value of properties identified by long ID or name in message.
        /// </summary>
        /// <param name="messageHandle">The specified message handle.</param>
        /// <param name="property">The PropertyName of specified property.</param>
        /// <param name="value">The value of specified property.</param>
        private void SetNamedProperty(uint messageHandle, PropertyNameObject property, byte[] value)
        {
            #region Call RopGetPropertyIdsFromNames to get property ID.
            PropertyName[] propertyNames = new PropertyName[1];
            propertyNames[0] = property.PropertyName;

            RopGetPropertyIdsFromNamesRequest getPropertyIdsFromNamesRequest = new RopGetPropertyIdsFromNamesRequest()
            {
                RopId = (byte)RopId.RopGetPropertyIdsFromNames,
                LogonId = CommonLogonId,
                InputHandleIndex = CommonInputHandleIndex,
                Flags = (byte)GetPropertyIdsFromNamesFlags.Create,
                PropertyNameCount = (ushort)propertyNames.Length,
                PropertyNames = propertyNames,
            };
            this.ResponseSOHs = this.MSOXCMSGAdapter.DoRopCall(getPropertyIdsFromNamesRequest, messageHandle, ref this.response, ref this.rawData, GetPropertiesFlags.None);
            RopGetPropertyIdsFromNamesResponse getPropertyIdsFromNamesResponse = (RopGetPropertyIdsFromNamesResponse)this.response;
            Site.Assert.AreEqual<uint>(TestSuiteBase.Success, getPropertyIdsFromNamesResponse.ReturnValue, "Call RopGetPropertyIdsFromNames should success.");
            #endregion

            #region Set property value.

            List<TaggedPropertyValue> taggedPropertyValues = new List<TaggedPropertyValue>();

            int valueSize = 0;
            PropertyTag propertyTag = new PropertyTag
            {
                PropertyId = getPropertyIdsFromNamesResponse.PropertyIds[0].ID,
                PropertyType = (ushort)property.PropertyType
            };
            TaggedPropertyValue taggedPropertyValue = new TaggedPropertyValue
            {
                PropertyTag = propertyTag,
                Value = value
            };
            valueSize += taggedPropertyValue.Size();
            taggedPropertyValues.Add(taggedPropertyValue);

            RopSetPropertiesRequest rpmSetRequest = new RopSetPropertiesRequest()
            {
                RopId = (byte)RopId.RopSetProperties,
                LogonId = CommonLogonId,
                InputHandleIndex = CommonInputHandleIndex,
                PropertyValueCount = (ushort)taggedPropertyValues.Count,
                PropertyValueSize = (ushort)(valueSize + 2),
                PropertyValues = taggedPropertyValues.ToArray()
            };
            this.ResponseSOHs = this.MSOXCMSGAdapter.DoRopCall(rpmSetRequest, messageHandle, ref this.response, ref this.rawData, GetPropertiesFlags.None);

            RopSetPropertiesResponse rpmSetResponse = (RopSetPropertiesResponse)this.response;
            Site.Assert.AreEqual<uint>(TestSuiteBase.Success, rpmSetResponse.PropertyProblemCount, "If ROP succeeds, the PropertyProblemCount of its response is 0(success).");
            #endregion
        }
        /// <summary>
        /// Create propertyName array
        /// </summary>
        /// <param name="propertyNameCount">The count of propertyName</param>
        /// <returns>Return propertyName array</returns>
        private PropertyName[] CreatePropertyNameArray(int propertyNameCount)
        {
            Guid newGUID;
            byte[] unicodeName;
            int lastIndex;

            PropertyName[] propertyNameArray = new PropertyName[propertyNameCount];

            for (int i = 0; i < propertyNameCount; i++)
            {
                newGUID = new Guid();
                propertyNameArray[i] = new PropertyName
                {
                    Kind = 0x01,
                    Guid = newGUID.ToByteArray()
                };

                // The property is identified by the Name field.

                // A Unicode (UTF-16) string, followed by two zero bytes as a null terminator
                // that identifies the property within its property set.
                unicodeName = Encoding.Unicode.GetBytes("ClientDefinedProperty" + i.ToString() + "x");

                lastIndex = unicodeName.Length - 1;
                unicodeName[lastIndex] = 0;
                unicodeName[--lastIndex] = 0;

                // 2-byte null terminator ALSO counted into NameSize.
                propertyNameArray[i].NameSize = (byte)unicodeName.Length;
                propertyNameArray[i].Name = unicodeName;
            }

            return propertyNameArray;
        }
        public void MSOXORULE_S04_TC02_AddModifyDeleteExtendedRule_OnPublicFolder()
        {
            this.CheckMAPIHTTPTransportSupported();

            #region TestUser1 logs on to the public folder.
            RopOpenFolderResponse openFolderResponse;
            RopLogonResponse logonResponse;
            bool ret = this.OxoruleAdapter.Connect(ConnectionType.PublicFolderServer, this.User1Name, this.User1ESSDN, this.User1Password);
            Site.Assert.IsTrue(ret, "connect to public folder server should be successful");
            uint publicFolderLogonHandler = this.OxoruleAdapter.RopLogon(LogonType.PublicFolder, this.User1ESSDN, out logonResponse);

            // Assert the client to log on to the public folder successfully.
            Site.Assert.AreEqual<uint>(0, logonResponse.ReturnValue, "Logon the public folder should be successful.");

            // Folder index 1 is the Interpersonal Messages subtree, and this is defined in MS-OXCSTOR.
            uint publicfolderHandler = this.OxoruleAdapter.RopOpenFolder(publicFolderLogonHandler, logonResponse.FolderIds[1], out openFolderResponse);

            // Get the store object's entry ID.
            this.GetStoreObjectEntryID(StoreObjectType.PublicFolder, this.Server, this.User1ESSDN);

            RopCreateFolderResponse createFolderResponse;
            string newFolderName = Common.GenerateResourceName(this.Site, Constants.FolderDisplayName);
            uint newFolderHandle = this.OxoruleAdapter.RopCreateFolder(publicfolderHandler, newFolderName, Constants.FolderComment, out createFolderResponse);
            ulong newFolderID = createFolderResponse.FolderId;
            Site.Assert.AreEqual<uint>(0, createFolderResponse.ReturnValue, "Creating folder operation should succeed.");
            #endregion

            #region TestUser1 creates an FAI message.
            RopCreateMessageResponse ropCreateMessageResponse;
            uint extendedRuleMessageHandle = this.OxoruleAdapter.RopCreateMessage(newFolderHandle, newFolderID, Convert.ToByte(true), out ropCreateMessageResponse);
            Site.Assert.AreEqual<uint>(0, ropCreateMessageResponse.ReturnValue, "Creating the first FAI message should succeed.");
            #endregion

            #region TestUser1 adds the extended rule with NamedProperty successfully.
            string ruleConditionSubjectNameForAdd = Constants.RuleConditionSubjectContainString;
            NamedPropertyInfo namedPropertyInfo = new NamedPropertyInfo
            {
                NoOfNamedProps = 2,
                PropId = new uint[2]
                {
                    0x8001, 0x8002
                }
            };
            PropertyName testPropertyName = new PropertyName
            {
                Guid = System.Guid.NewGuid().ToByteArray(),
                Kind = 0x01,
                Name = Encoding.Unicode.GetBytes(Constants.NameOfPropertyName + "\0")
            };

            // 0x01 means the property is identified by the name property.
            testPropertyName.NameSize = (byte)testPropertyName.Name.Length;

            PropertyName secondPropertyName = new PropertyName
            {
                Guid = System.Guid.NewGuid().ToByteArray(),
                Kind = 0x00,
                LID = 0x88888888
            };

            // 0x00 means the property is identified by the LID.
            namedPropertyInfo.NamedProperty = new PropertyName[2] { testPropertyName, secondPropertyName };
            namedPropertyInfo.NamedPropertiesSize = (uint)(testPropertyName.Serialize().Length + secondPropertyName.Serialize().Length);
            string ruleName = Common.GenerateResourceName(this.Site, Constants.ExtendRulename1);
            TaggedPropertyValue[] extendedRulePropertiesForAdd = AdapterHelper.GenerateExtendedRuleTestData(ruleName, 0, (uint)RuleState.ST_ENABLED, Constants.PidTagRuleProvider, ActionType.OP_MARK_AS_READ, new DeleteMarkReadActionData(), ruleConditionSubjectNameForAdd, namedPropertyInfo);

            // Set properties for extended rule FAI message.
            RopSetPropertiesResponse ropSetPropertiesResponse = this.OxoruleAdapter.RopSetProperties(extendedRuleMessageHandle, extendedRulePropertiesForAdd);
            Site.Assert.AreEqual<uint>(0, ropSetPropertiesResponse.ReturnValue, "Setting property for Extended rule FAI message should succeed.");

            // Save changes of message.
            RopSaveChangesMessageResponse ropSaveChangesMessagResponse = this.OxoruleAdapter.RopSaveChangesMessage(extendedRuleMessageHandle);
            Site.Assert.AreEqual<uint>(0, ropSaveChangesMessagResponse.ReturnValue, "Saving Extend rule message should succeed.");

            // Specify the properties to be got. 
            PropertyTag[] propertyTagArray = new PropertyTag[1];

            // PidTagRuleMessageProvider
            propertyTagArray[0].PropertyId = (ushort)PropertyId.PidTagRuleMessageProvider;
            propertyTagArray[0].PropertyType = (ushort)PropertyType.PtypString;

            // Get the specific properties of the extended rule.
            RopGetPropertiesSpecificResponse ropGetPropertiesSpecificResponse = this.OxoruleAdapter.RopGetPropertiesSpecific(extendedRuleMessageHandle, propertyTagArray);
            Site.Assert.AreEqual<uint>(0, ropGetPropertiesSpecificResponse.ReturnValue, "Getting specific properties operation should succeed.");
            string pidTagRuleMessageProviderData = AdapterHelper.PropertyValueConvertToString(ropGetPropertiesSpecificResponse.RowData.PropertyValues[0].Value);
            Site.Assert.AreEqual<string>(Constants.PidTagRuleProvider, pidTagRuleMessageProviderData, "The rule provider data should be RuleOrganizer.");
            #endregion

            #region Modify the created rule.
            ruleName = Common.GenerateResourceName(this.Site, Constants.ExtendRulename2);
            TaggedPropertyValue[] extendedRulePropertiesForModify = AdapterHelper.GenerateExtendedRuleTestData(ruleName, 0, (uint)RuleState.ST_ENABLED, Constants.PidTagRuleProvider, ActionType.OP_MARK_AS_READ, new DeleteMarkReadActionData(), ruleConditionSubjectNameForAdd, namedPropertyInfo);

            // Set properties for extended rule FAI message.
            ropSetPropertiesResponse = this.OxoruleAdapter.RopSetProperties(extendedRuleMessageHandle, extendedRulePropertiesForModify);
            Site.Assert.AreEqual<uint>(0, ropSetPropertiesResponse.ReturnValue, "Setting property for Extended rule FAI message should succeed.");

            // Save changes of message.
            ropSaveChangesMessagResponse = this.OxoruleAdapter.RopSaveChangesMessage(extendedRuleMessageHandle);
            Site.Assert.AreEqual<uint>(0, ropSaveChangesMessagResponse.ReturnValue, "Saving Extend rule message should succeed.");

            // PidTagSubject
            propertyTagArray[0].PropertyId = (ushort)PropertyId.PidTagRuleMessageName;
            propertyTagArray[0].PropertyType = (ushort)PropertyType.PtypString;

            // Get the specific properties of the extended rule.
            ropGetPropertiesSpecificResponse = this.OxoruleAdapter.RopGetPropertiesSpecific(extendedRuleMessageHandle, propertyTagArray);
            Site.Assert.AreEqual<uint>(0, ropGetPropertiesSpecificResponse.ReturnValue, "Getting specific properties operation should succeed.");
            string messageName = AdapterHelper.PropertyValueConvertToString(ropGetPropertiesSpecificResponse.RowData.PropertyValues[0].Value);
            Site.Assert.AreEqual<string>(ruleName, messageName, "The rule subject should be {0}.", ruleName);
            #endregion

            #region Release the created message to delete the created rule.
            this.OxoruleAdapter.ReleaseRop(extendedRuleMessageHandle);

            // Get the specific properties of the extended rule.
            ropGetPropertiesSpecificResponse = this.OxoruleAdapter.RopGetPropertiesSpecific(extendedRuleMessageHandle, propertyTagArray);
            Site.Assert.IsNull(ropGetPropertiesSpecificResponse.RowData, "The property value of the extended rule should be null!");
            #endregion

            #region Delete the folder.
            RopDeleteFolderResponse deleteFolder = this.OxoruleAdapter.RopDeleteFolder(publicfolderHandler, newFolderID);
            Site.Assert.AreEqual<uint>(0, deleteFolder.ReturnValue, "Deleting folder should succeed.");
            #endregion
        }
        /// <summary>
        /// RopGetPropertyIdsFromNames implementation
        /// </summary>
        /// <param name="objHandle">This index specifies the location in the Server object handle table where the handle for the input Server object is stored</param>
        /// <param name="flags">8-bit flags structure. These flags control the behavior of this operation</param>
        /// <param name="propertyNames">List of PropertyName structures. This field specifies the property names requested.</param>
        /// <param name="needVerify">Whether need to verify the response.</param>
        /// <returns>Structure of RopGetPropertyIdsFromNamesResponse</returns>
        private RopGetPropertyIdsFromNamesResponse RopGetPropertyIdsFromNames(uint objHandle, byte flags, PropertyName[] propertyNames, bool needVerify)
        {
            this.rawDataValue = null;
            this.responseValue = null;
            this.responseSOHsValue = null;

            RopGetPropertyIdsFromNamesRequest getPropertyIdsFromNamesRequest;
            RopGetPropertyIdsFromNamesResponse getPropertyIdsFromNamesResponse;

            getPropertyIdsFromNamesRequest.RopId = (byte)RopId.RopGetPropertyIdsFromNames;
            getPropertyIdsFromNamesRequest.LogonId = LogonId;
            getPropertyIdsFromNamesRequest.InputHandleIndex = (byte)HandleIndex.FirstIndex;
            getPropertyIdsFromNamesRequest.Flags = flags;
            getPropertyIdsFromNamesRequest.PropertyNames = propertyNames;
            if (getPropertyIdsFromNamesRequest.PropertyNames != null)
            {
                getPropertyIdsFromNamesRequest.PropertyNameCount = (ushort)propertyNames.Length;
            }
            else
            {
                // GetPropertyIdsFromNamesRequest.PropertyNames is null, so getPropertyIdsFromNamesRequest.PropertyNames is 0x00.
                getPropertyIdsFromNamesRequest.PropertyNameCount = 0x00;
            }

            this.responseSOHsValue = this.ProcessSingleRop(getPropertyIdsFromNamesRequest, objHandle, ref this.responseValue, ref this.rawDataValue, RopResponseType.SuccessResponse);
            getPropertyIdsFromNamesResponse = (RopGetPropertyIdsFromNamesResponse)this.responseValue;
            if (needVerify)
            {
                this.Site.Assert.AreEqual((uint)RopResponseType.SuccessResponse, getPropertyIdsFromNamesResponse.ReturnValue, string.Format("RopGetPropertyIdsFromNames Failed! Error: 0x{0:X8}", getPropertyIdsFromNamesResponse.ReturnValue));
            }

            return getPropertyIdsFromNamesResponse;
        }