Welcome to MSDN Blogs Sign in | Join | Help

HOWTO: Set and Get the a multibyte (japanese, chinese) custom header with CDOSYS

Custom headers in multibyte languages (Japanese, Chinese, etc) will not be encoded
properly when set using CDO alone. They may appear to be correctly set, however
when read back on a recieved message they may be messed-up.

Here is an example of a Japanese header:

I had a case where a customer wanted to read an iso-2022-jp header. This header is custom header and this header is in BodyPart of a message.

On a CDO.Message (obj) which is being sent:

' Append the extended header
    obj.Fields.Append _
        "urn:schemas:mailheader:X-Custom-header",_
        adBSTR, 256, , "???" ' Japanese Character
    obj.Fields.Update

On receiver site, you might get a custom header looking like this:
x-custom-header: =?iso-2022-jp?B?GyRCJUYlOSVIGyhC?=

The the recieved header will not be understandable or convertable to a usable format.

This is by design. X-headers are accessible only through the urn:schemas:mailheader namespace, and we do not decode headers returned through that namespace. It is documented this way in MSDN the reference is:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_schema_mailheader.asp:

Each field value (with a few exceptions) is stored as US-ASCII characters and is identical to the ASCII string found in the message stream. Non US-ASCII characters are encoded according to the RFC 1522 specification. No conversion is performed when the property value is set or updated. An application that sets the raw message header property must RFC 1522-encode non US-ASCII characters or the header value will be corrupted.

This comes down to the fact that the non-ASCII (multibyte) header text will need to be encoded into a BASE 64 string (to make it RFC 1522 compliant). Encoding of the text will need to be done when the email is being composed and decoding of the text when the header is being read back.

Here are a couple ways to work around this problem:

  1) Use a header field that has an httpmail equivalent to do the decoding:

      set m = createobject("cdo.message")
      m.fields("urn:schemas:mailheader:subject") = theEncodedString
      theDecodedString = m.fields("urn:schemas:httpmail:subject")


      2) Use or write a base64 decoder to extract the 2022-jp text from the header.

          a) With .NET you can use Convert.FromBase64String to convert the header.
 
          b) Use the example from:
              Sample Base 64 Encoding and Decoding - C++
              http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B191239


             c) Find vb/vbs code by searching the net using the following string:
                  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

Please read the following for an understanding on base 64 encoding:
XCON: A Description of Base64 MIME Content Transfer Encoding
http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B323489

WebDAV: XML DOM vs managed classes.

I've seen a lot of issues with using the MSXML DOM when trying to get credentials from the owaauth.dll for FBA authentication.  The managed classes seem to work all the time and in all versions. 

However, the MSXML DOM wont always reuturn cookies and sometimes just plain fails in the calls.  Each version of MSXML has an http method at the version of the MSXML component and versions of that method which are for each prior verion of MSXML.  You will find that you will get varrying mixed results per each of these methods, per version and per service pack of MSXML.  Because of this, you cannot be sure that the same code using MSXML will work on another box if it is used to go against the owaauth.dll   Also, note that you really should be using the managed classes under .NET and not the XMLDOM.

On a side note, if you are going against Exchange 2007, you really need to think hard of why you are using FBA authentication.  People have been using FBA authentication for WebDAV against Exchange 2000 and 2003 for one reason - to work around the issue of FBA being turned on for OWA, which casuses WebDAV to only be able to authentication using FBA (unless one of two specific work-arounds are used).  With Exchange 2007, the virual folder and its settings are split-up and seperate for OWA and WebDAV so that one will not affect the other.  This means that WebDAV can authenticate using Basic Authentication instead of FBA even if its turned-on for OWA.


 How WebDAV - Use Basic Authentication with WebDAV even when FBA is enabled.
 http://blogs.msdn.com/webdav_101/archive/2008/02/01/how-webdav-use-basic-authentication-with-webdav-even-when-fba-is-enabled.aspx


 

My .EDB file grows when I search.

When doing a WebDAV or any other search, you will likely see the .EDB file expands.

When performing a search against a mailbox using WebDAV or any other API, you may see the .EDB file grow in size.  Don't panic, this is normal.  When a search is performed, Exchange will put the results into a search folder.  When the same query is re-issued, the results can be reused.  Also, some properties may get promoted from the .stm to the .edb when requested in a search.
 

    Exchange Storage Architecture
    http://technet.microsoft.com/en-us/library/bb124808.aspx

    Private Information Store log files grow when a user performs a search in OWA
    http://support.microsoft.com/kb/811650/en-us

 

Howto: Add a body part to a message using CDOSYS.

The sample below shows how to add an additional body part to a message using cdosys.  This example demonstrates adding a "text/calendar" (vcalendar) body part, however it should be possible to use this to add "text/plain", "text/html" and other types.  In the sample, "this.VCalendarText" holds the text to be written to the body part (in this case its a VCalendar meeting request.


//============================ VCalendar ==============================
if (this.AddVCalendar == true)
{
    oMsg.BodyPart.ContentClass = this.ContentClass;
    oMsg.BodyPart.ContentMediaType = this.ContentType;

    CDO.IBodyPart iBpVCalendar = oMsg.BodyPart.AddBodyPart(-1);

    //oMsg.BodyPart.Fields["urn:schemas:mailheader:content-type"].Value = "multipart/alternative";
    iBpVCalendar.ContentClass = "urn:content-classes:calendarmessage";
    iBpVCalendar.Fields["urn:schemas:mailheader:content-type"].Value = "text/calendar;method=REQUEST;name=\"meeting.ics\"";
    iBpVCalendar.Fields["urn:schemas:mailheader:content-transfer-encoding"].Value = "8bit";
    iBpVCalendar.Fields.Update();

    // ----------  Write VCALENDAR -------------------------
    ADODB.Stream stm = null;
    stm = iBpVCalendar.GetDecodedContentStream();
    //stm.Write(this.VCalendarText);
    stm.WriteText(this.VCalendarText, ADODB.StreamWriteEnum.stWriteLine);
    stm.Flush();
    stm.Close();
    oMsg.BodyPart.Fields.Update();

    // ----------  Clean-up -------------------------
    System.Runtime.InteropServices.Marshal.ReleaseComObject(iBpVCalendar);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(stm);
    iBpVCalendar = null;
    stm = null;
}

Here is an example of what might be written to the body part. Please note that sending meeting requests with a custom created meeting request is not supported, however I'm including this for completeness.
 
Content-class: urn:content-classes:calendarmessage
Content-Type: text/calendar;
 method=REQUEST;
 name="meeting.ics"
Content-Transfer-Encoding: 8bit

BEGIN:VCALENDAR
METHOD:REQUEST
BEGIN:VEVENT
DTSTAMP:20080325T202857Z
DTSTART:20080325T200000Z
DTEND:20080325T220000Z
SUMMARY:Test meeting request
UID:040000008200E00074C5B7101A82E00800000000B2BB07349575C80100000000000000001000000019BF8D0149C50643A81325C54140C093
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="Myf":MAIL
 TO:myfriend@myserver.mycompany.com
ORGANIZER;CN="Me Myself":MAILTO:memyself@myserver.mycompany.com
LOCATION: Here
DESCRIPTION:Test Request\N
SEQUENCE:0
PRIORITY:5
CLASS:
CREATED:20080321T190958Z
STATUS:CONFIRMED
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

Howto: Add a body part using System.Net.Mail

The sample below shows how to add an additional body part to a message using System.Net.Mail.  This example demonstrates adding a "text/calendar" (vcalendar) body part, however it should be possible to use this to add "text/plain", "text/html" and other types.  In the sample, "this.VCalendarText" holds the text to be written to the body part (in this case its a VCalendar meeting request.

//=======================================================================
// Set_SSNM_BodyPart_VCalendar
//
// oMsg  - the message to modify.
//
// sBody - can be any body - text, html, vcalendar.
//=======================================================================
public bool Set_SSNM_BodyPart_VCalendar(ref System.Net.Mail.MailMessage oMsg, string sVCalendar)
{
    bool bRet = false;
    bRet = AddMessageAlternateView(ref oMsg, sVCalendar, "text/calendar");
    return bRet;
}

//============================================================================
// AddMessageAlternateView
//
// oMsg  - the message to modify.
//
// sBody - can be any body - text, html, vcalendar.
//
// Content Types:
//      Text:  "text/plain"
//      HTML:  "text/html"
//      VCalendar:  "text/calendar"
//============================================================================
public bool AddMessageAlternateView(ref System.Net.Mail.MailMessage oMsg, string sBody, string sContentType)
{
    bool bRet = false;
    try
    {
        AlternateView avAlternateView = null;     
        System.Net.Mime.ContentType ctContentType = new System.Net.Mime.ContentType(sContentType);
        avAlternateView = AlternateView.CreateAlternateViewFromString(sBody, ctContentType);
        oMsg.AlternateViews.Add(avAlternateView);
        bRet = true;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.InnerException.ToString(), "Error Adding Alternate View for \"" + sContentType + "\"", MessageBoxButtons.OK);
        bRet = false;
    }

    return bRet;
}

 

Here is an example of what might be written to the body part. Please note that sending meeting requests with a custom created meeting request is not supported, however I'm including this for completeness.
 
Content-class: urn:content-classes:calendarmessage
Content-Type: text/calendar;
 method=REQUEST;
 name="meeting.ics"
Content-Transfer-Encoding: 8bit

BEGIN:VCALENDAR
METHOD:REQUEST
BEGIN:VEVENT
DTSTAMP:20080325T202857Z
DTSTART:20080325T200000Z
DTEND:20080325T220000Z
SUMMARY:Test meeting request
UID:040000008200E00074C5B7101A82E00800000000B2BB07349575C80100000000000000001000000019BF8D0149C50643A81325C54140C093
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="MYF":MAIL
 TO:myfriend@myserver.mycompany.com
ORGANIZER;CN="Me Myself":MAILTO:memyself@myserver.mycompany.com
LOCATION: Here
DESCRIPTION:Test Request\N
SEQUENCE:0
PRIORITY:5
CLASS:
CREATED:20080321T190958Z
STATUS:CONFIRMED
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

Example of setting headers with System.Web.Mail

//============================ Optional Settings ========================

if (this.Priority.Length != 0)
{
    if (this.Priority == "Non Urgent") { oMsg.Priority = System.Web.Mail.MailPriority.Low; };
    if (this.Priority == "Normal") { oMsg.Priority = System.Web.Mail.MailPriority.Normal; };
    if (this.Priority == "Urgent") { oMsg.Priority = System.Web.Mail.MailPriority.High; };

}


if (this.RequestReadReceipt == true)
{
    oMsg.Fields["urn:schemas:mailheader:disposition-notification-to"] =  this.From;
}

if (this.RequestDeliveryReceipt == true)
{
    oMsg.Fields["urn:schemas:mailheader:return-receipt-to"] = this.From;
}

if (this.ReplyTo.Length != 0)
{
    //oMsg.ReplyTo = this.ReplyTo;
    //oMsg.Headers.Add("Return-Path",this.ReplyTo);
    oMsg.Fields["http://schemas.microsoft.com/cdo/configuration/senduserreplyemailaddress"] = this.ReplyTo;
}

if (this.ReturnPath.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:return-path"] = this.ReturnPath;
}

if (this.Sensitivity.Length != 0)
{
    //TODO: This should be working, but does not... it works in all other api tests
    oMsg.Fields["urn:schemas:mailheader:sensitivity"] = this.Sensitivity;
}

if (this.Sender.Length != 0)
{
    oMsg.Fields["http://schemas.microsoft.com/cdo/configuration/sendemailaddress"] = this.Sender;
}


// Other mail headers --------------------------------

if (this.MailHeaderName1.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:" + this.MailHeaderName1]  = this.MailHeaderVal1;
}
if (this.MailHeaderName2.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:" + this.MailHeaderName2]  = this.MailHeaderVal2;
}
if (this.MailHeaderName3.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:" + this.MailHeaderName3]  = this.MailHeaderVal3;
}

Example of setting headers with CDOSYS

 

//============================ Optional Settings ========================

if (this.Priority.Length != 0)
{
    // Well... it seems that priority is not what needs to be set to get priority set...
    // Looks like setting importance will set priority... at least for cdosys... hmmm
    if (this.Priority == "Non Urgent")
    {
        oMsg.Fields["urn:schemas:mailheader:importance"].Value = "Low";
        //oMsg.Fields["urn:schemas:mailheader:priority"].Value = "Nonurgent"; // -1
    };  // -1
    if (this.Priority == "Normal")
    {
        oMsg.Fields["urn:schemas:mailheader:importance"].Value = "Normal";
        //oMsg.Fields["urn:schemas:mailheader:priority"].Value = "Normal";  // 0
    }; // 0
    if (this.Priority == "Urgent")
    {
        oMsg.Fields["urn:schemas:mailheader:importance"].Value = "High";
        //oMsg.Fields["urn:schemas:mailheader:priority"].Value = "Urgent";  // 1
    }; // 1
}

if (this.RequestReadReceipt == true)
{
    oMsg.Fields["urn:schemas:mailheader:disposition-notification-to"].Value = this.From;
}

if (this.RequestDeliveryReceipt == true)
{
    oMsg.Fields["urn:schemas:mailheader:return-receipt-to"].Value = this.From;
}

if (this.Sensitivity.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:sensitivity"].Value = this.Sensitivity;
}

// Other headers ----------------------------------------

if (this.MailHeaderName1.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:" + this.MailHeaderName1].Value = this.MailHeaderVal1;
}
if (this.MailHeaderName2.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:" + this.MailHeaderName2].Value = this.MailHeaderVal2;
}
if (this.MailHeaderName3.Length != 0)
{
    oMsg.Fields["urn:schemas:mailheader:" + this.MailHeaderName3].Value = this.MailHeaderVal3;
}

oMsg.Fields.Update();

Example of setting headers with System.Net.Mail

Here is a partial sample on setting header fields with System.Net.Mail: 

//============================ Instance Message ===========================
oMsg = new System.Net.Mail.MailMessage();

//============================ Optional Settings ===========================

if (this.Priority.Length != 0)
{
    if (this.Priority == "Non Urgent") { oMsg.Priority = System.Net.Mail.MailPriority.Low; };
    if (this.Priority == "Normal") { oMsg.Priority = System.Net.Mail.MailPriority.Normal; };
    if (this.Priority == "Urgent") { oMsg.Priority = System.Net.Mail.MailPriority.High; };
}

if (this.RequestReadReceipt == true)
{
    oMsg.Headers.Add("Disposition-Notification-To", this.From);
}

if (this.RequestDeliveryReceipt == true)
{
    oMsg.Headers.Add("Return-Receipt-To", this.From);
}

if (this.ReplyTo.Length != 0)
{
    oMsg.ReplyTo = new MailAddress(this.ReplyTo);
}
if (this.ReturnPath.Length != 0)
{
    oMsg.Headers.Add("Return-Path", this.ReturnPath);
}

if (this.Sender.Length != 0)
{
    oMsg.Sender = new MailAddress(this.Sender);
}

if (this.Sensitivity.Length != 0)
{
    oMsg.Headers.Add("Sensitivity", this.Sensitivity);
}

// Other mail headers -------------------------------------

if (this.MailHeaderName1.Length != 0)
{
    oMsg.Headers.Add(this.MailHeaderName1, this.MailHeaderVal1);
}
if (this.MailHeaderName2.Length != 0)
{
    oMsg.Headers.Add(this.MailHeaderName2, this.MailHeaderVal2);
}
if (this.MailHeaderName3.Length != 0)
{
    oMsg.Headers.Add(this.MailHeaderName3, this.MailHeaderVal3);
}

 

Getting a list of mailboxes.

OK, WebDAV does not have the ability itself to get a list of mailboxes.  However you can use other APIs and even use an OWA call to get a list of mailboxes.

 

Reading The GAL.

You will need ADSI, CDO 1.21 or Extended MAPI for this.  Also, you may use an OWA command to get this information.

 

Format of the OWA call:

sServerURL = "http://"+ sServerURL + "/public/?Cmd=galfind

 

Information on the unsupported call is covered in the following article:

Customizing Microsoft Outlook Web Access

http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=6532E454-073E-4974-A800-1490A7CB358F

 

Here are the supported methods:

 

CDO 1.21:

How To Work with Distribution Lists Using CDO (1.x) from Visual Basic

http://support.microsoft.com/?id=178787

 

ADSI:

241474 HOWTO: Render the Global Address List with ADSI

http://support.microsoft.com/?id=241474

 

Extended MAPI:

166106 HOWTO: Getting the Contents of the Exchange Global Address List

http://support.microsoft.com/?id=166106

 

Read The Table:

The Mailbox Table in Exchange holds a list of Mailboxes and related mailbox information.  Reading this table can only be done with Extended MAPI or WMI can be used on an Exchange 2003 server.   

 

Extended MAPI can be used with Exchange 5.5 and later.

 

XCLN: How to Retrieve Last Logon Time of Exchange Users Using Extended MAPI

http://support.microsoft.com/default.aspx?scid=kb;en-us;259570

 

How to loop through mailboxes on Exchange by using the GetMailboxTable method

http://support.microsoft.com/kb/200160/

 

With Exchange 2003 and later, you can use WMI:

 

LastLogonTime Property

http://msdn.microsoft.com/en-us/library/aa144762.aspx 

 

 

'  GALFIND

' GALFIND will return results which can be consumed by your application as xml.

' You can test the URL by putting it into IE and seeing the results.

'  TODO: Have Basic Authentication turned ON on the virtual directory for the item and no anonymous.

'  Note: there is no Translate header set for a GET against an OWA URL – That’s because we are not using WebDAV, we are using the results from an OWA page.

 

sub ReadFileText (sFile)

    Dim objFSO 'As FileSystemObject

    dim oTS

    dim sText

   

    Set objFSO = CreateObject("Scripting.FileSystemObject")

 

    Set oTS = objFSO.OpenTextFile(sFile)

    sText = oTS.ReadAll

 

    oTS.close

    set oTS = nothing

    Set objFSO = nothing

 

    ReadFileText = sText

    

end sub

 

Private Sub WriteFileText(sFilePath, sText)

    Dim objFSO 'As FileSystemObject

    Dim objTextFile 'As Object

   

    Const ForReading = 1

    Const ForWriting = 2

    Const ForAppending = 8

   

    Set objFSO = CreateObject("Scripting.FileSystemObject")

    Set objTextFile = objFSO.CreateTextFile(sFilePath, True)

   

    ' Write a line.

    objTextFile.Write (sText)

 

    objTextFile.Close

    'objTextFile.Close

 

End Sub

 

 

dim sHREF

dim sUserName 

dim sPassword 

dim sResponse 

Dim HttpWebRequest

 

sHREF="http://myexchangeserver.company.com/exchange/?cmd=galfind&DN=bobbie"  ' TODO: change

 

sUserName = "Administrator"    ' TODO: change

sPassword = ""    ' TODO: change

 

set HttpWebRequest = CreateObject("microsoft.xmlhttp")

 

if sPassword = "" then

    HttpWebRequest.Open "GET", sHREF , False

else

    HttpWebRequest.Open "GET", sHREF , False,  sUserName, sPassword

end if

 

HttpWebRequest.Send

sResponse  = HttpWebRequest.ResponseText  ' Returns as text

 

Set HttpWebRequest = Nothing

wscript.echo sResponse 

Comparing strings - a few samples

I thought I would share some simple sample code which is useful for finding problems in string content.  Here are three methods I wrote which are useful to see whats in a string and differences between strings.  I use these with a simple program which uses two text boxes for pasting text into and a third window for displaying results.   Please note that I'm not using stringbuilder here on purpose - I do avoid it when writting code which i want to be as simple and language generic as possible (I would use stringbuilder if I were writting a production app of course).  I hope you find these useful.

 The routines:

     CompareStrings() will compare two different strings and tell you about differences.

     DumpString() Will dump out the string in a Hex Dump.

     GetStringStats() Provides information on the characters which are in a string.

 Samples:

       //*************************************************************************************
       //GetStringStats() Provides information on the characters which are in a string.
       //*************************************************************************************
       private string GetStringStats(string sString)
        {
            string sResults = string.Empty;
            char[] CharArr = sString.ToCharArray();
         
            //int iCharCode = 0;
            int iTotalLines = 0;
            int iTotalCharacters = 0;

            int iCurrentLine = 0;
            int iCurrentLineLength = 0;

            int iLongestLine = 0;
            int iLongestLineChars = 0;

            int iStandardEnglish = 0;
            int iNull = 0;
            int iTab = 0;
            int iCr = 0;
            int iLF = 0;
            int iLowerCaseAlpha = 0;
            int iUpperCaseAlpha = 0;
            int iNumeric = 0;
            int iAbove255 = 0;
            int iAbove127 = 0;
            int iControlCharacters = 0;

            char xChar = ' ';
            long ixChar = 0;
            iTotalCharacters = sString.Length;

            for (int iCount = 0; iCount < sString.Length; iCount++)
            {
                xChar = CharArr[iCount];

                iCurrentLineLength++;

                if (xChar == '\n' || xChar == '\r')
                {
                    if (xChar == '\n')
                    {
                        iCurrentLine++;
                        iTotalLines++;
                    }

                    iCurrentLineLength = 0;
                }

                if (iCurrentLineLength > iLongestLineChars)
                {
                    iLongestLineChars = iCurrentLineLength;
                    iLongestLine = iCurrentLine;
                }

                ixChar = (int)xChar;
                if (xChar >= 'a' && xChar <= 'z') iLowerCaseAlpha++;
                if (xChar >= 'A' && xChar <= 'Z') iUpperCaseAlpha++;
                if (xChar >= '0' && xChar <= '9') iNumeric++;
                if (ixChar > 255) iAbove255++;
                if (ixChar > 127) iAbove127++;
                if (ixChar <= 31) iControlCharacters++;
                if (ixChar > 31 & ixChar < 127 || xChar == '\r' || xChar == '\n' || xChar == '\t')
                {
                    iStandardEnglish++;
                }

                if (xChar == '\0') iNull++;
                if (xChar == '\t') iTab++;
                if (xChar == '\r') iCr++;
                if (xChar == '\n') iLF++;
            }

            sResults += "  Total Lines: " + iTotalLines.ToString() + "  ";
            sResults += "Total Characters: " + iTotalCharacters.ToString() + "  ";
            sResults += "Longest Line is " + iLongestLine.ToString() + " having " + iLongestLineChars.ToString() + " characters\r\n";
            sResults += "\r\n";
            sResults += "  Characters:\r\n";

            sResults += string.Format("    {0} In Normal English Range - letters, numbers, symbols and Cr/Lf/Tab (31-126 dec range+ 13/10/9 dec) \r\n", iStandardEnglish.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Tab(09 hex)\r\n", iTab.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Cr (0D hex)\r\n", iCr.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Lf (0A hex)\r\n", iLF.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Control Characters (Range 0-31 dec/0-1F Hex)\r\n", iControlCharacters.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Above 127 dec range\r\n", iAbove255.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Above 255 dec range\r\n", iAbove127.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Null (00 hex)\r\n", iNull.ToString().PadLeft(6, ' '));
 
            sResults += string.Format("    {0} Lower Case\r\n", iLowerCaseAlpha.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Upper Case\r\n", iUpperCaseAlpha.ToString().PadLeft(6, ' '));
            sResults += string.Format("    {0} Numeric\r\n", iNumeric.ToString().PadLeft(6, ' '));

            sResults += "\r\n";


            return sResults;
        }


        //****************************************************************
        // DumpString() Will dump out the string in a Hex Dump.
        //****************************************************************
        private string DumpString(string sString)
        {
            string sResults = string.Empty;
            char[] CharA = sString.ToCharArray();
            string sChar = " ";
            int iCharCode = 0;
            string sHex = "";
            string sLeft = "";
            string sRight = "";
            int iLine = 0;

            sResults = "        0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000A 000B 000C 000D 000E 000F   0123456789ABCDEF\r\n";
            sResults += "-------+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ +----------------\r\n";

            for (int iCount = 0; iCount < sString.Length; iCount++)
            {
                char x = CharA[iCount];
              
                if ((CharA[iCount] != '\n') &&
                    (CharA[iCount] != '\r') &&
                    (CharA[iCount] != '\0') &&
                    (CharA[iCount] != '\t')) 
                {
                    sChar = CharA[iCount].ToString();
                }
                else
                {
                    sChar = " ";
                }

                iCharCode = Convert.ToInt32(CharA[iCount]);
                sHex = String.Format("{0:X}", iCharCode);
                sHex = sHex.PadLeft(4, '0');

                sLeft += sHex + " ";
                sRight += sChar;

                //sResults += "" + sHex + " " + sChar + "  " ;

                if (iCount % 16 == 15)
                {

                    sResults += String.Format("{0:X}", iLine).PadLeft(6, '0') + "  " + sLeft + "  " + sRight + "\r\n";
                    sLeft = "";
                    sRight = "";
                    iLine++;
                }

            }
            if (sLeft.Length != 0)
            {
                sResults += String.Format("{0:X}", iLine).PadLeft(6, '0') + "  ";
                sResults += sLeft.PadRight(80,' ') + "  " + sRight + "\r\n";
            }

            sResults = sResults.TrimEnd();
            sResults += "\r\n";
            return sResults;
        }

 

        //****************************************************************************************
        // CompareStrings() will compare two different strings and tell you about differences...
        //****************************************************************************************
        private string CompareStrings(string StringA, string StringB)
        {
            string sResults = string.Empty;
            int iComp = 0;
            //int iLength = 0;

            StringA = txtFirstString.Text;
            StringB = txtSecondString.Text;

            sResults += "First String Length:  " + StringA.Length.ToString();
            sResults += "  ";
            sResults += "Second String Length: " + StringB.Length.ToString();
            sResults += "\r\n";

            iComp = StringA.CompareTo(StringB);
            if (iComp < 0)
                sResults += "Value of First String is LESS THAN (<) Second String.\r\n";
            if (iComp == 0)
                sResults += "Value of First String is EQUAL TO (=) Second String.\r\n";
            if (iComp > 0)
                sResults += "Value of First String is GREATER THAN (>) Second String.\r\n";


            Boolean bResultFound = false;

            char[] CharA = StringA.ToCharArray();
            char[] CharB = StringB.ToCharArray();
            string sLineA = "";
            string sLineB = "";

            int iLineCount = 1;
            int iCharInLineCount = 1;
            int iLongestLineCharCount = 0;
            int iLongestLineAtLine = 0;

            //  Value of First String is LESS THAN (<) Second String or the strings are equal...
            if (StringA.Length <= StringB.Length)
            {
                sResults += "\r\n*** First String VS Second String ***\r\n";
                for (int iCount = 0; iCount < StringA.Length; iCount++)
                {


                    if (CharA[iCount] != '\n' && CharA[iCount] != '\r')
                    {
                        sLineA += CharA[iCount].ToString();
                       
                    }
                    if (CharB[iCount] != '\n' && CharB[iCount] != '\r')
                    {
                      
                        sLineB += CharB[iCount].ToString();
                    }

                    iCharInLineCount++;

                    if (CharA[iCount] != CharB[iCount])
                    {
                        if (bResultFound == false)
                        {
                            sResults += "Found difference at string position: " + (int)(iCount + 1);
                            sResults += "  ...Hex: " + String.Format("{0:X6}", (iCount / 16))  + " + " + String.Format("{0:X2}",  (iCount % 16) );
                            sResults += " - Refer to Hex Dump below.";
                            sResults += "\r\n";
                            sResults += "                    Line:  '" + iLineCount.ToString() + "'  ";
                            sResults += "   Char:  '" + sLineA.Length.ToString() + "' \r\n";
                            sResults += "       First String Char:  '" + CharA[iCount].ToString() + "'   ";
                            sResults += "  Second String Char:  '" + CharB[iCount].ToString() + "' \r\n";
                            sResults += "   Start of First String:  '" + sLineA + "' \r\n";
                            sResults += "  Start of Second String:  '" + sLineB + "' \r\n";
                            //sResults += " Character pos in stream:  '" + iCount.ToString() + "' \r\n";

                            sResults += "\r\n";

                            bResultFound = true;
                        }
                    }

                    if (CharA[iCount] == '\n' || CharA[iCount] == '\r')
                    {
                        sLineA = "";
                        sLineB = "";
                        if (CharA[iCount] == '\n')
                        {
                            iLineCount++;
                            iCharInLineCount = 1;
                        }
                    }


                }
 

            }

            //  Value of First String is GREATER THAN (>) Second String.
            if (StringA.Length > StringB.Length)
            {
                sResults += "\r\n*** Second String VS First String ***\r\n";
                for (int iCount = 0; iCount < StringB.Length; iCount++)
                {

                    if (CharA[iCount] != '\n' && CharA[iCount] != '\r')
                    {
                        sLineA += CharA[iCount].ToString();
                      
                    }
                    if (CharB[iCount] != '\n' && CharB[iCount] != '\r')
                    {
                       
                        sLineB += CharB[iCount].ToString();
                    }
                    iCharInLineCount++;

                    if (CharA[iCount] != CharB[iCount])
                    {
                        if (bResultFound == false)
                        {
                            sResults += "Found difference at position: " + (int)(iCount + 1);
                            sResults += "  ...Hex: " + String.Format("{0:X6}", (iCount / 16)) + " + " + String.Format("{0:X2}", (iCount % 16) );
                            sResults += " - Refer to Hex Dump below.";
                            sResults += "\r\n";
                            sResults += "                    Line:  '" + iLineCount.ToString() + "'  ";
                            sResults += "   Char:  '" + sLineA.Length.ToString() + "' \r\n";
                            sResults += "       First String Char:  '" + CharA[iCount].ToString() + "'   ";
                            sResults += "  Second String Char:  '" + CharB[iCount].ToString() + "' \r\n";
                            sResults += "   Start of First String:  '" + sLineA + "' \r\n";
                            sResults += "  Start of Second String:  '" + sLineB + "' \r\n";
                           
                            sResults += "\r\n";

                            bResultFound = true;
                        }
                    }

                    if (CharB[iCount] == '\n' || CharB[iCount] == '\r')
                    {
                        sLineA = "";
                        sLineB = "";
                        if (CharB[iCount] == '\n')
                        {
                            iLineCount++;
                            iCharInLineCount = 1;
                        }
                    }
 

                    if (iCharInLineCount > iLongestLineCharCount)
                    {
                        iLongestLineCharCount = iCharInLineCount;
                        iLongestLineAtLine = iLineCount;
                    }

                }

            }

            if (bResultFound == false)
            {
                if (StringA.Length > StringB.Length)
                {
                    sResults += "The difference is in what is in first string and not in second string... \r\n";
                    sResults += "  * The first string is longer by " + String.Format("{0}", (StringA.Length - StringB.Length))  + " characters.\r\n";
                }
                if (StringA.Length < StringB.Length)
                {
                    sResults += "The difference is in what is in second string and not in first string... \r\n";
                    sResults += "  * The second string is longer by " + String.Format("{0}", (StringB.Length - StringA.Length)) + " characters.\r\n";
                }
 
            }

           return sResults;
        }

System.Net.Mail with SSL to authenticate against port 465

Sending mail using System.Net.Mail with SSL will fail:

 

System.Net.NetworkCredential aCred = new System.Net.NetworkCredential("myacct", "mypassword");

SmtpClient smtp = new SmtpClient("smtp.mail.myserver.com", 465);

smtp.EnableSsl = true;

smtp.UseDefaultCredentials = false;

smtp.Credentials = aCred;

 

System.Net.Mail only supports “Explicit SSL”. 

 

Explicit SSL

System.Net.Mail only supports “Explicit SSL”.  Explicit SSL starts as unencrypted on port 25, then issues a STARTDLS and switches to an Encrypted connection.  See RFC 2228.

 

Explicit  SLL would go something like: Connect on 25 -> StartTLS (starts to encrypt) -> authenticate -> send data

 

If the SMTP server expects SSL/TLS connection right from the start then this will not work.

 

Implicit SSL

There is no way to use Implicit SSL (SMTPS) with System.Net.Mail.  Implicit SSL would have the entire connection is wrapped in an SSL layer.  A specific port would be used (port 465 is common).  There is no formal RFC covering Implicit SSL.

 

Implicit  SLL would go something like: Start SSL (start encryption) -> Connect -> Authenticate -> send data