<?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>Hacking at 0300</title>
	<atom:link href="http://bililite.nfshost.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://bililite.nfshost.com/blog</link>
	<description>Thoughts on web design and programming from a very occasional volunteer webmaster</description>
	<lastBuildDate>Mon, 07 May 2012 14:38:35 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Using S3 files in PHP</title>
		<link>http://bililite.nfshost.com/blog/2012/04/03/using-s3-files-in-php/</link>
		<comments>http://bililite.nfshost.com/blog/2012/04/03/using-s3-files-in-php/#comments</comments>
		<pubDate>Tue, 03 Apr 2012 19:37:49 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2278</guid>
		<description><![CDATA[As I wrote, I'm using Amazon S3 to store files that are too expensive to keep on my web server, with the plan of having frequently-updated files on the server and relatively constant stuff on S3. The address for my S3 server is bililite.s3.amazonaws.com, which is stored in the global variable $_SERVER['CDN']. So to include [...]]]></description>
			<content:encoded><![CDATA[<p><a href="/blog/2012/04/02/using-nfs-net-with-amazon-s3/">As I wrote</a>, I'm using <a href="http://aws.amazon.com/s3/">Amazon S3</a> to store files that are too expensive to keep on my web server, with the plan of having frequently-updated files on the server and relatively constant stuff on S3. The address for my S3 server is bililite.s3.amazonaws.com, which is stored in the global variable <code class="language-php">$_SERVER['CDN']</code>.</p>
<p>So to <a href="http://php.net/include">include</a> a file, I would do:</p>
<pre><code class="language-php">$filename = '/toinclude.php';
if (file_exists($_SERVER['DOCUMENT_ROOT'].$filename)){
  $filename = $_SERVER['DOCUMENT_ROOT'].$filename;
}else{
  $filename = $_SERVER['CDN'].$filename;
}
include ($filename);</code></pre>
<p>Which I use often enough to want to generalize it into a class.</p>
<span id="more-2278"></span>
<p>The other thing that would be useful is a directory listing, which is harder than it sounds for S3 since it has no directory structure; it's just a flat database of keys (the equivalent of filenames) and values (the files themselves). Thus <code>bililite.s3.amazonaws.com/images/silk/add.png</code> has S3 return the file labelled <code>/images/silk/add.png</code>; it has no inherent relationship to <code>/images/silk/delete.png</code> or <code>/images/silk/</code>.</p>
<p>The key is that just retrieving the server URL returns an XML listing of all the files, and there is an API to limit the files returned. <a href="http://bililite.s3.amazonaws.com">bililite.s3.amazonaws.com</a> returns all the files (up to a numerical limit; see below). <a href="http://bililite.s3.amazonaws.com?prefix=images/silk/">bililite.s3.amazonaws.com?prefix=images/silk/</a> returns all the filenames that start with <code>images/silk/</code> (note no leading slash). That's not quite enough, since it gives us sub-folders as well, but the <code>delimiter</code> parameter tells S3 to group all files that contain the delimiter after the prefix into one entry in the XML list. That's the equivalent of a subfolder. So <a href="http://bililite.s3.amazonaws.com?prefix=images/silk/&#038;delimiter=/">bililite.s3.amazonaws.com?prefix=images/silk/&amp;delimiter=/</a> gives us the list we want.</p>
<p>One more subtlety: S3 returns a maximum of 1000 names, then sets a flag in the XML to say the list was truncated. You can then ask for the next 1000 by naming the last returned file.</p>
<p>The <a href="http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html">documentation</a> is pretty opaque, but it's all in there.</p>
<p><a href="/blog/blogfiles/highlight.php?source=s3.class.php">I put it all together</a> into an abstract class that just handles the file part (assuming that any CDN would work the same way, just appending the host name to the file name) and a concrete class that handles the S3-specific directory-simulating parts. <a href="/blog/blogfiles/highlight.php?source=s3.class.php">See the source code</a>. The method names are meant to parallel the built-in PHP functions.</p>
<div class="prelike"><dl>
<dt>constructor</dt>
<dd><code class="language-php">$s3 = new S3('http://bililite.s3.amazonaws.com');</code></dd>
<dt>realpath</dt>
<dd><pre><code class="language-php">$path = $s3->realpath('/toinclude.php');
include($path);
// or
$content = file_get_contents($path);</code></pre> returns the real path for the file, either from <code class="language-php">$_SERVER['DIRECTORY_ROOT']</code> or the S3 root passed in with the constructor. In other words, if the file exists on the web server, <code>realpath</code> returns something like '/public/www/toinclude.php' and if it does note, returns something like 'http://bililite.s3.amazonaws.com/public/www/toinclude.php'. Note that if the file does not exist on the web server, this will return the path on the S3 root without checking if the file actually exists; use <code>file_exists</code> for that.</dd>
<dt>isCDN</dt>
<dd><code class="language-php">$flag = $s3->isCDN($s3->realpath('/toinclude.php'));</code> returns <code class="language-php">FALSE</code> if the path represents a file on the web server (i.e. from <code class="language-php">$_SERVER['DIRECTORY_ROOT']</code>), <code class="language-php">TRUE</code> otherwise (note that it does not check if the file actually exists on the S3 server). Note also that this requires the path returned by <code>realpath</code>.</dd>
<dt>file_exists</dt>
<dd><code class="language-php">$flag= $s3->file_exists('/toinclude.php');</code> returns <code class="language-php">TRUE</code> if the file exists on the web server or the S3 server (this does check the S3 server).</dd>
<dt>filemtime</dt>
<dd><code class="language-php">$timestamp = $s3->filemtime('/toinclude.php');</code> returns the time the file was last modified.</dd>
<dt>scandir</dt>
<dd><code class="language-php">$files = $s3->scandir('/images/');</code> returns an array of names of files that exist <em>either</em> on the web server or the S3 server (it's the union of the directory contents).</dd>
</dl></div>
<p>This assumes that the ACL (access control list) for the files is set to allow anonymous reading; if not, use Donovan Schönknecht's excellent <a href="http://undesigned.org.za/2007/10/22/amazon-s3-php-class">S3 class</a>. Of course, you'd have to rename one of these classes to avoid the conflict (or use <a href="http://php.net/namespace">namespaces</a>).</p>
<p>Hope this helps someone.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/04/03/using-s3-files-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using NFS.net with Amazon S3</title>
		<link>http://bililite.nfshost.com/blog/2012/04/02/using-nfs-net-with-amazon-s3/</link>
		<comments>http://bililite.nfshost.com/blog/2012/04/02/using-nfs-net-with-amazon-s3/#comments</comments>
		<pubDate>Mon, 02 Apr 2012 21:28:22 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2265</guid>
		<description><![CDATA[Since I started using nearlyfreespeech.net 3 months ago, I've been very pleased. Getting them to set up access with a private key was straightforward and the email support person was prompt, helpful and friendly. The only downsides are the safe mode restrictions, which I have been easily able to work around, and the expensive storage [...]]]></description>
			<content:encoded><![CDATA[<p>Since I started using <a href="http://nearlyfreespeech.net">nearlyfreespeech.net</a> 3 months ago, I've been very pleased. Getting them to set up <a href="https://members.nearlyfreespeech.net/dwachss/support/faq?q=SSHKeys&#038;keywords=ssh&#038;form=1#SSHKeys">access with a private key</a> was straightforward and the email support person was prompt, helpful and friendly. The only <a href="http://bililite.nfshost.com/blog/2012/02/03/nearly-free-speech/" title="Nearly Free Speech">downsides</a> are the safe mode restrictions, which I have been easily able to work around, and the expensive storage ($1/MB/month), which would add up quickly with all the <a href="/webservices/?page=icons">icons</a> and <a href="/webservices/?page=fonts">fonts</a> I'm serving with the <a href="/webservices/">webservices</a>.</p>
<p>So I put them onto <a href="http://aws.amazon.com/s3/">Amazon's S3</a> at <a href="http://bililite.s3.amazonaws.com">bililite.s3.amazonaws.com</a> with the intent of using that like a <a href="http://en.wikipedia.org/wiki/Content_delivery_network">Content Delivery Network</a> (though it isn't really unless I pay for <a href="http://aws.amazon.com/cloudfront/">CloudFront</a> as well)&mdash;static, large files should come tranparently from S3 while the dynamic site runs on NFS.net.</p>
<p>I do this with a bit of <code>.htaccess</code> hackery. It's harder to create or modify files on S3, so files that are in active developement are on the webserver. I want to serve those files <em>if they exist</em>. Only if the desired files do not exist do I want to get them from S3. Unfortunately, NFS.net does not support <a href="http://httpd.apache.org/docs/2.0/mod/mod_proxy.html"><code>mod_proxy</code></a>, so the redirecting is not transparent (and we can't do things that require <a href="https://developer.mozilla.org/en/Same_origin_policy_for_JavaScript">same-origin security</a>). But for images and the like, this works:</p>
<pre></code>SetEnvIf Request_URI . CDN=http://bililite.s3.amazonaws.com
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/(images|inc|fonts)/
RewriteRule . %{ENV:CDN}%{REQUEST_URI}</code></pre>
<p>Line 1 creates a variable named <code>CDN</code>. The directive <a href="http://httpd.apache.org/docs/2.3/mod/mod_env.html#setenv"><code>SetEnv</code></a> would be more natural to use as <code>SetEnv CDN http://bililite.s3.amazonaws.com</code>, but URL rewriting is done <em>before</em> <code>SetEnv</code> runs. The newer <code>SetEnvIf</code> runs early enough for the variable to be used for rewriting, but it's conditional, so we use a dummy condition: <code>REQUEST_URI .</code>, which means "If the requested URI matches any character"</p>
<p>Line 4 tests whether the requested file exists on the server. Only if it does not exist (<code>!-f</code>) is the next line tested, which is whether the file is in any of the CDN directories.</p> 
<p>If it is to be redirected, create the new URL by concatenating the <code>CDN</code> variable with the requested URI, which does not contain the protocol or hostname. Thus <code><a href="http://bililite.com/images/silk/add.png">http://bililite.com/images/silk/add.png</a></code> has a <code>REQUEST_URI</code> of <code>/images/silk/add.png</code> and the rewritten URL is <code>http://bililite.s3.amazonaws.com/images/silk/add.png</code>.</p>
<p>The advantage of setting a variable in the <code>.htaccess</code> (aside from having the "magic constant" at the top of the file") is that this is passed to the PHP code as <code class="language-php>$_SERVER['CDN']</code> and can be used like <code class="language-php>imagecreatefrompng($_SERVER['CDN'].'/images/silk/add.png')</code>. So the name of the S3 server is written in just one place, with no need to change multiple files if it changes.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/04/02/using-nfs-net-with-amazon-s3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Inline Audio Player</title>
		<link>http://bililite.nfshost.com/blog/2012/04/02/inline-audio-player/</link>
		<comments>http://bililite.nfshost.com/blog/2012/04/02/inline-audio-player/#comments</comments>
		<pubDate>Mon, 02 Apr 2012 16:08:52 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2250</guid>
		<description><![CDATA[Download the code. Demo. Download the WP Audio Player Standalone. So the rabbi asked me to add the ability to play the audio on the taamim page (basically, a long list of short MP3's) directly on the page, rather than click the link to open a new page. No problem, right? We're living in an [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://youngisrael-stl.org/inc/inline_mp3.js">Download the code</a>.</p>
<p><a href="http://youngisrael-stl.org/articlereader.php?author=shulman&#038;archive=yes&#038;tags[]=trop&#038;tags[]=Audio">Demo</a>.</p>
<p><a href="http://wpaudioplayer.com/wp-content/downloads/audio-player-standalone.zip">Download the WP Audio Player Standalone</a>.</p>
<p>So the rabbi asked me to add the ability to play the audio on the <a href="http://youngisrael-stl.org/articlereader.php?author=shulman&#038;archive=yes&#038;tags[]=trop&#038;tags[]=Audio">taamim page</a> (basically, a long list of short MP3's) directly on the page, rather than click the link to open a new page. No problem, right? We're living in an HTML5 world, so I should be able to do:</p>
<pre><code class="language-javascript">$('a[href$=mp3]').each(function(){
  $('&lt;audio&gt;').attr({src: this.href, controls: 'controls'}).insertBefore(this);
});</code></pre>
<p>And everything ought to work: browsers that can't handle <code><a href="http://www.w3schools.com/html5/tag_audio.asp">&lt;audio&gt;</a></code> elements get nothing, modern browsers get a modern audio player. Nice progressive enhancement.</p>
<p>But of course, it's not that easy. Webkit (Chrome, Safari) supports MP3 playing, but Firefox does not (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=562730">and won't</a>), and Internet Explorer only does for IE9 and up, and I have to support Windows XP and IE8 (<a href="http://en.wikipedia.org/wiki/Html5_audio">source</a>; consistent with my experimentation). I don't like the <code>&lt;embed&gt;</code>ed players, so I'll go with Flash. I like the player that <a href="http://languagelog.ldc.upenn.edu/nll/">Language Log</a> uses, and viewing the source tells me that's <a href="http://wpaudioplayer.com/">WPAudioplayer</a>, which has a standalone version that requires just two files, the 11-kb <code>swf</code> file and a 12-kb javascript file.</p>
<p>To use it, include the javascript with a &lt;script&gt; element and initialize the player with <code class="language-javascript">AudioPlayer.setup('/path/to/player.swf', {width: 100});</code> where <code class="language-javascript">100</code> is the desired width of the player in pixels (it's constant for every player on the page and it's a mandatory option). Then, each player is implemented by <em>replacing</em> an existing element, identified by <code>id</code>: <code class="language-javascript">AudioPlayer.embed(id, {soundFile: '/path/to/soundfile.mp3'});</code>.</p>
<p>Of course, iOS won't run Flash, so I still need to use the <code><a href="http://www.w3schools.com/html5/tag_audio.asp">&lt;audio&gt;</a></code> element there. So I need to detect if the <code>audio</code> element works, and if not, insert the Flash substitute. Browsers that can't handle either get a blank spot.</p>
<p>Putting it together into a plugin:</p>
<pre><code class="language-javascript">(function($) {

var uid = 0;
var init = function (swf, width){
	AudioPlayer.setup(swf, {width: width});
	init = $.noop;
}
$.fn.inline_mp3 = function(swf){
  return this.each(function(){
		var id = 'audioplayer_'+(uid++);
		var player = $('&lt;audio&gt;').attr({
			src: this.href,
			controls: 'controls',
			id: id
		}).insertBefore(this);
		// audio.canPlayType test from http://diveintohtml5.com/everything.html#audio-mp3
		if (!(player[0].canPlayType &#038;& player[0].canPlayType('audio/mpeg;').replace(/no/, ''))){
			init (swf, player.width());
			AudioPlayer.embed(id, {soundFile: this.href});
		}
	});
};
})(jQuery);</code></pre>
<p>It uses a unique number to assign an id to each element, and lazy-initializes the Flash player. The player should be styled with a given width (since IE8 doesn't have a default <code>&lt;audio&gt;</code> size):</>
<pre><code class="language-css">audio {
	width: 80px; 
	display: inline-block;
}</code></pre>
<p>And use it:</p>
<pre><code class="language-javascript">$('a[href$=mp3]').inline_mp3('/path/to/player.swf');</code></pre>
<p>And there are <a href="http://praegnanz.de/html5video/">lots</a> <a href="http://www.schillmania.com/projects/soundmanager2/">of other packages of html5/Flash fallback audio players</a> but this is small and easy enough for me to understand.]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/04/02/inline-audio-player/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Corrected parsecss</title>
		<link>http://bililite.nfshost.com/blog/2012/03/06/corrected-parsecss/</link>
		<comments>http://bililite.nfshost.com/blog/2012/03/06/corrected-parsecss/#comments</comments>
		<pubDate>Tue, 06 Mar 2012 22:25:02 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2247</guid>
		<description><![CDATA[Brad noted an incredibly dumb bug; evidently I used a single = instead of a double == in an if condition, and never tested for the false case. It is now corrected (and named version 1.4).]]></description>
			<content:encoded><![CDATA[<a href="/blog/2009/01/16/jquery-css-parser/#comment-17240">Brad</a> noted an incredibly dumb bug; evidently I used a single <code>=</code> instead of a double <code>==</code> in an <code>if</code> condition, and never tested for the false case. It is now corrected (and named <a href="/inc/jquery.parsecss.js">version 1.4</a>).]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/03/06/corrected-parsecss/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nearly Free Speech</title>
		<link>http://bililite.nfshost.com/blog/2012/02/03/nearly-free-speech/</link>
		<comments>http://bililite.nfshost.com/blog/2012/02/03/nearly-free-speech/#comments</comments>
		<pubDate>Fri, 03 Feb 2012 10:05:17 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2228</guid>
		<description><![CDATA[I've used 1&#038;1 since I started mucking about on the web; they had a cheap plan for $3/month with one domain name and a simple LAMP stack. But the price has been going up (now $5/month; still comparatively cheap!), and I'm starting to chafe at the limitations (no SSH shell access, proprietary 404 pages, no [...]]]></description>
			<content:encoded><![CDATA[<p>I've used <a href="http://1and1.com">1&#038;1</a> since I started mucking about on the web; they had a cheap plan for $3/month with one domain name and a simple <a href="http://en.wikipedia.org/wiki/LAMP_(software_bundle)">LAMP stack</a>. But the price has been going up (now $5/month; still comparatively cheap!), and I'm starting to chafe at the limitations (no SSH shell access, proprietary 404 pages, no languages beside PHP) and I didn't want to shell out for my own virtual server (<a href="http://www.rackspace.com/cloud/cloud_hosting_products/servers/pricing/">Rackspace</a> goes as low as $11/month but I'm really cheap). So I was overjoyed when I found <a href="http://nearlyfreespeech.net">Nearly Free Speech</a>.<p>
<span id="more-2228"></span>
<p>It has several advantages:</p>
<ol>
<li>SSH into a real Linux shell. It's been a while since I used Unix, but the old habits come back fast and I like having all those tools available.</li>
<li><a href="http://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol">SFTP</a> support. More secure and faster (at least it seems that way) than FTP, especially with <a href="http://notepad-plus-plus.org/">Notepad++</a>.</li>
<li>It sure seems cheap (only $0.01/day for the site, $0.02/day for as many mySQL databases as you want).</li>
<li><a href="http://example72.nfshost.com/versions.php">Lots of languages</a> besides PHP. Maybe I will learn <a href="http://xkcd.com/353/">Python</a> someday.</li>
<li>Up-to-date versions of the tools, like PHP 5.3 (<a href="http://us.php.net/manual/en/functions.anonymous.php">anonymous functions</a>!) and Apache 2.2 (<a href="http://httpd.apache.org/docs/2.3/mod/mod_dir.html#fallbackresource">FallbackResource</a>!). 1and1 was still stuck on the old versions.</li>
<li>They enable <a href="http://us.php.net/manual/en/features.remote-files.php">file URL wrappers</a>, so I can use get_file_contents('http://example.com') and the like, rather than cURL.</li>
<li>A general feel that these guys are <a href="http://faq.nearlyfreespeech.net/full/easy#easy">more hacker-oriented rather than simple, one-click-install-package oriented</a>.</li>
</ol>
<p>But there are disadvantages as well.</p>
<ol>
<li>It's not nearly as cheap as it seems. A simple site as above is $0.03/day = $10.95/year, but it's all pay for what you use. They charge for server usage based on storage, $0.01 per megabyte-month, so my 100 megabytes of fonts and images is another $12/year. Domain name registration is $9.49/year, plus $0.41/year for DNS. Bandwidth is $0.20 to $1.00/gigabyte, depending on usage. For my relatively popular blog (about 300-600 visits/day) I'm sending about 100 MB/day or $0.07/day or $25.55/year. That adds up to $58.40/year, basically the same price as 1and1. And I feel like I have to watch every byte stored or transferred. I know it's not really much money, but it's an avocation rather than a vocation so I need to justify the cost to myself. The bandwidth I'm going to have to explore to find out what's going on and possibly stop hotlinking and the like. The storage costs I can hack around by putting the big stuff on <a href="http://aws.amazon.com/s3/pricing/">Amazon S3</a> at only $0.14/gigabyte.</li>
<li>They don't automatically take the money from a credit card. You have to deposit funds in your account, and if it runs dry, the site goes down. They'll send out low-balance warnings but I'd like an automated method. Alos, every payment comes with a $1.00 deposit fee, so making monthly payments is another $12/year.</li>
<li>PHP runs in <a href="http://us.php.net/manual/en/features.safe-mode.php">safe mode</a>, so a lot of file and system functions are disabled. This is deprecated in the latest PHP, so I don't know what NearlyFreeSpeech will do, but right now it makes life much more difficult. There are workarounds, notably using <a href="/blog/2012/01/12/writing-cgi-scripts/" title="Writing CGI scripts">CGI scripts</a> rather than straight PHP, but this is the biggest downside.</li>
<li><a href="https://www.nearlyfreespeech.net/about/faq?&#038;keywords=cron&#038;form=1#WontGo">No cron jobs</a>. You can't run any processes on their servers besides the webserver, mySQL and whatever you're doing when SSH'ing in. <a href="http://faq.nearlyfreespeech.net/section/programming/cron#cron">They promise they're working on a solution</a>, but no update since 2008. You can use hacks like <a href="http://codex.wordpress.org/Category:WP-Cron_Functions">WP-cron</a> but that is exactly that, a hack.</li>
</ol>
<p>But overall, it looks like a good thing and I'm looking forward to (slowly, as I have the time) moving all of bililite.com over to the new host (then transferring the domain when I'm sure it works).</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/02/03/nearly-free-speech/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>New Amazon Advertising API</title>
		<link>http://bililite.nfshost.com/blog/2012/02/01/newamzonapi/</link>
		<comments>http://bililite.nfshost.com/blog/2012/02/01/newamzonapi/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:48:06 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2225</guid>
		<description><![CDATA[Just got an email from Amazon reminding me that "As part of our continued effort to ensure that the Product Advertising API is an efficient and effective advertising tool, we’ve identified opportunities to streamline the API", which is their way of saying that the API exists to make them money. Anything that doesn't serve that [...]]]></description>
			<content:encoded><![CDATA[<p>Just got an email from Amazon reminding me that "As part of our continued effort to ensure that the Product Advertising API is an efficient and effective advertising tool, we’ve identified opportunities to streamline the API", which is their way of saying that the API exists to make <em>them</em> money. Anything that doesn't serve that purpose is eliminated, however nice it might be for me. Their right, even if they're <em>not</em> right. I've already taken into account <a href="/blog/2011/11/01/the-new-amazon-advertising-api/" title="The New Amazon Advertising API">the major changes</a>. The only thing is to change the API version to the most recent; in the <a href="http://mierendo.com/software/aws_signed_query/">aws_signed_request</a> routine, change the <code class="language-html">$params["Version"]</code> line to read <code class="language-html">$params["Version"] = "2011-08-01";</code> and that should be it.</p> ]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/02/01/newamzonapi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Writing CGI scripts</title>
		<link>http://bililite.nfshost.com/blog/2012/01/12/writing-cgi-scripts/</link>
		<comments>http://bililite.nfshost.com/blog/2012/01/12/writing-cgi-scripts/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 18:28:41 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2206</guid>
		<description><![CDATA[nearlyfreespeech runs with safe mode and safe_mode_gid on, so you can't run shell scripts or other cool things. Usually that's not an issue, but if you do, they let you run CGI scripts, in any of a number of languages. You just have to treat it as a shell script, with a shebang notation of [...]]]></description>
			<content:encoded><![CDATA[<p>nearlyfreespeech runs with <a href="http://www.php.net/manual/en/features.safe-mode.php">safe mode</a> and <a href="http://www.php.net/manual/en/ini.sect.safe-mode.php#ini.safe-mode-gid">safe_mode_gid</a> on, so you can't run shell scripts or other cool things. Usually that's not an issue, but if you do, they let you run <a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a> scripts, in any of a <a href="http://example72.nfshost.com/versions.php">number of languages</a>. You just have to treat it as a shell script, with a <a href="http://en.wikipedia.org/wiki/Shebang_(Unix)">shebang</a> notation of the proper language. There are a few caveats that took me a day to find out:</p>
<ol><li>The correct program is <code>/usr/local/bin/php-cgi</code> (or whereever your server puts PHP), not <code>/usr/local/bin/php</code>. That is the command-line interpreter, which does not output the headers. You can manually do <code class="language-php">echo "Content-type: text/html\n"</code> etc., followed by a blank line, but who wants to try to debug that?</li>
<li>The script has to be executable. You get a 500 Server Error otherwise. Do a <code>chmod +x scriptname.cgi</code>.</li>
<li>Scripts need the appropriate file and directory permissions to write to the server.  Changing the permissions of the server directory is probably too insecure (though for safe mode it's the only way); it's better to set-uid the script, but then keep it under wraps. </li>
</ol>
<p>So a sample CGI script would be:</p>
<pre><code class="language-html">#!/usr/local/bin/php-cgi
&lt;?php echo "Hello, world"; ?&gt;</code></pre>
<p>in an executable file with a <code>.cgi</code> extension.</p>
<p>Note that FTP and SFTP can't set the setuid bit, so that if you upload the file (or edit it with an FTP-aware editor like <a href="http://notepad-plus-plus.org/">Notepad++</a>), you need to <code>chmod 477 scriptname.cgi</code> again.]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/01/12/writing-cgi-scripts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Things I Learned About .htaccess, part 2</title>
		<link>http://bililite.nfshost.com/blog/2012/01/12/things-i-learned-about-htaccess-part-2/</link>
		<comments>http://bililite.nfshost.com/blog/2012/01/12/things-i-learned-about-htaccess-part-2/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 16:29:48 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2200</guid>
		<description><![CDATA[I've learned some things about .htaccess, but mostly I've learned that programming it is deep black magic. The most important thing is "Don't use mod-rewrite unless you absolutely need to". All the fancy stuff is better off done by the PHP (or whatever language you're using) code. Don't do: RewriteEngine On RewriteBase / RewriteRule ^([^/\.]+)/$ [...]]]></description>
			<content:encoded><![CDATA[<p>I've <a href="/blog/2009/07/27/things-i-learned-about-htaccess/">learned some things about .htaccess</a>, but mostly I've learned that programming it is deep black magic. The most important thing is "Don't use mod-rewrite unless you absolutely need to". All the fancy stuff is better off done by the PHP (or whatever language you're using) code.</p>
<p>Don't do:</p>
<pre><code>RewriteEngine On
RewriteBase /
RewriteRule ^([^/\.]+)/$ /index.php?main=$1&#038;part=$1 [QSA,L]
RewriteRule ^([^/\.]+)/([^/\.]+)$ /index.php?main=$1&#038;part=$2 [QSA,L]</code></pre>
<p>to map your site to your index page; do:</p>
<pre><code><a href="https://httpd.apache.org/docs/2.3/mod/mod_dir.html#fallbackresource">FallbackResource</a> /index.php</code></pre>
<p>And in <code>index.php</code> parse the <code class="language-php">$_SERVER['REQUEST_URI']</code> to get your page/subpage arguments. Much easier to debug!</p>
<p>And to use <code>FallbackResource</code> you need to be running Apache 2.2, so get a good web host.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/01/12/things-i-learned-about-htaccess-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Moving the Blog Again</title>
		<link>http://bililite.nfshost.com/blog/2012/01/08/moving-the-blog-again/</link>
		<comments>http://bililite.nfshost.com/blog/2012/01/08/moving-the-blog-again/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 04:38:37 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2196</guid>
		<description><![CDATA[I'm working on moving bililite.com to nearlyfreespeech.net; it's cheaper, more reliable and more hacker-friendly than 1&#38;1. I'll write a post with my experiences and reasons later. But for now just the blog has moved to bililite.nfshost.com/blog, with the plan to move the domain registration for bililite.com to nearlyfreespeech when everything is working and then return [...]]]></description>
			<content:encoded><![CDATA[<p>I'm working on moving <a href="http://bililite.com">bililite.com</a> to <a href="http://nearlyfreespeech.net">nearlyfreespeech.net</a>; it's cheaper, more reliable and more hacker-friendly than <a href="http://1and1.com">1&amp;1</a>. I'll write a post with my experiences and reasons later. But for now just the blog has moved to bililite.nfshost.com/blog, with the plan to move the domain registration for bililite.com to nearlyfreespeech when everything is working and then return the blog to bililite.com/blog. Moving it was straightforward, as <a href="/blog/2009/06/05/moving-the-blog/" title="Moving the Blog">I've done it before</a>, but there are some subtleties: nearlyfreespeech runs with <a href="http://www.php.net/manual/en/features.safe-mode.php">safe mode</a> and <a href="http://www.php.net/manual/en/ini.sect.safe-mode.php#ini.safe-mode-gid">safe_mode_gid</a> on, so you have to watch file and directory permissions; SaraPin <a href="http://snarp.dreamwidth.org/124165.html">explains exactly how</a>. WordPress loads with the group <code>me</code> and nearlyfreespeech's webserver runs with the group <code>web</code>, so you need to SSH into the account and change the WordPress directory with <code>chgrp -R * web</code>. The file permisssions should be 664 (writeable to self and group, readable to all) and the directory permissions should be 775 (writeable and executable to self and group, executable to all; under UNIX, getting a file from a directory is "executing" it), but the default WordPress installation does that.</p>
<p>If you don't know what that means, you probably shouldn't be using a d.i.y. host like nearlyfreespeech.</p>
<p>And now it works!</p>
<p>Almost. <a href="http://sexybookmarks.shareaholic.com/">SexyBookmarks</a>'s images aren't loading when I use the new code, so I went back to the old version. It slows the page load down anyway, so I may take it off entirely.</p>
<p>And the rest of the bililite site is still at the old host, and some of the sample code (in /blog/blogfiles) uses that, so for now I have a line in my <code>.htaccess</code> to refer it back: <code>RedirectMatch /blog/blogfiles(.*) http://bililite.com/blog/blogfiles$1</code>.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2012/01/08/moving-the-blog-again/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Awesome</title>
		<link>http://bililite.nfshost.com/blog/2011/12/21/awesome/</link>
		<comments>http://bililite.nfshost.com/blog/2011/12/21/awesome/#comments</comments>
		<pubDate>Thu, 22 Dec 2011 01:49:15 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2192</guid>
		<description><![CDATA[My office manager gave me this as a Christmas present. I have a great job.]]></description>
			<content:encoded><![CDATA[My office manager gave me <a href="http://www.hasbro.com/starwars/en_US/shop/details.cfm?R=A2A5597F-5056-900B-104C-AFD6347F1E11:en_US">this</a> as a Christmas present. I have a great job.]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/12/21/awesome/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

