Freigeben über


Saving an email as "Draft" in the Drafts folder

Have you ever tried to save a email into the Drafts folder using WebDAV? I followed the KB 313128 and altered the code to just save the email to the drafts folder. When I tried to open the email from the Drafts folder using Outlook Web Access(2003) or Outlook it appeared as a "Sent" email although I had not sent it by moving it to the DavMailSubmissionURI. Digging down deeper when I compared the properties of the mail that I saved as a Draft from Outlook and the one I created using WebDAV, here is what I found:

Message that I had created with WebDAV had the value of the PR_MESSAGE_FLAGS set to MSGFLAG_READ and the message that was created using Outlook or Outlook Web Access had MSGFLAG_READ | MSGFLAG_UNSENT. Unfortunately the PR_MESSAGE_FLAGS is a property that is initialized by the client or message store provider when a message is created and saved for the first time and then updated periodically by the message store provider, a transport provider, and the MAPI spooler as the message is processed and its state changes. There is no way we can change the value of the property after it is saved. More details can be found at the link below.

PR_MESSAGE_FLAGS
https://msdn.microsoft.com/en-us/library/ms527629(EXCHG.10).aspx

My objective was to be able to save the message as a "Draft" in the "Drafts" folder. Not wasting any more time, I started to try various options to fix the problem and get my code to work as expected. My findings:

The KB 313128 has a variable name "sQuery" which is initialized with the MIME and email body for the "Put" request. This is how is looks like in the article:

string sQuery;
DateTime mySentTime = new DateTime();
sQuery = "From: " + strFrom + "\n" +
"To: " + strTo + "\n" +
"Subject: " + strSubject + "\n" +
"Date: " + DateTime.Now.ToString() + "\n" +
"X-Mailer: My DAV mailer" + "\n" +
"MIME-Version: 1.0" + "\n" +
"Content-Type: text/plain;" + "\n" +
"Charset = \"iso-8859-1\"" + "\n" +
"Content-Transfer-Encoding: 7bit" + "\n" + "\n" +
strBody;

The only way I could get the code to work was to set the sQuery variable to ""(Blank) and then do a PROPPATCH to set the To, Subject and Body.

 private void SaveToDrafts()
{
try
   {
       // TODO: Replace with the name of the computer that is running Exchange 2000.
       string strServer = "ExchServe";
       // TODO: Replace with the sender's alias.
       string strSenderAlias = "sender";
       // TODO: Replace with the recipient's e-mail address.
       string strTo = "recipient@example.com";
       
       string strSubject = "Save to Drafts using HttpWebRequest";
       string strBody = "Hello World";

       string sUri;
       sUri = "https://" + strServer + "/Exchange/" + strSenderAlias;
       sUri = sUri + "/Drafts/Testxxxxxxxxx.eml";

       System.Uri myUri = new System.Uri(sUri);
       HttpWebRequest HttpWRequest = (HttpWebRequest)WebRequest.Create(myUri);

       string sQuery="";
    
       // Set the credentials.
       NetworkCredential myCred = new NetworkCredential(@"DomainName\User", "Password");
       CredentialCache MyCredentialCache = new CredentialCache();
       MyCredentialCache.Add(myUri, "Basic", myCred);

       HttpWRequest.Credentials = MyCredentialCache;

       // Set the headers.
       HttpWRequest.Headers.Add("Translate", "f");
       HttpWRequest.ContentType = "message/rfc822";
       HttpWRequest.ContentLength = sQuery.Length;

       //Set the request timeout to 5 minutes.
       HttpWRequest.Timeout = 300000;
       // Set the request method.
       HttpWRequest.Method = "PUT";

       // Store the data in a byte array.
       byte[] ByteQuery = System.Text.Encoding.ASCII.GetBytes(sQuery);
       HttpWRequest.ContentLength = ByteQuery.Length;
       Stream QueryStream = HttpWRequest.GetRequestStream();
    
       // write the data to be posted to the Request Stream
       QueryStream.Write(ByteQuery, 0, ByteQuery.Length);
       QueryStream.Close();

       // Send the request and get the response.
       HttpWebResponse HttpWResponse = (HttpWebResponse)HttpWRequest.GetResponse();

       // Get the Status code.
       int iStatCode = (int)HttpWResponse.StatusCode;
       
       // Get the request headers.
       string sReqHeaders = HttpWRequest.Headers.ToString();
       
       // Read the response stream.
       Stream strm = HttpWResponse.GetResponseStream();
       StreamReader sr = new StreamReader(strm);
       string sText = sr.ReadToEnd();
       MessageBox.Show("Response:"+ sText);

       // Close the stream.
       strm.Close();

       // Clean up.
       myCred = null;
       MyCredentialCache = null;
       HttpWRequest = null;
       HttpWResponse = null;
       QueryStream = null;
       strm = null;
       sr = null;

       SetToSubjectBody(sUri);
   }
   catch (Exception ex)
   {
       MessageBox.Show(ex.Message);
   } 
}

private void SetToSubjectBody(string itemURL)
{
    HttpWebRequest request;
    HttpWebResponse response;
    byte[] bytes;

    string format = "<?xml version=\"1.0\"?><g:propertyupdate xmlns:g=\"DAV:\" xmlns:m=\"urn:schemas:httpmail:\" xmlns:z=\"urn:schemas:mailheader:\">"
            + "<g:set>"
            + "<g:prop>"
            + "<z:to>a@a.com</z:to>"
            + "<m:subject>This is a test mail</m:subject>"
            + "<m:textdescription>This is a test mail</m:textdescription>"
            + "</g:prop>"
            + "</g:set>"
            + "<g:remove>" 
            + "<g:prop><m:date/></g:prop>"
            + "</g:remove>" 
            + "</g:propertyupdate>";

    request = (HttpWebRequest)HttpWebRequest.Create(itemURL);

    // Set the credentials.
    NetworkCredential myCred = new NetworkCredential(@"DomainName\User", "Password");
    CredentialCache MyCredentialCache = new CredentialCache();
    MyCredentialCache.Add(new System.Uri(itemURL), "Basic", myCred);
    
    request.Credentials = MyCredentialCache;

    // Set the headers.
    request.ContentType = "text/xml";
    request.Method = "PROPPATCH";

    // Store the data in a byte array.
    bytes = Encoding.UTF8.GetBytes(format);
    request.ContentLength = bytes.Length;

    // Get the Request Stream
    using (Stream requestStream = request.GetRequestStream())
    {
        // write the data to be posted to the Request Stream
        requestStream.Write(bytes, 0, bytes.Length);
        requestStream.Close();
    }

    // Send the request and get the response.
    response = (HttpWebResponse)request.GetResponse();

    // Get the Status code.
    int intStatus = (int)response.StatusCode;
    if (intStatus >= 200 && intStatus < 300)
    {
        MessageBox.Show("Success!");
    }
    else
    {
        MessageBox.Show("Failure Code:" + intStatus.ToString());
    }

    response.Close();

    // Clean up.
    myCred = null;
    MyCredentialCache = null;
    response = null;
    request = null;
}

To me it looks like a change in the Exchange 2003 transport that broke this, but now that I have a workaround I am GOOD!

Comments

  • Anonymous
    September 15, 2010
    This is a nice article Akash. You almost saved my Job :)