<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>SNF Labs</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/" />
    <link rel="self" type="application/atom+xml" href="http://labs.spaceshipnofuture.org/feeds/atom.xml" />
   <id>tag:labs.spaceshipnofuture.org,2008://5</id>
    <!-- <link rel="service.post" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5" title="SNF Labs" /> -->
    <updated>2008-08-17T19:52:11Z</updated>
    
    <author>
        <name>fhazel</name>
        <uri>http://profile.spaceshipnofuture.org/fhazel/</uri>
    </author>
    <author>
        <name>cobra libre</name>
        <uri>http://profile.spaceshipnofuture.org/cobra+libre/</uri>
    </author>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 3.36</generator>
 
<entry>
    <title>assemble the ways</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2008/08/shoplifters_for_iphone/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=356" title="assemble the ways" /> -->
    <id>tag:labs.spaceshipnofuture.org,2008://5.356</id>
    
    <published>2008-08-17T19:52:10Z</published>
    <updated>2008-08-17T19:52:11Z</updated>
    
    <summary>Shoplifters, Unite is now iPhone-friendly!</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="shoplifters" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p><a href="http://www.spaceshipnofuture.org/shoplifters/">Shoplifters, Unite</a> is now iPhone-friendly! More on this later.</p>
]]>
        
    </content>
</entry>
<entry>
    <title>oh stewardess, I speak five</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2008/07/html5/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=355" title="oh stewardess, I speak five" /> -->
    <id>tag:labs.spaceshipnofuture.org,2008://5.355</id>
    
    <published>2008-07-27T23:47:50Z</published>
    <updated>2008-07-28T04:38:45Z</updated>
    
    <summary>Hooray, chompy 3 is out!  Plus: How to get HTML 5 tags to render in all the popular browsers, including Firefox 2 and Internet Explorer.</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="web dev" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p><strong>Backwater</strong>, a.k.a. <a href="http://chompy.net/">chompy 3</a>, is public now, and it uses <a href="http://www.whatwg.org/specs/web-apps/current-work/">HTML 5</a>.  While HTML 5 adds many interactive features that aren't going to be supported by any browsers for a while (plus a few that are already supported, like canvas), it's not too hard to get some of the passive elements like section, nav, article, aside, header, and footer working on current browsers like Safari 3, Firefox 3, and Opera 9.5.  All you really need to do is set the display CSS property to block or inline as appropriate in your stylesheet.</p>

<p>Older browsers are a different story. Both Firefox 2 and Internet Explorer 7 have trouble handling unknown elements; if you use the above HTML 5 elements to contain child elements as they're intended, both browsers will prematurely close the tags and punt their child elements into sibling positions on the DOM tree.  You can verify this by using the DOM Inspector.  What you'll see is that something that should look like this:</p>

<pre>&lt;header&gt;
    &lt;h1&gt;we're sailing at the edges of time&lt;/h1&gt;
    &lt;p&gt;we're drifting at the waterline&lt;/p&gt;
&lt;/header&gt;
</pre>

<p>Looks like this to the renderer:</p>

<pre>&lt;header/&gt;
&lt;h1&gt;we're sailing at the edges of time&lt;/h1&gt;
&lt;p&gt;we're drifting at the waterline&lt;/p&gt;
</pre>

<p>It turns out that there's <a href="http://ejohn.org/blog/html5-shiv/">an easy solution</a> for IE; just use Javascript to programatically create one of each element that you wish to use.  Using the example above, the fix is simply to execute the following code:</p>

<pre>&lt;script type="text/javascript"&gt;document.createElement("header");&lt;/script&gt;
</pre>

<p>You don't even need to append the new element to anything in the document tree; just creating it somehow kicks IE's parser into working order.</p>

<p>So that's easy, but <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=311366">it doesn't work in Firefox 2</a>.  In Firefox 2, unknown elements can't contain child elements <a href="http://annevankesteren.nl/2008/01/firefox-html5#comment-6428">unless the page is served as XHTML</a>.  Serving a page as XHTML doesn't mean just slapping a different DOCTYPE declaration on the page and running it through the validator; this means writing the page using the XHTML serialization of HTML 5 and serving the page using the application/xhtml+xml content-type, which forces your browser to parse the page as XML, which means that you have to be utterly scrupulous about well-formedness, because one error will cause the browser to barf up an error page.</p>

<p>I did this for years with <a href="http://chompy.net/">chompy.net</a> and <a href="http://chompy.net/blogs/jacob/">remake/remodel</a>, and it's harder than it looks.  I'll probably end up writing scores of unit tests to shake out any XML well-formedness bugs in backwater's codebase.</p>
]]>
        
    </content>
</entry>
<entry>
    <title>GET action</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2008/07/get_action/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=353" title="GET action" /> -->
    <id>tag:labs.spaceshipnofuture.org,2008://5.353</id>
    
    <published>2008-07-12T02:56:12Z</published>
    <updated>2008-07-16T22:45:46Z</updated>
    
    <summary>For reasons of aesthetics and user friendliness, logouts are initiated by simply clicking on a link. This is a problem, however, because clicking on a link causes a GET request, and GET requests are supposed to be idempotent; that is, they are not supposed to generate side effects. The proper HTTP method for logouts, therefore,...</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="SNF" />
            <category term="users" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>For reasons of aesthetics and user friendliness, logouts are initiated by simply clicking on a link.  This is a problem, however, because clicking on a link causes a GET request, and GET requests are supposed to be <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2">idempotent</a>; that is, they are not supposed to generate side effects.  The proper HTTP method for logouts, therefore, is POST.  But how to POST a logout request through a link?</p>

<p>The solution is to first force the logout handler to reject any GET requests and only accept POST requests.  Then, give the user two means to POST a logout, depending on whether or not they have Javascript enabled:</p>

<ol>
<li>If Javascript is disabled, the link simply triggers a GET request, as it normally would, and an additional page is displayed with a logout confirmation form.  This form uses the POST method.</li>
<li>If Javascript is enabled, then a Javascript function is added to the onclick handler for the logout link.  This Javascript function submits an appropriate POST request to the logout handler when the logout link is clicked.</li>
</ol>

<p>This Javascript function is called SNF.logout() and can be found in /js/catfood.js.  Attach the function to the logout link like this:</p>

<pre>// This example uses Mootools
if ($('logout-link')) {
    $('logout-link').addEvent('click', function(event) {
        var clickEvent = new Event(event);
        // SNF.logout() posts the logout request
        SNF.logout();
        // Stopping the click event keeps the browser from loading 
        // the URL in the logout link's href; that's only there
        // as a fallback.
        clickEvent.stop();
    });
}
</pre>

<p>(You could also throw something like onclick="SNF.logout();" directly into the a tag, but the unobtrusive approach shown above is more maintainable.)</p>

<p>As of this writing, SNF.logout() is not completely robust and will not gracefully handle error conditions, but it works fine under normal circumstances.  I'll update it soon.</p>

<p>Finally, a nice side effect of forcing logouts to use POST is that a basic CSRF attack vector is eliminated.</p>

<p><em>Be sure to check the wiki for any updates to this discussion of the <a href="http://labs.spaceshipnofuture.org/icky/UMS/">logout process</a>.</em></p>
]]>
        
    </content>
</entry>
<entry>
    <title>moot tools</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2008/07/moot_tools/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=351" title="moot tools" /> -->
    <id>tag:labs.spaceshipnofuture.org,2008://5.351</id>
    
    <published>2008-07-10T17:31:22Z</published>
    <updated>2008-07-12T02:57:10Z</updated>
    
    <summary>I&apos;m quite unhappy with the way the Mootools team has been running their project lately, so I&apos;m going to evaluate jQuery for fitness to purpose, and if it looks good, switch SNF over. Rewriting a ton of Javascript won&apos;t be fun, but jQuery seems to attract a more sober set of developers and appears to...</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="SNF" />
            <category term="web dev" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>I'm quite unhappy with the way the <a href="http://www.mootools.net/">Mootools</a> team has been running their project lately, so I'm going to evaluate <a href="http://jquery.com/">jQuery</a> for fitness to purpose, and if it looks good, switch SNF over.  Rewriting a ton of Javascript won't be fun, but jQuery seems to attract a more sober set of developers and appears to be run a bit more professionally.  In the long run, switching might be good for the site.</p>

<p>This would be a good opportunity to bring the pre-AJAX-era AJAX in <a href="http://www.spaceshipnofuture.org/15ways/">15 Ways</a> up to date, too.</p>
]]>
        
    </content>
</entry>
<entry>
    <title>SNF: TNG</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2006/03/snf_tng/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=341" title="SNF: TNG" /> -->
    <id>tag:labs.spaceshipnofuture.org,2006://5.341</id>
    
    <published>2006-03-24T18:39:59Z</published>
    <updated>2006-03-24T21:41:15Z</updated>
    
    <summary>So, Chris, you had mentioned throwing everything out and starting from scratch...  What do you have in mind?  Let us discuss.</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="SNF" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>So, Chris, you had mentioned throwing everything out and starting from scratch...  What do you have in mind?  Let us discuss.</p>
]]>
        
    </content>
</entry>
<entry>
    <title>i was bored before i even began</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2005/12/so_much_for_shoplifters_2/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=340" title="i was bored before i even began" /> -->
    <id>tag:labs.spaceshipnofuture.org,2005://5.340</id>
    
    <published>2005-12-01T17:31:30Z</published>
    <updated>2005-12-01T17:32:34Z</updated>
    
    <summary>In PHP 5.0.4 and _only_ PHP 5.0.4, readfile() has a bug that causes it to halt output at about 1.9 MB.  This means that only the tiniest of files can be downloaded successfully from Shoplifters 2.</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="shoplifters" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>The sequel to <a href="http://www.spaceshipnofuture.org/shoplifters/">Shoplifters, Unite</a> will have to wait.  I began rewriting it yesterday and had completed the initial basic work &#8212; creating song and request classes, displaying a rudimentary index page, etc. &#8212; when I moved on to implementing downloads and quickly hit a wall.  The download code in Shoplifters works like this:</p>

<ol>
<li>verify that user is allowed to download</li>
<li>check that requested file is available</li>
<li>output HTTP headers</li>
<li>output the file using a call to readfile()</li>
</ol>

<p>It turns out that the last step no longer works.  In PHP 5.0.4 and <em>only</em> PHP 5.0.4, readfile() has a bug that causes it to halt output at about 1.9 MB.  This means that only the tiniest of files can be downloaded successfully from Shoplifters.</p>

<p>I sent a note to Dreamhost support and even created a <a href="http://labs.spaceshipnofuture.org/sandbox/php/readfile/">demonstration page</a> for the bug so that they can see what I&#8217;m talking about.  All they have to do to fix the issue is to upgrade PHP to any subsequent version, but they&#8217;re very conservative about PHP upgrades, so there&#8217;s no telling when that might happen.</p>

<p>There are things I could do to work around the problem, but I don&#8217;t like any of the options that I&#8217;ve been able to come up with:</p>

<ol>
<li>link directly to download files instead of passing them through an intermediary script</li>
<li>downgrade to PHP 4</li>
<li>write Shoplifters 2 in some other language</li>
</ol>
]]>
        
    </content>
</entry>
<entry>
    <title>help me, chris</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2005/10/help_me_chris/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=339" title="help me, chris" /> -->
    <id>tag:labs.spaceshipnofuture.org,2005://5.339</id>
    
    <published>2005-10-28T20:10:41Z</published>
    <updated>2005-10-28T20:12:45Z</updated>
    
    <summary><![CDATA[ The following query is intended to show the number of revisions to the wiki over the past 14 days: SELECT DATE_FORMAT(date, '%Y-%m-%d') AS dom, COUNT(id) AS revisions FROM version WHERE DATE_SUB(CURDATE(), INTERVAL 14 DAY) &lt;= date GROUP by dom; Because a few days during the last two weeks had no activity at all, the...]]></summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="wiki" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>
The following query is intended to show the number of revisions to the wiki over the past 14 days:
</p>

<pre>
SELECT DATE_FORMAT(date, '%Y-%m-%d') AS dom, COUNT(id) 
    AS revisions FROM version WHERE DATE_SUB(CURDATE(), 
    INTERVAL 14 DAY) &lt;= date GROUP by dom;
</pre>

<p>
Because a few days during the last two weeks had no activity at all, the results look like this:
</p>

<pre>
+------------+-----------+
| dom        | revisions |
+------------+-----------+
| 2005-10-17 |        11 |
| 2005-10-18 |         2 |
| 2005-10-19 |         9 |
| 2005-10-21 |        11 |
| 2005-10-22 |         2 |
| 2005-10-23 |        13 |
| 2005-10-24 |        11 |
| 2005-10-25 |         3 |
| 2005-10-26 |        10 |
| 2005-10-27 |         4 |
| 2005-10-28 |         3 |
+------------+-----------+
11 rows in set (0.03 sec)
</pre>

<p>
But I need my results to look like this:
</p>

<pre>
+------------+-----------+
| dom        | revisions |
+------------+-----------+
| 2005-10-15 |         0 |
| 2005-10-16 |         0 |
| 2005-10-17 |        11 |
| 2005-10-18 |         2 |
| 2005-10-19 |         9 |
| 2005-10-20 |         0 |
| 2005-10-21 |        11 |
| 2005-10-22 |         2 |
| 2005-10-23 |        13 |
| 2005-10-24 |        11 |
| 2005-10-25 |         3 |
| 2005-10-26 |        10 |
| 2005-10-27 |         4 |
| 2005-10-28 |         3 |
+------------+-----------+
14 rows in set (0.03 sec)
</pre>

<p>
That is, I want the days with no activity to show up in the results.  How do I do that?
</p>]]>
        
    </content>
</entry>
<entry>
    <title>happy jax</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2005/10/happy_jax/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=338" title="happy jax" /> -->
    <id>tag:labs.spaceshipnofuture.org,2005://5.338</id>
    
    <published>2005-10-24T19:23:14Z</published>
    <updated>2005-10-25T14:39:16Z</updated>
    
    <summary> I redesigned the wiki yesterday to more closely resemble the main SNF Labs site, and also added a sitewide recent comments page, plus a &quot;what links here&quot; feature for every article. The &quot;what links here&quot; feature provides a good chance to discuss when AJAX might be used and one way you can do it....</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="wiki" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>

    
    
    


I redesigned the wiki yesterday to more closely resemble the main SNF Labs site, and also added a sitewide <a href="http://labs.spaceshipnofuture.org/icky/comments/">recent comments page</a>, plus a "what links here" feature for every article.
</p>
<p>
The "what links here" feature provides a good chance to discuss when AJAX might be used and one way you can do it.  My thought process went something like this:  I could have put each article's incoming links on a separate page, but since each page's list of incoming links is likely to be small, it made more sense to display them on the same page as the article text.  At the same time, I didn't want an additional database query slowing down every page load, so I decided to make the list of incoming links only display when requested by the user.  This required making an xmlhttprequest to the backend from the client-side code. 
</p>
<p>
This is the Javascript code that does all the work:
</p>
<pre>
function getIncomingLinks(articleName)
{
&#160;&#160;&#160;&#160;var progress = document.getElementById('progress');
&#160;&#160;&#160;&#160;var loadingMsg = document.createTextNode(" loading...");

&#160;&#160;&#160;&#160;var loadingFunc = function(t) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;progress.appendChild(loadingMsg);
&#160;&#160;&#160;&#160;}

&#160;&#160;&#160;&#160;var loadedFunc = function(t) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;progress.removeChild(loadingMsg);
&#160;&#160;&#160;&#160;}

&#160;&#160;&#160;&#160;var errFunc = function(t) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;alert('Error ' + t.status + ' -- ' + t.statusText);
&#160;&#160;&#160;&#160;}

&#160;&#160;&#160;&#160;var url = 'http://labs.spaceshipnofuture.org/icky/ajax.php';
&#160;&#160;&#160;&#160;var params = 'mode=getIncomingLinks&amp;q=' + articleName;
&#160;&#160;&#160;&#160;var options = {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;method: 'get',
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;parameters: params,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;onLoading: loadingFunc,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;onLoaded: loadedFunc,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;onFailure: errFunc
&#160;&#160;&#160;&#160;}
&#160;&#160;&#160;&#160;new Ajax.Updater('incoming-links', url, options);
}
</pre>
<p>
This ought to be easy to follow.  When the user clicks the "what links here" link, a call to getIncomingLinks() is made.  getIncomingLinks() creates an Ajax.Updater object (part of the <a href="http://wiki.script.aculo.us/scriptaculous/show/Prototype">Prototype</a> Javascript library).  We provide Ajax.Updater with a <a href="http://labs.spaceshipnofuture.org/icky/ajax.php?mode=getIncomingLinks&amp;q=cave+of+monsters">URL</a> to contact, the HTTP method to use ('get'), and a number of callback functions that are triggered depending on the HTTP request state.  When the request completes, Ajax.Updater updates the contents of the HTTP element with the id 'incoming-links' using whatever output the backend sent.  That's it.
</p>
<p>
The client-side code and backend code are rather tightly coupled here.  If you don't want the backend to send formatted output, you could use Ajax.Request instead and have the backend send its output as <a href="http://www.crockford.com/JSON/">JSON</a>.  Your client-side code would then unpack the JSON output and format the data using the DOM.
</p>]]>
        
    </content>
</entry>
<entry>
    <title>SURBL bobble</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2005/10/surbl_bobble/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=337" title="SURBL bobble" /> -->
    <id>tag:labs.spaceshipnofuture.org,2005://5.337</id>
    
    <published>2005-10-21T20:33:49Z</published>
    <updated>2005-10-24T16:59:33Z</updated>
    
    <summary> In addition to the blacklist checking mentioned previously, a good future project would be to extract URLs from comment bodies and scan them against a SURBL. A SURBL differs from a conventional spam blacklist in that it doesn&apos;t check the sender&apos;s origin (that is, the IP address), but instead checks the sender&apos;s payload (any...</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="spam" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>
In addition to the blacklist checking mentioned previously, a good future project would be to extract URLs from comment bodies and scan them against a <a href="http://www.surbl.org/">SURBL</a>.  A SURBL differs from a conventional spam blacklist in that it doesn't check the sender's <em>origin</em> (that is, the IP address), but instead checks the sender's <em>payload</em> (any URLs in the message body).  This is also supported by <a href="http://pear.php.net/manual/en/package.networking.net-dnsbl.intro.php">Net_DNSBL</a>, so implementing this ought to be easy, and is probably much more effective than an IP address blacklist.
</p>]]>
        
    </content>
</entry>
<entry>
    <title>a spy in the haus of spam</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2005/10/a_spy_in_the_haus_of_spam/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=336" title="a spy in the haus of spam" /> -->
    <id>tag:labs.spaceshipnofuture.org,2005://5.336</id>
    
    <published>2005-10-19T18:14:36Z</published>
    <updated>2005-10-24T16:59:56Z</updated>
    
    <summary>Comment spam hasn&apos;t really been much of an issue with SNF, since we require registration, and that&apos;s sufficient to thwart almost all automated spam attacks.  Still, it doesn&apos;t hurt to provide other layers of reasonable safeguards, so today I changed the link-o-matic to check all incoming comments against the Spamhaus realtime IP address blacklist.  I thought this might turn into a real project, but it ended up being easy because a PEAR module called Net_DNSBL already exists to do all the work...</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
            <category term="link-o-matic" />
            <category term="spam" />
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>
Comment spam hasn't really been much of an issue with SNF, since we require registration, and that's sufficient to thwart almost all automated spam attacks.  Still, it doesn't hurt to provide other layers of reasonable safeguards, so today I changed the link-o-matic to check all incoming comments against the <a href="http://www.spamhaus.org/">Spamhaus realtime IP address blacklist</a>.  I thought this might turn into a real project, but it ended up being easy because a PEAR module called <a href="http://pear.php.net/manual/en/package.networking.net-dnsbl.intro.php">Net_DNSBL</a> already exists to do all the work.
</p>
<p>
I've implemented the check in a static method of the SNFComment abstract class:
</p>
<pre>
public static function checkBlacklist($user)
{
&#160;&#160;&#160;&#160;require_once('Net/DNSBL.php');
&#160;&#160;&#160;&#160;$dnsbl = new Net_DNSBL();
&#160;&#160;&#160;&#160;$dnsbl-&gt;setBlacklists(array('sbl-xbl.spamhaus.org'));
&#160;&#160;&#160;&#160;if ($dnsbl-&gt;isListed($user-&gt;ip)) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;throw new SNFCommentBlacklistException();
&#160;&#160;&#160;&#160;}
}
</pre>
<p>Here's how you would use it in your code:</p>
<pre>
require_once('SNF/exceptions.inc');
require_once('SNF/users.inc');
require_once('SNF/comments.inc');

$user = new User();

try {
&#160;&#160;&#160;&#160;// Check real-time IP address blacklist
&#160;&#160;&#160;&#160;SNFComment::checkBlacklist($user);
&#160;&#160;&#160;&#160;// Post comment...
} catch (SNFCommentBlacklistException $e) {
&#160;&#160;&#160;&#160;// Reject comment...
}
</pre>
<p>
This is pretty basic: if an SNFCommentBlacklistException is thrown, you reject the message; otherwise, you post the comment.  If you wanted, you could get fancy and shove the message into a moderation queue instead of throwing it away.
</p>
<p>
If everything seems to work smoothly with the link-o-matic for a while, then I'll add support to 15 Ways later.  (The weblogs are already safe, because Movable Type has built-in blacklist checks as of version of 3.2, and it's been working impressively well over at <a href="http://www.chompy.net/">chompy.net</a>.)
</p>]]>
        
    </content>
</entry>
<entry>
    <title>first things</title>
    <link rel="alternate" type="text/html" href="http://labs.spaceshipnofuture.org/2005/10/first_things/" />
    <!-- <link rel="service.edit" type="application/atom+xml" href="http://www.spaceshipnofuture.org/mt/mt-atom.cgi/weblog/blog_id=5/entry_id=335" title="first things" /> -->
    <id>tag:labs.spaceshipnofuture.org,2005://5.335</id>
    
    <published>2005-10-12T22:07:44Z</published>
    <updated>2005-10-24T16:59:33Z</updated>
    
    <summary>This is the pretend first entry, not to be confused with the for-real first entry. A for-real first entry would have a substantial amount of content and some sort of purpose distinct from the blog itself. This is a pretend first entry, so it&apos;s really about nothing except making sure that the blog works and...</summary>
    <author>
        <name>cobra libre</name>
        <uri>http://www.chompy.net/blogs/jacob/</uri>
    </author>
    
    <content type="html" xml:lang="en" xml:base="http://labs.spaceshipnofuture.org/">
        <![CDATA[<p>This is the pretend first entry, not to be confused with the for-real first entry.  A for-real first entry would have a substantial amount of content and some sort of purpose distinct from the blog itself.  This is a pretend first entry, so it's really about nothing except making sure that the blog works and filling up some space.</p>]]>
        
    </content>
</entry>

</feed> 

