<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Simply Does Not Work &#187; Rants</title>
	<atom:link href="http://nicholas.piasecki.name/blog/category/rants/feed/" rel="self" type="application/rss+xml" />
	<link>http://nicholas.piasecki.name/blog</link>
	<description>Confessions of a small business software developer</description>
	<lastBuildDate>Sat, 26 Feb 2011 16:06:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.5</generator>
		<item>
		<title>A brief rant on credit card security</title>
		<link>http://nicholas.piasecki.name/blog/2011/02/a-brief-rant-on-credit-card-security/</link>
		<comments>http://nicholas.piasecki.name/blog/2011/02/a-brief-rant-on-credit-card-security/#comments</comments>
		<pubDate>Sat, 26 Feb 2011 16:06:59 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[e-commerce]]></category>
		<category><![CDATA[fraud]]></category>
		<category><![CDATA[payments]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=833</guid>
		<description><![CDATA[I wish that the Address Verification System (AVS) worked well, that it worked against shipping instead of billing addresses (i.e., cardholders could maintain a list of valid shipping addresses online with their bank), and that it was required to process all card-not-present transactions (because this might give banks an incentive to make AVS actually work [...]]]></description>
			<content:encoded><![CDATA[<p>I wish that the Address Verification System (AVS) worked well, that it worked against <em>shipping</em> instead of billing addresses (i.e., cardholders could maintain a list of valid shipping addresses online with their bank), and that it was required to process all card-not-present transactions (because this might give banks an incentive to make AVS actually work well all of the time, when their cardholders start complaining that e-commerce stores are flagging all of their transactions, and for consumers to maintain updated address information at their banks).</p>

<p>To stop fraud, we need to stop allowing purchases to be made solely with the information that appears on the card because this is not secret information; it is basic information about an account. This is not something that merchants can fix alone. Merchants already have to foot the bill for chargebacks, and PCI standards are really just an attempt to make merchants responsible for maintaining the security of what is, at its core, a payment system that broke in the 1990s. CVN didn&#8217;t work&#8211;10 years later, we still have to explain to consumers where the number is because the card associations have not spent enough effort educating the public and, quite frankly, they don&#8217;t care. To use a credit card, anywhere, you should have to swipe it (PCI is still relevant in this scenario). Period.</p>

<p>Two-factor authentication, in the traditional sense, is not a solution. 3-D Secure is confusing for both merchants and consumers, expensive, and little used: for the program to work, it needs to be required. A better solution would be programs like Discover&#8217;s Secure Online Account Number (where you can generate a temporary account number), but they fucked that up by making the temporary account numbers expire on the same day as the real account number (to be effective, they should expire after a certain number of transactions or be able to be locked down to a specific merchant). And for such a program to work, this also needs to be required, too.</p>

<p>It&#8217;s time for consumers and banks to grow up and deal with credit card fraud because the problems with it today are largely ones that merchants cannot and no amount of obfuscating cardholder data can fix. Unfortunately, at the end of the day, it&#8217;s the merchant who usually ends up paying for the fraudulent charges when the dime finally drops, so we&#8217;ll continue to see little help from the card associations and the issuing banks: no one has the balls to make sweeping consumer- and bank-initiated security changes to the industry and <em>require</em> those changes for future transactions, unless they&#8217;re pointing their fingers at the merchants.</p>

<p>I guess we&#8217;ll just have to keep bending over and taking it&#8211;the responsibility, the consequences of fraud, and all of the blame when the system doesn&#8217;t work.</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2011/02/a-brief-rant-on-credit-card-security/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>DKIM Signing Outbound Messages in Exchange Server 2007</title>
		<link>http://nicholas.piasecki.name/blog/2010/12/dkim-signing-outbound-messages-in-exchange-server-2007/</link>
		<comments>http://nicholas.piasecki.name/blog/2010/12/dkim-signing-outbound-messages-in-exchange-server-2007/#comments</comments>
		<pubDate>Fri, 24 Dec 2010 00:55:26 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[dkim]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[exchange]]></category>
		<category><![CDATA[it-stuff]]></category>
		<category><![CDATA[sbs-2008]]></category>
		<category><![CDATA[skiviez]]></category>
		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=768</guid>
		<description><![CDATA[DomainKeys Identified Mail is a proposed standard that, in its own words, allows a domain (such as skiviez.com) to assert responsibility for the contents of an e-mail message. The outgoing mail transport agent (&#8220;mail server&#8221;) computes a hash of the body of the mail message and adds a special, cryptographically signed DKIM-Signature header to the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.dkim.org/">DomainKeys Identified Mail</a> is a <a href="http://www.dkim.org/specs/rfc4871-dkimbase.html">proposed standard</a> that, in its own words, allows a domain (such as <code>skiviez.com</code>) to assert responsibility for the contents of an e-mail message.</p>

<p>The outgoing mail transport agent (&#8220;mail server&#8221;) computes a hash of the body of the mail message and adds a special, cryptographically signed <code>DKIM-Signature</code> header to the mail message that contains this hash as well as some other information about the message. The public part of the key, used by receiving mail transport agents to verify the signature on incoming messages, is stored as a <code>TXT</code> record in the DNS for the signing domain.</p>

<p>The result of all of this is that the recipient can now know that the body and certain headers of the e-mail message were not tampered with or changed by a third party while the message was in transit. If the signature contained within the <code>DKIM-Signature</code> header doesn&#8217;t verify, then it&#8217;s possible that the message is a phishing message, spam, or some other false representation of members of the signing domain. (IMHO, however, it is more likely to just indicate a broken DKIM implementation, as there are several obnoxious corner cases that we will see.)</p>

<h2>Where is the Exchange support?</h2>

<p>No currently released version of Microsoft Exchange supports DKIM natively, not even 2010. This is purportedly because Microsoft threw its weight behind another standard called <a href="http://www.openspf.org/">SPF</a>, or Sender Protection Framework. SPF is much simpler to implement because it does not make a statement about the integrity of the message contents; that is, there is no signing step that requires processing of each outbound message and stamping it with a special header. Instead, a <code>TXT</code> record in the DNS for the sending domain lists the IP addresses from which e-mail messages from that domain are allowed to originate.</p>

<p>For example, let&#8217;s say that a store has a mail transport agent at <code>mail.example.com</code> and a Web site at <code>www.example.com</code>. An SPF record for <code>example.com</code> might make a statement like the following:</p>

<p>&#8220;E-mail messages purporting to be from @example.com should only originate from either <code>mail.example.com</code> or <code>www.example.com</code>. All other originating IP addresses should be treated with suspicion.&#8221;</p>

<p>You can see why this is easier to implement; you just add an entry to DNS, and you don&#8217;t have to configure the outbound mail transport agent to do anything. The only reason a mail transport agent would need to change is for it to gain the ability to inspect <em>incoming</em> messages and validate the <code>MAIL FROM</code> part of the message headers against the corresponding SPF <code>TXT</code> records in DNS. And that is what Exchange does today.</p>

<p>While SPF does not validate the integrity of the message&#8211;a hacked, legitimate company mail server that spews out spam unwittingly will still pass an SPF&#8211;it is a simple, convenient way to mark phishing and spoofing attempts originating from other locations, like a random house in Wisconsin that is unwittingly part of a botnet, with suspicion.</p>

<p>But, then again, a hacked mail transport agent using DKIM that spews out spam unwittingly will still pass a DKIM verification. Spam can be signed just like regular mail. So the advantage of DKIM over SPF is that DKIM provides the ability to prove that the contents of the message were not modified in transit, and if an organization so chose, they could sign different messages with different keys or decide to not sign certain kinds of messages at all, allowing the recipient to interpret these different kinds of mail in different ways.</p>

<p>In reality, most e-mail doesn&#8217;t need to be <em>that</em> secure, so the utility-to-difficulty ratio may be a little out-of-whack here, especially when other standards for signing messages already exist.</p>

<h2>So DKIM is more complicated and not supported by Microsoft. Why implement DKIM on Exchange?</h2>

<p>Because fuck <a href="http://www.yahoo.com/">Yahoo!</a>, that&#8217;s why.</p>

<p>Actually, let&#8217;s step back a minute.</p>

<p>Yahoo! invented something called <a href="http://domainkeys.sourceforge.net/">Domain Keys</a>, which predates DKIM and which DKIM is largely based on, in an attempt to combat spam in the ways described previously. The aim was for Yahoo! mail servers to be able to easily identify entire classes of suspect e-mail and send them to people&#8217;s spam folders (which, in Yahoo! weasel words, is referred to as the &#8220;bulk mail folder&#8221;). Then they integrated it into the Yahoo! mail system.</p>

<p>If your business sends mail of any kind of volume to Yahoo! mail servers, such as an e-mail newsletter that users subscribe to, you&#8217;ll quickly see a message along the lines of the following:</p>

<p>421 4.16.55 [TS01] Messages from x.x.x.x temporarily deferred due to excessive user complaints</p>

<p>And you might also notice your e-mail messages going straight to the spam folder on Yahoo! mail accounts by default, with a <code>X-Yahoo-Filtered-Bulk: x.x.x.x</code> header appearing in the message headers. If this has happened, it is because Yahoo! has decided that you are naughty and placed the IP address of your mail transport agent on an internal blacklist. If a Yahoo! user has previously marked e-mail from your domain as &#8220;Not Spam,&#8221; then that user will continue to receive your e-mail, but any other Yahoo! users will find your e-mail going to the spam folder by default until they either click the &#8220;Not Spam&#8221; button on one of them or add your from address to their contact list.</p>

<p>&#8220;But we&#8217;re a legitimate small business,&#8221; you say. &#8220;Only about 5,000 of the e-mails on our mailing list are actual Yahoo! addresses, the newsletter list is opt-in, and there are &#8216;Unsubscribe&#8217; links in every e-mail. It&#8217;s not like we&#8217;re spamming users out of the blue,&#8221; you continue. &#8220;These are users who have asked for this service and can cancel at anytime. So why has Yahoo! blacklisted us and treat us like spam?&#8221;</p>

<p>The answer, unfortunately, is that Yahoo! is incompetent and can&#8217;t build a mail service that can handle spam like Gmail can handle spam. Instead, their e-mail system depends on the following metrics:</p>

<ul>
<li>If a large burst of e-mail comes from the same IP address within a certain time period, temporarily consider it as a spam and temporarily deny the requests. The mail will still arrive, but the &#8220;fat pipe&#8221; will be squashed down so that overall delivery takes longer to complete. The idea is that if the mail is originating from a spammer, they are unlikely to try again.</li>
<li>If a &#8220;significant&#8221; number of Yahoo! users mark mail messages originating from the same IP address as spam by clicking on the &#8220;Spam&#8221; button in their account, add that IP address to the <code>X-Yahoo-Filtered-Bulk</code> blacklist.</li>
</ul>

<p>In other words, if enough people mark your e-mail as spam, your IP address is fucked, and you&#8217;ll soon be fielding complaints from customers who insist that they aren&#8217;t receiving your Web sites transactional e-mails, such as a &#8220;forgot my password&#8221; or &#8220;shipping confirmation&#8221; e-mail, because Yahoo! has now decided to deliver all e-mails that originate from your IP address to the spam folder, which many users do not think to check or cannot find. So they blame you.</p>

<p>If you think that Yahoo! has blacklisted you in error, or if you have improved your e-mail sending practices and wish to have them consider you for removal, you can fill out <a href="http://help.yahoo.com/l/us/yahoo//mail/postmaster/defer.html">this form with incorrect JavaScript required field validation</a> to request that the Yahoo! postmaster take you off the naughty list. About 27 hours later, a Yahoo! representative with a sub-room temperature IQ will e-mail you back a long, canned reply that generally amounts to &#8220;No.&#8221;</p>

<p>But in that &#8220;no&#8221; message, they offer the possibility of signing up to the <a href="http://feedbackloop.yahoo.net/">Complaint Feedback Loop</a>. When you participate in this program, if a Yahoo! user presses the &#8220;Spam&#8221; button on one of your e-mails, then Yahoo! will e-mail you the e-mail address of that user along with a copy of the e-mail that you sent. You can then unsubscribe that user from your mailing list yourself (since they neglected to use the unsubscribe link in your e-mail and will continue to damage the reputation of your IP address if you continue to send mail to them), or you can see which kinds of e-mails you are sending are creating the most problems.</p>

<p>But participation in their feedback loop requires that you use DKIM to sign all of your outbound mail messages. The reasons for this are not clear, but I assume it has to do with ensuring that you are only notified about the problematic e-mails that legitimately came from your organization, and not ones generated by a spammer. Or, it could simply be a way to drive adoption of Yahoo!&#8217;s DKIM standard.</p>

<p>At any rate, Yahoo! mail is the most widely used e-mail service on the planet, so when the idiots in the higher echelons of Yahoo! say &#8220;Jump!&#8221; then we must ask &#8220;How high?&#8221; When they say &#8220;implement DKIM&#8221;, we implement DKIM.</p>

<h2>So what are options for implementing DKIM on Exchange?</h2>

<p>One option is to simply not do it in Exchange and set up a relaying mail server that has DKIM support, like <a href="http://www.hmailserver.com/">hMailServer</a> or <a href="http://www.postfix.org/">Postfix</a> with dkim-milter. But if you&#8217;re at a small business, the idea of maintaining yet another server for what is conceptually a simple task is not a pleasant thought.</p>

<p>Another option is to use a dedicated device like a <a href="http://www.barracudanetworks.com/ns/products/spam_overview.php">Barracuda</a> or an <a href="http://www.ironport.com/">IronPort</a>. This device would sit in front of Exchange and rewrite the mail headers in transit, adding the <code>DKIM-Signature</code> header as it flies out of your office. But these devices are not cheap and are out of reach of many small businesses. And the thought of acquiring a specialized device for doing something any mail transport agent should be able to do natively is not a pleasant thought.</p>

<p>You can buy an off-the-shelf plug-in for Exchange like <a href="http://www.emailarchitect.net/domainkeys/">this one from a company in Hong Kong</a>. The reviews on the Internet do generally seem to indicate that it does work. But do we really want to spend $300 &#8211; $800 on a component from a company without a reputation and give that component read access to all of our organization&#8217;s outbound mail messages? Trust is certainly a driving factor here.</p>

<p>A fourth option, and the option I pursued since I considered giving up on DKIM if Yahoo! didn&#8217;t change its ways after our implementation, is to take advantage of Exchange 2007&#8242;s new <a href="http://technet.microsoft.com/en-us/library/bb125012%28EXCHG.80%29.aspx">Transport Agents</a> functionality. This allows you to write custom, managed code that runs on that .NET Framework that integrates with the Exchange message processing pipeline. In our case, we could write a custom transport agent that appends a <code>DKIM-Signature</code> header to outgoing MIME messages.</p>

<h2>Setting up the project</h2>

<p>In Visual Studio, we just need to use a plain old &#8220;C# Class Library&#8221; project. The project <em>must</em> target version 2.0 of the CLR (that&#8217;s .NET Framework 2.0, .NET Framework 3.0, or .NET Framework 3.5, thanks to the dipshits in Microsoft&#8217;s marketing department) and <em>not</em> the .NET Framework 1.x or, maddeningly, the .NET Framework 4.x. This is because the transport agent process provided by Exchange Server 2007 that will load our transport agent is running on CLR 2.0, and a process that&#8217;s been loaded with an earlier version of the CLR can&#8217;t load a later version.</p>

<p>We also need to reference two assemblies provided by the Exchange Server, <code>Microsoft.Exchange.Data.Common.dll</code> and <code>Microsoft.Exchange.Data.Transport.dll</code>. You would think that you would be able to find these in, say, the Exchange Server SDK that&#8217;s available from Microsoft Downloads. But you&#8217;d be wrong. Microsoft keeps diddling with the version of these assemblies with various update rollups for Exchange, and the only way to get a copy is to pull them off an actual Exchange Server 2007 installation. Mine were located in <code>C:\Program Files\Microsoft\Exchange Server\Public</code>; I just copied them to my local computer and referenced them in my new class library assembly for local development.</p>

<p>To start, we just need to create two classes. The first is our actual agent, which derives from the <code>RoutingAgent</code> class defined in the DLLs we just copied:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #0600FF; font-weight: bold;">sealed</span> <span style="color: #6666cc; font-weight: bold;">class</span> DkimSigningRoutingAgent <span style="color: #008000;">:</span> RoutingAgent
<span style="color: #008000;">&#123;</span>
	<span style="color: #0600FF; font-weight: bold;">public</span> DkimSigningRoutingAgent<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
		<span style="color: #008080; font-style: italic;">// What &quot;Categorized&quot; means in this sense,</span>
		<span style="color: #008080; font-style: italic;">// only the Exchange team knows</span>
		<span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">OnCategorizedMessage</span> <span style="color: #008000;">+=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">WhenMessageCategorized</span><span style="color: #008000;">;</span>
	<span style="color: #008000;">&#125;</span>
&nbsp;
	<span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #6666cc; font-weight: bold;">void</span> WhenMessageCategorized<span style="color: #008000;">&#40;</span>
		CategorizedMessageEventSource source,
		QueuedMessageEventArgs e<span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
		<span style="color: #008080; font-style: italic;">// This is where the magic will happen</span>
	<span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>The second is a factory class that the Exchange server&#8217;s little plug-in agent looks for; it&#8217;ll instance new copies of our agent:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #0600FF; font-weight: bold;">sealed</span> <span style="color: #6666cc; font-weight: bold;">class</span> DkimSigningRoutingAgentFactory <span style="color: #008000;">:</span> RoutingAgentFactory
<span style="color: #008000;">&#123;</span>
	<span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #0600FF; font-weight: bold;">override</span> RoutingAgent CreateAgent<span style="color: #008000;">&#40;</span>SmtpServer server<span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
		<span style="color: #0600FF; font-weight: bold;">return</span> <span style="color: #008000;">new</span> DkimSigningRoutingAgent<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
	<span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>You&#8217;re probably thinking, &#8220;Wow! <a href="http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.transport.routing.routingagent%28v=EXCHG.80%29.aspx">The documentation provided by the Exchange team sure blows donkey chunks</a>, how did you ever figure out that that was what you needed to do?&#8221; The answer is by spelunking through the <a href="http://msdn.microsoft.com/en-us/exchange/default">Exchange Server SDK</a> examples and by religiously following <a href="http://msexchangeteam.com/archive/2006/12/04/431755.aspx">this guy&#8217;s blog</a>.</p>

<p>Now we&#8217;re ready to start reading mail messages and &#8220;canonicalizing&#8221; them into the format that the DKIM spec expects.</p>

<h2>Exchange and &#8220;Internet Mail&#8221;</h2>

<p>If there is one thing that is annoying about Exchange, it is that 20-some years after the fact it seems skeptical that this whole &#8220;Internet Mail&#8221; thing is going to catch on. Getting Exchange to give you the raw message in MIME format is not as simple as one might think.</p>

<p>You see, to be able to hash the body of the message and sign certain headers in the message, we need to know exactly how Exchange is going to format it when it is sent. Even a single space or newline that is added after we compute the hash can throw the whole thing off.</p>

<p>In our routing agent&#8217;s <code>OnCategorizedMessage</code> event listener, the event arguments give us access to an instance of the <code>MailItem</code> class. It has a boatload of properties for accessing the body and headers of the message programmatically. Unfortunately, we can&#8217;t use these properties because they represent the <em>semantic</em> values, not the <em>raw</em> ones. Instead, we&#8217;ll need to use the <code>GetMimeReadStream()</code> and <code>GetMimeWriteStream()</code> methods to read the raw mail message and write out the modified version, respectively.</p>

<h2>Implementing the routing agent</h2>

<p>Let&#8217;s start by completing the Routing Agent implementation. We&#8217;ll keep it simple by moving all of the hard signing stuff into an <code>IDkimSigner</code> interface, which we&#8217;ll worry about implementing later:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #0600FF; font-weight: bold;">sealed</span> <span style="color: #6666cc; font-weight: bold;">class</span> DkimSigningRoutingAgent <span style="color: #008000;">:</span> RoutingAgent
<span style="color: #008000;">&#123;</span>
    <span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #0600FF; font-weight: bold;">static</span> ILog log <span style="color: #008000;">=</span> LogManager<span style="color: #008000;">.</span><span style="color: #0000FF;">GetLogger</span><span style="color: #008000;">&#40;</span>
        MethodBase<span style="color: #008000;">.</span><span style="color: #0000FF;">GetCurrentMethod</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">.</span><span style="color: #0000FF;">DeclaringType</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">private</span> IDkimSigner dkimSigner<span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">public</span> DkimSigningRoutingAgent<span style="color: #008000;">&#40;</span>IDkimSigner dkimSigner<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>dkimSigner <span style="color: #008000;">==</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ArgumentNullException<span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;dkimSigner&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #008000;">&#125;</span>
&nbsp;
        <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">dkimSigner</span> <span style="color: #008000;">=</span> dkimSigner<span style="color: #008000;">;</span>
&nbsp;
        <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">OnCategorizedMessage</span> <span style="color: #008000;">+=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">WhenMessageCategorized</span><span style="color: #008000;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #6666cc; font-weight: bold;">void</span> WhenMessageCategorized<span style="color: #008000;">&#40;</span>
        CategorizedMessageEventSource source,
        QueuedMessageEventArgs e<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">try</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">SignMailItem</span><span style="color: #008000;">&#40;</span>e<span style="color: #008000;">.</span><span style="color: #0000FF;">MailItem</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #008000;">&#125;</span>
        <span style="color: #0600FF; font-weight: bold;">catch</span> <span style="color: #008000;">&#40;</span>Exception ex<span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            log<span style="color: #008000;">.</span><span style="color: #0000FF;">Error</span><span style="color: #008000;">&#40;</span>
                Resources<span style="color: #008000;">.</span><span style="color: #0000FF;">DkimSigningRoutingAgent_SignFailed</span>,
                ex<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #6666cc; font-weight: bold;">void</span> SignMailItem<span style="color: #008000;">&#40;</span>MailItem mailItem<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">!</span>mailItem<span style="color: #008000;">.</span><span style="color: #0000FF;">Message</span><span style="color: #008000;">.</span><span style="color: #0000FF;">IsSystemMessage</span> <span style="color: #008000;">&amp;&amp;</span>
            mailItem<span style="color: #008000;">.</span><span style="color: #0000FF;">Message</span><span style="color: #008000;">.</span><span style="color: #0000FF;">TnefPart</span> <span style="color: #008000;">==</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008000;">&#40;</span>var inputStream <span style="color: #008000;">=</span> mailItem<span style="color: #008000;">.</span><span style="color: #0000FF;">GetMimeReadStream</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
            <span style="color: #008000;">&#123;</span>
                <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">dkimSigner</span><span style="color: #008000;">.</span><span style="color: #0000FF;">CanSign</span><span style="color: #008000;">&#40;</span>inputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
                <span style="color: #008000;">&#123;</span>
                    <span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008000;">&#40;</span>var outputStream <span style="color: #008000;">=</span> mailItem<span style="color: #008000;">.</span><span style="color: #0000FF;">GetMimeWriteStream</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
                    <span style="color: #008000;">&#123;</span>
                        <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">dkimSigner</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Sign</span><span style="color: #008000;">&#40;</span>inputStream, outputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
                    <span style="color: #008000;">&#125;</span>
                <span style="color: #008000;">&#125;</span>
            <span style="color: #008000;">&#125;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>The only real quirk is the <code>if</code> statement in the <code>SignMailItem()</code> function, which I mostly discovered through trial and error. If the mail item is a &#8220;system message&#8221; (whatever that means) then all of the <code>mailItem</code>&#8216;s methods will be read only (throwing exceptions if we try to mutate), so we shouldn&#8217;t even bother. And if the mail item has a TNEF part, then in it&#8217;s in a <a href="http://en.wikipedia.org/wiki/Transport_Neutral_Encapsulation_Format">bizarro proprietary Microsoft format</a>, and the DKIM spec just isn&#8217;t going to work with that. Finally, if something blows up, we catch the exception and log it&#8211;better to send a message without a signature than not send it all.</p>

<h2>Defining an interface for DKIM signing</h2>

<p>So the next step is to make up that <code>IDkimSigner</code> implementation and make it do the dirty work. You can see that I&#8217;ve made it simple in that we only need to write two methods:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #6666cc; font-weight: bold;">interface</span> IDkimSigner <span style="color: #008000;">:</span> IDisposable
<span style="color: #008000;">&#123;</span>
    <span style="color: #6666cc; font-weight: bold;">bool</span> CanSign<span style="color: #008000;">&#40;</span>Stream inputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #6666cc; font-weight: bold;">void</span> Sign<span style="color: #008000;">&#40;</span>Stream inputStream, Stream outputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<h3>A method for sanity checking</h3>

<p>The first method will scan our mail item&#8217;s content stream and do a sanity check and ensure that we can actually sign the message. For example, if our <code>IDkimSigner</code> implementation is configured to sign messages originating from <code>warehouse1.example.com</code> and we pass <code>CanSign()</code> a message from <code>warehouse2.example.com</code>, then we can return <code>false</code> to indicate that we just don&#8217;t know what to do with the message. Let&#8217;s implement that method.</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #6666cc; font-weight: bold;">string</span> domain<span style="color: #008000;">;</span>
&nbsp;
<span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #6666cc; font-weight: bold;">bool</span> CanSign<span style="color: #008000;">&#40;</span>Stream inputStream<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #6666cc; font-weight: bold;">bool</span> canSign<span style="color: #008000;">;</span>
    <span style="color: #6666cc; font-weight: bold;">string</span> line<span style="color: #008000;">;</span>
    StreamReader reader<span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">disposed</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ObjectDisposedException<span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;DomainKeysSigner&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>inputStream <span style="color: #008000;">==</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ArgumentNullException<span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;inputStream&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    canSign <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">false</span><span style="color: #008000;">;</span>
    reader <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> StreamReader<span style="color: #008000;">&#40;</span>inputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    inputStream<span style="color: #008000;">.</span><span style="color: #0000FF;">Seek</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">0</span>, SeekOrigin<span style="color: #008000;">.</span><span style="color: #0000FF;">Begin</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    line <span style="color: #008000;">=</span> reader<span style="color: #008000;">.</span><span style="color: #0000FF;">ReadLine</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #0600FF; font-weight: bold;">while</span> <span style="color: #008000;">&#40;</span>line <span style="color: #008000;">!=</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #6666cc; font-weight: bold;">string</span> header<span style="color: #008000;">;</span>
        <span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span> headerParts<span style="color: #008000;">;</span>
&nbsp;
        <span style="color: #008080; font-style: italic;">// We've reached the end of the headers (headers are</span>
        <span style="color: #008080; font-style: italic;">// separated from the body by a blank line).</span>
        <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>line<span style="color: #008000;">.</span><span style="color: #0000FF;">Length</span> <span style="color: #008000;">==</span> <span style="color: #FF0000;">0</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0600FF; font-weight: bold;">break</span><span style="color: #008000;">;</span>
        <span style="color: #008000;">&#125;</span>
&nbsp;
        <span style="color: #008080; font-style: italic;">// Read a line. Because a header can be continued onto</span>
        <span style="color: #008080; font-style: italic;">// subsequent lines, we have to keep reading lines until we</span>
        <span style="color: #008080; font-style: italic;">// run into the end-of-headers marker (an empty line) or another</span>
        <span style="color: #008080; font-style: italic;">// line that doesn't begin with a whitespace character.</span>
        header <span style="color: #008000;">=</span> line <span style="color: #008000;">+</span> <span style="color: #666666;">&quot;<span style="color: #008080; font-weight: bold;">\r</span><span style="color: #008080; font-weight: bold;">\n</span>&quot;</span><span style="color: #008000;">;</span>
        line <span style="color: #008000;">=</span> reader<span style="color: #008000;">.</span><span style="color: #0000FF;">ReadLine</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #0600FF; font-weight: bold;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">!</span><span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">.</span><span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #008000;">&#40;</span>line<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&amp;&amp;</span>
            <span style="color: #008000;">&#40;</span>line<span style="color: #008000;">.</span><span style="color: #0000FF;">StartsWith</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;<span style="color: #008080; font-weight: bold;">\t</span>&quot;</span>, StringComparison<span style="color: #008000;">.</span><span style="color: #0000FF;">Ordinal</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">||</span>
            line<span style="color: #008000;">.</span><span style="color: #0000FF;">StartsWith</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot; &quot;</span>, StringComparison<span style="color: #008000;">.</span><span style="color: #0000FF;">Ordinal</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            header <span style="color: #008000;">+=</span> line <span style="color: #008000;">+</span> <span style="color: #666666;">&quot;<span style="color: #008080; font-weight: bold;">\r</span><span style="color: #008080; font-weight: bold;">\n</span>&quot;</span><span style="color: #008000;">;</span>
            line <span style="color: #008000;">=</span> reader<span style="color: #008000;">.</span><span style="color: #0000FF;">ReadLine</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #008000;">&#125;</span>
&nbsp;
        <span style="color: #008080; font-style: italic;">// Extract the name of the header. Then store the full header</span>
        <span style="color: #008080; font-style: italic;">// in the dictionary. We do this because DKIM mandates that we</span>
        <span style="color: #008080; font-style: italic;">// only sign the LAST instance of any header that occurs.</span>
        headerParts <span style="color: #008000;">=</span> header<span style="color: #008000;">.</span><span style="color: #0000FF;">Split</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">new</span> <span style="color: #6666cc; font-weight: bold;">char</span><span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">&#123;</span> <span style="color: #666666;">':'</span> <span style="color: #008000;">&#125;</span>, <span style="color: #FF0000;">2</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>headerParts<span style="color: #008000;">.</span><span style="color: #0000FF;">Length</span> <span style="color: #008000;">==</span> <span style="color: #FF0000;">2</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #6666cc; font-weight: bold;">string</span> headerName<span style="color: #008000;">;</span>
&nbsp;
            headerName <span style="color: #008000;">=</span> headerParts<span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">;</span>
&nbsp;
            <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>headerName<span style="color: #008000;">.</span><span style="color: #0000FF;">Equals</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;From&quot;</span>, StringComparison<span style="color: #008000;">.</span><span style="color: #0000FF;">OrdinalIgnoreCase</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
            <span style="color: #008000;">&#123;</span>
                <span style="color: #008080; font-style: italic;">// We don't break here because we want to read the bottom-most</span>
                <span style="color: #008080; font-style: italic;">// instance of the From: header (there should be only one, but</span>
                <span style="color: #008080; font-style: italic;">// if there are multiple, it's the last one that matters).</span>
                canSign <span style="color: #008000;">=</span> header
                    <span style="color: #008000;">.</span><span style="color: #0000FF;">ToUpperInvariant</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
                    <span style="color: #008000;">.</span><span style="color: #0000FF;">Contains</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;@&quot;</span> <span style="color: #008000;">+</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">domain</span><span style="color: #008000;">.</span><span style="color: #0000FF;">ToUpperInvariant</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
            <span style="color: #008000;">&#125;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    inputStream<span style="color: #008000;">.</span><span style="color: #0000FF;">Seek</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">0</span>, SeekOrigin<span style="color: #008000;">.</span><span style="color: #0000FF;">Begin</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">return</span> canSign<span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>Barf. But we have to do this style of ghetto parsing because, after all, we&#8217;re dealing with the raw e-mail message format. All we&#8217;re doing is scanning through the headers until we reach the last <code>From:</code> header, and then we make sure that the <code>From:</code> e-mail address belongs to the domain that our instance knows how to sign. Then we seek back to the beginning of the stream to be polite.</p>

<h3>A method for signing</h3>

<p>The second method that we have to implement is the one that actually does all of the dirty work. And in DKIM signing, we can break it down into five steps:</p>

<ol>
<li>Compute a hash of the body of the message.</li>
<li>Create an unsigned version of the <code>DKIM-Signature</code> header that contains that body hash value and some other information, but has the signature component set to an empty string.</li>
<li>&#8220;Canonicalize&#8221; the headers that we are going to sign. By &#8220;canonicalize&#8221;, we mean &#8220;standardize capitalization, whitespace, and newlines into a format required by the spec, since other mail transport agents who get their grubby paws on this message might reformat the headers&#8221;.</li>
<li>Slap our unsigned version of the <code>DKIM-Signature</code> header to the end of our &#8220;canonicalized&#8221; headers, sign that data, and slap the resulting signature to the end of the <code>DKIM-Signature</code> header.</li>
<li>Write this signed <code>DKIM-Signature</code> into the headers of the mail message, and send it on its merry way.</li>
</ol>

<p>Divide and conquer!</p>

<h2>Implementing the <code>Sign()</code> method</h2>

<p>Our implementation for the <code>Sign()</code> method will tackle each step in turn:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #6666cc; font-weight: bold;">void</span> Sign<span style="color: #008000;">&#40;</span>Stream inputStream, Stream outputStream<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">disposed</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ObjectDisposedException<span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;DomainKeysSigner&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>inputStream <span style="color: #008000;">==</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ArgumentNullException<span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;inputStream&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>outputStream <span style="color: #008000;">==</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ArgumentNullException<span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;outputStream&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    var bodyHash <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">GetBodyHash</span><span style="color: #008000;">&#40;</span>inputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    var unsignedDkimHeader <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">GetUnsignedDkimHeader</span><span style="color: #008000;">&#40;</span>bodyHash<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    var canonicalizedHeaders <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">GetCanonicalizedHeaders</span><span style="color: #008000;">&#40;</span>inputStream<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    var signedDkimHeader <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">GetSignedDkimHeader</span><span style="color: #008000;">&#40;</span>unsignedDkimHeader, canonicalizedHeaders<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    WriteSignedMimeMessage<span style="color: #008000;">&#40;</span>inputStream, outputStream, signedDkimHeader<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<h3>Computing the body hash</h3>

<p>The first step, computing the hash of the body, is actually pretty easy. There is only one quirk in that DKIM spec says that if the body ends with multiple empty lines, then the body should be normalized to just one terminating newline for the purposes of computing the hash. The code is not exciting, and you can download it at the end of this article.</p>

<h3>Creating the &#8220;unsigned&#8221; header</h3>

<p>The next step is to create the &#8220;unsigned&#8221; <code>DKIM-Signature</code> header. This is where the DKIM spec is just weird. The <code>DKIM-Signature</code> header contains a lot of information in it, such as the selector, domain, and the hashing algorithm (SHA1 or SHA256) being used. Since that information is vital to ensuring the integrity of the signature, it&#8217;s important that that information be a part of the DKIM signature.</p>

<p>If I were designing this, I would append two headers to e-mail messages: a <code>DKIM-Information</code> header that contained all of the above information and is part of the data that is signed and a <code>DKIM-Signature</code> header that contains just the signature data. But the DKIM spec makes use only the one <code>DKIM-Signature</code> header, and for the purposes of signing, we treat the &#8220;signature part&#8221; of the header (<code>b=</code>) as an empty string:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #6666cc; font-weight: bold;">string</span> GetUnsignedDkimHeader<span style="color: #008000;">&#40;</span><span style="color: #6666cc; font-weight: bold;">string</span> bodyHash<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0600FF; font-weight: bold;">return</span> <span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Format</span><span style="color: #008000;">&#40;</span>
        CultureInfo<span style="color: #008000;">.</span><span style="color: #0000FF;">InvariantCulture</span>,
        <span style="color: #666666;">&quot;DKIM-Signature: v=1; a={0}; s={1}; d={2}; c=simple/simple; q=dns/txt; h={3}; bh={4}; b=;&quot;</span>,
        <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">hashAlgorithmDkimCode</span>,
        <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">selector</span>,
        <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">domain</span>,
        <span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Join</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot; : &quot;</span>, <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">eligibleHeaders</span><span style="color: #008000;">.</span><span style="color: #0000FF;">OrderBy</span><span style="color: #008000;">&#40;</span>x <span style="color: #008000;">=&gt;</span> x, StringComparer<span style="color: #008000;">.</span><span style="color: #0000FF;">Ordinal</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">.</span><span style="color: #0000FF;">ToArray</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>,
        bodyHash<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>You can see here that I&#8217;ve got some instance variables that were set in our <code>IDkimSigner</code> implementation&#8217;s constructor, such as the hash algorithm to use, the selector, domain, headers to include in the signature, and so on. We also insert our recently-computed hash of the body here.</p>

<p>You can also see that I&#8217;m using &#8220;simple&#8221; body canonicalization and &#8220;simple&#8221; header canonicalization. The DKIM spec gives us a few options in determining how the message is represented for signing and verification purposes. For the &#8220;simple&#8221; body canonicalization, it means &#8220;exactly as written, except for the weird rule about multiple newlines at the end of the body&#8221;. For the &#8220;simple&#8221; header canonicalization, it means &#8220;exactly as written, whitespace, newlines, and everything&#8221;.</p>

<p>There is a &#8220;relaxed&#8221; canonicalization method, but it&#8217;s more work, since you have to munge the headers and body into a very particular format, and I didn&#8217;t feel like writing a MIME parser.</p>

<h3>Extracting &#8220;canonicalized&#8221; headers</h3>

<p>The third step is to get a list of the canonicalized headers. In the constructor, I accept a list of headers to sign: <code>From</code>, <code>To</code>, <code>Message-ID</code>, and so on. (<code>From</code> is always required to be signed.) Then I use parsing code similar to that used in the <code>CanSign()</code> method and build a list of of the raw headers. The only real gotcha to watch out for is that headers can be wrapped onto more than one line, and since we&#8217;re using the &#8220;simple&#8221; canonicalization algorithm, we&#8217;ll need to preserve those whitespaces and newlines exactly as we extract them from the stream. Then I sort the headers alphabetically, since that&#8217;s how I specified them in the <code>GetUnsignedDkimHeader()</code> method specified above.</p>

<h3>Signing the message</h3>

<p>The logic behind signing the message is not that difficult. We smash all of the canonicalized headers together, add our unsigned <code>DKIM-Signature</code> header to the end, and compute our signature on this. Then we append the signature to the <code>b=</code> element, previously empty, of our <code>DKIM-Signature</code> header:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #6666cc; font-weight: bold;">string</span> GetSignedDkimHeader<span style="color: #008000;">&#40;</span>
    <span style="color: #6666cc; font-weight: bold;">string</span> unsignedDkimHeader, 
    IEnumerable<span style="color: #008000;">&lt;</span><span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">&gt;</span> canonicalizedHeaders<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #6666cc; font-weight: bold;">byte</span><span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span> signatureBytes<span style="color: #008000;">;</span>
    <span style="color: #6666cc; font-weight: bold;">string</span> signatureText<span style="color: #008000;">;</span>
    StringBuilder signedDkimHeader<span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008000;">&#40;</span>var stream <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> MemoryStream<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008000;">&#40;</span>var writer <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> StreamWriter<span style="color: #008000;">&#40;</span>stream<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0600FF; font-weight: bold;">foreach</span> <span style="color: #008000;">&#40;</span>var canonicalizedHeader <span style="color: #0600FF; font-weight: bold;">in</span> canonicalizedHeaders<span style="color: #008000;">&#41;</span>
            <span style="color: #008000;">&#123;</span>
                writer<span style="color: #008000;">.</span><span style="color: #0000FF;">Write</span><span style="color: #008000;">&#40;</span>canonicalizedHeader<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
            <span style="color: #008000;">&#125;</span>
&nbsp;
            writer<span style="color: #008000;">.</span><span style="color: #0000FF;">Write</span><span style="color: #008000;">&#40;</span>unsignedDkimHeader<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
            writer<span style="color: #008000;">.</span><span style="color: #0000FF;">Flush</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
            stream<span style="color: #008000;">.</span><span style="color: #0000FF;">Seek</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">0</span>, SeekOrigin<span style="color: #008000;">.</span><span style="color: #0000FF;">Begin</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
            signatureBytes <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">cryptoProvider</span><span style="color: #008000;">.</span><span style="color: #0000FF;">SignData</span><span style="color: #008000;">&#40;</span>stream, <span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">hashAlgorithmCryptoCode</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    signatureText <span style="color: #008000;">=</span> Convert<span style="color: #008000;">.</span><span style="color: #0000FF;">ToBase64String</span><span style="color: #008000;">&#40;</span>signatureBytes<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    signedDkimHeader <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> StringBuilder<span style="color: #008000;">&#40;</span>unsignedDkimHeader<span style="color: #008000;">.</span><span style="color: #0000FF;">Substring</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">0</span>, unsignedDkimHeader<span style="color: #008000;">.</span><span style="color: #0000FF;">Length</span> <span style="color: #008000;">-</span> <span style="color: #FF0000;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    signedDkimHeader<span style="color: #008000;">.</span><span style="color: #0000FF;">Append</span><span style="color: #008000;">&#40;</span>signatureText<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
    signedDkimHeader<span style="color: #008000;">.</span><span style="color: #0000FF;">Append</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;;<span style="color: #008080; font-weight: bold;">\r</span><span style="color: #008080; font-weight: bold;">\n</span>&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #0600FF; font-weight: bold;">return</span> signedDkimHeader<span style="color: #008000;">.</span><span style="color: #0000FF;">ToString</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>The only gotcha here, which I lost a few hours to, is a weird quirk of the .NET Framework 3.5 implementation of the <code>SignData()</code> function of the <code>RSACryptoServiceProvider</code> class. One of the overloads of the <code>SignData()</code> function accepts an instance of a <code>HashAlgorithm</code> to specify the kind of hash to use. The SHA-256 implementation was added in .NET 3.5 SP1, but it was done in such a way that an internal switch statement used internally by the .NET crypto classes wasn&#8217;t updated until .NET 4.0 to recognize the new <code>SHA256CryptoServiceProvider</code> type. <a href="http://blogs.msdn.com/b/shawnfa/archive/2008/08/25/using-rsacryptoserviceprovider-for-rsa-sha256-signatures.aspx">Some guy</a> blogs about why this is, but what it essentially means is that if you pass a <code>SHA256CryptoServiceProvider</code> instance to the <code>SignData()</code> method on .NET 2.0/3.0/3.5/3.5SP1, you get an exception, and on .NET 4.0 you don&#8217;t. Since Exchange 2007 uses .NET 3.5 SP1, we have to use the recommended workaround of using the overload that accepts a <code>string</code> representation of the hash algorithm.</p>

<h3>Writing out the message</h3>

<p>The last step is to write out the message with our newly created <code>DKIM-Signature</code> header. This really is a simple as taking the output stream, writing the <code>DKIM-Signature</code> header, and then dumping in the entire contents of the input stream.</p>

<h2>Getting a key to sign messages with</h2>

<p>Let us take a brief interlude from our DKIM circles of hell and obtain a key with which we will actually sign the <code>DKIM-Signature</code> header we&#8217;ve worked so hard to create.</p>

<p>We need to generate an RSA public/private key pair: a public key to store in DNS in the format required by the DKIM spec, and a private key to actually sign the messages with. <a href="http://www.port25.com/support/support_dkwz.php">The nice folks over at Port25 have a DKIM wizard that does exactly that.</a></p>

<p>It&#8217;s smashingly simple&#8211;just enter your domain name (say, &#8220;<code>example.com</code>&#8220;), a &#8220;selector&#8221; (say, &#8220;<code>key1</code>&#8220;), and select a key size (bigger is better, right?). The &#8220;selector&#8221; is a part of the DKIM spec that allows a single signing domain to use multiple keys. For example, you could use a key with selector name &#8220;newsletters&#8221; to sign all of the crap newsletter e-mails that you send out, and another key with selector name &#8220;tx&#8221; to sign all of the transactional e-mails that you send out.</p>

<p>It then spits out the syntax of the <code>TXT</code> records that you need to add to DNS for that selector:</p>

<p><pre>
key1._domainkey.example.com IN  TXT     "k=rsa\; p={BIG HONKING PUBLIC KEY HERE}"
_domainkey.example.com      IN  TXT     "t=y; o=~;"
</pre></p>

<p>The first record is where the public part of the key is stored. Whenever a mail transport agent sees one of our <code>DKIM-Signature</code> headers with a selector of <code>key1</code>, it&#8217;ll know to go hunting in DNS for a <code>TXT</code> record named <code>key1._domainkey.example.com</code> and pull the public key for verification from there.</p>

<p>The second record is part of the older DomainKeys specification and it is not strictly necessary. As written here, it means that we&#8217;re in testing mode (&#8220;t=y&#8221;)&#8211;that is, don&#8217;t freak out if you see a bad signature because we&#8217;re still dicking around with the setup of our implementation&#8211;and that not all messages originating from this domain will be signed (&#8220;o=~&#8221;)&#8211;maybe we won&#8217;t bother signing our newsletter e-mails, for example.</p>

<p>We&#8217;ll also have the private key specified in a format similar to the one below:</p>

<p><pre>
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANXBbZybdmjKDTONFVqAWXmGzR6GSZX5LV3OF//1jRz7dzGWTCKK
jembqBxqhr0Y2ua2l4D4EZi6FwDmdqgLS6MCAwEAAQJAD4qhypovEM1oClB+tfbR
Cpn3ffmrjgDxAHoEmrKi0PGBn8fumW22bad2tmrAjWWTVmeXJvQyEy1awq0M2PMR
0QIhAPEnqivb5dKZbTeKhiF4c6IUHfwEq8wNf2LWZvdH3ROrAiEA4un604mDss4Q
qAVEx686pUttfWyJrYkcZ/tx7kOoL+kCICEysqyDAypw0KY6vahR6qk/V7lf8z6O
BSFYHqigDgEtAiEAsK9r5UcQSyv1AD+J/MpOqeJ/kMfwtDUs7zJ01gfMb/ECIQDg
8d/XVJDi4Cqbt4wfcHZxADAgqyK8Z5M69fBecnExVg==
-----END RSA PRIVATE KEY-----
</pre></p>

<p>One thing I have glossed over in the code discussion until now was how that <code>this.cryptoProvider</code> instance that actually computes the signature got created.</p>

<p>We&#8217;ll need to read this key and load it into the cryptography classes used by the .NET Framework and by Windows to actually sign mail messages and get that <code>this.cryptoProvider</code> instance. Surely there is a simple API for this, yes?</p>

<h3>Instancing a <code>CryptoProvider</code></h3>

<p>One problem is that the <a href="http://msdn.microsoft.com/en-us/library/aa380255%28v=vs.85%29.aspx">documentation in MSDN for the CryptoAPI is bad</a>. I say &#8220;bad&#8221; because it certainly seems like .NET and Windows don&#8217;t expose native support for processing a PEM-encoded key, and if it does, well, I couldn&#8217;t find the documentation for it. Instead, the <code>RSACryptoServiceProvider</code> prefers to store its keys in <a href="http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa.fromxmlstring.aspx">an XML format that nothing else in the world seems to use</a>.</p>

<p>This means that our implementation is so close to being finished that we can almost taste it, but now we have to complete a side quest to actually read our damn key and get an instance of the <code>RSACryptoServiceProvider</code>. Or, we could generate a certificate ourselves and store it in the Certificates MMC snap-in, but why should we have to do that? I&#8217;d rather just plop the damn key in the application configuration file like the rest of the goddamned world does it, &#8220;secure container&#8221; my ass.</p>

<p><a href="http://www.jensign.com/opensslkey/opensslkey.cs">We can thank the moon and the stars that some guy has written a PEM reader for us</a>. How does it work? I have no idea, but I tested it on several keys and it seemed to work fine, which is good enough for me. I tossed this code into a static <code>CryptHelper</code> class, and now getting an instance of the <code>RSACryptoServiceProvider</code> is as simple as</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">cryptoProvider</span> <span style="color: #008000;">=</span> CryptHelper
     <span style="color: #008000;">.</span><span style="color: #0000FF;">GetProviderFromPemEncodedRsaPrivateKey</span><span style="color: #008000;">&#40;</span>encodedKey<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span></pre></div></div>


<h2>Loading the routing agent into Exchange Server 2007</h2>

<p>I took all of this code and then added boring administrative stuff like logging and moving some hardcoded values (such as the PEM-encoded key, the selector, and the domain) into the usual .NET <code>App.config</code> file mechanism.</p>

<p>Installing the agent on the Exchange Server is surprisingly simple. After compiling the project and futzing with the configuration file, we just copy the DLLs and configuration file to a folder on the Exchange Server, say, <code>C:\Program Files\Skiviez\Wolverine\DKIM Signer for Exchange\</code>.</p>

<p>Then we launch the Exchange Management Shell (remember to right-click it and &#8220;Run as Administrator&#8221;) and execute a command to tell Exchange to actually register our agent:</p>

<p><pre>
Install-TransportAgent 
     -Name "DKIM Signer for Exchange" 
     -TransportAgentFactory "Skiviez.Wolverine.Exchange.DkimSigner.DkimSigningRoutingAgentFactory" 
     -AssemblyPath "C:\Program Files\Skiviez\Wolverine\DKIM Signer for Exchange\Skiviez.Wolverine.Exchange.DkimSigner.dll"
</pre></p>

<p>followed by</p>

<p><pre>
Enable-TransportAgent -Name "DKIM Signer for Exchange"
</pre></p>

<p>Interestingly, there will be a note telling you to close the Powershell window. It is not kidding. For some reason, the <code>Install-TransportAgent</code> cmdlet will keep a file handle open on our DLL, preventing Exchange from actually loading it until we close the Powershell window.</p>

<p>To make it actually work, we need to restart the Microsoft Exchange Transport service. I&#8217;ve found that restarting the Microsoft Exchange Mail Submission right after that is a good idea; otherwise, there can be a short delay of about 15 minutes before people&#8217;s Outlooks attempt to send outbound mail again.</p>

<h2>Testing the implementation</h2>

<p>To make sure things are actually working, Port25 comes to the rescue with their <a href="http://www.port25.com/domainkeys/">verification tool</a>. You just send an e-mail to <a href="mailto:check-auth@verifier.port25.com">check-auth@verifier.port25.com</a> and within a few minutes, they&#8217;ll send you an e-mail back with a boatload of debugging information. If it&#8217;s all good, you&#8217;ll see a result like the following:</p>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">Summary of Results
SPF check:          pass
DomainKeys check:   neutral
DKIM check:         pass
Sender-ID check:    pass
SpamAssassin check: ham</pre></div></div>


<p>(What you&#8217;re looking for is the &#8220;pass&#8221; next to the DKIM check. The DomainKeys part being neutral is OK, since DomainKeys is the older standard and we&#8217;re choosing not to implement it.)</p>

<h2>Conclusions and Delusions</h2>

<p><a href="http://nicholas.piasecki.name/blog/wp-content/uploads/2010/12/works-on-my-machine.png"><img src="http://nicholas.piasecki.name/blog/wp-content/uploads/2010/12/works-on-my-machine.png" alt="" title="Works On My Machine" width="200" height="193" class="alignright size-full wp-image-814" /></a></p>

<p>I&#8217;ve been using this code for a few weeks now and it seems to work fine&#8211;the messages that I&#8217;ve sent through the server to Port25 and my Yahoo! test account all end up showing the DKIM passing. The usual &#8220;it works on my machine!&#8221; disclaimers apply, however, as I&#8217;m sure there are myriad configuration differences in Exchange that could this not to work. Bug fixes are welcome, but don&#8217;t come crying if it sends all of your e-mail to that big junk folder in the sky.</p>

<p>And thanks to some blowhards at Yahoo!, the world now has a public domain implementation of DKIM signing for Exchange to play with.</p>

<p>And in case you&#8217;re curious&#8211;after doing all this work to set up DKIM and participate in the Complaint Feedback Loop at their suggestion&#8211;their answer is still &#8220;no,&#8221; without elaboration. When Yahoo! finally goes under, I won&#8217;t be one shedding a nostalgic tear.</p>

<p>There are some unit tests, but they do have our private key in them, and I couldn&#8217;t be bothered to siphon those out. The code below is just the bits that do the actual signing.</p>

<p><a href='http://nicholas.piasecki.name/blog/wp-content/uploads/2010/12/DkimSigner.zip'>Download the code used in this article.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2010/12/dkim-signing-outbound-messages-in-exchange-server-2007/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>I Hate Software</title>
		<link>http://nicholas.piasecki.name/blog/2010/05/i-hate-software/</link>
		<comments>http://nicholas.piasecki.name/blog/2010/05/i-hate-software/#comments</comments>
		<pubDate>Wed, 05 May 2010 19:59:29 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[bugs]]></category>
		<category><![CDATA[fedex]]></category>
		<category><![CDATA[smartftp]]></category>
		<category><![CDATA[ups]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=585</guid>
		<description><![CDATA[Today, I realized that I hate software. First, it was FedEx It has been 45 days since I reported to FedEx that the new SmartPost integration offered by their Web services simply does not work when an EPL2 label is requested: The problem is that the USPS Delivery Confirmation barcode does not print when an [...]]]></description>
			<content:encoded><![CDATA[<p>Today, I realized that I hate software.</p>

<h2>First, it was FedEx</h2>

<p>It has been 45 days since I reported to FedEx that the new SmartPost integration offered by their Web services simply does not work when an EPL2 label is requested:</p>

<div id="attachment_588" class="wp-caption aligncenter" style="width: 310px"><a href="http://nicholas.piasecki.name/blog/wp-content/uploads/2010/05/0828_0012.jpg"><img src="http://nicholas.piasecki.name/blog/wp-content/uploads/2010/05/0828_0012.jpg" alt="" title="I Hate Software, Part 1" width="300" height="441" class="size-full wp-image-588" /></a><p class="wp-caption-text">I Hate Software, Part 1</p></div>

<p>The problem is that the USPS Delivery Confirmation barcode does not print when an EPL2 label is requested. That&#8217;s because in the EPL2 document that FedEx sends back, quotation marks in the barcode command are not properly escaped:</p>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">A90,473,0,3,1,2,N,&quot;ZIP - USPS DELIVERY CONFIRMATION e-VS&quot;
B50,535,0,1,3,4,175,N,&quot;&quot;F1&quot;42023221&quot;F1&quot;0000000000000000000000&quot;</pre></div></div>


<p>That should read <code>"\"F1\"420..."</code> for it to print correctly. (The ZPLII version of the label works fine.)</p>

<p>I understand that larger corporations have fixed software release cycles and different bug triage mechanisms, but having to through three levels of support and waiting 45+ days to simply say &#8220;this simply doesn&#8217;t work please add a backslash&#8221; is somewhat frustrating.</p>

<h2>Then, there was UPS</h2>

<p>Similarly, I&#8217;ve discovered a recently-introduced error with the UPS Web services. If you request a 4&#8243; x 6&#8243; EPL shipping label, then the UPS Web service will happily ignore request and send back a 4&#8243; x 8&#8243; label instead. That&#8217;s because they&#8217;re sending back the wrong width and height settings in the label response:</p>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">q795
Q1600,24</pre></div></div>


<p>That would be 795 dots / 203 dpi == 3.91&#8243; wide (OK) and 1600 dots / 203 dpi == 7.88&#8243; high (hmm, not what I asked for). (The ZPLII version of the label works fine.)</p>

<h2>Finally, SmartFTP sent me over the edge</h2>

<p>Since updating to the latest version of SmartFTP, I found myself frequently being unable to connect to the Skiviez private FTP server that is used to manage software updates, e-mails, and product catalog images. It would fail about 80% of the time with</p>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">[13:51:38] 234 Using authentication type TLS
[13:51:38] SSL: Error (Error=0x80090308).</pre></div></div>


<p>I&#8217;m sure that this means something. And even if I knew what it was, sometimes it would just work without me changing anything (about 10 percent of the time). Further still, I hadn&#8217;t touched the FTP server for some time.</p>

<p>That&#8217;s when I noticed in the SmartFTP change log:</p>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">FTP: Completely rewrote SSL layer</pre></div></div>


<p>Sigh. Downgraded to the version prior to that changelog entry, and it works fine.</p>

<h2>Conclusions and delusions</h2>

<p>I have certainly done my part to contribute buggy, crappy software to the world. I continue to spew out more buggy, crappy software with each passing day. But it is extra depressing and disheartening to know that I, some idiot working at a small company, can run across such simply-does-not-work bugs (and, in FedEx&#8217;s case, never-actually-worked-ever bugs) in software produced by large corporations and used by presumably hundreds to thousands of people around the world.</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2010/05/i-hate-software/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Buying Software Sucks</title>
		<link>http://nicholas.piasecki.name/blog/2010/01/buying-software-sucks/</link>
		<comments>http://nicholas.piasecki.name/blog/2010/01/buying-software-sucks/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 22:50:35 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[consulting]]></category>
		<category><![CDATA[cots]]></category>
		<category><![CDATA[marketing]]></category>
		<category><![CDATA[purchasing]]></category>
		<category><![CDATA[requirements]]></category>
		<category><![CDATA[salespeople]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=522</guid>
		<description><![CDATA[What follows is a rant about the state of marketing in the software industry. We happen to like our system So we&#8217;ve branched out and offered fulfillment services to other merchants, as I&#8217;ve mentioned before. Essentially, other e-commerce stores or merchants can store their items in our warehouse and then transmit orders as &#8220;fulfillment requests&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>What follows is a rant about the state of marketing in the software industry.</p>

<h2>We happen to like our system</h2>

<p>So we&#8217;ve branched out and offered <a href="http://www.warehousefulfillmentservices.com/">fulfillment services to other merchants</a>, as I&#8217;ve mentioned before. Essentially, other e-commerce stores or merchants can store their items in our warehouse and then transmit orders as &#8220;fulfillment requests&#8221; to us. We either ship the request or mark it as invalid with a little message (&#8220;item has insufficient stock, delivery point validation failed, tax identification number required, etc.&#8221;). If they want to re-attempt, then they re-submit an entirely new fulfillment request.</p>

<p>This simple model has worked surprisingly well: the system doesn&#8217;t track &#8220;orders&#8221; (since each merchant handles backordering and cancellations differently), and while it does maintain an inventory log, it doesn&#8217;t track the cost of goods in inventory or anything like that, since that is not necessary for us to do our job. (Instead, it tracks a whole boatload of other information that people don&#8217;t normally think about, like HS codes or unit weight.)</p>

<h2>Some of our merchants need some hand-holding</h2>

<p>We have an API so merchants can integrate with our system. And so I&#8217;ve written a few plug-ins for Magento and osCommerce that auto-transmit their orders as fulfillment requests, sync outbound shipments back, and deduct inventory from their systems (since we are the authoritative inventory count). This works great for their retail businesses.</p>

<p>We have a merchant that we&#8217;ve been doing retail business with for some time who now wants to do wholesale stuff with us. He wants a sales order/invoicing/inventory management solution, and it needs to be able to track multiple inventories across multiple warehouses (our integration, if any, would only be adjusting inventory for a particular warehouse). He wants to enter sales orders remotely, press a button that shows him how much is ordered so he knows how much to make, have that manufactured and sent to us, click another button to transmit the sales order as a fulfillment request to us once it&#8217;s in stock, and then have us sync back with the shipment info, creating an invoice.</p>

<p>&#8220;Shouldn&#8217;t they have had these features prior to switching to you guys?&#8221; you ask. Well, yes. In this case, all of these features were provided in an all-in-one system provided by their previous fulfillment warehouse. They have since learned their lesson about keeping all of their data in the hands of a third party because when that relationship went south, so did their access to their own data.</p>

<p>We would rather not add these features to our system. Since all merchants have different ways of handling backorders, pre-orders, cancellations, cost of goods sold (FIFO, LIFO, average, priority), we&#8217;ve been maintaining the position that&#8211;unlike our competitors&#8211;our system is essentially feature complete, since it&#8217;s <em>ours</em> and does what <em>we</em> need it to do to ship things out. The features that I&#8217;ve mentioned <em>should</em> be things that the merchants are keeping track of themselves&#8211;since that&#8217;s their business&#8211;and integrate with our system via the programming API. While an argument could be made that our system would be abso-freaking-fantastic for merchants who need an all-in-one software and data solution (yes, it certainly would), the reality is that our competitors have an outsourced team of software developers, and we are a small business working in an area that is tangential to our core business as a result of the &#8220;new economy&#8221; that has a software development team of just one person (me) and can&#8217;t even begin to dream of hiring any more until we start seeing some serious cash flow.</p>

<p>In any case, to land this deal, we need to find a system for this merchant, and fast, because there are some important trade shows coming up.</p>

<h2>Welcome to marketing Hell</h2>

<p>Now for the rant, because you would think that these requirements are not exotic:</p>

<ul>
<li>Let salespeople enter sales orders remotely.</li>
<li>Keep track of inventory in multiple locations, and track the cost of inventory.</li>
<li>Provide integration hooks so the user can send orders to the warehouse and so the warehouse can send shipment data back.</li>
<li>Keep track of sales-order-to-invoice conversions and payments.</li>
<li>Provide reporting features.</li>
</ul>

<h3>QuickBooks 2010: Same as last year! Now with more shininess!</h3>

<p>You might take a look at integrating with QuickBooks, but you&#8217;d realize that once you&#8217;ve penetrated the marketing speak that the software in 2010 is essentially no different in terms of fundamental feature set as it was in 2006, and that it doesn&#8217;t support inventory in multiple locations and doesn&#8217;t scale well. In fact, QuickBooks performance once you start approaching 10,000 SKUs is so bad that they sell an &#8220;enterprise&#8221; version that essentially&#8211;aside from some fine-grained access permissions&#8211;has no added features other than the feature of not crashing when dealing with large lists of information.</p>

<p>We could pay another couple of thousand dollars for <a href="http://www.fishbowlinventory.com/products/pricing/">Fishbowl inventory</a>, which would add multiple location support to Quickbooks, but then we&#8217;d have created a Rube Goldberg machine straight out of the gate, with me synchronizing with Fishbowl which is then synchronizing with Quickbooks. I&#8217;m sure nothing would go wrong there. That would be insane; we might as well just stick a few fax machines into the sync process and call it an insurance company.</p>

<h3>A gap in the market</h3>

<p>The reality is that there is a huge gap in the marketplace between merchants who are moving $200k or less per year&#8211;just use commercial off-the-shelf (COTS) QuickBooks, you can do most things manually and use your e-commerce system&#8217;s native order management functions&#8211;and merchants who are moving $5m or more&#8211;just use SAP or some other enterprisey software. If you&#8217;re in between, like we are and like the merchant that I&#8217;m researching for is, the options available to you are not pretty.</p>

<p>I&#8217;m not sure why this is. All I can think of is that perhaps companies historically did not spend much time in this space&#8211;they either stayed small or had venture capital to acquire the big boy systems and grow quickly. People aren&#8217;t exactly lending money anymore, so I suspect that this is a segment that is only going to grow.</p>

<p>If you try to look for COTS software in this segment, you&#8217;ll never find the feature matrix that you need:</p>

<ul>
<li><a href="http://www.inflowinventory.com/">inFlow Inventory</a> is pretty, but offers no integration features, as if an entire business could be run out of one app, and doesn&#8217;t offer Web access.</li>
<li><a href="http://www.workingpoint.com/">WorkingPoint</a> is Web-based but doesn&#8217;t offer inventory tracking in multiple locations or an integration API.</li>
<li><a href="http://quickbooks.intuit.com/">QuickBooks</a> has a <a href="http://www.vi4qb.com/">Web-based</a> extension that lets QuickBooks understand multiple inventories but costs thousands of dollars, assumes that the company owns its own warehouse (that is, needing picking/packing/shipping capabilities), and still does the same style of synchronization as Fishbowl does. You&#8217;d think Intuit would just add the @#$#@ feature to QuickBooks itself!</li>
</ul>

<h3>No COTS to sleep in</h3>

<p>The market seems to have determined that people in this segment have outgrown COTS software and need some consulting help. So any Web sites that advertise products will be full of pages and pages of <a href="http://www.netsuite.com/portal/home.shtml">impenetrable marketing bullshit</a> that use obnoxious acronyms like ERP, CRM, MRP, and WMS, promise the moon, and coyly make no reference to pricing or contract requirements so you can&#8217;t even tell if you&#8217;re dealing with the right league of product, when the reality is at the end of the day you could look at two or three screenshots and the SDK&#8217;s API and immediately tell if the product fit your needs.</p>

<p>Instead, I notice a disturbing trend of &#8220;pretty Web site, crap product,&#8221; such as <a href="http://www.simplyaccounting.com/productsServices/compare_products/?WT.mc_id=hp_comp">Sage&#8217;s Simply Accounting</a>, which certainly appears to have an impressive array of features but in reality doesn&#8217;t even know the difference between a <a href="http://en.wikipedia.org/wiki/Sales_order">sales order</a> and an <a href="http://en.wikipedia.org/wiki/Invoice">invoice</a>. You can try going to <a href="http://www.microsoft.com/dynamics/en/us/default.aspx">Microsoft&#8217;s Dynamics site</a>, but good luck figuring out what the difference between Dynamics AX, Dynamics CRM, Dynamics NAV, and Dynamics GP are: you&#8217;ll be told to contact your &#8220;Microsoft Dynamics solutions representative&#8221; for help. At that point, you&#8217;re thinking &#8220;Microsoft solutions representative? Who said I committed to Microsoft?! I&#8217;m just trying to figure out <em>what in the blue hell your product even <strong>does</strong></em>.&#8221;</p>

<p>If you do find a vendor that maybe sorta-kinda-hard-to-tell meets your solutions, then you can expect days of scheduling WebEx teleconferences and meetings and run-around with your &#8220;account rep&#8221; so that they can determine how much you&#8217;re worth and willing to pay so that <a href="http://www.endicia.com/Developers/">they can charge you a completely different amount</a> than what they charged Bob next door for the same services and bits. Trying to extract &#8220;$X/user&#8221; and &#8220;the login starts working on MM/DD/YYYY&#8221; and &#8220;the developer get a demo account so if you can know if this is feasible&#8221; answers from these people seems to require a hammer in one hand and their genitals in the other. We both know that to add a new account, they&#8217;re pressing a button that says &#8220;they really bought into that &#8216;enterprise&#8217; crap&#8221; and poof! a new account is created. Let&#8217;s quit pretending that the world&#8217;s carbon footprint has increased ten-fold by us merely asking to be on the platform.</p>

<p>Trying to extract technical capabilities from these salespeople is nigh-impossible either. I think part of the problem is that they seem to actually believe that the features that they are promising really exist, when in reality I just need them to show me what the data dictionary looks like and how the session needs to be handled and then I can tell for myself whether or not my scenario is actually supported. Instead? I&#8217;m waiting on a &#8220;discovery session&#8221; teleconference with an &#8220;engineer&#8221; tomorrow.</p>

<h2>Conclusions and Delusions</h2>

<p>It has to be easier than this. No wonder there are so many not-invented-here software solutions in the world today&#8211;custom crap that barely works at home may yet indeed be better than generic crap that you have to waste hundreds of dollars on in productivity time and research before you even get it in your hands and realize that it is also crap, just with a maintenance contract.</p>

<p>If it takes a consultant to help people decide what software to buy, or which of your products is right for them, or whether or not your product <em>even applies to their problem domain</em>, then your marketing <em>simply does not work.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2010/01/buying-software-sucks/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Thank You, Comcast</title>
		<link>http://nicholas.piasecki.name/blog/2009/11/thank-you-comcast/</link>
		<comments>http://nicholas.piasecki.name/blog/2009/11/thank-you-comcast/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 04:00:37 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[comcast-sucks]]></category>
		<category><![CDATA[it-stuff]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=427</guid>
		<description><![CDATA[There is nothing quite like dashing to work at night to restart the &#8220;SMC Comcast Business IP Gateway&#8221; because it stopped routing packets for one of our static IP addresses. The reason? Oh, it just does that sort of thing every 25 days or so.]]></description>
			<content:encoded><![CDATA[<p>There is nothing quite like dashing to work at night to restart the &#8220;SMC Comcast Business IP Gateway&#8221; because it stopped routing packets for one of our static IP addresses.</p>

<p>The reason? Oh, it just does that sort of thing every 25 days or so.</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2009/11/thank-you-comcast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wake up, USPS</title>
		<link>http://nicholas.piasecki.name/blog/2009/07/wake-up-usps/</link>
		<comments>http://nicholas.piasecki.name/blog/2009/07/wake-up-usps/#comments</comments>
		<pubDate>Thu, 16 Jul 2009 04:13:17 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[integration]]></category>
		<category><![CDATA[usps]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=310</guid>
		<description><![CDATA[Our latest shipping integration at work is near nirvana: you place a package on a scale, scan the barcode (the shipping program itself doesn&#8217;t even need to be focused!), choose the box type, and press enter. Then a label comes shooting out of the label printer, and maybe some customs forms documents, too. Behind that [...]]]></description>
			<content:encoded><![CDATA[<p>Our latest shipping integration at work is near nirvana: you place a package on a scale, scan the barcode (the shipping program itself doesn&#8217;t even need to be focused!), choose the box type, and press enter. Then a label comes shooting out of the label printer, and maybe some customs forms documents, too.</p>

<p>Behind that magic <code>&lt;ENTER&gt;</code> press, though, a lot of magic is happening behind the scenes:</p>

<ul>
<li>I call our payment processing service to cleanse the address into a format suitable for mailing.</li>
<li>I call the FedEx Web service to determine if the address is residential or commercial.</li>
<li>If standard shipping, I call the USPS XML API, FedEx SOAP API, and UPS XML APIs simultaneously to find the cheapest rate and best transit time.</li>
<li>I call the FedEx or UPS API directly to print a shipping label immediately OR invoke a Stamps.com COM object to pop up an extra dialog, which the user then presses enter to spit out the label interminably slowly.</li>
</ul>

<p>It&#8217;s the last part that has been the bane of my existence. USPS does not offer any online mechanism to print postage-paid shipping labels in an automated fashion. You get to choose between Endicia and Stamps.com. While Endicia has an XML API that functions nearly identically to those offered by UPS and FedEx, it&#8217;s ridiculously expensive, and Stamps.com has no such API. Instead, I have to interact with a COM object that is popping up dialogs and forcing the warehouse worker to worry about buying postage when it runs low.</p>

<p>To add insult to injury, Stamps.com doesn&#8217;t understand EPL2 and instead sends its labels as gigantic GDI documents to the label printer, giving us print times of nearly 15 seconds for a 4&#8243; x 6&#8243; label when going over parallel. Compare to less than 2 seconds for a label sent as an EPL2 document. But Stamps.com is cheap.</p>

<p>And there are no alternatives.</p>

<p>Sure, FedEx has SmartPost and FIMS and UPS has Mail Innovations. These are services where we would put our non-postage paid parcels into a trash bag that FedEx or UPS would pick up, sort, route through their system to a post office near the parcel&#8217;s final destination, and affix postage there and let it enter the USPS system by mailing it for us at the &#8220;last mile post office.&#8221; But Mail Innovations is worthless (a statistically significant number of our packages shipped through Mail Innovations to Canada were tampered with), FIMS is mind-boggingly slow (up to 60 days to send a package to Finland, and FedEx reps seem to have no idea that it is actually taking that long for their service to actually work), and SmartPost is expensive unless you&#8217;re talking enterprise-level volumes, which I am not. Plus, if a package disappears, there&#8217;s plenty of finger pointing, trust me.</p>

<p>And sure, there&#8217;s Click-N-Ship at the USPS&#8217;s Web site. But Click-N-Ship provides zero integration capabilities and as such is useless when you&#8217;re talking more than, say, 3 packages per day. It prints lots of instructions that you just end up throwing away, uses PDF for label output (so you can&#8217;t really automate printing unless you want to rig up a harebrained routing-through-Ghostscript scheme), and is really intended for home users.</p>

<p>There&#8217;s permit imprint, which would work if we were mailing 200 identically weighted parcel post packages, but that is never going to happen. Some widgets are bigger than others.</p>

<p>And sure, there&#8217;s that moronic new commercial where they show the USPS employees touting the simplicity of the new priority mail flat-rate boxes for businesses. You still have to stamp them&#8211;that&#8217;s half my problem. And using a flat rate box means that you&#8217;re throwing money away a good 50% of the time. We&#8217;re not stupid. It does not cost $9.85 to send a 12&#8243; x 9&#8243; x 6&#8243; box from Virginia to North Carolina. And where&#8217;s my delivery confirmation barcode? I&#8217;ll have to affix that too and record the number&#8211;how is this saving me any time? I want to throw my remote at the TV every time this commercial comes on.</p>

<p>So, I don&#8217;t understand why the post office doesn&#8217;t offer a postage Web service like UPS&#8217;s or FedEx&#8217;s XML shipping services, unless it&#8217;s just too much competition with their own licensees. The Visa business model does not make sense for everyone.</p>

<p>I don&#8217;t understand why the post office doesn&#8217;t sell laser printer versions of its CN-22 and CP-72 customs forms. We had to have our own specially made, and use the USPS&#8217;s API to generate the actual form. (More on this later.)</p>

<p>I don&#8217;t understand why the USPS has an address verification API available through their Web tools but doesn&#8217;t let anybody use it. (By anybody, I mean that only non-profits can use it. Not even local governments can use it.) Shouldn&#8217;t it be an obvious service to help customers verify that they&#8217;re shipping to a valid address? Well, perhaps I do understand, because there&#8217;s lots of money to be made selling CASS certification to vendors who then can charge ridiculously high fees for what is, at its core, just a giant database table that gets updated once a month. By giant, I mean it fits on a CD-ROM. CASS is beyond stupid.</p>

<p>I don&#8217;t understand why the USPS doesn&#8217;t get its act together and offer business accounts for its Priority mail and higher services. With the USPS, indicia is like currency: a misprinted label means you lose money, whereas with FedEx and UPS, a misprinted label means you throw it in the trash. But far, far more importantly, this means that businesses can&#8217;t leverage the &#8220;float&#8221; when using USPS: the shipping cost is incurred immediately instead of on net 30 terms.</p>

<p>I don&#8217;t understand why the USPS doesn&#8217;t allow scheduling a daily pickup for businesses. Sure, there&#8217;s the insane carrier request API, which we use, and have to remember to update every few months until we max out the pickup requests again. But it&#8217;s particularly insane that it requests the number of packages that will be picked up and that mail carriers treat this as some sort of contractual obligation. I am setting a date in the future&#8211;I can give you an estimated average, but please don&#8217;t complain when I have 90 packages instead of 50. I can&#8217;t see the future to know how many orders we will have to process that morning.</p>

<p>I don&#8217;t understand why the ICCC is staffed by weasels. When an API is not on your test server but you require us to test against the requested API on the test server before getting access to the production server, then something is really wrong, and someone at the USPS needs to be fired. <em>Right now.</em></p>

<p>If anyone at the post office is listening, one of the greatest barriers to using the post office directly is a lack of integration points. Quit moaning about the number of parcels dropping and make it easier to actually ship packages, and maybe we&#8217;ll be more apt to use it.</p>

<p><em>Update 11/11/2009</em>: See my comment below for more detailed information since this post was written.</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2009/07/wake-up-usps/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>When in doubt: OpenID is a bad idea for your Web site</title>
		<link>http://nicholas.piasecki.name/blog/2009/07/when-in-doubt-openid-is-a-bad-idea-for-your-web-site/</link>
		<comments>http://nicholas.piasecki.name/blog/2009/07/when-in-doubt-openid-is-a-bad-idea-for-your-web-site/#comments</comments>
		<pubDate>Tue, 07 Jul 2009 00:15:36 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[openid]]></category>
		<category><![CDATA[web stuff]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=291</guid>
		<description><![CDATA[Well, that&#8217;s a bit of an inflammatory title. But this screenshot speaks volumes: What has happened here is that my OpenID provider, Verisign Labs, is down for some reason while StackOverflow, the site that I&#8217;m trying to log on to, is still up. A single point of failure While this isn&#8217;t a big deal for [...]]]></description>
			<content:encoded><![CDATA[<p>Well, that&#8217;s a bit of an inflammatory title. But this screenshot speaks volumes:</p>

<div id="attachment_292" class="wp-caption aligncenter" style="width: 310px"><a href="http://nicholas.piasecki.name/blog/wp-content/uploads/2009/07/openid.png"><img src="http://nicholas.piasecki.name/blog/wp-content/uploads/2009/07/openid-300x136.png" alt="I can&#039;t log in!" title="openid" width="300" height="136" class="size-medium wp-image-292" /></a><p class="wp-caption-text">I can't log in!</p></div>

<p>What has happened here is that my <a href="http://openid.net/">OpenID</a> provider, <a href="https://pip.verisignlabs.com/">Verisign Labs</a>, is down for some reason while StackOverflow, the site that I&#8217;m trying to log on to, is still up.</p>

<h2>A single point of failure</h2>

<p>While this isn&#8217;t a big deal for a site that I use recreationally, it is kind of a big deal in principle: all sites that use OpenID are inaccessible to me, since I only have this single OpenID provider. It is a single point of failure.</p>

<p>Sure, I could have multiple OpenID accounts, such as a &#8220;backup&#8221; OpenID account at another provider, but this defeats the purpose of OpenID, of having a central point of authority regarding my online identity. If I have to create a second OpenID provider, I might as well just make a native username and password at the site that I&#8217;m trying to log into. I could also be my own OpenID provider, but I shouldn&#8217;t have to do that, and the average OpenID consumer is not going to know how to do that, anyway. It&#8217;s faster just to create a &#8220;native account&#8221; at the Web site that I&#8217;m trying to access and be done with it.</p>

<h2>An unnecessary third-party dependency</h2>

<p>For any e-commerce business, it&#8217;s usually unwise to take on <em>any</em> third-party dependency. Speaking from experience, that third-party will go down for some reason at some point and <em>you</em> will be the one fielding angry customer support calls for something that isn&#8217;t your problem. Even worse, if something as critical as authentication through OpenID goes down, then you&#8217;re losing sales, and that means that you&#8217;re not doing your job.</p>

<p>If you&#8217;re going to add OpenID to an e-commerce Web site or a &#8220;mission-critical&#8221; Web site, then you should implement it as a red-headed step-child, &#8220;convenience only&#8221; implementation; this way, your users can fall back onto their &#8220;native credentials&#8221; (those stored in <em>your</em> database) when their preferred OpenID provider explodes. In this way, OpenID is a shortcut, not a single authoritative source, much how like customers use PayPal&#8217;s Express Checkout to save time when entering payment and addressing information during checkout. If PayPal&#8217;s Web Service API goes down (and believe you me, it&#8217;s gone down several times this year alone), customers can still begrudgingly use the &#8220;native checkout&#8221; to complete their purchase. Some lost conversions due to the inconvenience&#8211;and for incorrectly faulting the integrity of your Web site for the third party&#8217;s failure&#8211;but at least <em>the sale is still actually possible.</em> If OpenID is your only authentication mechanism, and a user&#8217;s OpenID provider goes down, then that user is simply screwed. The onus should not be on the consumer to provide a backup authentication mechanism to <em>your</em> Web site.</p>

<h2>It&#8217;s just complicated</h2>

<p>OpenID is still just too complicated. I&#8217;m not the smartest software developer in the world by any means, but if I can barely wrap my mind around it enough just to get my first account, then I am 100% certain that our customers would have problems with it:</p>

<ul>
<li>Why do I have to go to some third-party provider to sign in? That doesn&#8217;t make any sense.</li>
<li>Why should I trust that third-party provider? I&#8217;m not even sure that I trust you!</li>
<li>I still don&#8217;t know which third-party provider to use! Oh well, I guess I&#8217;ll use Verisign, at least I&#8217;ve heard of them.</li>
<li>What happens to my credentials when that third-party provider goes under?</li>
<li>Can I call your business to reset my password when I forget it? I will anyway!</li>
</ul>

<p>Even with our &#8220;native registration&#8221; of e-mail addresses and passwords, we had to use an Amazon.com style login form because users kept filling out the &#8220;new customer&#8221; form even when they already had an account:</p>

<blockquote>
  <p>Login screen. Amazon&#8217;s sign-in screen remains a model to be emulated, minimizing the common problem of new customers who try to log in without having registered. Amazon presents two questions in linear order: (1) &#8220;What is your email address?&#8221; and (2) &#8220;Do you have an Amazon.com password?&#8221; For the second question, users can select one of two radio buttons: &#8220;No, I am a new customer,&#8221; or &#8220;Yes, I have a password.&#8221; Many other sites present the new- and established-user sections side-by-side, and thereby divert new users to the established-user section through the magnetic attraction of type-in fields. &#8212; Jakob Niesen, <a href="http://www.useit.com/alertbox/20050725.html">useit.com</a></p>
</blockquote>

<p>When our users have trouble navigating two fields and a radio button, you can imagine the hilarity that ensues when they are presented with multiple authentication mechanisms. Especially one that doesn&#8217;t ask for a password.</p>

<h2>A leaky abstraction</h2>

<p>While the idea of consolidating your online identity to a single source sounds like a good idea, it simply does not work in practice. Not even the United States government has a consistent picture of my identity: my identity is stored with the IRS, the Social Security Administration, the United States Postal service, the Virginia Department of Taxation, the Virginia Department of Motor Vehicles, and the City of Richmond&#8217;s Voter Registrar. These are all <em>independent</em> identity stores that, in general, agree that I am who I say that I am, but each identity is stored and updated independently of one another. Consider OpenID the RealID act of the Internet: what happens when your centralized identity gets screwed up? It&#8217;s an existentialist crisis and you just have to deal with it: identity is what other people say you are; it&#8217;s <em>not</em> what <em>you</em> say you are. Better to have multiple voices confirming your identity than just one.</p>

<h2>Conclusions and Delusions</h2>

<p>If you&#8217;re implementing a social-oriented Web site that is designed to integrate with Facebook or some of the Web-2.0-savvy consumers, then OpenID might make sense. Those nerds may have heard of OpenID and know how to use it, foibles and all. Until the dust settles on OpenID, though, I wouldn&#8217;t add it to a commercial site even if you paid for it: nobody has asked for it; it will cause problems.</p>

<p>My general recommendation would be to have the usual in house username and password mechanism that can be supplemented by OpenID. But realize that each alternative identification mechanism that you add runs the risk of customer confusion, bugs, security holes, and increased customer support. When in doubt, leave it out.</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2009/07/when-in-doubt-openid-is-a-bad-idea-for-your-web-site/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>File Sizes for Exporting SWF from Adobe InDesign CS4 are Huge Because Some Settings Simply Do Not Work</title>
		<link>http://nicholas.piasecki.name/blog/2009/04/file-sizes-for-exporting-swf-from-adobe-indesign-cs4-are-huge-because-some-settings-simply-do-not-work/</link>
		<comments>http://nicholas.piasecki.name/blog/2009/04/file-sizes-for-exporting-swf-from-adobe-indesign-cs4-are-huge-because-some-settings-simply-do-not-work/#comments</comments>
		<pubDate>Wed, 15 Apr 2009 01:10:37 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[adobe]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[indesign]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[web stuff]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=227</guid>
		<description><![CDATA[Update 5/5/2009: This is resolved in update 6.0.2. Original post follows. My boss at work used Adobe InDesign CS4 to create a new, 20-page printed catalog for our customers. He discovered that InDesign has this nifty &#8220;Export to SWF&#8221; feature whereby, insto-presto, the same document can be converted into a snazzy Flash version, complete with [...]]]></description>
			<content:encoded><![CDATA[<p><i><strong>Update 5/5/2009:</strong> This is resolved in update 6.0.2. Original post follows.</i></p>

<p>My boss at work used Adobe InDesign CS4 to create a new, 20-page printed catalog for our customers. He discovered that InDesign has this nifty &#8220;Export to SWF&#8221; feature whereby, insto-presto, the same document can be converted into a snazzy Flash version, complete with super fancy page-turning effects. Neat!</p>

<p>Unfortunately, the file size of the generated Flash file is enormous&#8211;at 7.4 megabytes, it definitely wasn&#8217;t something that we could realistically put on our Web site.</p>

<p>Seems easy enough to fix! Let&#8217;s look at the options dialog that InDesign pops up during the export process:</p>

<div id="attachment_228" class="wp-caption aligncenter" style="width: 265px"><a href="http://nicholas.piasecki.name/blog/wp-content/uploads/2009/04/irvid4030_id_9.jpg"><img src="http://nicholas.piasecki.name/blog/wp-content/uploads/2009/04/irvid4030_id_9-255x300.jpg" alt="Words, words, words..." title="Words, words, words..." width="255" height="300" class="size-medium wp-image-228" /></a><p class="wp-caption-text">Words, words, words...</p></div>

<p>Awesome! &#8220;JPEG Quality&#8221; sounds like just what we need, so let&#8217;s set it to &#8220;Minimum&#8221; and export the Flash&#8211;oh, wait, it&#8217;s the same size: 7.4 MB. Go back and set it to &#8220;Maximum&#8221;&#8211;7.4 MB. Set it to &#8220;Low&#8221;&#8211;7.4 MB.</p>

<p>Um.</p>

<p>A few wasted minutes of Googling gives us <a href="http://forums.adobe.com/message/1871330#1871330">this gem on Adobe&#8217;s Forums from the purported Product Manager for InDesign</a>:</p>

<blockquote>
First and foremost, unfortunately there is a bug in the JPEG Quality setting of the InDesign CS4 SWF Export dialog. The bug is that no matter what value you choose from the pop-up menu (Minimum, Low, Medium, High, Maximum), they will all end up as Medium quality. The bad news is that this bug was not discovered (internally) or reported (by pre-release and CS4 customers) before we shipped CS4 (6.0.0), or the 6.0.1 dot release. The good news is that it will be fixed in the next dot release (6.0.2). I can&#8217;t provide a specific date for the next dot release yet.
</blockquote>

<p>In other words, the feature Simply Does Not Work. This is the type of programming mistake that I make in our backend applications at a small business, sure: but it is <em>not</em> the type of mistake that you would expect from a multi-million dollar software development powerhouse who has been doing this for decades and charges over $600 for the product. <em>Ridiculous.</em></p>

<h2>Try exporting to a source file</h2>

<p>Great. So the fix is not available, and I have to get this stupid Flash file up before I can go home. Maybe I can use the other InDesign option to export to XFL. Then we can open up this file in Adobe Flash proper and sort out the images there.</p>

<p>That works great, but when exporting to XFL, InDesign doesn&#8217;t export any of the hyperlinks or page animations, which is kind of half the point of the exporting feature. The Help manual even documents this as if this were useful behavior. Obviously InDesign had to generate <em>some</em> source file to generate the enormous SWF; why can&#8217;t I have <em>that</em> file, and not this half-baked XFL thing?</p>

<h2>Now I&#8217;m getting irritated</h2>

<p>Okay. Now what? Perhaps I can decompile the enormous SWF file so that I can access the image resources and lower the quality of the JPEGs. If you Google this long enough, you will find that generally all third-party Flash products (especially those relating to decompilation) fall into three categories:</p>

<ul>
<li>outdated open source software that probably never worked;</li>
<li>poorly written shareware; and</li>
<li>Trojan horses.</li>
</ul>

<p>I finally downloaded a demo of <a href="http://www.eltima.com/products/flashdecompiler/">Eltima&#8217;s Flash Decompiler Trillix</a>. (What the hell does &#8220;Trillix&#8221; mean?) This application looks pretty, and it does let me see the image resources on the SWF and adjust their quality. But saving is disabled in the demo.</p>

<p>After roughly calculating out my hourly pay, I&#8217;d by that point determined that I had wasted much more of the company&#8217;s money than the $67 licensing fee, so I broke down and paid three tanks of gas for a magic license key number. I finally degraded the quality of all of the images and got the file size from 7.4 MB to 1.0 MB, which is good enough for us. Hopefully by the time the next catalog rolls around, Adobe will have released the 6.0.2 update.</p>

<h2>The pièce de résistance</h2>

<p>My boss asked me if I could use the new tool to just convert the SWF to a plain old FLA source file, the idea being that in the future we&#8217;d use this file as a template and bypass InDesign entirely.</p>

<p>Yep, you guessed it&#8211;the Eltiva software crashes with a bad pointer reference whenever it tries to decompile the scripts in the InDesign-generated Flash file. Why? Who knows&#8211;I don&#8217;t.</p>

<h2>Conclusions and Delusions</h2>

<p>This rant has some purpose: hopefully, it will help prevent some future poor sap from spending nearly half a day chasing his tale. The lessons learned: avoid the Export to SWF feature in Adobe InDesign CS4, and, if you must use it, use a decompiler to manually fix up the image resources.</p>

<p>This is why we drink.</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2009/04/file-sizes-for-exporting-swf-from-adobe-indesign-cs4-are-huge-because-some-settings-simply-do-not-work/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>On TextWriterTraceListener, Inheritance, InitializeData, ASP.NET, and Paths</title>
		<link>http://nicholas.piasecki.name/blog/2009/03/on-textwritertracelistener-inheritance-initializedata-aspnet-and-paths/</link>
		<comments>http://nicholas.piasecki.name/blog/2009/03/on-textwritertracelistener-inheritance-initializedata-aspnet-and-paths/#comments</comments>
		<pubDate>Wed, 25 Mar 2009 03:14:42 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[codeproject]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[trace]]></category>
		<category><![CDATA[web stuff]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=204</guid>
		<description><![CDATA[So you know that .NET has this nifty tracing framework built in; you just plug a few lines into your system.diagnostics section of your app.config or web.config file and now your Trace statements are being output to the listener that you specify. Neat. Let&#8217;s say that you&#8217;ve gone the extra mile and you&#8217;ve implemented your [...]]]></description>
			<content:encoded><![CDATA[<p>So you know that .NET has this nifty tracing framework built in; you just plug a few lines into your <code>system.diagnostics</code> section of your <code>app.config</code> or <code>web.config</code> file and now your <code>Trace</code> statements are being output to the listener that you specify. Neat.</p>

<p>Let&#8217;s say that you&#8217;ve gone the extra mile and you&#8217;ve implemented your own custom trace listener. Even better, let&#8217;s say that you&#8217;ve created a trace listener that extends from <code>TextWriterTraceListener</code>. After all, you&#8217;re probably logging to a text file, but perhaps you wanted to change the format around a little bit.</p>

<p>Recall that the way for us to specify the location of our trace logging file via configuration is to use the <code>initializeData</code> attribute as in the following example:</p>


<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;system.diagnostics<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;sharedListeners<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;add</span></span>
<span style="color: #009900;">      <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;Listener:ApplicationText&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;Skiviez.Hedgehog.Model.AlignedTextWriterTraceListener, Skiviez.Hedgehog.Model&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">initializeData</span>=<span style="color: #ff0000;">&quot;Media\Logs\Hedgehog.txt&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">traceOutputOptions</span>=<span style="color: #ff0000;">&quot;ThreadId, DateTime, ProcessId&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/sharedListeners<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/system.diagnostics<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>


<p>Right. Looks pretty sane: I&#8217;ve got my custom trace listener type (we&#8217;ll assume that <code>AlignedTextWriterTraceListener</code> simply extends the built-in <code>TextWriterTraceListener</code>), I&#8217;m telling it to log to <code>Media\Logs\Hedgehog.txt</code> (relative to my working directory, I would presume), and I&#8217;m passing in some output options and giving it a name.</p>

<p>Nothing we have done would here would leave us to believe that we have broken the way <code>TextWriterTraceListener</code> works. We have, but it won&#8217;t be apparent until we try to run this in a ASP.NET Web site.</p>

<h2>What have you got against Web sites?</h2>

<p>So when we run it an ASP.NET Web site, we will note that while we receive no error, we also see no log file sitting in the <code>Media\Logs</code> directory as we specified. I can understand how the tracing code probably swallows exceptions&#8211;so as to not take down your entire Web site or application because some hoo hah misconfigured a tracing statement in the configuration file&#8211;so let&#8217;s assume that we&#8217;ve screwed something up. Check the permissions on the <code>Media\Logs</code> directory? Check. Check the working directory? Hmm.</p>

<p>The working directory of ASP.NET applications is usually strange, somewhere in the <code>%SYSTEMDIR%</code> area&#8211;that&#8217;s because your code is usually running from some temporary location where your ASP.NET Web site was compiled just-in-time to serve the first request. So our trace listener is trying to be relative to a highly privileged directory&#8211;not relative to our Web application&#8217;s root directory&#8211;and obviously the log file can&#8217;t be created there, failing silently.</p>

<p>Okay, that makes sense. But that seems astonishing somehow.</p>

<h2>You&#8217;re not going crazy</h2>

<p>The reason why this seems astonishing is that if you replace your configuration to use the standard <code>TextWriterTraceListener</code> instead of your custom type that extends from it, <em>then the log file will magically appear in the expected location, relative to your Web application&#8217;s root directory, and not relative to the system directory.</em></p>


<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;system.diagnostics<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;sharedListeners<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;add</span></span>
<span style="color: #009900;">      <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;Listener:ApplicationText&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;System.Diagnostics.TextWriterTraceListener&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">initializeData</span>=<span style="color: #ff0000;">&quot;Media\Logs\Hedgehog.txt&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">traceOutputOptions</span>=<span style="color: #ff0000;">&quot;ThreadId, DateTime, ProcessId&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/sharedListeners<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/system.diagnostics<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>


<p>Okay. So you double-check your custom type to make sure you&#8217;re really not doing anything strange with configuration. Which of course you aren&#8217;t&#8211;you&#8217;re just forwarding constructors to the base <code>TextWriterTraceListener</code>&#8216;s constructors. You&#8217;re not supposed to care how they actually work.</p>

<p>So why is the path that the <code>initializeData</code> attribute seems to be relative to changing wildly between the two types?</p>

<h2>Enter the Reflector</h2>

<p>So after spelunking through Reflector for a little while, we stumble across this little gem in the <code>TraceUtil</code>&#8216;s class <code>GetRuntimeObject()</code> method:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">internal</span> <span style="color: #0600FF; font-weight: bold;">static</span> <span style="color: #6666cc; font-weight: bold;">object</span> GetRuntimeObject<span style="color: #008000;">&#40;</span>
	<span style="color: #6666cc; font-weight: bold;">string</span> className, 
	Type baseType, 
	<span style="color: #6666cc; font-weight: bold;">string</span> initializeData<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #008080; font-style: italic;">// ... snip ...</span>
&nbsp;
	<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">.</span><span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #008000;">&#40;</span>initializeData<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
		<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>IsOwnedTextWriterTL<span style="color: #008000;">&#40;</span>c<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
		<span style="color: #008000;">&#123;</span>
			<span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ConfigurationErrorsException<span style="color: #008000;">&#40;</span>
				SR<span style="color: #008000;">.</span><span style="color: #0000FF;">GetString</span><span style="color: #008000;">&#40;</span>
					<span style="color: #666666;">&quot;TextWriterTL_DefaultConstructor_NotSupported&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
		<span style="color: #008000;">&#125;</span>
		ConstructorInfo constructor <span style="color: #008000;">=</span> c<span style="color: #008000;">.</span><span style="color: #0000FF;">GetConstructor</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">new</span> Type<span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
		<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>constructor <span style="color: #008000;">==</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
		<span style="color: #008000;">&#123;</span>
			<span style="color: #0600FF; font-weight: bold;">throw</span> <span style="color: #008000;">new</span> ConfigurationErrorsException<span style="color: #008000;">&#40;</span>
				SR<span style="color: #008000;">.</span><span style="color: #0000FF;">GetString</span><span style="color: #008000;">&#40;</span>
					<span style="color: #666666;">&quot;Could_not_get_constructor&quot;</span>, 
					<span style="color: #008000;">new</span> <span style="color: #6666cc; font-weight: bold;">object</span><span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">&#123;</span> className <span style="color: #008000;">&#125;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
		<span style="color: #008000;">&#125;</span>
		obj2 <span style="color: #008000;">=</span> constructor<span style="color: #008000;">.</span><span style="color: #0000FF;">Invoke</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">new</span> <span style="color: #6666cc; font-weight: bold;">object</span><span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0600FF; font-weight: bold;">else</span>
	<span style="color: #008000;">&#123;</span>
		ConstructorInfo info2 <span style="color: #008000;">=</span> 
			c<span style="color: #008000;">.</span><span style="color: #0000FF;">GetConstructor</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">new</span> Type<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">&#123;</span> <span style="color: #008000;">typeof</span><span style="color: #008000;">&#40;</span><span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#125;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
		<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>info2 <span style="color: #008000;">!=</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
		<span style="color: #008000;">&#123;</span>
			<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>IsOwnedTextWriterTL<span style="color: #008000;">&#40;</span>c<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&amp;&amp;</span> 
				<span style="color: #008000;">&#40;</span>initializeData<span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">!=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">DirectorySeparatorChar</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&amp;&amp;</span> 
				<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>initializeData<span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">!=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">AltDirectorySeparatorChar</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&amp;&amp;</span> 
				<span style="color: #008000;">!</span>Path<span style="color: #008000;">.</span><span style="color: #0000FF;">IsPathRooted</span><span style="color: #008000;">&#40;</span>initializeData<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
			<span style="color: #008000;">&#123;</span>
				<span style="color: #6666cc; font-weight: bold;">string</span> configFilePath <span style="color: #008000;">=</span> DiagnosticsConfiguration<span style="color: #008000;">.</span><span style="color: #0000FF;">ConfigFilePath</span><span style="color: #008000;">;</span>
				<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">!</span><span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">.</span><span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #008000;">&#40;</span>configFilePath<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
				<span style="color: #008000;">&#123;</span>
					<span style="color: #6666cc; font-weight: bold;">string</span> directoryName <span style="color: #008000;">=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">GetDirectoryName</span><span style="color: #008000;">&#40;</span>configFilePath<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
					<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>directoryName <span style="color: #008000;">!=</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
					<span style="color: #008000;">&#123;</span>
						initializeData <span style="color: #008000;">=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">Combine</span><span style="color: #008000;">&#40;</span>
							directoryName, 
							initializeData<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
					<span style="color: #008000;">&#125;</span>
				<span style="color: #008000;">&#125;</span>
			<span style="color: #008000;">&#125;</span>
			obj2 <span style="color: #008000;">=</span> info2<span style="color: #008000;">.</span><span style="color: #0000FF;">Invoke</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">new</span> <span style="color: #6666cc; font-weight: bold;">object</span><span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">&#123;</span> initializeData <span style="color: #008000;">&#125;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
		<span style="color: #008000;">&#125;</span>
		<span style="color: #008080; font-style: italic;">// ... snip ...</span>
	<span style="color: #008000;">&#125;</span>
		<span style="color: #008080; font-style: italic;">// ... snip ...</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0600FF; font-weight: bold;">internal</span> <span style="color: #0600FF; font-weight: bold;">static</span> <span style="color: #6666cc; font-weight: bold;">bool</span> IsOwnedTextWriterTL<span style="color: #008000;">&#40;</span>Type type<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">typeof</span><span style="color: #008000;">&#40;</span>XmlWriterTraceListener<span style="color: #008000;">&#41;</span> <span style="color: #008000;">!=</span> type<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&amp;&amp;</span> 
		<span style="color: #008000;">&#40;</span><span style="color: #008000;">typeof</span><span style="color: #008000;">&#40;</span>DelimitedListTraceListener<span style="color: #008000;">&#41;</span> <span style="color: #008000;">!=</span> type<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
		<span style="color: #0600FF; font-weight: bold;">return</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">typeof</span><span style="color: #008000;">&#40;</span>TextWriterTraceListener<span style="color: #008000;">&#41;</span> <span style="color: #008000;">==</span> type<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0600FF; font-weight: bold;">return</span> <span style="color: #0600FF; font-weight: bold;">true</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>Now, I know that I for one have written some strange and hackish code in my time, but whoever wrote this function was obviously wearing his silly pants that day. Here&#8217;s what the function does:</p>

<p>If the following are ALL true:</p>

<ul>
<li>The trace output listener type is one of the three concrete types explicitly provided by Microsoft (and not an inheritor of that type)</li>
<li>The initializeData attribute is provided</li>
<li>The initializeData path seems to be relative</li>
</ul>

<p>Then the function mangles the initializeData attribute by combining it with the path of the configuration file that contained the attribute. It then instances the trace listener by providing this faked-just-in-time full path as the <code>fileName</code> argument.</p>

<p>So if I had configured a <code>TextWriterTraceListener</code> in my configuration file, and my configuration file were in <code>C:\Foo</code>, and I specified <code>Media\Logs\Hedgehog.txt</code> as the <code>initializeData</code> parameter, then the <code>TraceUtils</code> class would give the <code>TextWriterTraceListener</code> instance the value of <code>C:\Foo\Media\Logs\Hedgehog.txt</code> as the <code>fileName</code> argument.</p>

<p>But if I had configured <strong>anything but</strong> <code>TextWriterTraceListener</code>, <code>XmlWriterTraceListener</code>, or <code>DelimitedListTraceListener</code> as my output listener in my configuration file, and my configuration file were in <code>C:\Foo</code>, and I specified <code>Media\Logs\Hedgehog.txt</code> as the <code>initializeData</code> parameter, then the <code>TraceUtils</code> would kindly tell me to screw myself, pass the <code>TextWriterTraceListener</code> instance the value of <code>Media\Logs\Hedgehog.txt</code> as the <code>fileName</code> argument, which the instance would then match with the current working directory, which is something insane like <code>C:\Windows\System32\Temporary ASP.NET Files</code> if we&#8217;re talking about an ASP.NET Web site, and so I would end up with  <code>C:\Windows\System32\Temporary ASP.NET Files\Media\Logs\Hedgehog.txt</code>, which is about as useful as sticking my thumb up my butt.</p>

<p>So just by extending a class, we have completely broken the way one of its configuration attributes works. That&#8217;s pretty astonishing when it comes to object-oriented design.</p>

<h2>An ugly work-around</h2>

<p>So I wasn&#8217;t about to specify a full path in <code>initializeData</code> because I don&#8217;t enjoy munging a configuration value on every single machine that I happen to build and deploy my solution to. As this is written in the .NET Framework&#8217;s internal <code>TraceUtils</code> class, I simply cannot avoid this behavior when extending a Microsoft-provided type.</p>

<p>So if I want to be able to specify a relative path in <code>initializeData</code> with a type that extends from <code>TextWriterTraceListener</code> and have it mimic the <code>TextWriterTraceListener</code>&#8216;s <code>initializeData</code> relative-path-resolution behavior, my only recourse is to copy <code>TextWriterTraceListener</code> out of Reflector and make it my own type with my own relative-path munging semantics.</p>

<p>I change the constructors of my new type, which I&#8217;ve christened <code>TextWriterTraceListener</code> in my own namespace, as follows:</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #008080; font-style: italic;">/// &lt;summary&gt;</span>
<span style="color: #008080; font-style: italic;">/// Initializes a new instance of the TextWriterTraceListener class, </span>
<span style="color: #008080; font-style: italic;">/// using the file as the recipient of the debugging and tracing output. </span>
<span style="color: #008080; font-style: italic;">/// &lt;/summary&gt;</span>
<span style="color: #008080; font-style: italic;">/// &lt;param name=&quot;fileName&quot;&gt;The name of the file the </span>
<span style="color: #008080; font-style: italic;">/// TextWriterTraceListener writes to. &lt;/param&gt;</span>
<span style="color: #0600FF; font-weight: bold;">public</span> TextWriterTraceListener<span style="color: #008000;">&#40;</span><span style="color: #6666cc; font-weight: bold;">string</span> fileName<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">fileName</span> <span style="color: #008000;">=</span> MungeFileName<span style="color: #008000;">&#40;</span>fileName<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #008080; font-style: italic;">/// &lt;summary&gt;</span>
<span style="color: #008080; font-style: italic;">/// Initializes a new instance of the TextWriterTraceListener class </span>
<span style="color: #008080; font-style: italic;">/// with the specified name, using the file as the recipient of the </span>
<span style="color: #008080; font-style: italic;">/// debugging and tracing output. </span>
<span style="color: #008080; font-style: italic;">/// &lt;/summary&gt;</span>
<span style="color: #008080; font-style: italic;">/// &lt;param name=&quot;fileName&quot;&gt;the fileName to output to&lt;/param&gt;</span>
<span style="color: #008080; font-style: italic;">/// &lt;param name=&quot;name&quot;&gt;the name of the trace listener&lt;/param&gt;</span>
<span style="color: #0600FF; font-weight: bold;">public</span> TextWriterTraceListener<span style="color: #008000;">&#40;</span><span style="color: #6666cc; font-weight: bold;">string</span> fileName, <span style="color: #6666cc; font-weight: bold;">string</span> name<span style="color: #008000;">&#41;</span> <span style="color: #008000;">:</span> <span style="color: #0600FF; font-weight: bold;">base</span><span style="color: #008000;">&#40;</span>name<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0600FF; font-weight: bold;">this</span><span style="color: #008000;">.</span><span style="color: #0000FF;">fileName</span> <span style="color: #008000;">=</span> MungeFileName<span style="color: #008000;">&#40;</span>fileName<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>and the gloriously-named <code>MungeFileName()</code> method is</p>


<div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #008080; font-style: italic;">/// &lt;summary&gt;</span>
<span style="color: #008080; font-style: italic;">/// Munges the file name such that if it is a relative path, we go</span>
<span style="color: #008080; font-style: italic;">/// relative from the configuration file and not from the current working</span>
<span style="color: #008080; font-style: italic;">/// directory. This makes things work as expected on ASP.NET sites and</span>
<span style="color: #008080; font-style: italic;">/// makes other applications similarly work with non-astonishing</span>
<span style="color: #008080; font-style: italic;">/// behavior.</span>
<span style="color: #008080; font-style: italic;">/// &lt;/summary&gt;</span>
<span style="color: #008080; font-style: italic;">/// &lt;param name=&quot;fileName&quot;&gt;the file name to munge&lt;/param&gt;</span>
<span style="color: #008080; font-style: italic;">/// &lt;returns&gt;the munged filename&lt;/returns&gt;</span>
<span style="color: #0600FF; font-weight: bold;">private</span> <span style="color: #0600FF; font-weight: bold;">static</span> <span style="color: #6666cc; font-weight: bold;">string</span> MungeFileName<span style="color: #008000;">&#40;</span><span style="color: #6666cc; font-weight: bold;">string</span> fileName<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #6666cc; font-weight: bold;">string</span> configPath<span style="color: #008000;">;</span>
	<span style="color: #6666cc; font-weight: bold;">string</span> mungedFileName<span style="color: #008000;">;</span>
&nbsp;
	mungedFileName <span style="color: #008000;">=</span> fileName<span style="color: #008000;">;</span>
&nbsp;
	<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>fileName<span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">!=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">DirectorySeparatorChar</span> <span style="color: #008000;">&amp;&amp;</span>
		fileName<span style="color: #008000;">&#91;</span><span style="color: #FF0000;">0</span><span style="color: #008000;">&#93;</span> <span style="color: #008000;">!=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">AltDirectorySeparatorChar</span> <span style="color: #008000;">&amp;&amp;</span>
		<span style="color: #008000;">!</span>Path<span style="color: #008000;">.</span><span style="color: #0000FF;">IsPathRooted</span><span style="color: #008000;">&#40;</span>fileName<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>
		ConfigurationSection configSection<span style="color: #008000;">;</span>
&nbsp;
		configSection <span style="color: #008000;">=</span> <span style="color: #008000;">&#40;</span>ConfigurationSection<span style="color: #008000;">&#41;</span>ConfigurationManager
			<span style="color: #008000;">.</span><span style="color: #0000FF;">GetSection</span><span style="color: #008000;">&#40;</span><span style="color: #666666;">&quot;system.diagnostics&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
		<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>configSection <span style="color: #008000;">!=</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
		<span style="color: #008000;">&#123;</span>
			configPath <span style="color: #008000;">=</span> configSection<span style="color: #008000;">.</span><span style="color: #0000FF;">ElementInformation</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Source</span><span style="color: #008000;">;</span>
&nbsp;
			<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #008000;">!</span><span style="color: #6666cc; font-weight: bold;">string</span><span style="color: #008000;">.</span><span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #008000;">&#40;</span>configPath<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
			<span style="color: #008000;">&#123;</span>
				<span style="color: #6666cc; font-weight: bold;">string</span> directoryName<span style="color: #008000;">;</span>
&nbsp;
				directoryName <span style="color: #008000;">=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">GetDirectoryName</span><span style="color: #008000;">&#40;</span>configPath<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
				<span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>directoryName <span style="color: #008000;">!=</span> <span style="color: #0600FF; font-weight: bold;">null</span><span style="color: #008000;">&#41;</span>
				<span style="color: #008000;">&#123;</span>
					mungedFileName <span style="color: #008000;">=</span> Path<span style="color: #008000;">.</span><span style="color: #0000FF;">Combine</span><span style="color: #008000;">&#40;</span>
						directoryName, 
						fileName<span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
				<span style="color: #008000;">&#125;</span>
			<span style="color: #008000;">&#125;</span>
		<span style="color: #008000;">&#125;</span>
	<span style="color: #008000;">&#125;</span>
&nbsp;
	<span style="color: #0600FF; font-weight: bold;">return</span> mungedFileName<span style="color: #008000;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>


<p>What I&#8217;m doing here is saying, &#8220;Is the path relative? Then figure out where my <code>system.diagnostics</code> configuration came from, and make myself relative to THAT directory.&#8221; This way, I don&#8217;t have to have different code paths for this class depending on whether it&#8217;s running in a traditional Windows application or an ASP.NET Web site&#8211;the <code>initializeData</code> is always relative to the configuration file and not the current working directory, which is what I want 99% of the time, and it&#8217;s probably what most people want, too.</p>

<h2>Conclusions and Delusions</h2>

<p>I think the primary lesson to take away from this is to avoid doing magic configuration and baby-sitting for a class&#8217;s constructors. I have a feeling that <code>TextWriterTraceListener</code> was implemented by somebody at Microsoft and sometime much later in the development process they realized that specifying a relative <code>initializeData</code> was difficult for ASP.NET applications. Being unable to alter <code>TextWriterTraceListener</code> without breaking compatibility for non-ASP.NET applications that may have already been built, they devised an unusual <code>TraceUtils</code> class to munge the parameters read from configuration before they were passed to a new instance&#8217;s constructor.</p>

<p>The result? It worked well if you stuck to the built-in classes, but sure was damn surprising if you created a custom type and eventually used it on an ASP.NET project.</p>

<p>Hope this helps someone out there. You&#8217;re not insane, after all.</p>

<p><a href='http://nicholas.piasecki.name/blog/wp-content/uploads/2009/03/defaulttextwritertracelistenercs.txt'>Here&#8217;s the implementation for your convenience.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2009/03/on-textwritertracelistener-inheritance-initializedata-aspnet-and-paths/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Windows Needs a Unified Update Mechanism</title>
		<link>http://nicholas.piasecki.name/blog/2008/11/windows-needs-a-unified-update-mechanism/</link>
		<comments>http://nicholas.piasecki.name/blog/2008/11/windows-needs-a-unified-update-mechanism/#comments</comments>
		<pubDate>Fri, 28 Nov 2008 02:13:24 +0000</pubDate>
		<dc:creator>Nicholas Piasecki</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[updates]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://nicholas.piasecki.name/blog/?p=98</guid>
		<description><![CDATA[Why doesn&#8217;t Windows (or Mac OS X, for that matter) provide a built-in service that third-party users can use to provide updates to their applications? Currently, users are bewildered by a dizzying array of Not-Invented-Here update mechanisms. When I start Paint.NET (a program that I love and use often), it&#8217;ll irritatingly ask me to update [...]]]></description>
			<content:encoded><![CDATA[<p>Why doesn&#8217;t Windows (or Mac OS X, for that matter) provide a built-in service that third-party users can use to provide updates to their applications?</p>

<p>Currently, users are bewildered by a dizzying array of Not-Invented-Here update mechanisms. When I start Paint.NET (a program that I love and use often), it&#8217;ll irritatingly ask me to update as soon as I&#8217;ve started it. Adobe Acrobat will similarly pop up an insanely complex dialog at start up. Java has a process that sits there sucking up memory and displays a balloon in the system tray. Flash will display an owner-drawn dialog (that looks just like spyware, by the way) when I first log into my Windows session. And Apple will pop up its own updater dialog seemingly at random.</p>

<p>Then there are the processes that sit there in memory, sitting around twiddling their thumbs 99% of the time:</p>

<ul>
<li><code>GoogleUpdate.exe</code> &#8212; Google Chrome&#8217;s updater</li>
<li><code>jusched.exe</code> &#8212; Java&#8217;s updater</li>
<li><code>SoftwareUpdate.exe</code> &#8212; Apple&#8217;s hog takes up 13 MB</li>
</ul>

<p>This isn&#8217;t the individual developer&#8217;s fault. When it comes to pushing updates, you can either use a technology like ClickOnce (which is useful but in a very specific environment, namely deploying internal business applications), or you can roll your own solution.</p>

<p>This is stupid.</p>

<p>Windows and Microsoft Update finally got an integrated user interface in Windows Vista. It&#8217;s unremarkable, which is a good thing, because that means that I must not hate it. Imagine how wonderful it would be&#8211;both for the user experience and memory usage&#8211;if <em>all</em> applications could integrate with this same user interface. An MSI installer could register a URL that Windows could check for updates periodically. If updates are found, it could use the same BITS mechanism to unobtrusively download it in the background. And to verify integrity, it would verify that the update has been signed by the same key that was used to sign the original installation MSI, or it could be based on some sort of other trust mechanism. This way, all of the user&#8217;s updates would be accessible through one interface.</p>

<p>To make sure companies use it, make it a requirement for the Windows Logo program.</p>

<p>Why hasn&#8217;t this happened yet?</p>
]]></content:encoded>
			<wfw:commentRss>http://nicholas.piasecki.name/blog/2008/11/windows-needs-a-unified-update-mechanism/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

