<?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>Orange is my favorite color &#187; Web/Internet</title>
	<atom:link href="http://www.ghidinelli.com/c/webinternet/feed" rel="self" type="application/rss+xml" />
	<link>http://www.ghidinelli.com</link>
	<description></description>
	<lastBuildDate>Wed, 04 Jan 2012 08:06:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.3</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>CFHTTP Error Handling for Status Codes</title>
		<link>http://www.ghidinelli.com/2012/01/03/cfhttp-error-handling-http-status-codes</link>
		<comments>http://www.ghidinelli.com/2012/01/03/cfhttp-error-handling-http-status-codes#comments</comments>
		<pubDate>Wed, 04 Jan 2012 03:23:10 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[CFHTTP]]></category>
		<category><![CDATA[cfpayment]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1433</guid>
		<description><![CDATA[A test of the exception types thrown by ColdFusion for all HTTP status codes]]></description>
			<content:encoded><![CDATA[<p>We&#8217;re <a href="https://groups.google.com/forum/#!topic/cfpayment/e_gjlAbAWqY">discussing some refactoring</a> before releasing a <a href="http://cfpayment.riaforge.org">cfpayment 1.0</a> and one of the things I&#8217;m working on is figuring out when to use ColdFusion&#8217;s in-built error handling and when to use HTTP status codes.  Currently we use the throwonerror option for CFHTTP which results in issues like 404 throwing an exception.  This works but as more and more services expose RESTful APIs which use status codes other than 200 to communicate results, this is not perfect.</p>
<p>That got me to thinking&#8230; what status codes does ColdFusion throw an error for when throwonerror is set to yes?  Here&#8217;s the list for <a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes">every HTTP status code</a>:</p>
<h2>CFHTTP with throwonerror=&#8221;yes&#8221;</h2>
<table>
<tr>
<td>200</td>
<td>Returned normally with status 200 OK</td>
</tr>
<tr>
<td>201</td>
<td>Returned normally with status 201 Created</td>
</tr>
<tr>
<td>202</td>
<td>Returned normally with status 202 Accepted</td>
</tr>
<tr>
<td>203</td>
<td>Returned normally with status 203 Non-Authoritative Information</td>
</tr>
<tr>
<td>204</td>
<td>Returned normally with status 204 No Content</td>
</tr>
<tr>
<td>205</td>
<td>Returned normally with status 205 Reset Content</td>
</tr>
<tr>
<td>206</td>
<td>Returned normally with status 206 Partial Content</td>
</tr>
<tr>
<td>207</td>
<td>Returned normally with status 207 Multi-Status</td>
</tr>
<tr>
<td>208</td>
<td>Returned normally with status 208 Already Reported</td>
</tr>
<tr>
<td>226</td>
<td>Returned normally with status 226 IM Used</td>
</tr>
<tr>
<td>300</td>
<td>COM.Allaire.ColdFusion.HTTPMultipleChoices</td>
</tr>
<tr>
<td>301</td>
<td>COM.Allaire.ColdFusion.HTTPMovedPermanently</td>
</tr>
<tr>
<td>302</td>
<td>COM.Allaire.ColdFusion.HTTPMovedPermanently</td>
</tr>
<tr>
<td>303</td>
<td>COM.Allaire.ColdFusion.HTTPSeeOther</td>
</tr>
<tr>
<td>304</td>
<td>COM.Allaire.ColdFusion.HTTPNotModified</td>
</tr>
<tr>
<td>305</td>
<td>COM.Allaire.ColdFusion.HTTPUseProxy</td>
</tr>
<tr>
<td>306</td>
<td>Returned normally with status 306 Switch Proxy</td>
</tr>
<tr>
<td>307</td>
<td>Returned normally with status 307 Temporary Redirect</td>
</tr>
<tr>
<td>308</td>
<td>Returned normally with status 308 Resume Incomplete</td>
</tr>
<tr>
<td>400</td>
<td>COM.Allaire.ColdFusion.HTTPBadRequest</td>
</tr>
<tr>
<td>401</td>
<td>Returned normally with status 401 Unauthorized</td>
</tr>
<tr>
<td>402</td>
<td>COM.Allaire.ColdFusion.HTTPPaymentRequired</td>
</tr>
<tr>
<td>403</td>
<td>COM.Allaire.ColdFusion.HTTPForbidden</td>
</tr>
<tr>
<td>404</td>
<td>COM.Allaire.ColdFusion.HTTPNotFound</td>
</tr>
<tr>
<td>405</td>
<td>Returned normally with status 405 Method Not Allowed</td>
</tr>
<tr>
<td>406</td>
<td>COM.Allaire.ColdFusion.HTTPNotAcceptable</td>
</tr>
<tr>
<td>407</td>
<td>COM.Allaire.ColdFusion.HTTPProxyAuthenticationRequired</td>
</tr>
<tr>
<td>408</td>
<td>Returned normally with status 408 Request Timeout</td>
</tr>
<tr>
<td>409</td>
<td>COM.Allaire.ColdFusion.HTTPConflict</td>
</tr>
<tr>
<td>410</td>
<td>COM.Allaire.ColdFusion.HTTPGone</td>
</tr>
<tr>
<td>411</td>
<td>COM.Allaire.ColdFusion.HTTPFailure</td>
</tr>
<tr>
<td>412</td>
<td>COM.Allaire.ColdFusion.HTTPPreconditionFailed</td>
</tr>
<tr>
<td>413</td>
<td>COM.Allaire.ColdFusion.HTTPCFHTTPRequestEntityTooLarge</td>
</tr>
<tr>
<td>414</td>
<td>COM.Allaire.ColdFusion.HTTPRequestURITooLarge</td>
</tr>
<tr>
<td>415</td>
<td>COM.Allaire.ColdFusion.HTTPUnsupportedMediaType</td>
</tr>
<tr>
<td>416</td>
<td>Returned normally with status 416 Requested Range Not Satisfiable</td>
</tr>
<tr>
<td>417</td>
<td>Returned normally with status 417 Expectation Failed</td>
</tr>
<tr>
<td>418</td>
<td>Returned normally with status 418 I&#8217;m a teapot</td>
</tr>
<tr>
<td>422</td>
<td>Returned normally with status 422 Unprocessable Entity</td>
</tr>
<tr>
<td>423</td>
<td>Returned normally with status 423 Locked</td>
</tr>
<tr>
<td>424</td>
<td>Returned normally with status 424 Failed Dependency</td>
</tr>
<tr>
<td>425</td>
<td>Returned normally with status 425 Unordered Collection</td>
</tr>
<tr>
<td>426</td>
<td>Returned normally with status 426 Upgrade Required</td>
</tr>
<tr>
<td>428</td>
<td>Returned normally with status 428 Precondition Required</td>
</tr>
<tr>
<td>429</td>
<td>Returned normally with status 429 Too Many Requests</td>
</tr>
<tr>
<td>431</td>
<td>Returned normally with status 431 Request Header Fields Too Large</td>
</tr>
<tr>
<td>444</td>
<td>Returned normally with status 444 No Response</td>
</tr>
<tr>
<td>449</td>
<td>Returned normally with status 449 Retry With</td>
</tr>
<tr>
<td>450</td>
<td>Returned normally with status 450 Blocked by Windows Parental Controls</td>
</tr>
<tr>
<td>499</td>
<td>Returned normally with status 499 Client Closed Request</td>
</tr>
<tr>
<td>500</td>
<td>COM.Allaire.ColdFusion.HTTPServerError</td>
</tr>
<tr>
<td>501</td>
<td>COM.Allaire.ColdFusion.HTTPNotImplemented</td>
</tr>
<tr>
<td>502</td>
<td>COM.Allaire.ColdFusion.HTTPBadGateway</td>
</tr>
<tr>
<td>503</td>
<td>COM.Allaire.ColdFusion.HTTPServiceUnavailable</td>
</tr>
<tr>
<td>504</td>
<td>Returned normally with status 504 Gateway Timeout</td>
</tr>
<tr>
<td>505</td>
<td>COM.Allaire.ColdFusion.HTTPVersionNotSupported</td>
</tr>
<tr>
<td>506</td>
<td>Returned normally with status 506 Variant Also Negotiates</td>
</tr>
<tr>
<td>507</td>
<td>Returned normally with status 507 Insufficient Storage</td>
</tr>
<tr>
<td>508</td>
<td>Returned normally with status 508 Loop Detected</td>
</tr>
<tr>
<td>509</td>
<td>Returned normally with status 509 Bandwidth Limit Exceeded</td>
</tr>
<tr>
<td>510</td>
<td>Returned normally with status 510 Not Extended</td>
</tr>
<tr>
<td>511</td>
<td>Returned normally with status 511 Network Authentication Required</td>
</tr>
<tr>
<td>598</td>
<td>Returned normally with status 598 Network read timeout error</td>
</tr>
<tr>
<td>599</td>
<td>Returned normally with status 599 Network connect timeout error</td>
</tr>
<tr>
<td>999</td>
<td>Returned normally with status 999 Invalid HTTP status code</td>
</tr>
</table>
<p>I also ran it with throwonerror=&#8221;no&#8221;, which has <em>almost</em> the results you would expect with the exception of code 411:</p>
<h2>CFHTTP with throwonerror=&#8221;no&#8221;</h2>
<table>
<tr>
<td>200</td>
<td>Returned normally with status 200 OK</td>
</tr>
<tr>
<td>201</td>
<td>Returned normally with status 201 Created</td>
</tr>
<tr>
<td>202</td>
<td>Returned normally with status 202 Accepted</td>
</tr>
<tr>
<td>203</td>
<td>Returned normally with status 203 Non-Authoritative Information</td>
</tr>
<tr>
<td>204</td>
<td>Returned normally with status 204 No Content</td>
</tr>
<tr>
<td>205</td>
<td>Returned normally with status 205 Reset Content</td>
</tr>
<tr>
<td>206</td>
<td>Returned normally with status 206 Partial Content</td>
</tr>
<tr>
<td>207</td>
<td>Returned normally with status 207 Multi-Status</td>
</tr>
<tr>
<td>208</td>
<td>Returned normally with status 208 Already Reported</td>
</tr>
<tr>
<td>226</td>
<td>Returned normally with status 226 IM Used</td>
</tr>
<tr>
<td>300</td>
<td>Returned normally with status 300 Multiple Choices</td>
</tr>
<tr>
<td>301</td>
<td>Returned normally with status 301 Moved Permanently</td>
</tr>
<tr>
<td>302</td>
<td>Returned normally with status 302 Found</td>
</tr>
<tr>
<td>303</td>
<td>Returned normally with status 303 See Other</td>
</tr>
<tr>
<td>304</td>
<td>Returned normally with status 304 Not Modified</td>
</tr>
<tr>
<td>305</td>
<td>Returned normally with status 305 Use Proxy</td>
</tr>
<tr>
<td>306</td>
<td>Returned normally with status 306 Switch Proxy</td>
</tr>
<tr>
<td>307</td>
<td>Returned normally with status 307 Temporary Redirect</td>
</tr>
<tr>
<td>308</td>
<td>Returned normally with status 308 Resume Incomplete</td>
</tr>
<tr>
<td>400</td>
<td>Returned normally with status 400 Bad Request</td>
</tr>
<tr>
<td>401</td>
<td>Returned normally with status 401 Unauthorized</td>
</tr>
<tr>
<td>402</td>
<td>Returned normally with status 402 Payment Required</td>
</tr>
<tr>
<td>403</td>
<td>Returned normally with status 403 Forbidden</td>
</tr>
<tr>
<td>404</td>
<td>Returned normally with status 404 Not Found</td>
</tr>
<tr>
<td>405</td>
<td>Returned normally with status 405 Method Not Allowed</td>
</tr>
<tr>
<td>406</td>
<td>Returned normally with status 406 Not Acceptable</td>
</tr>
<tr>
<td>407</td>
<td>Returned normally with status 407 Proxy Authentication Required</td>
</tr>
<tr>
<td>408</td>
<td>Returned normally with status 408 Request Timeout</td>
</tr>
<tr>
<td>409</td>
<td>Returned normally with status 409 Conflict</td>
</tr>
<tr>
<td>410</td>
<td>Returned normally with status 410 Gone</td>
</tr>
<tr>
<td>411</td>
<td>Returned normally with status Connection Failure.  Status code unavailable.</td>
</tr>
<tr>
<td>412</td>
<td>Returned normally with status 412 Precondition Failed</td>
</tr>
<tr>
<td>413</td>
<td>Returned normally with status 413 Request Entity Too Large</td>
</tr>
<tr>
<td>414</td>
<td>Returned normally with status 414 Request-URI Too Long</td>
</tr>
<tr>
<td>415</td>
<td>Returned normally with status 415 Unsupported Media Type</td>
</tr>
<tr>
<td>416</td>
<td>Returned normally with status 416 Requested Range Not Satisfiable</td>
</tr>
<tr>
<td>417</td>
<td>Returned normally with status 417 Expectation Failed</td>
</tr>
<tr>
<td>418</td>
<td>Returned normally with status 418 I&#8217;m a teapot</td>
</tr>
<tr>
<td>422</td>
<td>Returned normally with status 422 Unprocessable Entity</td>
</tr>
<tr>
<td>423</td>
<td>Returned normally with status 423 Locked</td>
</tr>
<tr>
<td>424</td>
<td>Returned normally with status 424 Failed Dependency</td>
</tr>
<tr>
<td>425</td>
<td>Returned normally with status 425 Unordered Collection</td>
</tr>
<tr>
<td>426</td>
<td>Returned normally with status 426 Upgrade Required</td>
</tr>
<tr>
<td>428</td>
<td>Returned normally with status 428 Precondition Required</td>
</tr>
<tr>
<td>429</td>
<td>Returned normally with status 429 Too Many Requests</td>
</tr>
<tr>
<td>431</td>
<td>Returned normally with status 431 Request Header Fields Too Large</td>
</tr>
<tr>
<td>444</td>
<td>Returned normally with status 444 No Response</td>
</tr>
<tr>
<td>449</td>
<td>Returned normally with status 449 Retry With</td>
</tr>
<tr>
<td>450</td>
<td>Returned normally with status 450 Blocked by Windows Parental Controls</td>
</tr>
<tr>
<td>499</td>
<td>Returned normally with status 499 Client Closed Request</td>
</tr>
<tr>
<td>500</td>
<td>Returned normally with status 500 Internal Server Error</td>
</tr>
<tr>
<td>501</td>
<td>Returned normally with status 501 Not Implemented</td>
</tr>
<tr>
<td>502</td>
<td>Returned normally with status 502 Bad Gateway</td>
</tr>
<tr>
<td>503</td>
<td>Returned normally with status 503 Service Unavailable</td>
</tr>
<tr>
<td>504</td>
<td>Returned normally with status 504 Gateway Timeout</td>
</tr>
<tr>
<td>505</td>
<td>Returned normally with status 505 HTTP Version Not Supported</td>
</tr>
<tr>
<td>506</td>
<td>Returned normally with status 506 Variant Also Negotiates</td>
</tr>
<tr>
<td>507</td>
<td>Returned normally with status 507 Insufficient Storage</td>
</tr>
<tr>
<td>508</td>
<td>Returned normally with status 508 Loop Detected</td>
</tr>
<tr>
<td>509</td>
<td>Returned normally with status 509 Bandwidth Limit Exceeded</td>
</tr>
<tr>
<td>510</td>
<td>Returned normally with status 510 Not Extended</td>
</tr>
<tr>
<td>511</td>
<td>Returned normally with status 511 Network Authentication Required</td>
</tr>
<tr>
<td>598</td>
<td>Returned normally with status 598 Network read timeout error</td>
</tr>
<tr>
<td>599</td>
<td>Returned normally with status 599 Network connect timeout error</td>
</tr>
<tr>
<td>999</td>
<td>Returned normally with status 999 Invalid HTTP status code</td>
</tr>
</table>
<p>Notice Status Code 411 doesn&#8217;t return a numerical status code but rather a string &#8220;Connection Failure. Status code unavailable.&#8221;  The FileContent is set to &#8220;Connection Failure&#8221; while all other status codes are left blank (assuming that the request doesn&#8217;t return any data, as my test script does).  Good to know.</p>
<p><img src="http://www.ghidinelli.com/wp-content/uploads/2012/01/statuscode411.png" alt="How ColdFusion handles Status Code 411" title="How ColdFusion handles Status Code 411" width="426" height="220" class="aligncenter size-full wp-image-1441" /></p>
<h2>Problematic SSL Certificates</h2>
<p>For processing credit cards and payment processing transactions in general, the most insidious error I have run into is the &#8220;I/O Exception: peer not authenticated&#8221; error.  This basically means ColdFusion is <a href="http://www.ghidinelli.com/2008/01/19/using-invalid-expired-or-mismatched-ssl-certs-from-coldfusion">unable to read and authenticate the SSL certificate</a> and initiate a secure connection.  Generally this is either a self-signed certificate or, these days, a <a href="http://www.elliottsprehn.com/cfbugs/bugs/82997#comments">wildcard SSL certificate</a>.  Interestingly,  ColdFusion handles these scenarios a little bit differently depending on how throwonerror is set:</p>
<table border="1">
<tr>
<th>Test</th>
<th>ThrowOnError</th>
<th>Status/Message</th>
<th>ErrorDetail/Exception</th>
</tr>
<tr>
<td>Self-signed SSL Cert</td>
<td>Yes</td>
<td>Connection Failure: Status code unavailable</td>
<td>COM.Allaire.ColdFusion.HTTPFailure</td>
</tr>
<tr>
<td>Self-signed SSL Cert</td>
<td>No</td>
<td>Connection Failure. Status code unavailable.</td>
<td>I/O Exception: peer not authenticated</td>
</tr>
<tr>
<td>Wildcard SSL Cert</td>
<td>Yes</td>
<td>Connection Failure: Status code unavailable</td>
<td>COM.Allaire.ColdFusion.HTTPFailure</td>
</tr>
<tr>
<td>Wildcard SSL Cert</td>
<td>No</td>
<td>Connection Failure. Status code unavailable.</td>
<td>I/O Exception: peer not authenticated</td>
</tr>
</table>
<p>In summary, using throwonerror=no looks reasonably safe but you still must account for the catch-all COM.Allaire.ColdFusion.HTTPFailure.  My <a href="http://cfpayment.riaforge.org">battle-tested code</a> (which has processed more than $15MM) suggests you also need to check for coldfusion.runtime.RequestTimedOutException which is a different kind of exception when CFHTTP times out or the page times out.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2012/01/03/cfhttp-error-handling-http-status-codes/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Autopilot in the ColdFusion Updater Minefield</title>
		<link>http://www.ghidinelli.com/2011/12/20/autopilot-in-coldfusion-updater-minefield</link>
		<comments>http://www.ghidinelli.com/2011/12/20/autopilot-in-coldfusion-updater-minefield#comments</comments>
		<pubDate>Wed, 21 Dec 2011 06:18:05 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[server]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1426</guid>
		<description><![CDATA[Applying ColdFusion server security updates automatically with Unofficial Updater 2]]></description>
			<content:encoded><![CDATA[<p>If you manage a ColdFusion 8 or 9 server, you are likely aware of the complete and total train wreck that is applying security updates from Adobe.  Not only are directions vague, but on occasion Adobe likes to go back and modify the security update without changing filenames or rev-ing the version number.  It can drive a SysOp to insanity.</p>
<p>So seems to be the mental state of <a href="http://www..com">David Epler</a> as he has been driven to build the &#8220;<a href="http://www.dcepler.net/post.cfm/unofficial-updater-2-for-coldfusion-8">Unofficial Updater 2</a>&#8220;.  It is an Ant script on steroids bundled in a JAR that knows how to go out and fetch all of the updates from Adobe.com and apply them to your CF8 or CF9 server installation.</p>
<p>I&#8217;m not going to belabor the awesomness; here&#8217;s how to get your server up to date in about 5 minutes:</p>
<ol>
<li>Start by verifying your current security issues, use Pete Freitag&#8217;s <a href="http://www.hackmycf.com">HackMyCF.com</a> for a baseline</li>
<li>Backup your /opt/jrun directory (just be safe): tar -cf /opt/jrun4</li>
<li>Download the Unofficial Updater from http://uu2.riaforge.org/</li>
<li>Run it: java -jar Unofficial-Updater2.jar text</li>
<li>Tell it where stuff lives in your installation (it knows how to handle Standalone, Jrun Multi-server and EAR/WAR installs)</li>
<li>Wait about 4 minutes for it to finish grabbing everything and install&#8230;</li>
<li>Re-run HackMyCF.com and BOOM!  Rest easy, friend.</li>
<li>Proceed to <a href="http://twitter.com/#!/ghidinelli/status/149361873878396928">have David Epler&#8217;s baby</a></li>
</ol>
<p>Please Adobe, please send David a big check so you can take his IP and use it for your next update, mmmm kay?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/12/20/autopilot-in-coldfusion-updater-minefield/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Announcing the Fred Jehle Spanish Verb Database</title>
		<link>http://www.ghidinelli.com/2011/12/06/announcing-fred-jehle-spanish-verb-database</link>
		<comments>http://www.ghidinelli.com/2011/12/06/announcing-fred-jehle-spanish-verb-database#comments</comments>
		<pubDate>Wed, 07 Dec 2011 00:29:24 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Research/HOWTO]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[espanol]]></category>
		<category><![CDATA[spanish]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1397</guid>
		<description><![CDATA[Download a free database of 600 conjugated Spanish verbs under a Creative Commons license courtesy of Professor Fred Jehle]]></description>
			<content:encoded><![CDATA[<p>On a recent trip to Mexico I had the chance to use my Spanish (oh I&#8217;m <em>quite</em> the Renaissance man) and found that what I lose first is my ability to quickly say &#8220;he did X&#8221; or &#8220;they do Y&#8221;.  Worse is my need to reverse-engineer what I’m hearing back to an infinitive.  If someone says &#8220;Ellos me hablaban&#8221;, I decipher it like: &#8220;hablaban, ok, that is the third person plural for hablaba, which is hablar, which means to speak, so it’s they were speaking.&#8221;  That probably explains the glassy look in my eyes as I listen to native speakers.</p>
<p>Once home I searched for a database of conjugated verbs to make flash cards that, rather than working with infinitives, would read simple actions like &#8220;They walk&#8221;, &#8220;He used to sing&#8221; or &#8220;We would have spoken&#8221; and the reverse would have the proper Spanish conjugation.  Despite my uber Google skills, I was unable to find any non-commercial products.  However, I did come across one great resource that had the data I needed.  </p>
<p>Fred Jehle, formerly a professor at  Indiana University-Purdue University Fort Wayne, <a href="http://users.ipfw.edu/jehle/VERBLIST.HTM">published approximately 600 verbs</a>, fully conjugated in all moods and tenses, on his website in 1998.  The resource helped students improve their verb use in addition to a variety of notes on other aspects of the language.  I contacted Mr. Jehle to inquire if a database of his verbs were behind the scenes but unfortunately only the static web pages exist.</p>
<p>Out of curiosity, I opened up a couple of pages to see what the source HTML looked like and, luckily, it was pretty uniform.  I broke out my editor and wrote a script to read in each page, parse out the various conjugations and dump them in to a (PostgreSQL 9.x) database.   The roughly 600 verbs converted to 11,467 combinations of moods + tenses. </p>
<p>In coordination with copyright holder Professor Jehle, this data is available free of charge via a <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons license</a> for anyone to use for non-commercial purposes so long as you provide attribution.  If you alter, transform or build upon this work then you <em>may</em> distribute the resulting work only under the same license.  </p>
<div style="border: 1px dashed black; padding: 5px; background-color: #eee; text-align: center;"><a href="http://www.ghidinelli.com/free-spanish-conjugated-verb-database">Click here to download the database</a></div>
<p>My thanks go to Mr. Jehle for quickly answering my questions and allowing me to publish the data for other would-be Spanish students.  I recommend that you also check out his website for additional Spanish content at <a href="http://users.ipfw.edu/jehle/VERBLIST.HTM">http://users.ipfw.edu/jehle/VERBLIST.HTM</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/12/06/announcing-fred-jehle-spanish-verb-database/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>October Qmail Follow-up</title>
		<link>http://www.ghidinelli.com/2011/10/20/october-qmail-follow-up</link>
		<comments>http://www.ghidinelli.com/2011/10/20/october-qmail-follow-up#comments</comments>
		<pubDate>Thu, 20 Oct 2011 20:04:54 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Research/HOWTO]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[qmail]]></category>
		<category><![CDATA[server]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1365</guid>
		<description><![CDATA[A couple of tweaks for QmailToaster install to update CHKUSER 2.0.9 and improve DSPAM integration]]></description>
			<content:encoded><![CDATA[<p>Some notes that I don&#8217;t want to forget to follow-up on my <a href="http://www.ghidinelli.com/2011/05/04/march-qmail-server-madness">March (Q)mail Server Madness</a> post earlier this year.  Things are running great with the exception of SpamAssassin being pretty useless.  An SA rules update in the last couple of weeks has been quarantining every email from Facebook plus another 25% of my legitimate email so we&#8217;ve disabled it and we&#8217;re just letting DSPAM do it&#8217;s thing instead.  Here&#8217;s a couple of tweaks we&#8217;ve made:</p>
<h2>Upgrade to CHKUSER 2.0.9</h2>
<p>CHKUSER 2.0.9 includes one update that is very important to us: CHKUSER_DISABLE_VARIABLE.  This enhancements allows us to disable CHKUSER checks for certain relays.  In our case, our web application servers relay mail and sometimes the TO address is invalid.  Without this change, CHKUSER would deny even sending the email so bad emails would queue up on the web servers.  What we want to happen is for the mail server to accept them, bounce them and let our bounce handling routines run notifying the sender of the bad address.  Here&#8217;s the necessary steps:</p>
<ol>
<li>Download development package <a href="http://qmailtoaster.com/testing/qmail-toaster-1.03-1.3.21.src.rpm">qmail-toaster-1.03-1.3.21.src.rpm</a> which includes the CHKUSER 2.0.9 code and a few other enhancements (see <a href="http://comments.gmane.org/gmane.mail.qmail.toaster.devel/693">this thread</a>)</li>
<li>rpm -Uvh qmail-toaster-1.03-1.3.21.src.rpm</li>
<li>bunzip2 /usr/src/redhat/SOURCES/qmailtoaster-1.3.2.patch.bz2</li>
<li>vi /usr/src/redhat/SOURCES/qmailtoaster-1.3.2.patch</li>
<li>Search for &#8220;/* #define CHKUSER_DISABLE_VARIABLE&#8221; and remove the comments, save</li>
<li>Search for &#8220;/* #define CHKUSER_ENABLE_USERS_EXTENSIONS&#8221; and remove the comments to <a href="http://www.mail-archive.com/vchkpw@inter7.com/msg27903.html">re-enable dash-aliasing</a></li>
<li>bzip2 /usr/src/redhat/SOURCES/qmailtoaster-1.3.2.patch</li>
<li>cd /usr/src/redhat/SPECS</li>
<li>rpmbuild -bb &#8211;with cnt50 &#8211;target i686 qmail-toaster.spec</li>
<li>rpm -Uvh /usr/src/redhat/RPMS/i686/qmail-toaster-1.3.2.rpm</li>
</ol>
<p>Note, I&#8217;m running on x64 CentOS 5.  For some reason I had to specify the target as i686 as without the target it wanted to generate an i386 binary while everything else is i686.  I didn&#8217;t know if that would cause problems so I specified the target explicitly.</p>
<p>For any IP address we want to bypass the CHKUSER checks, we simply add an entry to /etc/tcprules.d/tcp.smtp with a RELAYCLIENT variable.  An entry might look like:</p>
<p><code>192.168.0.1:allow,RELAYCLIENT=""</code></p>
<p>Finally, the RPM update for qmail-toaster overrides your SSL certificates for SMTP.  To restore my real certificates, I had to perform the following steps (lifted from <a href="http://coreygilmore.com/blog/2008/03/14/chained-ssl-certificates-and-qmail-error-140943f2/">coregilmore.com</a>):</p>
<pre><code># cp /etc/httpd/certs/mail.msr.com.pem /var/qmail/control/servercert.pem
# ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
# chown -h qmaild:root /var/qmail/control/clientcert.pem
# chmod 400 /var/qmail/control/servercert.pem
# qmailctl restart</code></pre>
<p>mail.msr.com.pem is simply a single concatenated file containing your key, certificate and any intermediate certificates (required for registrars like Go Daddy for example):</p>
<p><code># cat /path/to/ssl_cert.key /path/to/ssl_cert.crt /path/to/gd_intermediate_bundle.crt &amp;gt; /var/qmail/control/servercert.pem</code></p>
<h2>Updated Mailfilter Script</h2>
<p>We also expanded our mailfilter script to be a little bit smarter and handle DSPAM better.  Here&#8217;s the latest:</p>
<pre><code>SHELL="/bin/bash"
import EXT
import HOST

VUSER=`echo ${EXT%-*}`
export VUSER
USERHOME=`/mail/bin/vuserinfo -d $VUSER@$HOST`
export USERHOME

MARKTIME=`date +%s.%N`
UNIQUE=`date +%N`

logfile "/var/log/maildrop/mailfilter.log"

# Test for the existance of the Junk directory.  Create it if it doesn't exist
VERBOSE=1
log "$VUSER@$HOST[$UNIQUE] : Start user filter"

# make sure the Junk folder exists and is subscribed too
`test -d $USERHOME/Maildir/.Junk`

if ( $RETURNCODE == 1 )
{
    `/usr/bin/maildirmake -f Junk $USERHOME/Maildir`
    `echo INBOX.Junk &gt;&gt; $USERHOME/Maildir/courierimapsubscribed`
}

# make sure the retrain folder exists
`test -d $USERHOME/Maildir/.JunkRetrain`

if ( $RETURNCODE == 1 )
{
    `/usr/bin/maildirmake -f JunkRetrain $USERHOME/Maildir`
    `echo INBOX.JunkRetrain &gt;&gt; $USERHOME/Maildir/courierimapsubscribed`
}

# now analyze the SA header
if (/^X-Spam-Status: Yes/)
{
        log "$VUSER@$HOST[$UNIQUE] : SpamAssassin found SPAM"
        to "$USERHOME/Maildir/.Junk"
}
else
{
        # spamassassin thinks its ham, which means nothing, because SA sucks
        # so now we check with dspam

        # warning: exception must have one space and then the curly bracket or else syntax errors are generatead
        exception {
                xfilter "/usr/local/bin/dspam --deliver=innocent,spam --stdout --user $USERHOME"
        }

        if (/^X-DSPAM-Result: Spam/)
        {
                # dspam says spam
                log "$VUSER@$HOST[$UNIQUE] : DSPAM found SPAM"
                to "$USERHOME/Maildir/.Junk"
        }
        else
        {
                # ham in both places, deliver
                log "$VUSER@$HOST[$UNIQUE] : DSPAM found HAM (returncode = $RETURNCODE)"
                if (/^X-DSPAM-Result: Innocent/)
                {
                        log "$VUSER@$HOST[$UNIQUE] : HAM includes X-DSPAM-Result: Innocent"
                }
                else
                {
                        if (/^X-DSPAM-Result: Whitelisted/)
                        {
                                log "$VUSER@$HOST[$UNIQUE] : HAM includes X-DSPAM-Result: Whitelisted"
                        }
                        else
                        {
                                log "$VUSER@$HOST[$UNIQUE] : HAM does NOT include X-DSPAM-Result: Innocent/Whitelisted"
                        }
                }
                to "$USERHOME/Maildir/"
        }
}</code></pre>
<p>Since we&#8217;re dropping SpamAssassin, this file could be streamlined to be DSPAM specific but it doesn&#8217;t hurt to leave it in the event we decide to re-enable it at a later date.  Simply save the script somewhere and enable it via a .qmail file in a user directory with the following:</p>
<p><code>|preline /usr/bin/maildrop /path/to/mailfilter-to-spam-plus-dspam</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/10/20/october-qmail-follow-up/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Javascript for Mobile</title>
		<link>http://www.ghidinelli.com/2011/09/06/javascript-for-mobile</link>
		<comments>http://www.ghidinelli.com/2011/09/06/javascript-for-mobile#comments</comments>
		<pubDate>Tue, 06 Sep 2011 17:49:15 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1344</guid>
		<description><![CDATA[Packt Publishing's "jQuery Mobile First Look" arrives at a timely juncture]]></description>
			<content:encoded><![CDATA[<p>Richard Dias from Packt Publishing dropped me a note recently asking if I was interested in taking a look at their latest jQuery title: <a href="http://link.packtpub.com/qhDdWa">jQuery Mobile First Look</a>.  The timing is perfect since we just had a team meeting to discuss how we wanted to continue evolving <a href="http://www.motorsportreg.com">MotorsportReg.com</a> for mobile consumption.  About 10% of our usage is on mobile devices and while our HTML/CSS system today is pretty solid on iOS and Android devices, we want to improve the experience.   We already use jQuery extensively on MSR so <a href="http://jquerymobile.com/">jQuery Mobile</a> is a leading candidate before we even crack the book.  I&#8217;m hoping HTML5 matures quickly so we can leverage the skills and technologies we&#8217;re already using rather than go down the path of platform-specific applications.  Once I dig in I&#8217;ll post a short review of the book.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/09/06/javascript-for-mobile/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>8GB Ram + Crucial M4 SSD in Thinkpad X61</title>
		<link>http://www.ghidinelli.com/2011/08/18/8gb-ram-crucial-m4-ssd-thinkpad-x61</link>
		<comments>http://www.ghidinelli.com/2011/08/18/8gb-ram-crucial-m4-ssd-thinkpad-x61#comments</comments>
		<pubDate>Thu, 18 Aug 2011 15:57:14 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Research/HOWTO]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1335</guid>
		<description><![CDATA[Breathing new life into my Thinkpad X61 with 8GB RAM, SSD and x64 Windows 7]]></description>
			<content:encoded><![CDATA[<p>I really like my Lenovo Thinkpad X61 laptop &#8211; it&#8217;s small, perfect for <a href="http://www.ghidinelli.com/go/">traveling around the world</a> and very lightweight.  It was starting to run slowly and since I use it for work, I contemplated my options.  Replacing it was certainly one, but it was going to be $1500+ to get the equivalent.  I could sell the X61 and make some of it back but I have a poor track record of actually doing that (still have my Thinkpad T41 and a &#8220;temporary&#8221; Toshiba NB205 netbook laying around&#8230;)  </p>
<p>After hearing my friend rave about how great an SSD made his laptop, I went down the upgrade path instead.  From what I could gather online, the X61 should support 64-bit Windows 7, SSDs (with a <a href="http://forums.thinkpads.com/viewtopic.php?f=27&#038;t=84953&#038;p=558702#p558702">1.5GB/s SATA cap</a>) and, unofficially, 8GB of RAM.  I had become accustomed to working under 3GB in Windows XP even for development work which was brutal so the RAM looked like it would be a huge boon.</p>
<p><a href="http://www.ghidinelli.com/wp-content/uploads/2011/08/8gbx61.png"><img src="http://www.ghidinelli.com/wp-content/uploads/2011/08/8gbx61-300x238.png" alt="8GB PC2-6400 800mhz CL6 SODIMM DDR2 RAM in Thinkpad X61" title="8GB PC2-6400 800mhz CL6 SODIMM DDR2 RAM in Thinkpad X61" width="300" height="238" class="aligncenter" /></a></p>
<p>There it is!  Basically the X61 has two SODIMM slots and while the manufacturer officially supports 4GB RAM total, the hardware actually supports 8GB.  I used 2 of these <a href="http://www.amazon.com/gp/product/B0027IVMBY">Patriot PSD24G8002S 4GB PC2 6400 800mhz CL6 DDR2 SODIMM</a> modules at $55/ea for a total of $110.  Corsair has an <a href="http://www.amazon.com/Corsair-Memory-VS8GSDSKIT800D2-PC2-6400-200-pin/dp/B001V738V4/ref=pd_cp_e_1">equivalent VS8GSDSKIT800D2</a> kit for $130 but memory is more or less a commodity so I went with the cheaper chips.   One of the two Patriot modules was defective and wouldn&#8217;t POST but Amazon&#8217;s awesome return customer service worked like a charm and this morning my replacement stick showed up.  Technically the Thinkpad uses PC2-5300 CL5 memory but it&#8217;s difficult/impossible to find in 4GB sticks and the newer, faster memory can &#8220;slow down&#8221; to work.  </p>
<p>The other department where I needed more zoom was the hard drive.  I replaced the internal drive with the 256GB Crucial M4, the latest and greatest generation of SSDs.   Even though the hardware doesn&#8217;t support the full 6GB/s SATA capability of the M4, I decided to get the fastest drive possible <em>now</em>, so that I could transfer it to a newer laptop down the road. </p>
<p>Finally, with the new hard drive I took the opportunity to move to Windows 7 64-bit.  I&#8217;ve actually had 4GB of memory since I bought the Thinkpad but Windows XP could only access 3GB of it.  Moving to 64-bit ensured that I could use the full 4GB immediately and 8GB later.  I disabled some of the Aero features to improve graphics performance (the low score in my Windows Experience score) and <a href="http://exodusdev.com/content/revert-xp-style-alt-tab-coolswitch-behavior-windows-7-and-windows-vista">reverted back to XP&#8217;s ALT+TAB behavior</a> and I&#8217;m happy with the results. </p>
<p>Having used it for a few weeks now, the speed improvements are stunning.  Boot time and hibernate are dramatically shorter and applications pop on the screen as soon as they launch.  Interestingly, my development work doesn&#8217;t process that much differently because it&#8217;s processor-bound but the improved disk access time and throughput clearly assists all kinds of applications from creating zip files to loading PSDs in photoshop to copying large files from the network.  Total spend was $115 (RAM) + $425 (256GB Crucial M4) + Windows 7 64-bit (Free from friend) and I have what feels like the latest and greatest computer in the same lightweight footprint.   Success!</p>
<p><em>Side note</em>: If you feel that 1.5GB/s SATA is really killing your day, there is an unsupported, unofficial, totally-may-screw-your-computer BIOS upgrade for 3.0GB/s SATA-II available <a href="http://forum.notebookreview.com/lenovo-ibm/459591-t61-x61-sata-ii-1-5-gb-s-cap-willing-pay-solution-8.html#post6501443">in this thread</a>.</p>
<p>PS &#8211; I use <a href="http://www.crashplan.com">CrashPlan</a> to backup my home computers and it made transitioning to Windows 7 easy.  I&#8217;ve already restored files twice from my XP install that I had failed to migrate.  At the moment I have two computers backed up in Crashplan, my X61-XP and now my X61-W7.  It&#8217;s great being able to go back and get files from the old machine &#8211; highly recommended!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/08/18/8gb-ram-crucial-m4-ssd-thinkpad-x61/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ActiveMQ as an x64 Windows 7 Service</title>
		<link>http://www.ghidinelli.com/2011/08/04/install-activemq-x64-win7-service</link>
		<comments>http://www.ghidinelli.com/2011/08/04/install-activemq-x64-win7-service#comments</comments>
		<pubDate>Thu, 04 Aug 2011 18:16:47 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[activemq]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1326</guid>
		<description><![CDATA[Installing Apache ActiveMQ as a service on 64-bit Windows 7]]></description>
			<content:encoded><![CDATA[<p>I bought an SSD for my aging Thinkpad X61 (which has made it feel like a new computer again) and upgraded to 64-bit Windows 7.  I&#8217;m reassembling my development environment and need to install Apache ActiveMQ (5.4.2) as part of the stack.</p>
<p>Unfortunately, once installed as a service, it would start and then immediately fail, unable to open a temporary file store according to the logs:</p>
<pre><code>2011-08-04 11:00:26,817 | WARN  | tmpdir | org.eclipse.jetty.util.log | WrapperSimpleAppMain java.io.IOException: The system cannot find the path specified
        at java.io.WinNTFileSystem.createFileExclusively(Native Method)
        at java.io.File.checkAndCreate(File.java:1704)
        at java.io.File.createTempFile(File.java:1792)
        at java.io.File.createTempFile(File.java:1828)
        at org.eclipse.jetty.webapp.WebInfConfiguration.resolveTempDirectory(WebInfConfiguration.java:279)
        at org.eclipse.jetty.webapp.WebInfConfiguration.preConfigure(WebInfConfiguration.java:43)
        at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:378)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
        at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:165)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
        at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:92)
        at org.eclipse.jetty.security.SecurityHandler.doStart(SecurityHandler.java:338)
        at org.eclipse.jetty.security.ConstraintSecurityHandler.doStart(ConstraintSecurityHandler.java:229)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
        at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:165)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
        at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:92)
        at org.eclipse.jetty.server.Server.doStart(Server.java:242)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1536)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1409)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:574)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
        at org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:64)
        at org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:52)
        at org.apache.activemq.xbean.XBeanBrokerFactory$1.&lt;init&gt;(XBeanBrokerFactory.java:117)
        at org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:115)
        at org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:71)
        at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:71)
        at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:54)
        at org.apache.activemq.console.command.StartCommand.startBroker(StartCommand.java:115)
        at org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:74)
        at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:57)
        at org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:143)
        at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:57)
        at org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:85)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.activemq.console.Main.runTaskClass(Main.java:251)
        at org.apache.activemq.console.Main.main(Main.java:107)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
        at java.lang.Thread.run(Thread.java:662)
2011-08-04 11:00:26,873 | INFO  | ActiveMQ Message Broker (localhost, ID:brian-x61-57604-1312480826352-0:1) is shutting down | org.apache.activemq.broker.BrokerService | ActiveMQ ShutdownHook
2011-08-04 11:00:28,622 | INFO  | Connector openwire Stopped | org.apache.activemq.broker.TransportConnector | ActiveMQ ShutdownHook
2011-08-04 11:00:28,629 | INFO  | PListStore:C:\jrun4\apache-activemq-5.4.2\bin\win32\..\..\data\localhost\tmp_storage stopped | org.apache.activemq.store.kahadb.plist.PListStore | ActiveMQ ShutdownHook</code></pre>
<p>I tried following <a href="http://stackoverflow.com/questions/4626824/how-do-i-install-activemq-as-a-64-bit-service-on-windows">these directions</a> to modify the InstallService.bat but the same problem persisted.   Instead, I modified the service to Run As &#8220;NT Authority\Local Service&#8221; instead of &#8220;Local System&#8221;.   Now it starts up and runs and I can access the web admin at http://localhost:8161.</p>
<p>I&#8217;m new to Windows 7 &#8211; is this the right way to correct the problem or have I opened myself up to some kind of security problem?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/08/04/install-activemq-x64-win7-service/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>March (Q)Mail Server Madness</title>
		<link>http://www.ghidinelli.com/2011/05/04/march-qmail-server-madness</link>
		<comments>http://www.ghidinelli.com/2011/05/04/march-qmail-server-madness#comments</comments>
		<pubDate>Wed, 04 May 2011 20:14:02 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Research/HOWTO]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1258</guid>
		<description><![CDATA[March was a busy month &#8211; we were upgrading and consolidating infrastructure around here which meant taking a 2U VA Linux 2230 server that had &#8211; quite literally &#8211; been serving mail for more than a decade, and upgrading it to a more contemporary machine for which spare parts exist.  Knock on wood the [...]]]></description>
			<content:encoded><![CDATA[<p>March was a busy month &#8211; we were upgrading and consolidating infrastructure around here which meant taking a 2U VA Linux 2230 server that had &#8211; quite literally &#8211; been serving mail for more than a decade, and upgrading it to a more contemporary machine for which spare parts exist.  Knock on wood the new machine can have the same reliable track record.</p>
<p>I had been considering outsourcing everything &#8211; move incoming email to Google Apps and use someone like SMTP.com or SendGrid for outbound but there&#8217;s a few wrinkles like mailing lists and customer-relayed email.  We ultimately decided to roll a new machine but given how quickly email deliverability evolves, we needed to get up to date with the various authentication and verification technologies that help get emails into the inbox.  That meant getting waist-deep in <a href="http://en.wikipedia.org/wiki/Sender_Policy_Framework">SPF</a>, <a href="http://www.microsoft.com/mscorp/safety/technologies/senderid/default.mspx">Sender-ID</a>, <a href="http://en.wikipedia.org/wiki/DomainKeys">DomainKeys</a> and <a href="http://www.dkim.org/">DKIM</a>.  (See current <a href="https://fit.nokia.com/lars/meter/dkim.html">DKIM</a> and <a href="https://fit.nokia.com/lars/meter/spf.html">SPF</a> deployment rates &#8211; 23% and 51% globally as of this writing)</p>
<p>This isn&#8217;t a step-by-step guide; writing one would simply repeat what other people have already done a good job of documenting.  What this <em>is</em>, is an overview that ties together missing pieces and endless Google searches to move from concept to execution with one full set of examples.</p>
<h2>The Moving Pieces</h2>
<p>If you&#8217;re new to all this, here&#8217;s how these technologies work briefly: Sender ID is basically a Microsoft version of Sender Policy Framework (SPF).  If you get SPF to work, Sender ID is backwards compatible and will verify successfully.  DKIM is almost the same thing to DomainKeys but it adds <a href="http://en.wikipedia.org/wiki/Author_Domain_Signing_Practices">Author Domain Signing Practices (ADSP)</a> as an optional component that specifies how strict you are about signing messages.</p>
<p>SPF and Sender ID rely on specially crafted DNS records that identify which mail servers are allowed to send mail for your domain.  By contrast, DomainKeys/DKIM take pieces of your message, cryptographically hash them with a key (part of which only your server knows) and embed the signature as a header which is verified using the other half of your key (which is public) that is stored in a specially crafted DNS record for your domain or mail server.   You must have the ability to create DNS TXT records or none of these will work for you.  Here&#8217;s a good article with a <a href="http://www.digitalsanctuary.com/tech-blog/debian/setting-up-spf-senderid-domain-keys-and-dkim.html">more details on how it all works</a>.</p>
<p><img src="http://www.ghidinelli.com/wp-content/uploads/2011/03/dkim-verified.png" alt="DomainKeys verified mail on Yahoo gets a special lock icon showing it&#039;s verified" title="DomainKeys verified mail on Yahoo gets a special lock icon showing it&#039;s verified" width="357" height="61" class="aligncenter" /></p>
<p>The idea is to eliminate any joe spammer from using an arbitrary From address in spam, phishing and other email attacks.  None of these approaches are perfect but they are used by large ISPs like Hotmail, Gmail and Yahoo so if you want your email to reach those inboxes, you need to play along.</p>
<h2>QmailToaster</h2>
<p>Step one, set up a <a href="http://www.qmailtoaster.com">QmailToaster</a>.  Our current server is (older) Qmail + Vpopmail + Courier based and it has worked well so we stuck with what we know.  Not to mention, Jake Vickers and the QmailToaster team have removed all of the pain of setting it up so now you just speed along to the part where it works well.  Other than getting the needed dependencies on my CentOS/RHEL box, the installation was a piece of cake using the <a href="http://qtp.qmailtoaster.com/trac/wiki/qtp-newmodel">qtp-newmodel approach</a>.</p>
<ul>
<li><a href="http://wiki.qmailtoaster.com/index.php/CentOS_5_QmailToaster_Install#Add_a_domain">http://wiki.qmailtoaster.com/index.php/CentOS_5_QmailToaster_Install</a> &#8211; the CentOS/RHEL 5 Toaster install gives the quick and dirty of adding DomainKeys support</li>
<li><a href="http://wiki.qmailtoaster.com/index.php/Domainkeys">http://wiki.qmailtoaster.com/index.php/Domainkeys</a> &#8211; detailed directions for DomainKeys support</li>
<li><a href="http://wiki.qmailtoaster.com/index.php/How_to_Setup_DKIM_with_Qmail_Toaster">http://wiki.qmailtoaster.com/index.php/How_to_Setup_DKIM_with_Qmail_Toaster</a> &#8211; Add DKIM support with a new qmail-queue</li>
</ul>
<p>After you install the DKIM package from above on RHEL/CentOS 5, you <em>would</em> see this error in your qmail logs:</p>
<p><code>@400000004d7d3bc00da74aec delivery 21: success: ould_not_find_ParserDetails.ini_in_/usr/lib/perl5/vendor_perl/5.8.8/XML/SAX/rUser_and_password_not_set,_continuing_without_authentication./&lt;check-auth@verifier.port25.com&gt;_96.244.219.19_accepted_message./Remote_host_said:_250_2.6.0_message_received/</code></p>
<p>Basically, something is borked in the CPAN-installed SAX package.  You can fix it by pre-emptively running (thanks to <a href="http://centosfiles.blogspot.com/2009/09/could-not-find-parserdetailsini-in.html">centosfiles</a> for the fix):</p>
<pre><code>perl -MXML::SAX -e "XML::SAX-&gt;add_parser(q(XML::SAX::PurePerl))-&gt;save_parsers()"</code></pre>
<p>At this point you have a mail server <em>capable</em> of handling SPF, Sender ID, DomainKeys and DKIM.  Now you need to enable it in your configuration and your DNS.</p>
<h2>SPF/Sender ID</h2>
<p>Unlike DomainKeys and DKIM which require public/private keys and some computational effort when delivering email, SPF and Sender ID are exclusively a DNS configuration option.</p>
<ul>
<li><a href="http://wiki.qmailtoaster.com/index.php/SPF">http://wiki.qmailtoaster.com/index.php/SPF</a> &#8211; there technically isn&#8217;t anything to set up with the QmailToaster since your outbound configuration is all DNS.  However, you can configure how to handle incoming email that doesn&#8217;t pass using /var/qmail/control/spfbehavior.</li>
<li><a href="http://www.saout.de/misc/spf/">Qmail SPF</a> &#8211; The underlying implementation of SPF for qmail</li>
<li><a href="http://old.openspf.org/wizard.html?mydomain=example.com&#038;submit=Go!">OpenSPF.org&#8217;s Record Wizard</a> &#8211; generate an SPF record for your domain to be placed in a DNS TXT record</li>
</ul>
<p>Once generated, take the TXT record and put it into your DNS files.  If you&#8217;re using <a href="http://cr.yp.to/djbdns.html">djbdns</a>, a simple record with one mail server would look something like:</p>
<pre><code>'sample.com:v=spf1 ip4:your.ip.add.ress ~all:3600</code></pre>
<p>The ~all says to softfail messages that don&#8217;t verify.  If you are sure you have listed all of your possible outbound MTAs, then you can set it to -all which will tell remote servers to kill emails that don&#8217;t pass.</p>
<h2>DomainKeys/DKIM</h2>
<p>Once installed, you enable incoming verification and outgoing signing through the tcp.smtp file stored in /etc/tcprules.d:</p>
<ul>
<li><a href="http://wiki.qmailtoaster.com/index.php/Domainkeys#Outgoing">http://wiki.qmailtoaster.com/index.php/Domainkeys#Outgoing</a> &#8211; using DKSIGN for outgoing messages and DKQUEUE/DKVERIFY for incoming messages.  Has a flag-by-flag breakdown of how to configure DKVERIFY for how to respond to incoming mail that passes or fails verification.</li>
<li><a href="http://wiki.qmailtoaster.com/index.php/Tcp.smtp">http://wiki.qmailtoaster.com/index.php/Tcp.smtp</a> &#8211; a complete breakdown of the tcp.smtp file that includes DKSIGN, DKVERIFY and DKQUEUE breakdown</li>
<li><a href="http://www.zytrax.com/books/dns/ch9/dkim.html">HOWTO define DKIM/ADSP RRs</a> &#8211; from the &#8220;Pro DNS and BIND&#8221; book, this page does a phenomenal job of explaining the components of the DomainKeys/DKIM records with examples.  Also explains selectors clearly.</li>
</ul>
<p>For DKVERIFY, the default is &#8220;DEGIJKfh&#8221;.  If you want to permanently reject incoming mail that has a DomainKey signature but fails to verify, add a &#8220;B&#8221; to the list: &#8220;BDEGIJKfh&#8221;.  </p>
<h3>Outbound</h3>
<p>To sign an outbound message for DomainKeys/DKIM, you need to generate a public/private key pair.  Part of it you keep secret and sign your outgoing messages and the other half you publish in your DNS so other mail servers can verify it&#8217;s authentic.  Here&#8217;s what the docs say to do:</p>
<pre><code># cd /var/qmail/control/domainkeys
# mkdir yourdomain.com
# cd yourdomain.com
# dknewkey private &gt; public.txt
# chmod 440 private
# cd ..
# chown -R root:vchkpw yourdomain.com</code></pre>
<p>The word &#8220;private&#8221; above is going to be our <strong>selector</strong>.  It&#8217;s 100% arbitrary but must be used consistently.  QmailToaster defaults to the word &#8220;private&#8221;.  In your DNS, you need a TXT record that looks something like:</p>
<p><code>private._domainkey.yourdomain.com:k=rsa; p=MEwwDQYJKoZIhvcNAQEBBQ . . .</code></p>
<p>The rest of the record comes out of the file we just created with dknewkey in /var/qmail/control/domainkeys/yourdomain.com/public.txt.   In my last Qmail install, I used &#8220;default&#8221; for my selector, so my DNS entries look like:</p>
<p><code>default._domainkey.yourdomain.com:k=rsa; p=MEwwDQYJKoZIhvcNAQEBBQ . . .</code></p>
<p>This selector, be it &#8220;private&#8221;, &#8220;default&#8221; or something else, MUST match what you use to generate your public/private key using dknewkey and it must also match what you use in your /etc/tcprules.d/tcp.smtp rules where you specify when to sign outgoing messages:</p>
<pre><code># example record using QmailToaster defaults
127.:allow,RELAYCLIENT="",DKSIGN="/var/qmail/control/domainkeys/%/private"

# since I used 'default' as my selector, this is what my file looks like:
127.:allow,RELAYCLIENT="",DKSIGN="/var/qmail/control/domainkeys/%/default"</code></pre>
<h4>Crypto Warning</h4>
<p>dknewkey creates a (relatively) short 384-bit public/private keypair.  There are <a href="http://mipassoc.org/pipermail/dkim-ops/2010-June/thread.html#202">people who don&#8217;t think that&#8217;s good enough</a> and they&#8217;re probably right.  You can edit /usr/bin/dknewkey to change it to something more secure like 512 or (better yet) 1024 bits.</p>
<p>A quick recap with how this selector is used throughout your Qmail and DNS configuration:</p>
<ol>
<li>Generate the public/private key: dknewkey my-selector-name > public.txt; this leaves behind a file like /var/qmail/control/domainkeys/yourdomain.com/my-selector-name.</li>
<li>Create the DNS TXT record: my-selector-name._domainkey.yourdomain.com:k=rsa; p=MEwwDQYJKoZIhvcNAQEBBQ&#8230; (using the contents of the public.txt file dknewkey creates)</li>
<li>In tcp.smtp, use a DKSIGN environment variable: DKSIGN=&#8221;/var/qmail/control/domainkeys/%/my-selector-name&#8221;</li>
</ol>
<h2>Testing</h2>
<p>Once you have it set up <strong>and</strong> your DNS has propagated, there are a number of online services and email reflectors that will verify your configuration and report back to you:</p>
<h3>Web Tools</h3>
<ul>
<li><a href="http://domainkeys.sourceforge.net/policycheck.html">SourceForge DomainKeys Policy Checker</a></li>
<li><a href="http://www.kitterman.com/spf/validate.html">Scott Kitterman&#8217;s SPF record checker</a> &#8211; test your SPF record from the web</li>
<li><a href="http://www.dnswatch.info/dkim/create-dns-record">DKIM DNS Record Wizard</a> &#8211; based on  RFC 4871, helps construct the TXT record for your DNS for DKIM signing</li>
<li><a href="http://www.sendmail.org/dkim/tools">SendMail ADSP wizard and DKIM verifier</a> &#8211; helps build an (optional) ADSP TXT record and can check your DKIM DNS entries</li>
</ul>
<h3>Email Reflectors</h3>
<p>Unlike the above services which try to see if things look OK, these are email addresses you can send a test message to that will actually tell you how your email performed:</p>
<ul>
<li><a href="mailt:check-auth@verifier.port25.com">check-auth@verifier.port25.com</a> &#8211; the port25 reflector sends you an email report with a summary and breakdown by type.  This is the best one in my opinion.</li>
<li><a href="mailto:spf-test@openspf.org">spf-test@openspf.org</a> &#8211; this message will be rejected but the rejection will say what happened to the SPF test.  You must tail the logs (like /var/log/qmail/smtp/current) to see the results.</li>
<li><a href="mailto:autorespond+dkim@dk.elandsys.com">autorespond+dkim@dk.elandsys.com</a> &#8211; Eland Systems offers a DKIM reflector along with <a href="http://www.elandsys.com/resources/sendmail/dkim.html">some notes</a></li>
<li><a href="http://www.myiptest.com/staticpages/index.php/DomainKeys-DKIM-SPF-Validator-test">MyIpTest.com Tester</a> &#8211; tests DK, DKIM and SPF.  Web page gives you a random email address to send to and then you can load your results after sending a test message.</li>
<li><a href="http://www.brandonchecketts.com/emailtest.php">http://www.brandonchecketts.com/emailtest.php</a> &#8211; similar to above, send an email to a random address provided on the web page and then you can check results.</li>
</ul>
<p>Port25&#8217;s reflector report should look like this when everything is working:</p>
<pre><code>Summary of Results
==========================================================
SPF check: pass
DomainKeys check: pass
DKIM check: pass
Sender-ID check: pass
SpamAssassin check: ham</code></pre>
<p>Microsoft will let you &#8220;register&#8221; your Sender ID settings with them.  Not sure how important this is, but it can&#8217;t hurt to let them know at <a href="https://support.msn.com/eform.aspx?productKey=senderid&#038;page=support_senderid_options_form_byemail">https://support.msn.com/eform.aspx?productKey=senderid&#038;page=support_senderid_options_form_byemail</a>.</p>
<h3>Email Services</h3>
<p>Better than a reflector is to see how a real mail service will treat your message.  Once you think you&#8217;re configured correctly, try sending an email to Gmail and Yahoo.  Then you can view the headers for each to see how your SPF and DK records were interpreted.</p>
<p><strong>Gmail</strong>:<br />
<img src="http://www.ghidinelli.com/wp-content/uploads/2011/03/gmail-test.png" alt="Verify SPF, DomainKeys and DKIM headers on Gmail" title="Verify SPF, DomainKeys and DKIM headers on Gmail" width="624" height="243" class="alignnone" /></p>
<p><strong>Yahoo</strong>:<br />
<img src="http://www.ghidinelli.com/wp-content/uploads/2011/03/yahoo-test.png" alt="View SPF, DomainKeys and DKIM headers on Yahoo mail" title="View SPF, DomainKeys and DKIM headers on Yahoo mail" width="486" height="168" class="alignnone" /></p>
<h2>Final Thoughts</h2>
<p>Putting it all together, here&#8217;s how my setup looks (using the selector &#8216;default&#8217;):</p>
<h3>Public/private key pairs on file system</h3>
<pre><code>[root@mail domainkeys]# ls -al /var/qmail/control/domainkeys/sample.com/
total 32
drwxr-xr-x 2 qmailq root  4096 Mar 13 13:46 .
drwxr-xr-x 7 root   qmail 4096 Mar 13 13:46 ..
-r--r----- 1 qmailq root   396 Mar 13 13:46 default
-rw-r--r-- 1 qmailq root   142 Mar 13 13:46 public.txt</code></pre>
<h3>DKIM signconf.xml</h3>
<p>On the file system in /var/qmail/control/dkim/signconf.xml:</p>
<pre><code>&lt;dkimsign&gt;
  &lt;global algorithm="rsa-sha1" domain="/var/qmail/control/me" keyfile="/var/qmail/control/dkim/global.key" method="simple" selector="dkim1"&gt;
    &lt;types id="dkim" /&gt;
  &lt;/global&gt;
&lt;/dkimsign&gt;</code></pre>
<p>You could override the use of /var/qmail/control/me or change the selector from &#8216;dkim1&#8242; if needed.</p>
<p><strong>Alternative note:</strong> It is also possible to sign your DomainKeys using the DKIM package.  You can change signconf.xml to have a per-domain record with DK and then abandon DKSIGN altogether (this is the route I&#8217;ve gone):</p>
<pre><code>&lt;sample.com domain="sample.com" selector="default"&gt;
  &lt;types id="dkim" /&gt;
  &lt;types id="domainkey" method="nofws" /&gt;
&lt;/sample.com&gt;</code></pre>
<h3>tcp.smtp</h3>
<pre><code># localhost relaying
127.:allow,RELAYCLIENT="",QMAILQUEUE="/var/qmail/bin/simscan",DKVERIFY="DEGIJKfh",DKQUEUE="/var/qmail/bin/qmail-queue.orig",DKSIGN="/var/qmail/control/domainkeys/%/default"
# external web box
69.36.226.12:allow,RELAYCLIENT="",DKSIGN="/var/qmail/control/domainkeys/%/default"
# everyone else that connects gets the full cavity check
:allow,BADMIMETYPE="",BADLOADERTYPE="M",CHKUSER_RCPTLIMIT="50",CHKUSER_WRONGRCPTLIMIT="3",QMAILQUEUE="/var/qmail/bin/simscan",DKVERIFY="DEGIJKfh",DKQUEUE="/var/qmail/bin/qmail-queue.orig",DKSIGN="/var/qmail/control/domainkeys/%/default"</code></pre>
<h3>DNS TXT Records</h3>
<p>Using djbdns: </p>
<pre><code>'sample.com:v=spf1 ip4\07269.36.226.12 include\072emailcenterpro.com ~all:3600
'_domainkey.sample.com.:o=-;:3600
'default._domainkey.sample.com.:k=rsa; p=MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhAPBdKl9mx3KIjLwGfq83LNzUN4aDWc4t3PVTnc0d+duI9HVD+UG+i1suifV6obDzo4ugHji6EH+zei39Cch9vTXcpVWkaFCEmVu0AXjY/98WIjYb5Hh5+SYQFyQkz0Mq0wIDAQAB;:3600
'dkim1._domainkey.mail.sample.com.:v=DKIM1; k=rsa; p=MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxAK+vhWHoPv+3DhM1u3MaHk7AayBa+CdNnjPHhg3/3Nv7hEIYUbOfKSUqNxDtjJkgcQIDAQAB;:3600
'_adsp._domainkey.mail.sample.com:dkim=all:3600
'_adsp._domainkey.sample.com:dkim=all:3600</code></pre>
<p>First line is my SPF/Sender ID record.  It also uses an <em>include</em> to allow a third party (in this case, <a href="http://www.emailcenterpro.com">EmailCenterPro</a>) to send mail on our behalf.</p>
<p>The second line says that we sign all outgoing messages with DomainKeys/DKIM (o=-).</p>
<p>Third line is for DomainKeys using our selector &#8216;default&#8217; (which correspondingly shows up in tcp.smtp in DKSIGN and on the filesystem as /var/qmail/control/domainkeys/sample.com/<em>default</em>).</p>
<p>The fourth line is the DKIM record which uses the global.key and public.txt stored in /var/qmail/control/dkim.  Why is it dkim1._domainkey?  Because that&#8217;s the arbitrary selector specified in the signconf.xml file as listed above.  Again, totally arbitrary, could be &#8216;private&#8217; or &#8216;default&#8217; or something else, but best to have it different from your DomainKeys.  You might also notice that it is mail.sample.com instead of sample.com?  By default it must match /var/qmail/control/me file because that&#8217;s how the message will be signed by the QmailToaster DKIM package.</p>
<p>The fifth and sixth line are ADSP records that say we sign all outbound messages with DKIM.  Some hosts appear to be checking for it without the mail subdomain so I&#8217;m currently running both.  I don&#8217;t think it should be strictly required but at this time seems to cover the bases.</p>
<p>The 3600 on every line is the TTL, meaning these records expire after 1 hour.</p>
<h2>Quarantining Spam in IMAP Folder</h2>
<p>Before upgrading, we used <a href="http://dspam.nuclearelephant.com">DSPAM</a> as our spam filter of choice.  In a word, it&#8217;s awesome.  It quarantined mail so effectively that I eventually stopped checking the quarantine until it became so large I could no longer view or delete it from the web interface.  The secret sauce in my setup, which involves <a href="http://www.mozillamessaging.com">Mozilla Thunderbird</a> was to use Thunderbird&#8217;s built-in &#8220;Junk&#8221; folder as a feedback loop for DSPAM.  For some reason, Thunderbird is really good at catching certain kinds of spam that DSPAM was not and, if a spam did get through, I can select it and press &#8220;J&#8221; which trains Thunderbird and moves the message to my Junk folder.  The server processed those Junk folders nightly, feeding them to DSPAM to further increase its accuracy.</p>
<p>I like not downloading all of the assumed junk mail to my phone or, worse yet, on a slow net connection while traveling somewhere around the world.  Unfortunately, QmailToaster&#8217;s SpamAssassin approach is to flag but leave it in your inbox.  No bueno.  Luckily we can use some of the software already installed and a very short script to replicate the previous behavior.</p>
<h3>Create generic mailfilter</h3>
<p>If you accept users will use a &#8220;Junk&#8221; folder (or &#8220;Spam&#8221; or whatever you want to call it), then you can create a single maildrop mailfilter script in ~vpopmail/etc/mailfilter-to-spam:</p>
<pre><code>SHELL="/bin/bash"
import EXT
import HOST

VUSER=`echo ${EXT%-*}`
export VUSER
USERHOME=`/mail/bin/vuserinfo -d $VUSER@$HOST`
export USERHOME

logfile "/var/log/maildrop/mailfilter.log"

# Test for the existance of the Junk directory.  Create it if it doesn't exist
#VERBOSE=9
#log "testing for $VUSER@$HOST in $USERHOME"

if ( /^X-Spam-Status: Yes/ )
{
        `test -d $USERHOME/Maildir/.Junk`

        if ( $RETURNCODE == 1 )
        {
            `/usr/bin/maildirmake -f Junk $USERHOME/Maildir`
            `echo INBOX.Junk &gt;&gt; $USERHOME/Maildir/courierimapsubscribed`
        }

        to "$USERHOME/Maildir/.Junk"
}
else
{
        to "$USERHOME/Maildir/"
}</code></pre>
<p>Now create a ~vpopmail/domains/sample.com/username/.qmail file for those users who want their spam quarantined in a &#8220;Junk&#8221; folder rather than just tagged:</p>
<p><code>|preline /usr/bin/maildrop ~vpopmail/etc/mailfilter-to-spam</code></p>
<p>Done!  All messages that meet your SpamAssassin minimum score (default of 5.0) will be quarantined in the &#8220;Junk&#8221; folder for review from SquirrelMail or your IMAP client.  You can process these folders using a script like the following (<em>warning, not tested in production yet!</em>):</p>
<pre><code>#!/bin/bash
cd ~vpopmail/domains

# find all Junk folders
for ii in `find . -type d -name '.Junk'`; do

  # find all mail 5 days or older
  for jj in `find $ii/cur/ -type f -mtime +4`; do

    # learn from it and delete it
    echo "processing $jj"
    /usr/bin/sa-learn --spam --no-sync $jj
    rm -f $jj

  done;

done;

# now synchronize the overall SA database
/usr/bin/sa-learn --sync</code></pre>
<p>The argument &#8220;-mtime +4&#8243; says look for files that are 5 days or older.  You could adjust that to +1 for 48 hours or &#8220;-mmin +60&#8243; for 60 minutes or older depending on your policies.  Schedule this to run as a cronjob nightly and you&#8217;ll be provided an ongoing set of spam to learn from.</p>
<p>Hope these links and notes help someone else in the future!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/05/04/march-qmail-server-madness/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Push the last X backups to Amazon S3</title>
		<link>http://www.ghidinelli.com/2011/02/15/push-last-x-backups-amazon-s3</link>
		<comments>http://www.ghidinelli.com/2011/02/15/push-last-x-backups-amazon-s3#comments</comments>
		<pubDate>Wed, 16 Feb 2011 00:54:19 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1247</guid>
		<description><![CDATA[Store only a subset of files on Amazon S3 using s3cmd and a little date logic.  Lets us put the last 5 database backups (or something similar) in a safe place without saving every dump forever and costing us $$$.]]></description>
			<content:encoded><![CDATA[<p>Not going to get fancy with this one: it&#8217;s a bash shell script that will take the last X backups and push them to Amazon S3 for disaster recovery.   I wrote a couple of versions until I settled on this simple one.  It requires you to have <a href="http://s3tools.org/s3tools">s3cmd</a>, which if you&#8217;re using Linux, is pretty much the only way you could interface S3 and stay sane.  Here are docs on how to <a href="">install it with yum</a> if you are on Redhat/CentOS.</p>
<p>Now, to the script:</p>
<pre><code>#!/bin/bash

# init vars
BACKUP_ROOT=/dblogs/backups
S3CMD=/usr/bin/s3cmd
S3DIR=s3://your-bucket-name/your-folder-name

# take the last X backups and make sure they are on S3 (but not older ones)
BACKUP_COUNT=5
BACKUP_FILELIST=/tmp/.pgbackup_s3cmd_filelist

# first list out the files we want to sync up
ls -1 $BACKUP_ROOT | tail -$BACKUP_COUNT &gt; $BACKUP_FILELIST

# now sync them; requires trailing slash to sync directories
$S3CMD sync --progress --delete-removed --acl-private --exclude '*' --include-from $BACKUP_FILELIST $BACKUP_ROOT $S3DIR/

# clean up
rm -f $BACKUP_FILELIST
</code></pre>
<p>Setup s3cmd after installing it by running <tt>s3cmd --configure</tt> with your Amazon credentials handy.  I also use s3fox, a Firefox add-on, as another way of quickly accessing S3 with a GUI.</p>
<p>This script maintains only the last 5 backups on S3 and it deletes what would be the 6th backup each night via cron.  You could adjust the number to keep with the BACKUP_COUNT parameter.  I&#8217;m personally using this to backup a <a href="http://www.postgresql.org">PostgreSQL server</a> but you can adjust it to back up any type of directory with files in it.</p>
<p><strong>Update 2/17</strong> &#8211; realized the default behavior is not to delete files on the remote host that are not included locally.  I updated the above to include the &#8211;delete-removed argument and now it deletes the oldest file as expected.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/02/15/push-last-x-backups-amazon-s3/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>3 Useful Regular Expressions</title>
		<link>http://www.ghidinelli.com/2011/02/08/three-useful-regular-expressions</link>
		<comments>http://www.ghidinelli.com/2011/02/08/three-useful-regular-expressions#comments</comments>
		<pubDate>Tue, 08 Feb 2011 21:42:35 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[Eclipse]]></category>
		<category><![CDATA[regexp]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1238</guid>
		<description><![CDATA[Three useful regular expressions for cleaning up code]]></description>
			<content:encoded><![CDATA[<p>When I posted <a href="http://www.ghidinelli.com/2011/01/17/regex-cffunction-output-false">the regexp for adding output=&#8221;false&#8221;</a> recently, it reminded me that I had saved a number of other regular expressions for previous search-and-replace operations.  Here&#8217;s three others I&#8217;ve found helpful in cleaning up code in the past.</p>
<h2>Strip whitespace from end of lines</h2>
<p>Extra white space at the end of a line probably won&#8217;t cause any problems but it&#8217;s unnecessary and just taking up space.  You can strip it with this.</p>
<pre><code>Find: ([\S])[ |\t]+$
Replace: $1</code></pre>
<p>A variant of this one is to find &#8220;^[ |\t]+$&#8221; which will find all lines that only contain spaces and tabs.  </p>
<h2>Find unescaped ampersands</h2>
<p>Most ampersand characters need to be escaped in web apps &#8211; this will track all ampersands not followed by the most commonly escaped variants like &amp;nbsp; &#8211; add your own as needed.</p>
<pre><code>Find: [^ ]&amp;amp;[^ |(amp;)|(nbsp;)|(gt;)|(lt;)]

 </code></pre>
<h2>Convert isDefined to structKeyExists</h2>
<p><a href="http://livedocs.adobe.com/coldfusion/8/functions_s_27.html">StructKeyExists</a> is the preferred method of checking if a variable is defined but updating all of those isDefined()s by hand is a drag.  Convert your entire code base with this search and replace:</p>
<pre><code>Find: [iI]sDefined\("(FORM|URL).([A-Za-z0-9]+)"\)
Replace: structKeyExists($1, "$2")</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2011/02/08/three-useful-regular-expressions/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

