Receiving AS2 messages with .NET

By request, this is a follow up to my “Send an AS2 message with .NET” from July 2010. This time we will be receiving AS2 (Applicability Statement 2) messages.

Start with an “ASP.NET Web Application” and then add a “Generic Handler” to it, call it AS2Listener.ashx.

Add the following code to the ProcessRequest method:


public void ProcessRequest(HttpContext context)
{
    string sTo = context.Request.Headers["AS2-To"];
    string sFrom = context.Request.Headers["AS2-From"];
    string sMessageID = context.Request.Headers["Message-ID"];

    if (context.Request.HttpMethod == "POST" || context.Request.HttpMethod == "PUT" ||
       (context.Request.HttpMethod == "GET" && context.Request.QueryString.Count > 0))
    {

        if (sFrom == null || sTo == null)
        {
            //Invalid AS2 Request.
            //Section 6.2 The AS2-To and AS2-From header fields MUST be present
            //    in all AS2 messages
            if (!(context.Request.HttpMethod == "GET" && context.Request.QueryString[0].Length == 0))
            {
                AS2Receive.BadRequest(context.Response, "Invalid or unauthorized AS2 request received.");
            }
        }
        else
        {
            AS2Receive.Process(context.Request, WebConfigurationManager.AppSettings["DropLocation"]);
        }
    }
    else
    {
        AS2Receive.GetMessage(context.Response);
    }
}

Now you’ll need to create your AS2Receive class. The simple methods are AS2Receive.BadRequest and AS2Receive.GetMessage:


public static void GetMessage(HttpResponse response)
{
    response.StatusCode = 200;
    response.StatusDescription = "Okay";

    response.Write(@"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2 Final//EN"">"
    + @"<HTML><HEAD><TITLE>Generic AS2 Receiver</TITLE></HEAD>"
    + @"<BODY><H1>200 Okay</H1><HR>This is to inform you that the AS2 interface is working and is "
    + @"accessable from your location.  This is the standard response to all who would send a GET "
    + @"request to this page instead of the POST context.Request defined by the AS2 Draft Specifications.<HR></BODY></HTML>");
}

public static void BadRequest(HttpResponse response, string message)
{
    response.StatusCode = (int)HttpStatusCode.BadRequest;
    response.StatusDescription = "Bad context.Request";

    response.Write(@"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2 Final//EN"">"
    + @"<HTML><HEAD><TITLE>400 Bad context.Request</TITLE></HEAD>"
    + @"<BODY><H1>400 Bad context.Request</H1><HR>There was a error processing this context.Request.  The reason given by the server was:"
    + @"<P><font size=-1>" + message + @"</Font><HR></BODY></HTML>");
}

The one that does all the work is AS2Receive.Process. In this simple example all it will do is read the message and write it to a new text file in the given dropLocation folder.

public static void Process(HttpRequest request, string dropLocation)
{
    string filename = ParseFilename(request.Headers["Subject"]);

    byte[] data = request.BinaryRead(request.TotalBytes);
    bool isEncrypted = request.ContentType.Contains("application/pkcs7-mime");
    bool isSigned = request.ContentType.Contains("application/pkcs7-signature");

    string message = string.Empty;

    if (isSigned)
    {
        string messageWithMIMEHeaders = System.Text.ASCIIEncoding.ASCII.GetString(data);
        string contentType = request.Headers["Content-Type"];

        message = AS2MIMEUtilities.ExtractPayload(messageWithMIMEHeaders, contentType);
    }
    else if (isEncrypted) // encrypted and signed inside
    {
        byte[] decryptedData = AS2Encryption.Decrypt(data);

        string messageWithContentTypeLineAndMIMEHeaders = System.Text.ASCIIEncoding.ASCII.GetString(decryptedData);

        // when encrypted, the Content-Type line is actually stored in the start of the message
        int firstBlankLineInMessage = messageWithContentTypeLineAndMIMEHeaders.IndexOf(Environment.NewLine + Environment.NewLine);
        string contentType = messageWithContentTypeLineAndMIMEHeaders.Substring(0, firstBlankLineInMessage);

        message = AS2MIMEUtilities.ExtractPayload(messageWithContentTypeLineAndMIMEHeaders, contentType);
    }
    else // not signed and not encrypted
    {
        message = System.Text.ASCIIEncoding.ASCII.GetString(data);
    }

    System.IO.File.WriteAllText(dropLocation + filename, message);
}

We have some logic at the start to figure out if the message is signed, or if it’s encrypted and signed. Note that this code always assumes that if a message is encrypted it is also signed, and doesn’t allow for the message to be encrypted but not signed – although that is a valid AS2 scenario.

Let’s deal with receiving signed messages first, in AS2MIMEUtilities:

public class AS2MIMEUtilities
{
    public const string MESSAGE_SEPARATOR = "\r\n\r\n";

    /// <summary>
    /// Extracts the payload from a signed message, by looking for boundaries
    /// Ignores signatures and does checking - should really validate the signature
    /// </summary>
    public static string ExtractPayload(string message, string contentType)
    {
        string boundary = GetBoundaryFromContentType(contentType);

        if (!boundary.StartsWith("--"))
            boundary = "--" + boundary;

        int firstBoundary = message.IndexOf(boundary);
        int blankLineAfterBoundary = message.IndexOf(MESSAGE_SEPARATOR, firstBoundary) + (MESSAGE_SEPARATOR).Length;
        int nextBoundary = message.IndexOf(MESSAGE_SEPARATOR + boundary, blankLineAfterBoundary);
        int payloadLength = nextBoundary - blankLineAfterBoundary;

        return message.Substring(blankLineAfterBoundary, payloadLength);
}

/// <summary>
/// Extracts the boundary from a Content-Type string
/// </summary>
/// <param name="contentType">e.g: multipart/signed; protocol="application/pkcs7-signature"; micalg="sha1"; boundary="_956100ef6a82431fb98f65ee70c00cb9_"</param>
/// <returns>e.g: _956100ef6a82431fb98f65ee70c00cb9_</returns>
public static string GetBoundaryFromContentType(string contentType)
{
    return Trim(contentType, "boundary=\"", "\"");
}

/// <summary>
/// Trims the string from the end of startString until endString
/// </summary>
private static string Trim(string str, string start, string end)
{
    int startIndex = str.IndexOf(start) + start.Length;
    int endIndex = str.IndexOf(end, startIndex);
    int length = endIndex - startIndex;

    return str.Substring(startIndex, length);
}

Oh, OK, erm the <summary> of ExtractPayload says it all. All I do is truncate the message signature off the message, by looking for the message part boundaries. It should of course really check that the message signature is valid.

Now let’s handle encrypted messages with AS2Encryption.Decrypt:

internal static byte[] Decrypt(byte[] encodedEncryptedMessage)
{
    EnvelopedCms envelopedCms = new EnvelopedCms();
    envelopedCms.Decode(encodedEncryptedMessage);
    envelopedCms.Decrypt();
    return envelopedCms.Encode();
}

And that, Dear reader is a simple example of how to receive either: unsigned & unencrypted;  signed; or encrypted & signed AS2 messages. What is missing is the necessary checking that the signature is valid, and handling of encrypted but unsigned messages.

Entity Framework Code-First CTP5 walkthrough

A couple of months ago I watched some of videos from Microsoft PDC 2010 at http://player.microsoftpdc.com/

One of the interesting presentations was Jeff Derstadt and Tim Laverty’s “Code First with Entity Framework”, viewable here (or direct download here). In the demo, Jeff and Tim very quickly create a basic twitter clone using ASP.NET MVC 2 and EF Code-First CTP4.

For fun I decided to watch the talk and follow along in Visual Studio. This would give me a chance to play with some Microsoft bleeding edge pre-release stuff: ASP.NET MVC 3 (RC2) and Razor, NuGet, and EF Code-First CTP5 (CTP4 was the “Magic Unicorn Edition“).

If you are as lame as me, here’s some tips to help you do a similar walkthrough.

Getting started

You’ll need Visual Studio 2010, and to download ASP.NET MVC 3 (currently RC2) which also installs NuGet.

Once you’ve created your MVC application, you need to add EF Code-First by using NuGet. NuGet’s only been around for a couple of months and already it’s a gotten disorganised and is hard to find the right package. In the demo, Tim adds a reference to “EFCTP4” and indeed that is still present in NuGet. But if you dig around a bit and search for “EFCodeFirst”, well that appears to be EFCTP5. The package you should install is now called “EntityFramework”.

Changes

The first issue I came across was getting EF to talk to SQL Server, as mentioned in my previous post.

A few other things I’ve found that must have changed between CTP4 and CTP5:

1. I don’t think the [StoreIgnore] attribute exists any more. I couldn’t find it, and in my experiments it didn’t seem to be necessary to tell EF to ignore the calculated “TweetActivity” field. I did discover the [NotMapped] attribute which you can use if you want to tell EF not to save a property. I think it’s the same as [StoreIgnore].

2. When it comes time to setup the DB initialiser for populating the DB with static data, Tim does something like this:

public class TweetInitializer : AlwaysRecreateDatabase<TweetContext>

However, AlwaysRecreateDatabase doesn’t exist any more. I think:

public class TweetInitializer : DropCreateDatabaseIfModelChanges<TweetContext>

might be its replacement.

3. I had a strange issue when I tried to create the oData feed. Visual Studio couldn’t find the System.Data.Objects namespace. A quick fix was to add an empty “ADO.NET Entity Data Model” to my project, and then delete it.

4. When you view your oData feed in your browser, by default it appears all purty:

feed

In order to see the raw XML you need to disable IE’s “feed reading view”.

Tools –> Internet Options –> Content tab –> Feeds and web slices –> Settings –> Uncheck “Turn on feed reading view”.

xml

System.Data.ProviderIncompatibleException was unhandled by user code

Message=The provider did not return a ProviderManifestToken string.

Or, how to run EF code-first without SQL Express.

I got the above exception being thrown when I started playing around with Entity Framework’s Code-First CTP5. The inner exception revealed the true problem:

ysod

The problem is that by default EF Code-First tries to create your database on a SQL Express instance.

I don’t run SQL Express, I run SQL Server Developer edition. The solution is to add a connection string for the code-first database to your web.config (even though this database doesn’t yet exist):


<connectionStrings>
 <add name="TweetContext" connectionString="Data Source=.; Initial Catalog=Tweet; Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
 </connectionStrings>

Note that the name (TweetContext) must match the name of your class which derives from DbContext, i.e.


public class TweetContext : DbContext

And you have to specify the DB name, i.e Initial Catalog=Tweet, but that database should not already exist, as EF code-first will create it for you.

SSRS the report definition is not valid hexadecimal value 0x00

I had this weird error the other day, when all my report definition .rdl files seemed to get corrupted somehow. I’m not sure what caused it but it might have been something to do with Tortoise SVN.

The error was something like “[rsInvalidReportDefinition] SSRS the report definition is not valid. Details: ‘.’, hexadecimal value 0x00 is an invalid character”. A screenshot is below:

When I opened the rdl it looked fine, and when I viewed the source of it in Visual Studio it also looked fine. But the telltale sign was that I could not  delete the last character in the .rdl file, a ‘>’ which closed off the final </Report> tag. In the end the fix was quite simple, all I did was delete that last </Report> tag and type it in again.

Hope this helps someone.

Add a link to another page on Windows Phone 7

Greetings from rainy Thailand, where I am on holiday. And on rainy days like today I’m learning Silverlight on the Windows Phone 7 platform.

If you need to link to another page in your Silverlight application on Windows Phone 7, the easiest way is to use a HyperlinkButton, like so:

 <HyperlinkButton Content="image page" NavigateUri="/ImagePage.xaml" />

It took me a bit of trial and error to realise that I needed to put a “/” at the start of the NavigateUri. MSDN’s help page didn’t have a code sample either.

Send an AS2 message with .NET

UPDATE Jan 2011: I’ve written a post about receiving AS2 messages.

A recent project at work required us to generate and send AS2 (Applicability Statement 2) messages. A colleague had written a project years ago using .NET 1.1 and a COM dll for encryption, so I set about porting that to Visual Studio 2008 and .NET 3.5.

I also wanted to use .NET’s System.Security libraries for encryption instead of having to use reflection to call the old COM dll.

It was quite difficult to get the signing and encryption working – in the end I had to resort to reverse engineering, by getting BizTalk to send an AS2 message and intercepting the message content with Fiddler, so that I could see how the AS2 messages and their headers were supposed to look.

Here’s some code which constructs an AS2 message and sends it over HTTP:

using System;
using System.IO;
using System.Net;

namespace WebTestPlugins.AS2Helpers
{
    public struct ProxySettings
    {
        public string Name;
        public string Username;
        public string Password;
        public string Domain;
    }

    public class AS2Send
    {
        public static HttpStatusCode SendFile(Uri uri, string filename, byte[] fileData, string from, string to, ProxySettings proxySettings, int timeoutMs, string signingCertFilename, string signingCertPassword, string recipientCertFilename)
        {
            if (String.IsNullOrEmpty(filename)) throw new ArgumentNullException("filename");

            if (fileData.Length == 0) throw new ArgumentException("filedata");

            byte[] content = fileData;

            //Initialise the request
            HttpWebRequest http = (HttpWebRequest)WebRequest.Create(uri);
           
            if (!String.IsNullOrEmpty(proxySettings.Name))
            {
                 WebProxy proxy = new WebProxy(proxySettings.Name);
               
                NetworkCredential proxyCredential = new NetworkCredential();
                proxyCredential.Domain = proxySettings.Domain;
                proxyCredential.UserName = proxySettings.Username;
                proxyCredential.Password = proxySettings.Password;

                proxy.Credentials = proxyCredential;
               
                http.Proxy = proxy;
            }

            //Define the standard request objects
            http.Method = "POST";

            http.AllowAutoRedirect = true;

            http.KeepAlive = true;

            http.PreAuthenticate = false; //Means there will be two requests sent if Authentication required.
            http.SendChunked = false;

            http.UserAgent = "MY SENDING AGENT";

            //These Headers are common to all transactions
            http.Headers.Add("Mime-Version", "1.0");
            http.Headers.Add("AS2-Version", "1.2"); 

            http.Headers.Add("AS2-From", from);
            http.Headers.Add("AS2-To", to);
            http.Headers.Add("Subject", filename + " transmission.");
            http.Headers.Add("Message-Id", "<AS2_" + DateTime.Now.ToString("hhmmssddd") + ">");
            http.Timeout = timeoutMs;

            string contentType = (Path.GetExtension(filename) == ".xml") ? "application/xml" : "application/EDIFACT";

            bool encrypt = !string.IsNullOrEmpty(recipientCertFilename);
            bool sign = !string.IsNullOrEmpty(signingCertFilename);

            if (!sign && !encrypt)
            {
                http.Headers.Add("Content-Transfer-Encoding", "binary");
                http.Headers.Add("Content-Disposition", "inline; filename=\"" + filename + "\"");
            }
            if (sign)
            {
                // Wrap the file data with a mime header
                content = AS2MIMEUtilities.CreateMessage(contentType, "binary", "", content);

                content = AS2MIMEUtilities.Sign(content, signingCertFilename, signingCertPassword, out contentType);

                http.Headers.Add("EDIINT-Features", "multiple-attachments");

            }
            if (encrypt)
            {
                if (string.IsNullOrEmpty(recipientCertFilename))
                {
                    throw new ArgumentNullException(recipientCertFilename, "if encrytionAlgorithm is specified then recipientCertFilename must be specified");
                }

                byte[] signedContentTypeHeader = System.Text.ASCIIEncoding.ASCII.GetBytes("Content-Type: " + contentType + Environment.NewLine);
                byte[] contentWithContentTypeHeaderAdded = AS2MIMEUtilities.ConcatBytes(signedContentTypeHeader, content);

                content = AS2Encryption.Encrypt(contentWithContentTypeHeaderAdded, recipientCertFilename, EncryptionAlgorithm.DES3);
               

                contentType = "application/pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"";
            }
           
            http.ContentType = contentType;           
            http.ContentLength = content.Length;

            SendWebRequest(http, content);

            return HandleWebResponse(http);
        }

        private static HttpStatusCode HandleWebResponse(HttpWebRequest http)
        {
            HttpWebResponse response = (HttpWebResponse)http.GetResponse();
           
            response.Close();
            return response.StatusCode;
        }

        private static void SendWebRequest(HttpWebRequest http, byte[] fileData)
        {
            Stream oRequestStream = http.GetRequestStream();
            oRequestStream.Write(fileData, 0, fileData.Length);
            oRequestStream.Flush(); 
            oRequestStream.Close();
        }
    }
}

And here’s the code which signs and/or encrypts the message:

using System;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;

namespace WebTestPlugins.AS2Helpers
{
    public static class EncryptionAlgorithm
    {
        public static string DES3 = "3DES";
        public static string RC2 = "RC2";
    }

    public class AS2Encryption
    {
        internal static byte[] Encode(byte[] arMessage, string signerCert, string signerPassword)
        {
            X509Certificate2 cert = new X509Certificate2(signerCert, signerPassword);
            ContentInfo contentInfo = new ContentInfo(arMessage);

            SignedCms signedCms = new SignedCms(contentInfo, true); // <- true detaches the signature
            CmsSigner cmsSigner = new CmsSigner(cert);

            signedCms.ComputeSignature(cmsSigner);
            byte[] signature = signedCms.Encode();

            return signature;
        }

        internal static byte[] Encrypt(byte[] message, string recipientCert, string encryptionAlgorithm)
        {
            if (!string.Equals(encryptionAlgorithm, EncryptionAlgorithm.DES3) && !string.Equals(encryptionAlgorithm, EncryptionAlgorithm.RC2))
                throw new ArgumentException("encryptionAlgorithm argument must be 3DES or RC2 - value specified was:" + encryptionAlgorithm);

            X509Certificate2 cert = new X509Certificate2(recipientCert);

            ContentInfo contentInfo = new ContentInfo(message);

            EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo,
                new AlgorithmIdentifier(new System.Security.Cryptography.Oid(encryptionAlgorithm))); // should be 3DES or RC2

            CmsRecipient recipient = new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, cert);

            envelopedCms.Encrypt(recipient);

            byte[] encoded = envelopedCms.Encode();

            return encoded;
        }

        internal static byte[] Decrypt(byte[] encodedEncryptedMessage, out string encryptionAlgorithmName)
        {
            EnvelopedCms envelopedCms = new EnvelopedCms();

            // NB. the message will have been encrypted with your public key.
            // The corresponding private key must be installed in the Personal Certificates folder of the user
            // this process is running as.
            envelopedCms.Decode(encodedEncryptedMessage);

            envelopedCms.Decrypt();
            encryptionAlgorithmName = envelopedCms.ContentEncryptionAlgorithm.Oid.FriendlyName;

            return envelopedCms.Encode();
        }

    }
}

For completeness you’ll also need the AS2MimeUtilities class. Disclaimer: I didn’t write this class, it’s legacy hungarian-notated goodness.

using System;
using System.Text;

namespace WebTestPlugins.AS2Helpers
{
    /// <summary>
    /// Contains a number of useful static functions for creating MIME messages.
    /// </summary>
    public class AS2MIMEUtilities
    {
        public const string MESSAGE_SEPARATOR = "\r\n\r\n";
        public AS2MIMEUtilities()
        {
        }

        /// <summary>
        /// return a unique MIME style boundary
        /// this needs to be unique enought not to occur within the data
        /// and so is a Guid without - or { } characters.
        /// </summary>
        /// <returns></returns>
        protected static string MIMEBoundary()
        {
            return "_" + Guid.NewGuid().ToString("N") + "_";
        }

        /// <summary>
        /// Creates the a Mime header out of the components listed.
        /// </summary>
        /// <param name="sContentType">Content type</param>
        /// <param name="sEncoding">Encoding method</param>
        /// <param name="sDisposition">Disposition options</param>
        /// <returns>A string containing the three headers.</returns>
        public static string MIMEHeader(string sContentType, string sEncoding, string sDisposition)
        {
            string sOut = "";

            sOut = "Content-Type: " + sContentType + Environment.NewLine;
            if (sEncoding != "" )
                sOut += "Content-Transfer-Encoding: " + sEncoding + Environment.NewLine;

            if (sDisposition != "" )
                sOut += "Content-Disposition: " + sDisposition + Environment.NewLine;

            sOut = sOut + Environment.NewLine;

            return sOut;
        }

        /// <summary>
        /// Return a single array of bytes out of all the supplied byte arrays.
        /// </summary>
        /// <param name="arBytes">Byte arrays to add</param>
        /// <returns>The single byte array.</returns>
        public static byte[] ConcatBytes(params byte[][] arBytes)
        {
            long lLength = 0;
            long lPosition = 0;

            //Get total size required.
            foreach(byte[] ar in arBytes)
                lLength += ar.Length;

            //Create new byte array
            byte[] toReturn = new byte[lLength];
               
            //Fill the new byte array
            foreach(byte[] ar in arBytes)
            {
                ar.CopyTo(toReturn,lPosition);
                lPosition += ar.Length;
            }

            return toReturn;
        }

        /// <summary>
        /// Create a Message out of byte arrays (this makes more sense than the above method)
        /// </summary>
        /// <param name="sContentType">Content type ie multipart/report</param>
        /// <param name="sEncoding">The encoding provided...</param>
        /// <param name="sDisposition">The disposition of the message...</param>
        /// <param name="abMessageParts">The byte arrays that make up the components</param>
        /// <returns>The message as a byte array.</returns>
        public static byte[] CreateMessage(string sContentType, string sEncoding, string sDisposition, params byte[][] abMessageParts)
        {
            int iHeaderLength=0;
            return CreateMessage(sContentType, sEncoding, sDisposition, out iHeaderLength, abMessageParts);
        }
        /// <summary>
        /// Create a Message out of byte arrays (this makes more sense than the above method)
        /// </summary>
        /// <param name="sContentType">Content type ie multipart/report</param>
        /// <param name="sEncoding">The encoding provided...</param>
        /// <param name="sDisposition">The disposition of the message...</param>
        /// <param name="iHeaderLength">The length of the headers.</param>
        /// <param name="abMessageParts">The message parts.</param>
        /// <returns>The message as a byte array.</returns>
        public static byte[] CreateMessage(string sContentType, string sEncoding, string sDisposition, out int iHeaderLength, params byte[][] abMessageParts)
        {
            long lLength = 0;
            long lPosition = 0;

            //Only one part... Add headers only...
            if (abMessageParts.Length==1)
            {
                byte[] bHeader = ASCIIEncoding.ASCII.GetBytes(MIMEHeader(sContentType, sEncoding, sDisposition));
                iHeaderLength = bHeader.Length;
                return ConcatBytes(bHeader, abMessageParts[0]);
            }
            else
            {
                // get boundary and "static" subparts.
                string sBoundary = MIMEBoundary();
                byte[] bPackageHeader = ASCIIEncoding.ASCII.GetBytes(MIMEHeader(sContentType + "; boundary=\"" + sBoundary + "\"", sEncoding, sDisposition));
                byte[] bBoundary = ASCIIEncoding.ASCII.GetBytes(Environment.NewLine + "--" + sBoundary + Environment.NewLine);
                byte[] bFinalFooter = ASCIIEncoding.ASCII.GetBytes(Environment.NewLine + "--" + sBoundary + "--" + Environment.NewLine);

                //Calculate the total size required.
                iHeaderLength = bPackageHeader.Length;

                foreach(byte[] ar in abMessageParts)
                    lLength += ar.Length;
                lLength += iHeaderLength + bBoundary.Length*abMessageParts.Length +
                    bFinalFooter.Length;

                //Create new byte array to that size.
                byte[] toReturn = new byte[lLength];
               
                //Copy the headers in.
                bPackageHeader.CopyTo(toReturn, lPosition);
                lPosition += bPackageHeader.Length;

                //Fill the new byte array in by coping the message parts.
                foreach(byte[] ar in abMessageParts)
                {
                    bBoundary.CopyTo(toReturn, lPosition);
                    lPosition += bBoundary.Length;

                    ar.CopyTo(toReturn,lPosition);
                    lPosition += ar.Length;
                }

                //Finally add the footer boundary.
                bFinalFooter.CopyTo(toReturn, lPosition);

                return toReturn;
            }
        }

        /// <summary>
        /// Signs a message and returns a MIME encoded array of bytes containing the signature.
        /// </summary>
        /// <param name="arMessage"></param>
        /// <param name="bPackageHeader"></param>
        /// <returns></returns>
        public static byte[] Sign(byte[] arMessage, string signerCert, string signerPassword, out string sContentType)
        {
            byte[] bInPKCS7 = new byte[0];

            // get a MIME boundary
            string sBoundary = MIMEBoundary();

            // Get the Headers for the entire message.
            sContentType = "multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=\"sha1\"; boundary=\"" + sBoundary + "\"";
           
            // Define the boundary byte array.
            byte[] bBoundary = ASCIIEncoding.ASCII.GetBytes(Environment.NewLine + "--" + sBoundary + Environment.NewLine);
               
            // Encode the header for the signature portion.
            byte[] bSignatureHeader = ASCIIEncoding.ASCII.GetBytes(MIMEHeader("application/pkcs7-signature; name=\"smime.p7s\"", "base64", "attachment; filename=smime.p7s"));
   
            // Get the signature.
            byte[] bSignature = AS2Encryption.Encode(arMessage, signerCert, signerPassword);
           
            // convert to base64
            string sig = Convert.ToBase64String(bSignature) + MESSAGE_SEPARATOR;
            bSignature = System.Text.ASCIIEncoding.ASCII.GetBytes(sig);

            // Calculate the final footer elements.
            byte[] bFinalFooter = ASCIIEncoding.ASCII.GetBytes("--" + sBoundary + "--" + Environment.NewLine);

            // Concatenate all the above together to form the message.
            bInPKCS7 = ConcatBytes(bBoundary, arMessage, bBoundary,
                bSignatureHeader, bSignature, bFinalFooter);

            return bInPKCS7;
        }
    }
}

Anyway, I hope this helps someone out there because I couldn’t find a sample of how to send an AS2 message in .NET.

A member of the db_owner role must use… error in Visual Studio 2010

I keep getting this error message pop up in Visual Studio – “A member of the db_owner role must use the Visual Studio database diagramming functionality in order to set up the required database diagramming objects on the SQL Server.”

Typically it would happen whenever I drag a table from the Server Explorer onto the LINQ To Sql designer dbml. The error displays, and then after that the Server Explorer window locks up with the Windows 7 hourglass aka the toilet bowl. I can keep using Visual Studio but I can’t do anything in Server Explorer so I eventually have to restart Visual Studio 2010.

I was a bit confused because 1. I’m a dbo, and 2. I’d already setup diagramming in SQL Server Management Studio 2008.

I eventually realised – the solution I’m working on has two DB connections in Server Explorer – my application’s main db for settings etc (which is the one I’m working with when the error is thrown), and another read-only external database which my application reads data from. That external db doesn’t have diagramming set up yet so I presume VS is complaining about that.

There are two workarounds – 1. ask a dbo to set up diagramming on the other db, or 2. remove the connection to the other db from Server Explorer.

I did option 2, and the downside is I have to re-add the connection whenever I want to update my dbml. Fortunately I don’t have to do that very often.

Using jsTree to get a Treeview with checkboxes in ASP.NET MVC

UPDATE 17 December 2011!

After one year this is still the most popular post on my blog. However, this uses jsTree v0.9.9a2 and jsTree is now in version v1.0 which has changed completely. This post does not work with jsTree v1.0!!!

I have written a post using the latest version of jsTree here

jsTree is a jQuery plugin for creating a treeviews, and jsTree’s checkbox plugin allows you to create a treeview with tri-state checkboxes, like so:

Notice how “Origination” appears half-checked because only some of its children are checked.

Getting started

For this demo I am using ASP.NET MVC 2 and jsTree v0.9.9a2. Let’s start with a new “ASP.NET MVC 2 Web Application”, name it jsTreeDemo, and add the required jsTree files to our solution:

In the View, create a div which you want to become a treeview. I’ll name mine demoTree. Also add references to the required jQuery and jsTree scripts:

Views/Home/Index.aspx

<asp:content id="Content2" runat="server" contentplaceholderid="MainContent">
 <h2><%: ViewData["Message"] %></h2>
 <div id="demoTree">
 </div>
 <script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
 <script src="../../Scripts/jquery.tree.js" type="text/javascript"></script>
 <script src="../../Scripts/plugins/jquery.tree.checkbox.js" type="text/javascript"></script>
</asp:content>

Next, create index.js, add a reference to it in Index.aspx, and add the following code:

Scripts/index.js

/// <reference path="http://ajax.microsoft.com/ajax/jQuery/jquery-1.4.1-vsdoc.js"/>

$(function () {
  $("#demoTree").tree({
   ui: {
     theme_name: "checkbox"
   },
   data: {
     type: "json",
     opts: {
       static: [
           {
              data: "Origination",
              children: [
                { data: "New Connection" },
                { data: "Disconnection" },
                { data: "Load Change" },
                { data: "Corporate" },
              ]
            },
            {
              data: "Confirm Application"
            }
       ]
     }
   },
   plugins: {
     checkbox: {}
   }
  });
});

This should create a basic treeview with checkboxes, with static data. Not very interesting yet.

Populating the tree with an AJAX request

Let’s create an ActionMethod to return some JSON data and use that data to populate our treeview. The jsTree documentation specifies how the JSON data should look but to help clarify, here’s an example:

[{"data":"Origination","attributes":{"id":"10"}",children":[
                         {"data":"New Connection","attributes":{"id":"11"}},
                         {"data":"Disconnection","attributes":{"id":"12"}},
                         {"data":"Load Change","attributes":{"id":"13"}},
                         {"data":"Corporate","attributes":{"id":"14"}}
                         ]},
{"data":"Confirm Application","attributes":{"id":"20"}}
...]

While it might be possible to write a LINQ query with anonymous types to return this data, it quickly gets rather hairy, so to simplify things let’s create a model to help us construct the data correctly. In the Models folder, add the following classes:

Models/JsTreeModel.cs

namespace jsTreeDemo.Models
{
 public class JsTreeModel
 {
   public string data;
   public JsTreeAttribute attributes;
   public JsTreeModel[] children;
 }

 public class JsTreeAttribute
 {
   public string id;
   public bool selected;
 }
}

In Controllers/HomeController.cs add the following function:

Controllers/HomeController.cs

[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GetTreeData()
{
 var tree = new JsTreeModel[]
 {
   new JsTreeModel {
     data = "Origination",
     attributes = new JsTreeAttribute  { id="10"},
     children = new JsTreeModel[]
     {
       new JsTreeModel { data = "New Connection", attributes = new JsTreeAttribute { id="11"} },
       new JsTreeModel { data = "Disconnection", attributes = new JsTreeAttribute { id="12", selected=true } },
       new JsTreeModel { data = "Load Change", attributes = new JsTreeAttribute { id="13"} },
       new JsTreeModel { data = "Corporate", attributes = new JsTreeAttribute { id="14", selected=true} },
     }
   },
   new JsTreeModel {
     data = "Confirm Application",
     attributes = new JsTreeAttribute { id="20" }
   },
   new JsTreeModel {
     data = "Things",
     attributes = new JsTreeAttribute  { id="30", selected=true },
     children = new JsTreeModel[]
     {
       new JsTreeModel { data = "Thing 1", attributes = new JsTreeAttribute { id="31"} },
       new JsTreeModel { data = "Thing 2", attributes = new JsTreeAttribute { id="32"} },
       new JsTreeModel { data = "Thing 3", attributes = new JsTreeAttribute { id="33"} },
       new JsTreeModel { data = "Thing 4", attributes = new JsTreeAttribute { id="34"} },
     }
   },
   new JsTreeModel {
     data = "Colors",
     attributes = new JsTreeAttribute  { id="40"},
     children = new JsTreeModel[]
     {
       new JsTreeModel { data = "Red", attributes = new JsTreeAttribute { id="41"} },
       new JsTreeModel { data = "Green", attributes = new JsTreeAttribute { id="42"} },
       new JsTreeModel { data = "Blue", attributes = new JsTreeAttribute { id="43"} },
       new JsTreeModel { data = "Yellow", attributes = new JsTreeAttribute { id="44"} },
     }
   }
 };

 return Json(tree);
}

Now we need to change our javascript to tell it to use GetTreeData:

Scripts/index.js


  $("#demoTree").tree({
   ui: {
     theme_name: "checkbox"
   },
   data: {
     type: "json",
     opts: {
         method: "POST",
         url: "/Home/GetTreeData"
     }
   },
...

Now our tree should be populated with the same data as Figure 1.

Determining which items are checked when posting

Let’s put our tree inside a <form> and submit it.

Views/Home/Index.aspx

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 <h2><%: ViewData["Message"] %></h2>

 <% using (Html.BeginForm("Submit", "Home", FormMethod.Post, new { id = "frmTree" }))
 { %>
   <div id="demoTree" style="height:300px">
   </div>
   <div>
     <input type="submit" value="Submit" id="btnSubmit" />
   </div>
 <% } %>

 <script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
 <script src="../../Scripts/jquery.tree.js" type="text/javascript"></script>
 <script src="../../Scripts/plugins/jquery.tree.checkbox.js" type="text/javascript"></script>
 <script src="../../Scripts/index.js" type="text/javascript"></script>

</asp:Content>

And now let’s add the Submit method to the HomeController

Controllers/HomeController.cs

[AcceptVerbs(HttpVerbs.Post)]
 public ActionResult Submit(FormCollection form)
 {
   return View(form);
 }

and the View

Views/Home/Submit.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<FormCollection>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Submit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

 <h2>Submitted</h2>
 You chose:
 <% foreach (var item in Model)
    { %>
      <%: item %>
 <% } %>
</asp:Content>

If you press the Submit button, nothing will happen as nothing is passed through in the FormCollection to Submit() in HomeController. Despite appearances, the jsTree doesn’t actually render any HTML <input>s for the checkboxes. So we need to write some javascript to call jsTree’s get_checked() function to figure out which nodes are checked, and then add them to the form somehow. One way to add stuff to the form is to generate a hidden field, since hidden fields within a form are posted. This code generates hidden <input>s with the same name as each of the checked items, and adds them to the form.

Scripts/index.js

function generateHiddenFieldsForTree(treeId) {
  $.tree.plugins.checkbox.get_checked($.tree.reference("#" + treeId)).each(function () {
    var checkedId = this.id;
    $("<input>").attr("type", "hidden").attr("name", checkedId).val("on").appendTo("#" + treeId);
  });
}

and bind it to the form’s submit event:

Scripts/index.js

$(function () {
  $("#frmTree").submit(function () { generateHiddenFieldsForTree("demoTree"); });
}

Now when we submit our form, we should see the ids of the values which were checked:

What about telling the tree to pre-check some items?

So how do we render the tree with some items already checked? My colleague Paul came up with this problem and the solution. Notice how we added “public bool selected” to JsTreeAttribute? This doesn’t do anything as far as the checkboxes are concerned, but it does add a custom property called “selected” to each node’s <li>. We can use that to tell the jsTree to check the given node, by using the jsTree’s callback option.

Scripts/index.js


$("#demoTree").tree({
   ui: {
     theme_name: "checkbox"
   },
   data: {
     type: "json",
     opts: {
         method: "POST",
         url: "/Home/GetTreeData"
     }
   },
   plugins: {
     checkbox: {}
   },
   callback: {
     onload: function (tree) {
       $('li[selected=true]').each(function () {
         $.tree.plugins.checkbox.check(this);
       });
     }
   }
  });

You’ll see that the nodes that are marked with selected=true in GetTreeData() are now checked when you load the page.

Here’s a link to the solution (VS 2010) with code samples.

UPDATE 17 December 2011!

After one year this is still the most popular post on my blog. However, this uses jsTree v0.9.9a2 and jsTree is now in version v1.0 which has changed completely. This post does not work with jsTree v1.0!!!

I have written a post using the latest version of jsTree here

“You cannot write updates to the target” in Visual Studio 2010

First, the good news. Data dude is now included in Visual Studio 2010.

If you get a message “You cannot write updates to the target when you compare the specified types of schema models” when you try and import your database into a data dude project, it’s because you’ve done a schema compare of your database against a “SQL Server Data-tier Application” instead of a “SQL Server 2008 Database” project.

UPDATE: you might also get this error message if you import the schema from a SQL 2005 database into a SQL Server 2008 Database Project.