HOWTO: EWS: Update IsRead Flag for items using Exchange Web Services


First thing first, you cannot use Exchange Web Services (as of now) to update the IsRead flag for items other than MessageType. MessageType has IsRead property in place to update the MAPI property PR_MESSAGE_FLAGS. Product group is aware of this limitation and very soon we should see an fix for this. In the mean time if you like to have a solution in place for all items (what I call as workaround), you can use WebDAV to update the IsRead flag for all other items.

 

       //Check the item's message class to either use WebDAV or EWS to update the PR_MESSAGE_FLAGS (IsRead)
        public static bool SetReadFlag(ItemIdType itemId, ReadStatus status)
        {
            bool retVal = false;
            
            //Use EWS to set the IsRead flag if it's MessageType, else use WebDAV
            if (GetItem(itemId.Id).ItemClass.StartsWith("IPM.Note", StringComparison.OrdinalIgnoreCase))
                retVal = SetReadFlagForMessage(itemId, status);
            else
                retVal = WebDAV_SetIsRead(GetFlatspaceURI(itemId.Id), status);

            return retVal;
        }
       
        //WebDAV code to update the IsRead flag, it uses OWA's FlatspaceURI to update the property
        private static bool WebDAV_SetIsRead(string itemUrl, ReadStatus status)
        {
            // Variables.
            bool retVal = false;
            
            System.Net.HttpWebRequest Request;
            System.Net.WebResponse Response;
            
            string strBody = "";
            byte[] bytes = null;
            System.IO.Stream RequestStream;

            // Build the PROPPATCH request body.
            strBody = "<?xml version=\"1.0\"?>"
                    + "<a:propertyupdate xmlns:a=\"DAV:\" xmlns:d=\"urn:schemas-microsoft-com:exch-data:\" "
                    + "xmlns:b=\"urn:schemas:httpmail:\" xmlns:c=\"xml:\">"
                    + "<a:set><a:prop><b:read>" + Convert.ToInt32((status == ReadStatus.Read ? true : false))
                    + "</b:read></a:prop>"
                    + "</a:set></a:propertyupdate>";

            
            // Create the HttpWebRequest object.
            Request = (System.Net.HttpWebRequest)HttpWebRequest.Create(itemUrl);

            // Add the network credentials to the request.
            Request.Credentials = ExchangeBinding.CurrentInstance.Credentials;

            // Specify the method.
            Request.Method = "PROPPATCH";

            // Encode the body using UTF-8.
            bytes = Encoding.UTF8.GetBytes((string)strBody);

            // Set the content header length.  This must be
            // done before writing data to the request stream.
            Request.ContentLength = bytes.Length;

            // Get a reference to the request stream.
            RequestStream = Request.GetRequestStream();

            // Write the XML body to the request stream.
            RequestStream.Write(bytes, 0, bytes.Length);

            // Close the Stream object to release the connection
            // for further use.
            RequestStream.Close();

            // Set the content type header.
            Request.ContentType = "text/xml";

            // Send the PROPPATCH method request and get the
            // response from the server.
            Response = (HttpWebResponse)Request.GetResponse();

            retVal = true;

            // Clean up.
            Response.Close();

            return retVal;

        }
        
        //EWS code to update the IsRead flag, *ONLY* for MessageType
        private static bool SetReadFlagForMessage(ItemIdType messageId, ReadStatus status)
        {

            SetItemFieldType setField = new SetItemFieldType();
            MessageType message = new MessageType();
            if (status == ReadStatus.UnRead)
                message.IsRead = false;
            else
                message.IsRead = true;
            message.IsReadSpecified = true;
            setField.Item1 = message;

            PathToUnindexedFieldType path = new PathToUnindexedFieldType();
            path.FieldURI = UnindexedFieldURIType.messageIsRead;
            setField.Item = path;


            ItemChangeType[] updatedItems = new ItemChangeType[1];
            updatedItems[0] = new ItemChangeType();
            updatedItems[0].Updates = new ItemChangeDescriptionType[1];
            updatedItems[0].Updates[0] = setField;

            ItemChangeDescriptionType[] updates = new ItemChangeDescriptionType[1];
            updates[0] = new ItemChangeDescriptionType();
            updates[0].Item = path;

            updatedItems[0].Item = new ItemIdType();
            ((ItemIdType)updatedItems[0].Item).Id = messageId.Id;
            ((ItemIdType)updatedItems[0].Item).ChangeKey = messageId.ChangeKey;


            UpdateItemType request = new UpdateItemType();
            request.ItemChanges = updatedItems;
            request.ConflictResolution = ConflictResolutionType.AutoResolve;
            request.MessageDisposition = MessageDispositionType.SaveOnly;
            request.MessageDispositionSpecified = true;
            request.SendMeetingInvitationsOrCancellations = CalendarItemUpdateOperationType.SendToChangedAndSaveCopy;
            request.SendMeetingInvitationsOrCancellationsSpecified = true;


            UpdateItemResponseType response = ExchangeBinding.CurrentInstance.UpdateItem(request);

            if (response.ResponseMessages.Items[0].ResponseClass != ResponseClassType.Success)
                return false;
            else
                return true;
        }

        //Get the Flatspace URI , which can be consumed by WebDav
        public static string GetFlatspaceURI(string itemId)
        {

            GetItemType getRequest = new GetItemType();

            getRequest.ItemIds = new ItemIdType[1];

            ItemIdType id = new ItemIdType();
            //
            id.Id = itemId;
            getRequest.ItemIds[0] = id;
            getRequest.ItemShape = new ItemResponseShapeType();
            getRequest.ItemShape.BaseShape = DefaultShapeNamesType.IdOnly;
            getRequest.ItemShape.AdditionalProperties = new PathToExtendedFieldType[1];

            PathToExtendedFieldType PR_FLAT_URL_NAME = new PathToExtendedFieldType();
            PR_FLAT_URL_NAME.PropertyTag = "0x670E";
            PR_FLAT_URL_NAME.PropertyType = MapiPropertyTypeType.String;

            getRequest.ItemShape.AdditionalProperties[0] = PR_FLAT_URL_NAME;
            GetItemResponseType response = ExchangeBinding.CurrentInstance.GetItem(getRequest);
            ResponseMessageType rmt = response.ResponseMessages.Items[0];

            Console.WriteLine(rmt.ResponseClass.ToString());
            if (rmt.ResponseCodeSpecified)
            {
                Console.WriteLine(rmt.ResponseCode);
            }

            ItemInfoResponseMessageType iirmt = rmt as ItemInfoResponseMessageType;
            ItemType item = iirmt.Items.Items[0];
            ExtendedPropertyType ept = item.ExtendedProperty[0];

            return ept.Item.ToString();
        }
 
Programming for Exchange 2007? You need Inside Microsoft Exchange 2007 Web Services
 
Comments (16)

  1. Jayakarthik says:

    You can easily update "0x0E07" using ExtendedProperties. No need of WebDav.

  2. Vikas - MSFT says:

    Can you share the sample with me, what version of exchange are you targetting… is it SP1 ?

  3. Jayakarthik says:

    mail.IsRead = true/false;

    mail.IsReadSpecified = true;

    Try setting "IsReadSpecified" as true along with IsRead. It should work with both RTM and SP1. If not,

    PathToExtendedFieldType result = new PathToExtendedFieldType();

    result.PropertyTag = "0x0E07";

    result.PropertyType = MapiPropertyTypeType.Integer;

    ExtendedPropertyType property = new ExtendedPropertyType();

    property.ExtendedFiledURI = result;

    property.Item = <value in string>;

    MessageType mail = new MessageType();

    :

    :

    // Set all the properties

    :

    :

    // Set the extended property

    mail.ExtendedProperty = new ExtendedPropertyType[] {property};

    :

    :

    // Call proxy.CreateItem() method.

    This way, you can avoid the second call.

  4. Vikas - MSFT says:

    I think you have missed something in my blog, my code target both MessageType and all other folder items. As of today both RTM & SP1 cannot update IsRead for anything other than MessageType.

    I have used WebDAV just to mark the read flag on items other than MessageType, for MessageType we can use EWS and I am doing it in the above sample

    SetItemFieldType setField = new SetItemFieldType();

               MessageType message = new MessageType();

               if (status == ReadStatus.UnRead)

                   message.IsRead = false;

               else

                   message.IsRead = true;

               message.IsReadSpecified = true;

               setField.Item1 = message;

  5. Jayakarthik says:

    Well, I missed it. Extended Property would work with ItemType as well.

  6. Vikas - MSFT says:

    You cannot set the PR_MESSAGE_FLAG as it is a computed property, and there is no option of IsRead property in ItemType… it is available only for MessageType

  7. Jayakarthik says:

    Not really… Check out David’s reply in http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=1842684&SiteID=17

    Vikas, in your case you can always build the values for computed properties. for eg.,

    Lets look at Appointment State.

    0x0001 – Appointment is a meeting

    0x0002 – Appointment has been received

    0x0004 – Appointment is cancelled

    0x0003 – Appointment is a meeting and has been recieved (Combination of 1 & 2).

    If you are able to set the property at the time of creation itself, why would you need the second call?

  8. Vikas - MSFT says:

    "If you are able to set the property at the time of creation itself, why would you need the second call?"

    I want to mark an *existing* item as read/unread, thats how this sample came.

    "This logic is in store and enshrined in MAPI docs – most bits in PR_MESSAGE_FLAGS cannot be set once SaveChanges has been called for the first time on a message."

  9. Jayakarthik says:

    Well, if that is the case you can use WebDAV to retrieve the URI. EWS is costly. It can do magic when it works with TNEF. Try out.

  10. Urvish says:

    Hello

    I am interested in updating mail properties / attributes before mail has been sent.

    Consider a case :OWA plug-in

    1)User open OWA

    2)create a new mail

    3)Click on send mail

    4) process mail object and update some of the properties based on business logic (i.e spell check tool)

    5) let mail send in normal flow

    let me know if EWS has specific methods  to add or update attributes of Mail item

  11. Vikas - MSFT says:

    EWS does not have such functionality you might want to look for OWA customizations in Exchange 2007 SP1

    References:

    http://msdn.microsoft.com/en-us/library/bb891803(EXCHG.80).aspx

  12. I’ve put together a list of articles which cover common questions on Exchange Web Services (EWS). These

  13. Mikhail says:

    what is the flatspace URI look like? (new to WEBDAV)

    I’m parsing that through webdav for individual messages.

    The requeststream returns a "This stream does not support seek operations"

    I used itemURL= "http://xxxx.yyy.com/exchange/userid/inbox/youremailsubject.eml&quot;

  14. Nitin Motwani says:

    Hi All,

    I was wondering if EWS coulf be used to set MAPI attributes such as PR_CONVERSATION_INDEX in an outgoing email item. If this is at all possible I would greatly appreciate if someone can explain how I can go about doing this

    Thanks.

  15. Dennis says:

    Thanx man!

    Your code was a big help te me.

Skip to main content