Arturito.net

Come to The Dark Side, We Have Cookies!

C# Saving Email Attachments in Exchange using WebDAV

with 9 comments

I’ve been trying to find a good way to download messages from Exchange without using CDO or MAPI as the program was  going to run on the server which doesn’t have Outlook installed and admin didn’t want to have it there. I’ve been looking at WebDAV and I sucessfully managed to search emails and extract attachment names. However I failed to download them. I’ve searched a lot on the Internet but I could not find any working solution. I posted qustion on StackOverflow http://stackoverflow.com/questions/2491595/downloading-attachments-from-exchange-with-webdav but I didn’t get any reply so far.

In desperation I tried this library http://www.independentsoft.de/webdavex/index.html and it works pretty well. The only problem is the library is not free.

I ended up using code below.

Exchange version: 2003
Tool: Visual Studio 2008
Language: C#

Important: I have used this code in the program which is accessing Exchange server within Local Network Area.
I have no idea whether it works for remote connections or not. But it should be :)

public WebDAV(string mailboxURI,string user, string password, string domain)
{
this.strUserName = user;
this.strPassword = password;
this.strDomain = domain;
this.strMailboxURI = mailboxURI;
this.myCred = new NetworkCredential(this.strUserName, this.strPassword, this.strDomain);
}

....

// emlURL - http//server//Exchange/MailboxName/Inbox/SomeMessage.EML/SomeAttachment.xls
// fileName - new filename
// path - local path where file will be saved
// datareceived - optional I normally stick email date here

private void DownloadSingleAttachment(string emlURI, string fileName, string path, string datereceived)
{
// Create our request object
HttpWebRequest Req;
Req = (HttpWebRequest)WebRequest.Create(emlURI);

try
{
// Use Basic Authentication
CredentialCache myCredentialCache = new CredentialCache();
myCredentialCache.Add(new System.Uri(emlURI), "NTLM", myCred);
Req.Credentials = myCredentialCache;</code>

//Add the headers
Req.Headers.Add("Translate", "f");
Req.KeepAlive = true;
Req.AllowAutoRedirect = false;
Req.Method = "GET";

// Get the response for our request
HttpWebResponse Resp = (HttpWebResponse)Req.GetResponse();

switch (Resp.ContentType)
{
// add here whatever you need
case "message/rfc822":
case "application/vnd.ms-excel":
case "application/octet-stream":
case "application/pdf":
case "application/msword":
case "application/zip":

Stream stm = Resp.GetResponseStream();

// create file

FileStream file = new FileStream(path+fileName, System.IO.FileMode.Create);
byte[] buffer = new byte[4096];
int length;
length = smt.Read(buffer, 0, 4096);
while (length &amp;gt; 0)
{
file.Write(buffer, 0, length);
length = stm.Read(buffer, 0, 4096);
}
file.Close();
stm.Close();

break;
case "text/plain":
case "text/xml":

StreamReader sr = new StreamReader(Resp.GetResponseStream());
string strResp = sr.ReadToEnd();
sr.Close();

StreamWriter sw = new StreamWriter(fileName);
sw.Write(strResp);
sw.Close();

break;
}
Resp.Close();
}
catch (Exception)
{
throw;
}
}

Code I have tried and didn’t work for me.
I recieved this error:

The remote server returned an error:  Forbidden.
static void Main(string[] args)
{

HttpWebRequest Request;
WebResponse Response;
CredentialCache MyCredentialCache;
string attachment = "http://mailserver/Exchange/Username/Inbox/Test.EML/Test.txt";
string strUserName = "username";
string strPassword = "password";
string strDomain = "domain";

try
{
//using  HttpWebRequest
MyCredentialCache = new System.Net.CredentialCache();
MyCredentialCache.Add(new System.Uri(attachment), "NTLM", new NetworkCredential(strUserName, strPassword, strDomain));

Request = (HttpWebRequest)HttpWebRequest.Create(attachment);
Request.Credentials = MyCredentialCache;
Request.Method = "GET";
Response = (HttpWebResponse)Request.GetResponse();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}

try
{
//using Web Client
string downloadPath = "D:\\Downloads";

WebClient wcClient = new WebClient();
wcClient.Credentials = new NetworkCredential(strUserName, strPassword, strDomain);
string file = Path.GetFileName(attachment);
string filename = Path.Combine(downloadPath, file);
wcClient.DownloadFile(attachment, filename);
}

catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
Console.ReadLine();
 }

If anyone would like to see the rest of this class, which searches emails and scans for attachements I can post it here but it is pretty much the same code as in documentation on Microsoft website.

Hope that someone will find it useful. :)

Written by guru

March 26th, 2010 at 5:45 pm

Posted in C sharp

  • Pingback: Accesing shared mailbox in Exchange Web Services 2007 Service Pack 1 and downloading attachments with C# « Arturito.net

  • Michael

    This was a great article. Finally got me headed in the right direction. I really need to see the code which searches emails and scans for attachements. You can email me directly if you prefer. moconnor@directalliance.com

  • arturito

    Micheal – I have sent you an email with code samples. Good Luck!

  • Michael

    Ok folks I have taken the awesome example presented here and added to it. Now the application can Append the Subject line to know what was processed and record the data to the database using owa.

    This goes in the WebDav.cs class.
    public void AppendSubject(string URI)
    {
    XMLHTTP30 oXMLHttp = new XMLHTTP30();
    string sUrl;
    string sQuery;
    string modSubject = “COMPLETED”;

    sUrl = URI;

    //Open the Item
    oXMLHttp.open(“PROPPATCH”, sUrl, false, “url\\target”, “pass”);

    //Set up the query to modify the subject
    sQuery = “” +
    ” +
    ” +
    “” + modSubject + “” +
    ” +
    ““;

    //Set up request headers
    oXMLHttp.setRequestHeader(“Content-Type”, “text/xml”);

    try
    {
    //Send the query
    oXMLHttp.send(sQuery);
    }
    catch (Exception)
    {
    throw;
    }
    finally
    {
    //Cleanup
    oXMLHttp = null;
    }
    }

    Also in the WebDav class I added to this method. I am using a Stored Procedure to do the Insert. You should be able to follow the code to reproduce that SP.

    public void RecordData(string fileName)
    {
    //Get data from files
    Application oXL;
    Workbook oWB = null;
    Worksheet oSheet;
    Range oRng;

    try
    {
    //create an Application object
    oXL = new ApplicationClass();

    //get WorkBook object
    oWB = oXL.Workbooks.Open(fileName, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value,
    Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value,
    Missing.Value, Missing.Value);

    //get WorkSheet object
    oSheet = (Microsoft.Office.Interop.Excel.Worksheet)oWB.Sheets[1];
    System.Data.DataTable dt = new System.Data.DataTable(“dtExcel”);
    DataSet ds = new DataSet();
    ds.Tables.Add(dt);
    DataRow dr;

    StringBuilder sb = new StringBuilder();
    int jValue = oSheet.UsedRange.Cells.Columns.Count;
    int iValue = oSheet.UsedRange.Cells.Rows.Count;

    //get data columns
    for (int j = 1; j <= jValue; j++)
    {
    dt.Columns.Add(“column” + j, System.Type.GetType(“System.String”));
    }

    //get data in cells
    for (int i = 1; i <= iValue; i++)
    {
    dr = ds.Tables["dtExcel"].NewRow();
    for (int j = 1; j <= jValue; j++)
    {
    oRng = (Microsoft.Office.Interop.Excel.Range)oSheet.Cells[i, j];
    string strValue = oRng.Text.ToString();
    dr["column" + j] = strValue;
    }
    ds.Tables["dtExcel"].Rows.Add(dr);
    }

    //Vars for DB insert
    string accountNumber = String.Empty;
    string fName = String.Empty;
    string lName = String.Empty;
    string email = String.Empty;
    string productCode = String.Empty;
    string productName = String.Empty;
    string salesRepCode = String.Empty;
    string repFName = String.Empty;
    string repLName = String.Empty;
    string repPhone = String.Empty;
    string repExt = String.Empty;
    string repEmail = String.Empty;
    string channelUser = String.Empty;
    string channelUserEmail = String.Empty;
    string invoiceNum = String.Empty;
    string campCreativeCode = String.Empty;
    string campCreativeSourceCode = String.Empty;
    DateTime createDate = new DateTime();
    string result = String.Empty;
    string sSQL = String.Empty;

    //Insert Values into DB
    for (int i = 1; i < ds.Tables["dtExcel"].Rows.Count + 1; i++ )
    {
    //Set up DB access
    SqlConnection conn = new SqlConnection(“Data Source=Server;Initial Catalog=DB;user id=sa; password=pass”);
    SqlCommand cmd = new SqlCommand(“dbo.uspIns_EmailAttachmentInfo”, conn);

    cmd.CommandType = CommandType.StoredProcedure;
    conn.Open();

    accountNumber = ds.Tables["dtExcel"].Rows[i][0].ToString();
    fName = ds.Tables["dtExcel"].Rows[i][1].ToString();
    lName = ds.Tables["dtExcel"].Rows[i][2].ToString();
    email = ds.Tables["dtExcel"].Rows[i][3].ToString();
    productCode = ds.Tables["dtExcel"].Rows[i][4].ToString();
    productName = ds.Tables["dtExcel"].Rows[i][5].ToString();
    salesRepCode = ds.Tables["dtExcel"].Rows[i][6].ToString();
    repFName = ds.Tables["dtExcel"].Rows[i][7].ToString();
    repLName = ds.Tables["dtExcel"].Rows[i][8].ToString();
    repPhone = ds.Tables["dtExcel"].Rows[i][9].ToString();
    repExt = ds.Tables["dtExcel"].Rows[i][10].ToString();
    repEmail = ds.Tables["dtExcel"].Rows[i][11].ToString();
    channelUser = ds.Tables["dtExcel"].Rows[i][12].ToString();
    channelUserEmail = ds.Tables["dtExcel"].Rows[i][13].ToString();
    invoiceNum = ds.Tables["dtExcel"].Rows[i][14].ToString();
    campCreativeCode = ds.Tables["dtExcel"].Rows[i][15].ToString();
    campCreativeSourceCode = ds.Tables["dtExcel"].Rows[i][16].ToString();
    createDate = Convert.ToDateTime(ds.Tables["dtExcel"].Rows[i][17].ToString());
    result = ds.Tables["dtExcel"].Rows[i][18].ToString();

    cmd.Parameters.Add(new SqlParameter(“@accountNumber”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “AccountNumber”, DataRowVersion.Current, accountNumber));
    cmd.Parameters.Add(new SqlParameter(“@fName”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “FirstName”, DataRowVersion.Current, fName));
    cmd.Parameters.Add(new SqlParameter(“@lname”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “LastName”, DataRowVersion.Current, lName));
    cmd.Parameters.Add(new SqlParameter(“@email”, SqlDbType.VarChar, 50, ParameterDirection.Input, false, 0, 0, “EmailAddress”, DataRowVersion.Current, email));
    cmd.Parameters.Add(new SqlParameter(“@productCode”, SqlDbType.VarChar, 50, ParameterDirection.Input, false, 0, 0, “ProductCode”, DataRowVersion.Current, productCode));
    cmd.Parameters.Add(new SqlParameter(“@productName”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “ProductName”, DataRowVersion.Current, productName));
    cmd.Parameters.Add(new SqlParameter(“@repCode”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “SalesRepCode”, DataRowVersion.Current, salesRepCode));
    cmd.Parameters.Add(new SqlParameter(“@repFName”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “SalesRepFirstName”, DataRowVersion.Current, repFName));
    cmd.Parameters.Add(new SqlParameter(“@repLName”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “SalesRepLastName”, DataRowVersion.Current, repLName));
    cmd.Parameters.Add(new SqlParameter(“@repPhone”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “SalesRepPhone”, DataRowVersion.Current, repPhone));
    cmd.Parameters.Add(new SqlParameter(“@repExt”, SqlDbType.VarChar, 6, ParameterDirection.Input, false, 0, 0, “SalesRepExt”, DataRowVersion.Current, repExt));
    cmd.Parameters.Add(new SqlParameter(“@repEmail”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “SalesRepEmail”, DataRowVersion.Current, repEmail));
    cmd.Parameters.Add(new SqlParameter(“@channelUser”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “ChannelUser”, DataRowVersion.Current, channelUser));
    cmd.Parameters.Add(new SqlParameter(“@channelUserEmail”, SqlDbType.VarChar, 50, ParameterDirection.Input, false, 0, 0, “ChannelUserEmail”, DataRowVersion.Current, channelUserEmail));
    cmd.Parameters.Add(new SqlParameter(“@invoiceNum”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “InvoiceNumber”, DataRowVersion.Current, invoiceNum));
    cmd.Parameters.Add(new SqlParameter(“@campCreativeCode”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “CampaignCreativeCode”, DataRowVersion.Current, campCreativeCode));
    cmd.Parameters.Add(new SqlParameter(“@campCreativeSourceCode”, SqlDbType.VarChar, 20, ParameterDirection.Input, false, 0, 0, “CampaignCreativeSourceCode”, DataRowVersion.Current, campCreativeSourceCode));
    cmd.Parameters.Add(new SqlParameter(“@createDate”, SqlDbType.DateTime, 22, ParameterDirection.Input, false, 0, 0, “CreateDate”, DataRowVersion.Current, createDate));
    cmd.Parameters.Add(new SqlParameter(“@results”, SqlDbType.VarChar, 8000, ParameterDirection.Input, false, 0, 0, “Results”, DataRowVersion.Current, result));

    try
    {
    cmd.ExecuteNonQuery();
    }
    catch (Exception)
    {
    throw;
    }
    finally
    {
    conn.Close();
    }
    }
    }
    catch (Exception)
    {
    throw;
    }

    oWB.Application.ActiveWorkbook.Close(false, fileName, false);
    }

  • Bablo

    Can you please email me the code too at my email

    • arturito

      Since some people have been asking it, I will publish it here.

  • Lamees

    Hi,
    Can you please send the code sample that scans the mail for attachments to my email as well? I didn’t find it posted

  • mj

    I’d also love to take a look at the full sample code if its available please!

  • arturito