Using Vermeer Content Types and Impersonation to handle SharePoint Document Uploads

Slalom Consultant Marc Schuricht

Slalom Consultant Marc Schuricht is a Denver based software Solution Architect with over 12 years of professional experience in IT projects ranging from configuration and change management to custom enterprise application development.

I had an interesting request from a client today. I am maintaining an existing project that has been in production for a few years now. This is one of those situations where I have a limited budget and time and also I cannot fundamentally change the overall process. These caveats aside, nothing out of the ordinary, but interesting nonetheless.

First a little background.

This project involves a simple approval workflow that controls routing and notifications for documents being placed into two document libraries. Furthermore, an out of the box SharePoint task list collects and manages workflow tasks.  When approved, an event receiver on the task list for the workflow routes documents to the “Approved” document library, otherwise, it routes them to the “Further Review” document library. Then, when a document is added to either library, a new workflow is automatically initiated– remember the caveats above. This workflow adds another task to the task list to be evaluated by the event receiver. This time, the event receiver on the task list sends an email to the original document submitter notifying them that they either need to review the document, and make changes, or it was successfully submitted.

Keep in mind this was working, until….

Client stands up new farm and everything seems to be working, with the exception that no emails are being sent from the item event receiver. Digging into it a little more, it turns out the new production farm is SP2 whereas the old (Legacy) farm was RTM. As of MOSS 2007 SP1, workflows cannot be automatically initiated by the “System Account.” The workflow should be auto-initiated when a new document is added to one of the two document libraries I mentioned earlier.  However, the item event receiver is using the following method to add documents to the document library:

1:  SPSecurity.RunWithElevatedPrivileges(delegate(){
2:      //... prepare document and do some other preprocessing
3:      // skipping for brevity
4:      web.Folders("Review").Files.Add(documentUrl, document, contents);
5:  });

Which causes the document library to be added to the document library by the “System Account” and consequently, the workflow is not initiated.

Smart consultant guy I am, I say, let’s get rid of the elevated privileges and use SharePoint permissions and impersonation to handle document upload. Unfortunately, I am stricken with “Administrator Fever” where I am only used to working in an environment where I am the administrator. We roll to Test and get lots of smoke a little fizzle and small *pop*. We’re getting access denied errors, COMPlusExceptions – 80004004 (E_ABORT) specifically — love those — and generally no bueno. After digging through more archived blogs and cached Google sites than I can recall, I remember needing to give WSS_WPG and WSS_WPG_ADMIN and physical database access to run code. This will never work in a real production environment and I would have to hang my head in consulting shame if I went this direction. Then I came across an interesting MSDN article regarding Vermeer content types – ahh, I see you too have never heard of them.

The gist is that X-Vermeer-Content-Type is how we can make RPC calls to the SharePoint API without needing SharePoint. Wha…? you ask…I’ll go deeper. Just like specifying “application/vnd.ms-excel” as the MIME content type for automating Excel when providing a CSV through an HTTP GET, you can essentially do the same thing with SharePoint 2007 or 2010 by using “application/x-vermeer-urlencoded”. In fact, interacting with SharePoint 2010 in this way is not only supported, it is a recommended approach for ISVs and OEMs. Microsoft has posted copious amounts of documentation about how this is done:

For my purposes, I will use the put+document method to solve my problem. Here is a link to the Microsoft documentation about it:

I can dynamically build out my HTTP PUT with the correct content type and then I am able to upload documents to a SharePoint as the impersonated user and I can control security through the normal SharePoint interface. Here is the end result:

 1:  public static bool Upload(string webUrl,
 2:                              string documentName,
 3:                              byte[] bytes,
 4:                              Dictionary metaInfo,
 5:                              out string result)
 6:    {
 7:    string putOption = "overwrite,createdir,migrationsemantics";
 8:    // see http://msdn2.microsoft.com/en-us/library/ms455325.aspx
 9:    string comment = null;
10:    bool keepCheckedOut = false;
11:    string method =
12:        @"method=put+document%3a12.0.4518.1016&service_name=%2f&
13:            document=[document_name={0};
14:            meta_info=[{1}]]&
15:            put_option={2}&
16:            comment={3}&
17:            keep_checked_out={4}\n";
18:    method = String.Format(method, documentName, EncodeMetaInfo(metaInfo), putOption,
19:    HttpUtility.UrlEncode(comment), keepCheckedOut.ToString().ToLower());
20:    var data = new List();
21:    data.AddRange(Encoding.UTF8.GetBytes(method));
22:    data.AddRange(bytes);
23:    try
24:        {
25:            using (var webClient = new WebClient())
26:            {
27:                webClient.Credentials = CredentialCache.DefaultCredentials;
28:                webClient.Headers.Add("Content-Type", "application/x-vermeer-urlencoded");
29:                webClient.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");
30:                result = Encoding.UTF8.GetString(webClient.UploadData(webUrl + "/_vti_bin/_vti_aut/author.dll", "POST", data.ToArray()));
31:                if (result.IndexOf("\n<p>message=successfully") < 0)
32:                    throw new Exception(result);
33:                }
34:            }
35:            catch (Exception ex)
36:            {
37:                result = ex.Message;
38:                return false;
39:            }
40:        return true;
41:    }


The key here is two-fold:

1. Specifying the content type:

webClient.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");

2. Building out the method correctly and appending the byte[] contents of the document to the end of the method:

string method =  @"method=put+document%3a12.0.4518.1016&service_name=%2f&
  document=[document_name={0};
  meta_info=[{1}]]&
  put_option={2}&
  comment={3}&
  keep_checked_out={4}\n";
     ...
data.AddRange(Encoding.UTF8.GetBytes(method));
data.AddRange(bytes);

Other than that, it’s a pretty straight forward HTTP PUT (webClient.UploadData).

I hope this is useful to you! Let me know if you have any questions or other ideas about how to do this.

Slalom Consulting's Denver office Slalom Consulting has 6 Gold Microsoft Partner Competencies
More about Slalom Consulting’s Denver office. More about Slalom Consulting’s Microsoft Partnership.

subscribe by emailSubscribe to be emailed about new SharePoint posts.

2 Responses to Using Vermeer Content Types and Impersonation to handle SharePoint Document Uploads

  1. James Smoth says:

    Nice work on the article. Your thoughts on the upcoming box.net? Being hearing a lot of buzz on them.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 130 other followers

%d bloggers like this: