<?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>Fazal Majid&#039;s low intensity weblog &#187; IT</title>
	<atom:link href="http://majid.info/blog/category/it/feed/" rel="self" type="application/rss+xml" />
	<link>http://majid.info/blog</link>
	<description>Sporadic pontification</description>
	<lastBuildDate>Sat, 24 Mar 2012 16:49:06 +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>Withings smart baby monitor review</title>
		<link>http://majid.info/blog/withings-smartbaby-monitor-review/</link>
		<comments>http://majid.info/blog/withings-smartbaby-monitor-review/#comments</comments>
		<pubDate>Sat, 24 Mar 2012 16:47:21 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Stuff]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242667</guid>
		<description><![CDATA[One of the joys challenges of being a first-time parent is being exposed to a bewildering array of gadgets and equipment required to care for the baby, from baby car seats, strollers and diaper pails to 2-axis rocking robots (thanks Rohit!). &#8230; <a href="http://majid.info/blog/withings-smartbaby-monitor-review/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>One of the <span style="text-decoration: line-through;">joys</span> challenges of being a first-time parent is being exposed to a bewildering array of gadgets and equipment required to care for the baby, from baby car seats, strollers and diaper pails to <a href="http://www.4moms.com/mamaroo">2-axis rocking robots</a> (thanks Rohit!). There is an entire cottage industry of books like <a href="http://www.indiebound.org/book/9781889392400?aff=fazalmajid">Baby Bargains</a> that help you navigate through the confusing and sometimes questionable or outright unnecessary choices.</p>
<p>I have a <a href="http://majid.info/blog/withings-scale/">Withings body weight scale</a> that I really like and I was excited to learn they were going to release a networked <a href="http://www.withings.com/en/babymonitor">video baby monitor</a>. It took a while to get to market in the US, however, so in the interim I purchased a Philips Advent DECT digital baby monitor, which ended up unusable in practice, because its microphone sensitivity is so poor that you can barely hear anything. When the Withings baby monitor finally became available in the US, I immediately ordered it.</p>
<p>Withings is clearly taking design cues from Apple, from the lavishly designed packaging to the glossy white plastic RoundedRect aesthetic and the use of a magnetic clip to attach the baby monitor to the crib. The clip is serviceable, but the magnets are not quite strong enough to hold the unit firmly onto the crib. I would not trust it to keep the monitor from toppling when the baby grows and kicks at the crib.</p>
<p>The wall wart is a generic black model with swappable AC prongs for international markets, and detracts from the overall package, but since the monitor has a micro-USB input, you can always use another adapter like the iPhone&#8217;s. A rechargeable battery is included, with 2 hours&#8217; claimed life, I did not verify that spec.</p>
<p>The initial out of the box experience is good: you connect to the device from your iPhone or iPad using Bluetooth (no messing around with a USB cable as with the Withings scale), enter the WiFi settings in the Withbaby app, and then use WiFi to access the device afterwards. It is as streamlined an experience as you can expect without a keyboard on the unit. There is also an Ethernet jack (it is unclear whether it supports power over Ethernet), but my house was built in 1928 and is not wired upstairs where the baby lives.</p>
<p>Once you enter your credentials into the app, it connects to the monitor and shows you the video and sound. If you put it in the background, you have the option of monitoring audio. Withings will also send you alerts via push notifications if the temperature or humidity is excessive, or if it detects noise or motion. The default settings are way too twitchy, however, and you will find yourself disabling audio notifications as the deluge of alerts is just too much.</p>
<p>The device includes a night light with selectable color, a lullaby player, and the ability to speak to your baby, all controlled through the app. At the front you also have touch controls to turn some of these features on. This is actually a bad idea, as on two occasions I started the lullaby by accident as I was fumbling with it in a dark room, and woke up my baby as a result. Another design flaw is the pulsing blue night light when the unit is rebooting, the Airport Express like amber/green status LED in the back is quite sufficient. Frankly the only one of these features that is useful is the speaker, and the ability to stream from your music collection, such as Dr. Harvey Karp&#8217;s white noise selections would be preferable to the canned lullabies.</p>
<p>The video camera is advertised as having a 3 megapixel sensor. It has a wide-angle lens and you can &#8220;pan&#8221; using the usual iPhone or iPad gestures. The lens is a fixed-focus plastic one, and optical clarity is so-so at best, optimal focus seems to be at 50cm or so. One great feature is the monitor has a normal and night vision mode, similar to the one on some Sony HAD camcorders, with an IR illuminator that provides light for the night vision mode. This means you can watch your baby toss and turn in an otherwise pitch-black room.</p>
<p>You can use the baby monitor from outside your network, but I have not tested that function. Withings allows you up to 15 minutes per day, anything beyond that requires paying them $6 for each 100 minutes. Coming on top of an already expensive device, this seems like a naked money grab from anxious parents.</p>
<p>When the unit works, it is absolutely great: good sound sensitivity and the video feature mostly works as advertised. Unfortunately it frequently does not function, and I find myself performing a hard reboot by removing the battery far more often than I would like. Among the pathologies:</p>
<ul>
<li>Once it falsely reported the unit was closed and thus video inaccessible</li>
<li>Once the camera was in a frozen state, it took a power cycling to get the video moving again.</li>
<li>Yesterday I could not connect at all, no matter how many times I rebooted my Airport Extreme, the monitor and my wife&#8217;s or my iPads. Some detective work using a packet sniffer showed the app was trying to connect to <tt>babyws.withings.net</tt> using HTTP, which is aliased to <tt>s11.withings.net</tt>, and that server was down. Some of the documentation suggests you can use the Bluetooth connection to access the monitor, but I was not able to figure out how to do this.</li>
</ul>
<p>This brings me to a crucial point. The baby monitor is a safety device, and it is utterly unacceptable for its functioning to be dependent on a cloud service, which can and will be a single point of failure. It should use Bonjour or similar discovery methods to work on the LAN, and rely on Withings&#8217; servers only when accessing it from outside the home LAN&#8217;s perimeter. I wonder if Withings&#8217; eagerness to nickel-and-dime users by charging for outside monitoring led to this critical design flaw.</p>
<p>The bottom line is the Withings smart baby monitor is a very frustrating device, with its obvious potential marred by failures of execution. If it worked consistently, it would be a top-notch product worthy of its Apple inspiration and lofty price tag, but the general lack of reliability means I cannot recommend it until the bugs are ironed out. Consider it an alpha release at best.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/withings-smartbaby-monitor-review/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>The HP-15C was reissued at long last!</title>
		<link>http://majid.info/blog/hp-15c-limited-edition/</link>
		<comments>http://majid.info/blog/hp-15c-limited-edition/#comments</comments>
		<pubDate>Sat, 24 Sep 2011 01:45:26 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242618</guid>
		<description><![CDATA[It is strange no one seems to have picked up the news yet, but HP has reissued the legendary HP-15C in a special &#8220;30th anniversary limited edition&#8221;, and it became available for purchase last week. The new HP-15C is not strictly speaking a &#8230; <a href="http://majid.info/blog/hp-15c-limited-edition/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>It is strange no one seems to have picked up the news yet, but HP has reissued the <a href="/blog/30-years-after-the-king-of-calculators-rides-again/">legendary HP-15C</a> in a special &#8220;30th anniversary limited edition&#8221;, and it became <a href="http://www.shopping.hp.com/product/calculator/Scientific/1/storefronts/NW250AA%2523ABA;HHOJSID=3NwVTjNPw13rxShH4SKMzPNvZQRLWJyRxbtxJLyGvQ12Q861lQZh!-1191803681">available for purchase</a> last week.</p>
<p><img class="aligncenter" title="HP-15C Limited Edition" src="http://hpshopping.speedera.net/s7d2.scene7.com/is/image/HPShopping/big_fmt/nw250aa_main.jpg" alt="HP-15C Limited Edition" width="300" height="196" /></p>
<p>The new HP-15C is not strictly speaking a reissue but a replica, as it does not use the original&#8217;s Saturn processor, but instead an emulation thereof running on an ARM CPU. Even emulated, it should be much faster than the original 640 <em>kilohertz</em> processor. I ordered two, and received them today.</p>
<p>As expected, the quality is in line with the current HP-12C, i.e. not as good as the 1980s models in terms of key feel, but still leagues ahead of any competing product. The originals used a special 47-point bonding process to ensure the utmost in rigidity and reliability, I doubt the current model had as much attention paid to detail. It is made in China, obviously, the Corvallis facility is long gone. The slipcover fits very poorly (too tight, and the seams are not trimmed properly) and feels thinner and outright cheap compared to the original. The labels on the keys are accurately positioned, at least, unlike the train wreck that was the HP-12C Platinum. The cheat sheet in the back is a garish black on silver as on the 35S, instead of the original&#8217;s silver on black. It also uses two 3V CR2032 batteries instead of the 3 button cells in  the original.</p>
<p>Speed-wise, the Limited Edition integrates the normal distribution nearly instantly, when that test that took 34 seconds on the original.</p>
<p>In short: not as good as the original, but still an excellent calculator for those who prize ergonomics.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/hp-15c-limited-edition/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A waiter for a server</title>
		<link>http://majid.info/blog/pwait-hack/</link>
		<comments>http://majid.info/blog/pwait-hack/#comments</comments>
		<pubDate>Fri, 13 May 2011 06:52:16 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[solaris]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242526</guid>
		<description><![CDATA[I had to monitor a long-running process on a Solaris server tonight, but didn&#8217;t want to stay glued at a computer monitor. A neat trick: ssh myserver.example.com "pwait 17601"; say "batch done" You would replace 17601 with the process ID &#8230; <a href="http://majid.info/blog/pwait-hack/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I had to monitor a long-running process on a Solaris server tonight, but didn&#8217;t want to stay glued at a computer monitor. A neat trick:</p>
<pre>ssh myserver.example.com "pwait 17601"; say "batch done"</pre>
<p>You would replace <tt>17601</tt> with the process ID of the job you are waiting for, of course. That way, my Mac connects to the server, waits for the job to complete, then gives me an spoken alert when it is done. I can watch a movie, do chores or whatever during that time. I am sure there are equivalent commands to <tt>pwait</tt> for Linux.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/pwait-hack/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Doing my bit for the Internet</title>
		<link>http://majid.info/blog/doing-my-bit-for-the-internet/</link>
		<comments>http://majid.info/blog/doing-my-bit-for-the-internet/#comments</comments>
		<pubDate>Sat, 05 Mar 2011 10:04:21 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Network]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242452</guid>
		<description><![CDATA[My first IPv6 connectivity, courtesy of Hurricane Electric&#8217;s Tunnel Broker. It only took me three years&#8230;]]></description>
			<content:encoded><![CDATA[<p>My first IPv6 connectivity, courtesy of Hurricane Electric&#8217;s <a href="http://tunnelbroker.com/">Tunnel Broker</a>. It only took me three years&#8230;</p>
<p><a rel="attachment wp-att-141242453" href="http://majid.info/blog/doing-my-bit-for-the-internet/screen-shot-2011-03-05-at-01-59-46/"><img class="aligncenter size-medium wp-image-141242453" title="Screen shot 2011-03-05 at 01.59.46" src="http://majid.info/blog/wp-content/uploads/2011/03/Screen-shot-2011-03-05-at-01.59.46-450x500.png" alt="" width="450" height="500" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/doing-my-bit-for-the-internet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Will Adobe ever learn?</title>
		<link>http://majid.info/blog/adobe-shenanigans/</link>
		<comments>http://majid.info/blog/adobe-shenanigans/#comments</comments>
		<pubDate>Tue, 05 Oct 2010 02:42:57 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Soapbox]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242373</guid>
		<description><![CDATA[In a triumph of hope over experience, I recently &#8220;upgraded&#8221; from Adobe CS3 Design Standard to CS5 Design Standard. I hardly ever use Photoshop any more since I started using Aperture and Lightroom (originally a Macromedia product, no matter what &#8230; <a href="http://majid.info/blog/adobe-shenanigans/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In a triumph of hope over experience, I recently &#8220;upgraded&#8221; from Adobe CS3 Design Standard to CS5 Design Standard. I hardly ever use Photoshop any more since I started using Aperture and Lightroom (originally a Macromedia product, no matter what the lame &#8220;Adobe Photoshop Lightroom&#8221; face-saving branding may try to claim), the main driver for the purchase was actually InDesign CS5 and its ePub functions.</p>
<p>Of course, this is Adobe. <a href="http://majid.info/blog/adobe-creative-suite-3-a-mixed-bag/">Previous versions</a> gratuitously included crud like a full Opera install (an older version, insecure, naturally) just to display a splash screen, or a full MySQL install to power Acrobat search. I never install Acrobat, of course, since that bloated and bug-ridden piece of garbage managed to steal the crown for most insecure software from Internet Explorer, no small feat.</p>
<p>Adobe does not want to confuse users with streamlined and efficient software, so they <a href="http://blogs.adobe.com/oobe/2010/05/growl_installation_with_adobe.html">decided to include</a> the mostly useless Growl on-screen notification program to nag you into registering. Increasing bloat and attack surface for malware is not a good idea, nor is interrupting creative people&#8217;s flow with interruptions. Of course, helping clients Get Things Done is a low priority at Adobe, as evidenced by their product choices.</p>
<p>You have to pity the Growl developers, whose reputation will suffer from guilt by association. I dislike interruptions and do not find it useful, but many people do and rave about it. They installed it by choice, not as a sneaky drive-by install for slimy marketing purposes.</p>
<p>Some more annoyances in CS5:</p>
<ul>
<li>The pricing for the suite is more than the sum of its parts: $200 each for Photoshop, InDesign or Illustrator, $700 for Design Standard. I suppose they must think Acrobat and their online tie-ins have a value of $100 (hint: they forgot the negative sign).</li>
<li>Of course, they won&#8217;t let you upgrade individual component applications.</li>
<li>On the plus side, they now have the decency to include Acrobat on a separate CD, so you can discard it immediately and not risk installing it as a side-effect of installing the apps that are actually useful.</li>
<li>The icons were designed by the world&#8217;s laziest and most creatively bankrupt designer, just as with CS3 and CS4</li>
<li>Performance on a high-end <a href="http://macperformanceguide.com/Reviews-MacProWestmere-Photoshop-CoresSlower.html">8-core or 12-core Mac is actually slower</a> than on a lower-end configuration, thanks to <a href="http://macperformanceguide.com/blog/2010/20100910_PhotoshopScalability--photoshop.html">legacy cruft and incompetence</a>.</li>
<li>It is slower to load on my wife&#8217;s MacBook Pro. Each successive version of OS X is faster on the same hardware, Microsoft and Adobe deliver software that gets progressively slower.</li>
</ul>
<p>In other words, unlike Lightroom, CS5 is designed to be endured, not to delight.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/adobe-shenanigans/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Incensed at Mozilla</title>
		<link>http://majid.info/blog/incensed-at-mozilla/</link>
		<comments>http://majid.info/blog/incensed-at-mozilla/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 23:39:39 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Soapbox]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242332</guid>
		<description><![CDATA[One of the greatest features in the Webkit-based browsers (Apple&#8217;s Safari and Google Chrome) is WebSQLdatabase, the ability for a web site to store information in a SQLite database on your browser accessible via JavaScript. This allows web developers to &#8230; <a href="http://majid.info/blog/incensed-at-mozilla/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>One of the greatest features in the Webkit-based browsers (Apple&#8217;s Safari and Google Chrome) is <a href="http://www.w3.org/TR/webdatabase/">WebSQLdatabase</a>, the ability for a web site to store information in a SQLite database on your browser accessible via JavaScript. This allows web developers to build database-enabled applications that run entirely in the browser, without requiring a server. This is very useful for mobile devices, which in the US enjoy flaky network connectivity at best. One very handsome example is the iPad-optimized <a href="http://everytimezone.com/">Every Time Zone</a> webapp.</p>
<p><a href="http://www.sqlite.org/">SQLite</a> is probably the most important open-source project you have never heard of. It is a simple, streamlined and efficient embedded database. Firefox stores its bookmarks in it. Google distributes its database of phishing sites in that format. Sun&#8217;s industrial-strength Solaris operating system stores the list of services it runs on boot in it—if it were to fail, a server would be crippled so that is a pretty strong vote of confidence. Adobe Lightroom and Apple&#8217;s Aperture use it to store their database, as do most Mac applications that use the CoreData framework, and many iPhone apps. In other words, it is robust and proven mission-critical software that is widely yet invisibly deployed.</p>
<p>WebSQLdatabase basically makes the power of SQLite available to web developers trying to build apps that work offline, specially on mobile devices. No good deed goes unpunished, and the Mozilla foundation teamed up with unlikely bedfellow Microsoft to torpedo formal adoption of WebSQLdatabase as a web standard, on <a href="http://hacks.mozilla.org/2010/06/beyond-html5-database-apis-and-the-road-to-indexeddb/">spurious grounds</a>, and pushed an alternate standard called IndexedDB instead. To <a href="http://www.chromium.org/developers/design-documents/indexeddb">quote</a> the Chromium team:</p>
<blockquote><p>Q: Why this over WebSQLDatabase?</p>
<p>A: Microsoft and Mozilla have made it very clear they will not implement SQL in the browser.  If you want to argue this is silly, talk to them, not me.</p></blockquote>
<p>IndexedDB is several steps backwards. Instead of using powerful, expressive and mature SQL technology, it uses a verbose JavaScript B-tree API that is a throwback to the 1960s bad old days of hierarchical databases and ISAM, requires a lot more work from the developer, for no good reason. To add injury to insult, Firefox 4&#8242;s implementation of IndexedDB is actually built on top of SQLite. The end result will be that web developers will need to build a SQL emulation library on top of IndexedDB to restore the SQLite functionality deliberately crippled by IndexedDB. If there is one constant in software engineering, it is that multiple layers add brittleness and impair performance.</p>
<p>Of course, both Mozilla and Microsoft are irrelevant on mobiles, where WebKit has essentially won the day, so why should this matter? Microsoft has always been a hindrance to the development of the web, since they have to protect the Windows API from competition by increasingly capable webapps, but I cannot understand Mozilla&#8217;s attitude, except possibly knee-jerk not-invented-here syndrome and petulance at being upstaged by WebKit. WebSQLdatabase is not perfect—to reach its full potential, it needs and automatic replication and sync facility between the local database and the website&#8217;s own database, but it is light years ahead of IndexedDB in terms of power and productivity.</p>
<p>I am so irritated by Mozilla&#8217;s attitude that after 10 years of using Mozilla-based browsers, I switched today from Firefox to Chrome as my primary browser. Migrating was surprisingly easy. Key functionality like bookmark keywords, AdBlock, FlashBlock, a developer console, and the ability to whitelist domains for cookies, all have equivalents on Chrome. The main regressions are bookmark tags, and Chrome&#8217;s sync options are not yet equivalent to <a href="http://majid.info/blog/mozilla-weave/">Weave</a>&#8216;s. At some point I will need to roll my own password syncing facility (Chrome stores its passwords in the OS X keychain, which is also used by Safari and Camino).</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/incensed-at-mozilla/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>RapidSSL 1 &#8211; GoDaddy 0</title>
		<link>http://majid.info/blog/godaddy-incompetent-morons/</link>
		<comments>http://majid.info/blog/godaddy-incompetent-morons/#comments</comments>
		<pubDate>Fri, 25 Jun 2010 04:32:38 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Network]]></category>
		<category><![CDATA[Soapbox]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242290</guid>
		<description><![CDATA[My new company&#8217;s website uses SSL. I ordered an &#8220;extended validation&#8221; certificate from GoDaddy, instead of my usual CA, RapidSSL/GeoTrust, because GoDaddy&#8217;s EV certificates were cheap. EV certificates are security theater more than anything else, I probably should not have &#8230; <a href="http://majid.info/blog/godaddy-incompetent-morons/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>My new company&#8217;s website uses SSL. I ordered an &#8220;extended validation&#8221; certificate from GoDaddy, instead of my usual CA, <a href="http://www.rapidssl.com/">RapidSSL/GeoTrust</a>, because GoDaddy&#8217;s EV certificates were cheap. EV certificates are security theater more than anything else, I probably should not have bothered.</p>
<p>Immediately after switching from my earlier &#8220;snake oil&#8221; self-signed test certificate to the production certificate, I saw SSL errors on Google Chrome for Mac and Safari for Mac, i.e. the two browsers that use OS X&#8217;s built-in crypto and certificate store. I suppose I should have tested the certificate on another server before  going live, but I trusted GoDaddy (they are my DNS registrars, and  competent, if garish).</p>
<p>Big mistake.</p>
<p><a href="http://majid.info/blog/wp-content/uploads/2010/06/Screen-shot-2010-06-24-at-20.49.14-.png"><img class="aligncenter size-large wp-image-141242291" title="Screen shot 2010-06-24 at 20.49.14" src="http://majid.info/blog/wp-content/uploads/2010/06/Screen-shot-2010-06-24-at-20.49.14--598x620.png" alt="" width="598" height="620" /></a><a href="http://majid.info/blog/wp-content/uploads/2010/06/Screen-shot-2010-06-24-at-20.48.20-.png"><img class="aligncenter size-large wp-image-141242292" title="Screen shot 2010-06-24 at 20.48.20" src="http://majid.info/blog/wp-content/uploads/2010/06/Screen-shot-2010-06-24-at-20.48.20--557x620.png" alt="" width="557" height="620" /></a>I called their tech support hotline, which is incredibly grating because of the verbose phone tree that keeps trying to push add-ons (I guess it is consistent with the monstrosity that is their home page).</p>
<p>After a while, I got a first-level tech. He asked whether I saw the certificate error on Google Chrome for Windows. At that point, I was irate enough to use a four-letter word. Our customers are Android mobile app developers. A significant chunk of them use Macs, and almost none (less than 5%) use IE, so know-nothing &#8220;All the world is IE&#8221; demographics are not exactly applicable.</p>
<p>After about half an hour of getting the run-around and escalating to level 2, with my business partner Michael getting progressively more anxious in the background, the level 1 CSR tells me the level 2 one can&#8217;t reproduce the problem (I reproduced it on three different Macs in two different locations). I gave them an ultimatum: fix it within 10 minutes or I would switch. At this point, the L1 CSR told me he had exhausted all his options, but I could call their &#8220;RA&#8221; department, and offered to switch me. Inevitably, the call transfer failed.</p>
<p>I dialed their SSL number, and in parallel started the certificate application process on RapidSSL. They offered a free competitive upgrade, I tried it, and within 3 minutes I had my fresh new, and functional certificate, valid for 3 years, all for free and in less time than it takes to listen to GoDaddy&#8217;s obnoxious phone tree (all bout &#8220;we pride ourselves in customer service&#8221; and other Orwellian corporate babble).</p>
<p>I then called GoDaddy&#8217;s billing department to get a refund. Surprisingly, the process was very fast and smooth. I guess it is well-trod.</p>
<p>The moral of the story: GoDaddy—bad. RapidSSL—good.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/godaddy-incompetent-morons/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>One month with the iPad</title>
		<link>http://majid.info/blog/one-month-with-the-ipad/</link>
		<comments>http://majid.info/blog/one-month-with-the-ipad/#comments</comments>
		<pubDate>Sun, 16 May 2010 20:03:54 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242153</guid>
		<description><![CDATA[Since I got my iPad six weeks ago, I have only used my MacBook Air once. I am not going to repeat the extensive reviews posted elsewhere, but after over a month of extensive use, give some perspective for those &#8230; <a href="http://majid.info/blog/one-month-with-the-ipad/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Since I got my iPad six weeks ago, I have only used my MacBook Air once.</p>
<p>I am not going to repeat the extensive reviews <a href="http://arstechnica.com/apple/reviews/2010/04/ipad-review.ars">posted elsewhere</a>, but after over a month of extensive use, give some perspective for those who don&#8217;t get the point of the iPad, or other similar devices.</p>
<p>First of all, commentators have focused on entirely the wrong thing: feeds and speeds, missing features like multitasking or Flash, Apple&#8217;s iron fist over app developers. The iPad begins and ends with the user experience, and that means multi-touch and the incredibly long battery life. That&#8217;s why comparisons to stylus-driven devices like the unsuccessful Microsoft Tablet PC miss the point. The amazing battery life, specially on standby (I have never managed to go under 60%, even after three days without charging), means you can use it as a real mobile device and not subconsciously watch the battery meter.</p>
<p>Is it a perfect device? Of course not. Mobile Safari has a hard time with complex and heavy pages like those from my Temboz RSS/Atom feed reader, the screen is too prone to reflections and fingerprints, and Apple&#8217;s use of high-quality materials like aluminium and glass instead of plastic and acrylic makes it heavier to hold than necessary.</p>
<p>As to whether it is a replacement for a laptop, the answer is yes and no. The iPad is the first in an entirely new class of devices, and I think it has the potential to replace desktop and laptop computers as the dominant form of consumer computing. The touch user interface makes for a very engaging user experience, far more than using a mouse and keyboard ever did. To be sure, the input limitations do not make it a very efficient content creation device, but that&#8217;s where opinions diverge.</p>
<p>I use desktop computers for real work (an eight-core Mac Pro with 12G of RAM and a 30&#8243; display at home, a quad-core iMac with a 27&#8243; display at work). A laptop just feels too constricting for extended use. I have the luxury of using proper desktops because I do not travel much for work, and the extent of my mobile use is reading books or browsing the web while commuting by bus. The improvements that most benefit me are in synchronizing my iPad with multiple computers, and offline capability (I got the WiFi model since there is no way I will pay AT&amp;T for their garbage excuse of a network).</p>
<p>Road warriors need a more featured device, even if cramped, and will not be so impressed. I think genuine mobile users are a minority, however. Surveys in the past showed that most laptops are tethered, i.e. users would unplug them from home, take them to work and plug them there, and back. That is why Windows laptop makers introduced monstrosities like Pentium 4 powered laptops with battery lives that barely exceeded the hour. Laptop sales exceeded those of desktops because many people wanted the option of mobility, even if they seldom, if ever, availed themselves of it, and a less obtrusive presence in their homes than the typical beige box with its rat&#8217;s warren of cables. Those people would be better served by a well-designed desktop like the iMac and an iPad for the occasional mobile use.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/one-month-with-the-ipad/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Clueless SaaS providers can leave you with egg on your face</title>
		<link>http://majid.info/blog/clueless-saas-providers/</link>
		<comments>http://majid.info/blog/clueless-saas-providers/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 10:09:43 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Network]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242093</guid>
		<description><![CDATA[While cleaning out my spam folders, I noticed a disturbing trend: a number of the spam were sent to vendor-specific email addresses I had set up to communicate with Parallels, Joyent and Shoeboxed. As a security measure, I do not &#8230; <a href="http://majid.info/blog/clueless-saas-providers/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>While cleaning out my spam folders, I noticed a disturbing trend: a number of the spam were sent to vendor-specific email addresses I had set up to communicate with Parallels, Joyent and Shoeboxed. As a security measure, I do not give my personal email address to vendors, only aliases. The email address I used in the past for Dell was <code>dell@majid.fm</code>, for instance (I now use a different domain). A few years back, I started receiving pornographic spam at that address, which led me to think either Dell had secretly adopted a radically new diversification plan, or that their customer database had been compromised. Needless to say, this did not reflect well on Dell. I canceled that alias and stopped dealing with Dell.</p>
<p>I contacted the support for the three vendors. Joyent got back to me, and said:</p>
<blockquote><p>We have traced this back to a third-party provider that was used to distribute service notifications. We have been in contact with this service provider, and they have determined that subscriber email addresses of their clients were compromised. They have launched their own investigation, which is ongoing, and have also reached out to their local FBI office.</p></blockquote>
<p>After some digging, I found some <a href="http://blog.otherinbox.com/2010/01/data-breach-at-icontact-d%C3%A9j%C3%A0-vu.html">interesting</a> <a href="http://nekkidninjas.com/index.php/2010/02/17/apology-to-our-members-icontact-is-the-r">posts</a>. Some email marketing company called iContact, that I had never heard about before, was the <a href="http://www.icontact.com/blog/index.php?blog=1&amp;p=401&amp;more=1&amp;c=1&amp;tb=1&amp;pb=1">source of the compromise</a>. They claim to be SAS-70 compliant, but of course like most bureaucratic certifications, SAS-70 is mostly security theater that makes sysadmins&#8217; life miserable for no meaningful security benefit (SAS-70 auditors, on the other hand, profit handsomely).</p>
<p>Just another example of how outsourcing critical functions to outside vendors can backfire spectacularly and take down your own reputation in the process.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/clueless-saas-providers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Just enough Weave</title>
		<link>http://majid.info/blog/just-enough-weave/</link>
		<comments>http://majid.info/blog/just-enough-weave/#comments</comments>
		<pubDate>Thu, 10 Sep 2009 00:36:07 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2009/09/09-1.html</guid>
		<description><![CDATA[Note: I am keeping this code around for historical purposes, but it has not worked since Weave 1.0 RC2. I created this because Mozilla&#8217;s public sync servers were initially quite unreliable, but they have remedied the situation and performance problems &#8230; <a href="http://majid.info/blog/just-enough-weave/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="background-color: yellow;">Note: I am keeping this code around for historical purposes, but it has not worked since Weave 1.0 RC2. I created this because Mozilla&#8217;s public sync servers were initially quite unreliable, but they have remedied the situation and performance problems are a thing of the past. I also learned the inner workings of Weave/Firefox Sync in the process, and am satisfied as to the security of the system. Since I no longer use Firefox myself, I do not expect to ever revive this project. Feel free to take it over, otherwise you are best served by using Mozilla&#8217;s cloud.</p>
<p>Like most of my readers, I use multiple computers: my Mac Pro at home, my MacBook Air when on the road, 3 desktop PCs at work, a number of virtual machines, and so on. I have Firefox installed on all of them. The <a href="http://labs.mozilla.com/weave/">Mozilla Weave</a> extension allows me to sync bookmarks, passwords et al between them. Weave encrypts this data before uploading it to the server, but I do not like to rely on third-party web services for mission-critical functions (my Mozilla server was down last Monday, for instance, due to the surge of traffic from people returning to work and performing a full sync against 0.5). Through Weave 0.5, I ran <a href="http://majid.info/blog/?p=334">my own instance</a> of the Mozilla public Weave server version 0.3. Unfortunately, Weave 0.6 requires server version 0.5 and I had to upgrade.</p>
<p>The open-source Weave server is implemented in PHP. It doesn&#8217;t require Apache compiled with mod_dav as early versions did (I prefer to run <a href="http://wiki.nginx.org/Main">nginx</a>), but it is still a fairly gnarly piece of code that is anything but plug-and-play. Somehow I had managed to get version 0.3 running on my home server, but no amount of blundering around got me to a usable state with 0.5. I ended up deciding to implement a minimalist Weave server in Python, as it seemed less painful than continuing to struggle with the Mozilla spaghetti code, which confusingly features multiple pieces of code that appear to do exactly the same thing in three different places. Famous last words&#8230;</p>
<p>Three days of hacking later, I managed to get it working. 200 or so lines of Python code replaced approximately 12,000 lines of PHP. Of course, I am not trying to reproduce an entire public cloud infrastructure like Mozilla&#8217;s, just enough for my own needs, using the &#8220;simplest thing that works&#8221; principle. Interestingly, the Mozilla code includes a vestigial Python reference implementation of a Weave server for testing purposes. It does not seem to have been working for a while, though. I used it as a starting point but ended up rewriting almost everything. Here are the simplifying hypotheses:</p>
<ul>
<li>My weave server is meant for a single user (my wife prefers Safari)</li>
<li>It does not implement authentication, logging or SSL encryption — it is meant to be used behind a nginx (or Apache) reverse proxy that will perform these functions.</li>
<li>It has no configuration file. There are just three variables to set at the top of the source file.</li>
<li>It does not implement the full server protocol, just the parts that are actually used by the extension today.</li>
<li>More controversially, it does not even implement persistence, keeping all data in RAM instead. Python running on Solaris is very reliable, and the expected uptime of the server is likely months on end. If the server fails, the Firefoxes will just have to perform a full sync and reconciliation. Fortunately, that has been much improved in Weave 0.6, so the cost is minimal. This could even be construed as a security feature, since there is no data on disk to be misplaced. It would take catastrophically losing all my browsers simultaneously to risk data loss. Short of California falling into the ocean, that&#8217;s not going to happen, and if it does, I probably have more pressing concerns&#8230;</li>
</ul>
<p>The code could be extended fairly easily to lift these hypotheses, e.g. adding persistence or multiple user support using SQLite, PostgreSQL or MySQL.</p>
<p>Here is the server itself, <a href="http://majid.info/blog/wp-content/uploads/2009/09/weave_server.py">weave_server.py</a>:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/local/bin/python</span>
<span style="color: #483d8b;">&quot;&quot;&quot;
  Based on tools/scripts/weave_server.py from
  http://hg.mozilla.org/labs/weave/
&nbsp;
  do the Simplest Thing That Can Work: just enough to get by with Weave 0.6
  - SSL, authentication and loggin are done by nginx or other reverse proxy
  - no persistence, in case of process failure do a full resync
  - only one user. If you need more, create multiple instances on different
    ports and use rewrite rules to route traffic to the right one
&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>, <span style="color: #dc143c;">time</span>, <span style="color: #dc143c;">logging</span>, <span style="color: #dc143c;">socket</span>, <span style="color: #dc143c;">urlparse</span>, <span style="color: #dc143c;">httplib</span>, <span style="color: #dc143c;">pprint</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
  <span style="color: #ff7700;font-weight:bold;">import</span> simplejson <span style="color: #ff7700;font-weight:bold;">as</span> json
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>:
  <span style="color: #ff7700;font-weight:bold;">import</span> json
<span style="color: #ff7700;font-weight:bold;">import</span> wsgiref.<span style="color: black;">simple_server</span>
&nbsp;
URL_BASE = <span style="color: #483d8b;">'https://your.server.name/'</span>
<span style="color: #808080; font-style: italic;">#BIND_IP = ''</span>
BIND_IP = <span style="color: #483d8b;">'127.0.0.1'</span>
DEFAULT_PORT = <span style="color: #ff4500;">8000</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> HttpResponse:
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #dc143c;">code</span>, content=<span style="color: #483d8b;">''</span>, content_type=<span style="color: #483d8b;">'text/plain'</span><span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: black;">status</span> = <span style="color: #483d8b;">'%s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #dc143c;">code</span>, <span style="color: #dc143c;">httplib</span>.<span style="color: black;">responses</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">code</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">headers</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Content-type'</span>, content_type<span style="color: black;">&#41;</span>,
                    <span style="color: black;">&#40;</span><span style="color: #483d8b;">'X-Weave-Timestamp'</span>, <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">content</span> = content <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">self</span>.<span style="color: black;">status</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> JsonResponse<span style="color: black;">&#40;</span>value<span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, value, content_type=<span style="color: #483d8b;">'application/json'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> HttpRequest:
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, environ<span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: black;">environ</span> = environ
    content_length = environ.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'CONTENT_LENGTH'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> content_length:
      stream = environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'wsgi.input'</span><span style="color: black;">&#93;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">contents</span> = stream.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>content_length<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">contents</span> = <span style="color: #483d8b;">''</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  <span style="color: #808080; font-style: italic;"># Weave rounds to 2 digits and so must we, otherwise rounding errors will</span>
  <span style="color: #808080; font-style: italic;"># influence the &quot;newer&quot; and &quot;older&quot; modifiers</span>
  <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">round</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> WeaveApp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  <span style="color: #483d8b;">&quot;&quot;&quot;WSGI app for the Weave server&quot;&quot;&quot;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span> = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> url_base<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;XXX should derive this automagically from self.request.environ&quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> URL_BASE
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> ts_col<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, col<span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'timestamps'</span>, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>col<span style="color: black;">&#93;</span> = <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> parse_url<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span>
    command, args = path.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, <span style="color: #ff4500;">4</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span>:<span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> command, args
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> opts_test<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, opts<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'older'</span> <span style="color: #ff7700;font-weight:bold;">in</span> opts:
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">float</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#91;</span><span style="color: #483d8b;">'older'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__ge__</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #483d8b;">'newer'</span> <span style="color: #ff7700;font-weight:bold;">in</span> opts:
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">float</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#91;</span><span style="color: #483d8b;">'newer'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__le__</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff7700;font-weight:bold;">lambda</span> x: <span style="color: #008000;">True</span>
&nbsp;
  <span style="color: #808080; font-style: italic;"># HTTP method handlers</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_PUT<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
    col, key = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">assert</span> command == <span style="color: #483d8b;">'storage'</span>
    val = <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">contents</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> val<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'{'</span>:
      val = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
      val<span style="color: black;">&#91;</span><span style="color: #483d8b;">'modified'</span><span style="color: black;">&#93;</span> = timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      val = json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>val, sort_keys=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span> = val
    <span style="color: #008000;">self</span>.<span style="color: black;">ts_col</span><span style="color: black;">&#40;</span>col<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_POST<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
      status = <span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span>
      <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
        command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
        col = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        vals = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">contents</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> vals:
          val<span style="color: black;">&#91;</span><span style="color: #483d8b;">'modified'</span><span style="color: black;">&#93;</span> = timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
          <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>val<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id'</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span> = json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">ts_col</span><span style="color: black;">&#40;</span>col<span style="color: black;">&#41;</span>
        status = <span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>
    <span style="color: #ff7700;font-weight:bold;">finally</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span>status<span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_DELETE<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">assert</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>
    response = HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">endswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/storage/0'</span><span style="color: black;">&#41;</span>:
      <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">clear</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
      command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
      col, key = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> key:
        opts = <span style="color: #dc143c;">urlparse</span>.<span style="color: black;">parse_qs</span><span style="color: black;">&#40;</span>environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'QUERY_STRING'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">test</span> = <span style="color: #008000;">self</span>.<span style="color: black;">opts_test</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#41;</span>
        col = <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> key <span style="color: #ff7700;font-weight:bold;">in</span> col.<span style="color: black;">keys</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
          <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">test</span><span style="color: black;">&#40;</span>json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>col<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'modified'</span>, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
            <span style="color: #dc143c;">logging</span>.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'DELETE %s key %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>path, key<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">del</span> col<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span>
      <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">try</span>:
          <span style="color: #ff7700;font-weight:bold;">del</span> <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span><span style="color: black;">&#91;</span>col<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">KeyError</span>:
          <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> response
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_GET<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
      command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">handle_storage</span><span style="color: black;">&#40;</span>command, args, path, environ<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1/'</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, <span style="color: #008000;">self</span>.<span style="color: black;">url_base</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/state'</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, <span style="color: #dc143c;">pprint</span>.<span style="color: black;">pformat</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> handle_storage<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, command, args, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> command == <span style="color: #483d8b;">'info'</span>:
      <span style="color: #ff7700;font-weight:bold;">if</span> args == <span style="color: #483d8b;">'collections'</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> JsonResponse<span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'timestamps'</span>, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> command == <span style="color: #483d8b;">'storage'</span>:
      <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'/'</span> <span style="color: #ff7700;font-weight:bold;">in</span> args:
        col, key = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">else</span>:
        col, key = args, <span style="color: #008000;">None</span>
      <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> key: <span style="color: #808080; font-style: italic;"># list output requested</span>
          opts = <span style="color: #dc143c;">urlparse</span>.<span style="color: black;">parse_qs</span><span style="color: black;">&#40;</span>environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'QUERY_STRING'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
          <span style="color: #dc143c;">test</span> = <span style="color: #008000;">self</span>.<span style="color: black;">opts_test</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#41;</span>
          result = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
          <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>.<span style="color: black;">itervalues</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
            val = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">test</span><span style="color: black;">&#40;</span>val.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'modified'</span>, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
              result.<span style="color: black;">append</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
          result = <span style="color: #008000;">sorted</span><span style="color: black;">&#40;</span>result,
                          key=<span style="color: #ff7700;font-weight:bold;">lambda</span> val: <span style="color: black;">&#40;</span>val.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'sortindex'</span><span style="color: black;">&#41;</span>,
                                           val.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'modified'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>,
                          reverse=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
          <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'limit'</span> <span style="color: #ff7700;font-weight:bold;">in</span> opts:
            result = result<span style="color: black;">&#91;</span>:<span style="color: #008000;">int</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#91;</span><span style="color: #483d8b;">'limit'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
          <span style="color: #dc143c;">logging</span>.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'result set len = %d'</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
          <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'application/newlines'</span> <span style="color: #ff7700;font-weight:bold;">in</span> environ.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'HTTP_ACCEPT'</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>:
            value = <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> result<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, value,
                                content_type=<span style="color: #483d8b;">'application/text'</span><span style="color: black;">&#41;</span>
          <span style="color: #ff7700;font-weight:bold;">else</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> JsonResponse<span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
          <span style="color: #ff7700;font-weight:bold;">return</span> JsonResponse<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">KeyError</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> key: <span style="color: #ff7700;font-weight:bold;">raise</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span>, <span style="color: #483d8b;">'&quot;record not found&quot;'</span>,
                            content_type=<span style="color: #483d8b;">'application/json'</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> __process_handler<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, handler<span style="color: black;">&#41;</span>:
    path = <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">environ</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'PATH_INFO'</span><span style="color: black;">&#93;</span>
    response = handler<span style="color: black;">&#40;</span>path, <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">environ</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> response
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, environ, start_response<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Main WSGI application method&quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">request</span> = HttpRequest<span style="color: black;">&#40;</span>environ<span style="color: black;">&#41;</span>
    method = <span style="color: #483d8b;">'_handle_%s'</span> <span style="color: #66cc66;">%</span> environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'REQUEST_METHOD'</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># See if we have a method called 'handle_METHOD', where</span>
    <span style="color: #808080; font-style: italic;"># METHOD is the name of the HTTP method to call.  If we do,</span>
    <span style="color: #808080; font-style: italic;"># then call it.</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">hasattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, method<span style="color: black;">&#41;</span>:
      handler = <span style="color: #008000;">getattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, method<span style="color: black;">&#41;</span>
      response = <span style="color: #008000;">self</span>.__process_handler<span style="color: black;">&#40;</span>handler<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      response = HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">METHOD_NOT_ALLOWED</span>,
                              <span style="color: #483d8b;">'Method %s is not yet implemented.'</span> <span style="color: #66cc66;">%</span> method<span style="color: black;">&#41;</span>
&nbsp;
    start_response<span style="color: black;">&#40;</span>response.<span style="color: black;">status</span>, response.<span style="color: black;">headers</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#91;</span>response.<span style="color: black;">content</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> NoLogging<span style="color: black;">&#40;</span>wsgiref.<span style="color: black;">simple_server</span>.<span style="color: black;">WSGIRequestHandler</span><span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">def</span> log_request<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #66cc66;">*</span>args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
  <span style="color: #dc143c;">socket</span>.<span style="color: black;">setdefaulttimeout</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">300</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'-v'</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">DEBUG</span><span style="color: black;">&#41;</span>
    handler_class = wsgiref.<span style="color: black;">simple_server</span>.<span style="color: black;">WSGIRequestHandler</span>
  <span style="color: #ff7700;font-weight:bold;">else</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">ERROR</span><span style="color: black;">&#41;</span>
    handler_class = NoLogging
  <span style="color: #dc143c;">logging</span>.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Serving on port %d.'</span> <span style="color: #66cc66;">%</span> DEFAULT_PORT<span style="color: black;">&#41;</span>
  app = WeaveApp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  httpd = wsgiref.<span style="color: black;">simple_server</span>.<span style="color: black;">make_server</span><span style="color: black;">&#40;</span>BIND_IP, DEFAULT_PORT, app,
                                            handler_class=handler_class<span style="color: black;">&#41;</span>
  httpd.<span style="color: black;">serve_forever</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Here is the relevant fragment from my nginx configuration file:</p>

<div class="wp_syntax"><div class="code"><pre class="sh" style="font-family:monospace;"># Mozilla Weave
location /0.5 {
  auth_basic            &quot;Weave&quot;;
  auth_basic_user_file  /home/majid/web/conf/htpasswd.weave;
  proxy_pass            http://localhost:8000;
  proxy_set_header      Host $http_host;
}
location /1.0 {
  auth_basic            &quot;Weave&quot;;
  auth_basic_user_file  /home/majid/web/conf/htpasswd.weave;
  proxy_pass            http://localhost:8000;
  proxy_set_header      Host $http_host;
}
location /1/ {
  auth_basic            &quot;Weave&quot;;
  auth_basic_user_file  /home/majid/web/conf/htpasswd.weave;
  proxy_pass            http://localhost:8000;
  proxy_set_header      Host $http_host;
}</pre></div></div>

<p>This code is hereby released into the public domain. You are welcome to use it as you wish. Just keep in mind that since it is reverse-engineered, it may well break with future releases of the Weave extension, or if Mozilla changes the server protocol.</p>
<p>Update (2009-10-03):</p>
<p>I implemented some minor changes for compatibility with Weave 0.7. The diff with the previous version is as follows:</p>
<pre>--- weave_server.py~	Thu Sep  3 17:46:44 2009
+++ weave_server.py	Sat Oct  3 02:59:19 2009
@@ -65,8 +65,7 @@
     command, args = path.split('/', 4)[3:]
     return command, args

-  def opts_test(self, environ):
-    opts = urlparse.parse_qs(environ['QUERY_STRING'])
+  def opts_test(self, opts):
     if 'older' in opts:
       return float(opts['older'][0]).__ge__
     elif 'newer' in opts:
@@ -92,7 +91,7 @@
   def _handle_POST(self, path, environ):
     try:
       status = httplib.NOT_FOUND
-      if path.startswith('/0.5/') and path.endswith('/'):
+      if path.startswith('/0.5/'):
         command, args = self.parse_url(path)
         col = args.split('/')[0]
         vals = json.loads(self.request.contents)
@@ -113,7 +112,8 @@
       command, args = self.parse_url(path)
       col, key = args.split('/', 1)
       if not key:
-        test = self.opts_test(environ)
+        opts = urlparse.parse_qs(environ['QUERY_STRING'])
+        test = self.opts_test(opts)
         col = self.collections.setdefault(col, {})
         for key in col.keys():
           if test(json.loads(col[key]).get('modified', 0)):
@@ -142,10 +142,14 @@
       if args == 'collections':
         return JsonResponse(json.dumps(self.collections.get('timestamps', {})))
     if command == 'storage':
-      col, key = args.split('/')
+      if '/' in args:
+        col, key = args.split('/')
+      else:
+        col, key = args, None
       try:
         if not key: # list output requested
-          test = self.opts_test(environ)
+          opts = urlparse.parse_qs(environ['QUERY_STRING'])
+          test = self.opts_test(opts)
           result = []
           for val in self.collections.setdefault(col, {}).itervalues():
             val = json.loads(val)
@@ -155,6 +159,8 @@
                           key=lambda val: (val.get('sortindex'),
                                            val.get('modified')),
                           reverse=True)
+          if 'limit' in opts:
+            result = result[:int(opts['limit'][0])]
           logging.info('result set len = %d' % len(result))
           if 'application/newlines' in environ.get('HTTP_ACCEPT', ''):
             value = '\n'.join(json.dumps(val) for val in result)</pre>
<p>Update (2009-11-17):</p>
<p>Weave 1.0b1 uses 1.0 as the protocol version string instead of 0.5 but is otherwise unchanged. I updated the script and <tt>nginx</tt> configuration accordingly.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/just-enough-weave/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

