<?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>Aaron Toponce &#187; Scripting</title>
	<atom:link href="http://pthree.org/category/scripting/feed/" rel="self" type="application/rss+xml" />
	<link>http://pthree.org</link>
	<description>Linux.  GNU.  Freedom.</description>
	<lastBuildDate>Sun, 05 Feb 2012 14:33:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4-alpha</generator>
		<item>
		<title>A Note About Removing Files With find(1)</title>
		<link>http://pthree.org/2011/12/20/a-note-about-removing-files-with-find1/</link>
		<comments>http://pthree.org/2011/12/20/a-note-about-removing-files-with-find1/#comments</comments>
		<pubDate>Wed, 21 Dec 2011 01:01:55 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2149</guid>
		<description><![CDATA[I&#8217;ve seen on the internet, and elsewhere, that when there are too many arguments for rm(1) to handle, that the following command will suffice: % find /path -exec rm -rf {} \; While certainly functional, it&#8217;s not optimal. If there are thousands of files (as is often the case at my job), this command is [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve seen on the internet, and elsewhere, that when there are too many arguments for rm(1) to handle, that the following command will suffice:</p>
<pre>% find /path -exec rm -rf {} \;</pre>
<p>While certainly functional, it&#8217;s not optimal. If there are thousands of files (as is often the case at my job), this command is slow, slow, slow. The reason being are all the excessive fork() and exec() calls for each pass with rm(1). Instead, you could optimize find(1) by using &#8220;-delete&#8221;:</p>
<pre>% find /path -delete</pre>
<p>This is much more optimal, but it has one VERY nasty side effect. If you place &#8220;-delete&#8221; in the wrong spot in your find(1) command, you could delete all the files listed under &#8220;/path&#8221; before processing the necessary logic. From the find(1) manual:</p>
<blockquote><p><strong>Warnings:</strong> Don&#8217;t forget that the find command line is evaluated as an expression, so putting -delete first will make find try to delete everything below the starting points you specified. When testing a find line that you later intend to use with -delete, you should explicitly specify -depth in order to avoid later surprises. Because -delete implies -depth, you cannot usefully use -prune and -delete together.</p></blockquote>
<p>One nice benefit of &#8220;-delete&#8221;, however, is the proper handling of NUL characters in your filename, such as spaces, tabs or the newline character. Thankfully, there is another option, which is not only supported in GNU/Linux, but also in FreeBSD (and perhaps others):</p>
<pre>% find /path -print0 | xargs -0 rm -rf</pre>
<p>This avoids the excessive fork() and exec() system calls from our first command, and doesn&#8217;t have the nasty side effects of &#8220;-delete&#8221;. Further, because of &#8220;-print0&#8243; as a find(1) argument, and &#8220;-0&#8243; with xargs(1), we can handle files properly with NUL characters. Time the three commands above, and you&#8217;ll see that the last is most optimal.</p>
<p>We can squeeze some extra juice out of the command, though. All we need to do is cd(1) to the directory we wish to operate our find(1) command on:</p>
<pre>% cd /path &#038;&#038; find . -print0 | xargs -0 rm -rf</pre>
<p>Working with removing millions of files (yes, I do actually remove that many, often), I have found this latest find(1) command to be the most optimized in terms of sheer speed. It moves. You may find the same results as I.</p>
<p>FYI.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/12/20/a-note-about-removing-files-with-find1/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Steganography</title>
		<link>http://pthree.org/2011/12/15/steganography/</link>
		<comments>http://pthree.org/2011/12/15/steganography/#comments</comments>
		<pubDate>Thu, 15 Dec 2011 23:58:05 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2134</guid>
		<description><![CDATA[I have been familiar with steganography for a number of years. In fact, back when I was in middle school, I developed a fascination for encryption, and hiding messages, mostly so I could pass notes back and forth to classmates during class. It wasn&#8217;t long before I found &#8220;invisible ink&#8221;, which is a form of [...]]]></description>
			<content:encoded><![CDATA[<p>I have been familiar with steganography for a number of years. In fact, back when I was in middle school, I developed a fascination for encryption, and hiding messages, mostly so I could pass notes back and forth to classmates during class. It wasn&#8217;t long before I found &#8220;invisible ink&#8221;, which is a form of steganography. While I&#8217;m certainly no expert on the subject, I decided to have a bit of fun with my email.</p>
<p>I placed a hidden message in my email headers for a bit (I&#8217;ve since stopped, for various reasons). I considered it an &#8220;Easter Egg&#8221; of sorts, waiting for someone to notice. Here is what I placed in the headers:</p>
<pre>Crypto-Challenge: iVBORw0KGgoAAAANSUhEUgAAADwAAAA8AQMAAAAAMksxAAAABlBMVEX
       ///8AAABVwtN+AAAAtklEQVQokXXQMQ6CMBQG4McCiykXMPEKsuEiV2nCBdoLWNgN
       Xqld7MYZeoQSFgbisyZWER//9E1//vcAYhQOvkB0X3AQotBAoZ5lV9hpA63ZxCP5Q
       SiE5N28QpjRxT0rhFzi7BWUlx3LQvMH+x1jx7IEAoer8hVqR4Crhp1fhf9QI976tF
       oAIGlYWTkCCr3I7yTCpTkaDQTqWUhjtFtCNizNgEbbZxMFDnIYrHUEwjPRnywQiHk
       CI/3gDHrryF4AAAAASUVORK5CYII=
Crypto-Hint: image/png</pre>
<p>Quickly, you should identify the &#8220;Crypto-Challenge&#8221; header as base 64-encoded string. The hint says it&#8217;s an image, of type PNG. So, the following Python code should do the trick:</p>
<div class="codecolorer-container python twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;"># assuming the 'img_string' variable is the actual base64 string above</span><br />
f <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'crypto-image.png'</span><span style="color: #66cc66;">,</span><span style="color: #483d8b;">'w'</span><span style="color: black;">&#41;</span><br />
f.<span style="color: black;">write</span><span style="color: black;">&#40;</span>img_string.<span style="color: black;">decode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'base64'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
f.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></td></tr></tbody></table></div>
<p>Running that code with the base 64-encoded string above gives the following image:</p>
<p><img src="http://pthree.org/wp-content/uploads/2011/12/crypto-image.png" alt="" title="crypto-image" width="60" height="60" class="aligncenter size-full wp-image-2135" /></p>
<p>Scanning the QR code reveals the text &#8220;42&#8243;, of which most geeks should recognize as &#8220;The Answer to the Ultimate Question of Life, the Universe, and Everything&#8221;.</p>
<p>Of course, steganography isn&#8217;t encryption. It&#8217;s security by obscurity, which isn&#8217;t security, where a message is hidden by obscuring it through some means. Wikipedia has a great article on it at <a href="https://en.wikipedia.org/wiki/Steganography">https://en.wikipedia.org/wiki/Steganography</a>.</p>
<p>What can you do with hidden messages in images (or vice versa, as in the case with my email &#8220;Easter Egg&#8221;)? Well, for one, you can easily get around email attachment restrictions. For example, take a ZIP archive. Perhaps some organization blocks email with .zip attachments. Why not convert the archive to base 64, then convert the result to an image. You might end up with something like this:</p>
<div class="codecolorer-container python twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> PIL <span style="color: #ff7700;font-weight:bold;">import</span> Image<br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">base64</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">math</span><br />
<br />
<span style="color: #808080; font-style: italic;"># function to return max image size</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> get_size<span style="color: black;">&#40;</span>size<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; width <span style="color: #66cc66;">=</span> height <span style="color: #66cc66;">=</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">math</span>.<span style="color: black;">ceil</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">math</span>.<span style="color: black;">sqrt</span><span style="color: black;">&#40;</span>size/<span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; diff <span style="color: #66cc66;">=</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>width * height<span style="color: black;">&#41;</span> * <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span> - size<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#40;</span>width<span style="color: #66cc66;">,</span> height<span style="color: #66cc66;">,</span> diff<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># open our binary non-image file</span><br />
f <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'archive.zip'</span><span style="color: #66cc66;">,</span><span style="color: #483d8b;">'rb'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># convert the binary to a base64-encoded string</span><br />
enc_bytes <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">base64</span>.<span style="color: black;">b64encode</span><span style="color: black;">&#40;</span>f.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
f.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># get file size to hold data (square)</span><br />
<span style="color: black;">&#40;</span>w<span style="color: #66cc66;">,</span>h<span style="color: #66cc66;">,</span>d<span style="color: black;">&#41;</span> <span style="color: #66cc66;">=</span> get_size<span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>enc_bytes<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># pad with zeros, if necessary</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> d <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>d<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; enc_bytes +<span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\0</span>'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># create our final image</span><br />
img <span style="color: #66cc66;">=</span> Image.<span style="color: black;">frombuffer</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'RGB'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#40;</span>w<span style="color: #66cc66;">,</span>h<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span>enc_bytes<span style="color: #66cc66;">,</span><span style="color: #483d8b;">'raw'</span><span style="color: #66cc66;">,</span><span style="color: #483d8b;">'RGB'</span><span style="color: #66cc66;">,</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br />
img.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'image.png'</span><span style="color: black;">&#41;</span></div></td></tr></tbody></table></div>
<p>Your final image might end up like:</p>
<p><img src="http://pthree.org/wp-content/uploads/2011/12/image.png" alt="" title="image" width="427" height="427" class="aligncenter size-full wp-image-2138" /></p>
<p>In our case, I just created a file from /dev/urandom, zipped it up, and converted to an image. Thus, the reason the data in the image appears so random. More structured files will show actual structure in the final image. Also, notice the string of black at the bottom as a result of our padded zeros to adjust for a square image, without losing data.</p>
<p>Of course, to get back to the archive, you just need to reverse the process of converting the image to a base64 string, then back to the original file. Now, I&#8217;m no Python expert, and I realize there is much more to add to the code, such as &#8220;try/except&#8221; blocks for testing files, writable directories, etc. The point of the code was just to demonstrate an overall algorithm.</p>
<p>Hopefully, this is of some interest to some of my readers. I&#8217;m open to code improvements. Thanks to <a href="https://diablohorn.wordpress.com/2010/12/04/whats-in-a-picture">https://diablohorn.wordpress.com/2010/12/04/whats-in-a-picture</a> for use of the code above.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/12/15/steganography/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Rejected And Legal</title>
		<link>http://pthree.org/2011/11/10/rejected-and-legal/</link>
		<comments>http://pthree.org/2011/11/10/rejected-and-legal/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 15:32:50 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2096</guid>
		<description><![CDATA[Some of the roles I fill at work are: storage architecture, cloud engineering, system administration and backend coding. When approaching my tasks head on, it&#8217;s always important to me that standards are adhered to. From PEP coding style to adhering to an RFC for mail server. Unfortunately, I think I&#8217;m a dying breed, or something, [...]]]></description>
			<content:encoded><![CDATA[<p>Some of the roles I fill at work are: storage architecture, cloud engineering, system administration and backend coding. When approaching my tasks head on, it&#8217;s always important to me that standards are adhered to. From PEP coding style to adhering to an RFC for mail server. Unfortunately, I think I&#8217;m a dying breed, or something, because more and more, I&#8217;m seeing standards ignored.</p>
<p>Case in point: I just filled out a form for a survey to &#8220;enter to win a $1000 shopping spree). You know, the crap that you constantly get bombarded with at the checkout stand when the cashier gives you your receipt. I always ignore them, but then thought to myself &#8220;I&#8217;ll never win if I don&#8217;t at least try&#8221;, so I gave my first survey a go. At the end of the survey, it asked for my email address. I figure they&#8217;ll sell it for marketing purposes, and I have a Google Mail address, so I&#8217;m not really that worried about the SPAM (their SPAM filters are amazing). But, I would like to track who they are selling my address to. So, I gave them the following address:</p>
<pre>aaron.toponce+survey-provider@gmail.com</pre>
<p>To which, I received an error that the email address is not a valid address. <b>AHEM!</b> Yes it is, and it&#8217;s this lack of support for standards that I&#8217;m talking about. My email address was rejected, yet it&#8217;s perfectly legal according to RFC 5322. You see, according to that RFC, I get the following flexibilities with my email address:</p>
<ul>
<li>ASCII upper and lower case letters (a-z &#038; A-Z).</li>
<li>ASCII digits 0-9</li>
<li>ASCII characters !#$%&#038;&#8217;*+-/=?^_`{|}~</li>
<li>ASCII dot (.) so long as the local part of the address does not contain the dot consecutively, and it does not start with a dot.</li>
<li>ASCII characters &#8221; &#8221; (space) and &#8220;(),:;<>@[\] are allowed with certain restrictions.</li>
</ul>
<p>So, I could have the following email addresses, all of which are perfectly legit according to the RFC:</p>
<ul>
<li>&#8220;[Aaron Toponce]&#8220;@gmail.com</li>
<li>a&#038;t@gmail.com</li>
<li>aaron.toponce+business@gmail.com</li>
<li>aaron&#8217;s-travel-agency@example.travel</li>
<li>{atoponce}@gmail.com</li>
</ul>
<p>Yet, these will get ejected outright in most web forms I&#8217;ve come across. Specifically interesting is the .travel TLD. I&#8217;ve had web forms enforce TLDs that are less than 4 characters, which is absolutely absurd for the .travel and .museum TLDs. I&#8217;m guessing one of two things is happening with these web forms:</p>
<ol>
<li>The developer used the regular expression [A-Za-z0-9_\-\.]+@[A-Za-z0-9\-\.]+ for validating addresses.</li>
<li>There is absolute denial for the use of &#8220;plus-addressing&#8221; as a DEA.</li>
</ol>
<p>I&#8217;m guessing the first is more likely the scenario than the second. Regardless, Of course, when we&#8217;re talking about the rules of RFC 5322, we&#8217;re no longer talking about regular expression syntax. We&#8217;re talking about grammar. If your page is designed in PHP, Python, CGI, or whatever, you should use a real parser for parsing the email address, rather than reinventing the wheel yourself. What&#8217;s unfortunate, is this disease of not properly parsing valid email addresses is found in some big companies and sites too, not just the little guys.</p>
<p>Now, Google COULD provide true DEAs, such as Yahoo! Plus does with their subscribers, However, I should be able to create an DEA with an already existing email address, rather than creating completely new ones, because people refuse to conform to the standards. So Google, if you&#8217;re reading (I know you are), you may want to consider proper DEAs, seeing as though &#8220;plus addressing&#8221; isn&#8217;t working, and it is important to some.</p>
<p><a href="http://www.bortzmeyer.org/arreter-d-interdire-des-adresses-legales.html">Stéphane Bortzmeyer has already blogged about this</a>, and he uses the <a href="http://identi.ca/tag/ral">#ral hashtag</a> on Identica and Twitter to vent his frustrations, which stands for &#8220;Refus d&#8217;Adresses Légales&#8221; or &#8220;Rejection of Legal Address&#8221;. Well, I&#8217;ve determined that I will be doing the same, although I&#8217;ll bacronym the hashtag to &#8220;Rejected And Legal&#8221;, along with the url to the site that refuses to adhere to the RFC.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/11/10/rejected-and-legal/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Use wget(1) To Expand Shortened URLs</title>
		<link>http://pthree.org/2011/10/18/use-wget1-to-expand-shortened-urls/</link>
		<comments>http://pthree.org/2011/10/18/use-wget1-to-expand-shortened-urls/#comments</comments>
		<pubDate>Tue, 18 Oct 2011 14:22:04 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2083</guid>
		<description><![CDATA[I&#8217;m a fan of all things microblogging, but let&#8217;s face it: until URLs become part of the XML, and not part of your character count (which is ridiculous anyway), shortened URLs are going to be a way of life. Unfortunately, those shortened URLs can be problematic. They could host malicious scripts and/or software that could [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m a fan of all things microblogging, but let&#8217;s face it: until URLs become part of the XML, and not part of your character count (which is ridiculous anyway), shortened URLs are going to be a way of life. Unfortunately, those shortened URLs can be problematic. They could host malicious scripts and/or software that could infect your browser and/or system. They could lead you to an inappropriate site, or just something you don&#8217;t want to see. And because these URLs are a part of our microblogging lives, they&#8217;ve also become a part of our email, SMS, IM, IRC, lives as well as other online aspects.</p>
<p>So, the question is: do you trust the short URL? Well, I&#8217;ve generally gotten into the habit of asking people to expand the shortened url for me if on IRC, email or IM, and it&#8217;s worked just fine. But, I got curious if there was a way to do it automagically, and thankfully, you can use wget(1) for this very purpose. Here&#8217;s a &#8220;quick and dirty&#8221; approach to expanding shortened URLs (emphasis mine):</p>
<pre>$ wget --max-redirect=0 -O - http://t.co/LDWqmtDM
--2011-10-18 07:59:53--  http://t.co/LDWqmtDM
Resolving t.co (t.co)... 199.59.148.12
Connecting to t.co (t.co)|199.59.148.12|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
<b>Location: http://is.gd/jAdSZ3 [following]</b>
0 redirections exceeded.</pre>
<p>So, in this case &#8220;http://t.co/LDWqmtDM&#8221; is pointing to &#8220;http://is.gd/jAdSZ3&#8243;, another shortened URL (thank you Twitter for shortening what is already short (other services are doing this too, and it&#8217;s annoying- I&#8217;m looking at you StatusNet)). So, let&#8217;s increase our &#8220;&#8211;max-redirect&#8221; (again, emphasis mine):</p>
<pre>$ wget --max-redirect=1 -O - http://t.co/LDWqmtDM
--2011-10-18 08:02:12--  http://t.co/LDWqmtDM
Resolving t.co (t.co)... 199.59.148.12
Connecting to t.co (t.co)|199.59.148.12|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
<b>Location: http://is.gd/jAdSZ3 [following]</b>
--2011-10-18 08:02:13--  http://is.gd/jAdSZ3
Resolving is.gd (is.gd)... 89.200.143.50
Connecting to is.gd (is.gd)|89.200.143.50|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
<b>Location: https://wiki.ubuntu.com/UbuntuOpenWeek [following]</b>
1 redirections exceeded.</pre>
<p>So, in this case, the link finally points to <a href="https://wiki.ubuntu.com/UbuntuOpenWeek">https://wiki.ubuntu.com/UbuntuOpenWeek</a>. I&#8217;m familiar enough with the Ubuntu Wiki, that I know I should be safe visiting the initial shortened URL. If you want to add this to a script or shell function, then you can get a bit more fancy:</p>
<pre>$ expandurl() { wget -O - --max-redirect=$2 $1 2>&#038;1 | grep ^Location; }
$ expandurl http://t.co/LDWqmtDM 1
Location: http://is.gd/jAdSZ3 [following]
Location: https://wiki.ubuntu.com/UbuntuOpenWeek [following]</pre>
<p>In this case, our &#8220;expandurl()&#8221; function takes two arguments: the first being the URL you wish to expand, and the second being the max redirects. You&#8217;ll notice further that I added &#8220;-0 -&#8221; to print to STDERR. This is just in case you give too many redirects, it will print the content of the page&#8217;s HTML to the terminal, rather than saving to a file. Because you&#8217;re grepping for &#8220;^Location&#8221;, and sending the HTML to your terminal anyway, technically you could get rid of the &#8220;&#8211;max-redirects&#8221; altogether. But, keeping it in play does seriously increase the time it takes to get the locations. Whatever works for you.</p>
<p><strong>UPDATE (Oct 18, 2011):</strong> After some comments have come in on the post, and some discussion on IRC, there is a better way to handle this. According to the wget(1) manpage, &#8220;-S&#8221; or &#8220;&#8211;server-response&#8221; will print the headers and responses printed by the FTP/HTTP servers. So, here&#8217;s the updated function that you might find to be less chatty, and faster to execute as well:</p>
<pre>$ expandurl() { wget -S $1 2>&#038;1 | grep ^Location; }
$ expandurl http://t.co/LDWqmtDM
Location: http://is.gd/jAdSZ3 [following]
Location: https://wiki.ubuntu.com/UbuntuOpenWeek [following]</pre>
<p>Perfect.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/10/18/use-wget1-to-expand-shortened-urls/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>How To Properly Create And Burn CD/DVD ISO Images From The Command Line</title>
		<link>http://pthree.org/2011/09/26/how-to-properly-create-and-burn-cddvd-iso-images-from-the-command-line/</link>
		<comments>http://pthree.org/2011/09/26/how-to-properly-create-and-burn-cddvd-iso-images-from-the-command-line/#comments</comments>
		<pubDate>Tue, 27 Sep 2011 02:59:31 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Debian]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2067</guid>
		<description><![CDATA[Too often, I see the recommendation on forums, IRC, and elsewhere across the internet to use improper tools for creating ISO images. For example, consider the following two commands, both of which are not the correct way to build a CD/DVD image: $ dd if=/dev/scd0 of=cdimage.iso # NO! Or worse yet: $ cat /dev/scd0 > [...]]]></description>
			<content:encoded><![CDATA[<p>Too often, I see the recommendation on forums, IRC, and elsewhere across the internet to use improper tools for creating ISO images. For example, consider the following two commands, both of which are not the correct way to build a CD/DVD image:</p>
<pre>$ dd if=/dev/scd0 of=cdimage.iso # NO!</pre>
<p>Or worse yet:</p>
<pre>$ cat /dev/scd0 > cdimage.iso # NO!</pre>
<p>As you are probably thinking, the problem with the two commands above, is that they provide no error checking while building the image. So, in order to make sure you have all the bits, you need to use another tool, such as using the MD5 hashing algorithm:</p>
<pre>$ md5sum /dev/scd0 cdimage.iso
d642d524dd2187834a418710001bbf82  /dev/cdrom
d642d524dd2187834a418710001bbf82  cdimage.iso</pre>
<p>Thankfully, the hashes above match. But, what if they didn&#8217;t? Then, you get to redo your dd(1) command (or, shudder, cat(1)) from above, and then rerun md5sum(1) to make sure you got all the bits. Doesn&#8217;t sound like much fun to me. Thankfully, there is a better way, one which handles the checksum while doing the copy.</p>
<p>You want to use readom(1) (&#8220;read optical media&#8221;) from the wodim(1) (&#8220;write optical disk media&#8221;) package. Consider the following command:</p>
<pre>$ readom dev=/dev/scd0 f=cdimage.iso # YES!</pre>
<p>If readom(1) fails to get the bits during the copy, it will let you know that it&#8217;s struggling. If it got all the bits, you know you have them all, because of the error checking during the copy. Sure will save you a lot of time running manual hashes when finished.</p>
<p>Now, what about burning a copy of the ISO image? Surely you use dd(1), yes? Something like:</p>
<pre>$ dd if=cdimage.iso of=/dev/scd0 # NO!</pre>
<p>NO! Instead, use wodim(1) directly:</p>
<pre>$ wodim -v -eject cdimage.iso # YES!</pre>
<p>For the same reasons that you want to use readom(1) for creating ISO images from CD/DVD, you want to use wodim(1) for burning ISO images to CD/DVD. What happens if after using dd(1) to create your CD/DVD, the md5sum(1) hash doesn&#8217;t line up with the image? You didn&#8217;t get all the bits, and created a coaster. Use wodim(1) and should it succeed, you can rest assured that you have all the bits.</p>
<p>So, remember, readom(1) and wodim(1) are the tools you want when creating and/or burning ISO images from the command line. Any other tool, and you&#8217;re likely doing it wrong.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/09/26/how-to-properly-create-and-burn-cddvd-iso-images-from-the-command-line/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Avoid Using which(1)</title>
		<link>http://pthree.org/2011/09/26/avoid-using-which1/</link>
		<comments>http://pthree.org/2011/09/26/avoid-using-which1/#comments</comments>
		<pubDate>Mon, 26 Sep 2011 11:30:40 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Debian]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2054</guid>
		<description><![CDATA[This post comes from BashFAQ/081 on Greg&#8217;s Wiki. He argues why you should not be using which(1) to determine if a command is in your $PATH at the end of the page. I&#8217;ll put that argument at the front: The command which(1) (which is often a csh script, although sometimes a compiled binary) is not [...]]]></description>
			<content:encoded><![CDATA[<p>This post comes from <a href="http://mywiki.wooledge.org/BashFAQ/081">BashFAQ/081</a> on Greg&#8217;s Wiki. He argues why you <strong>should not</strong> be using which(1) to determine if a command is in your $PATH at the end of the page. I&#8217;ll put that argument at the front:</p>
<blockquote><p>The command which(1) (which is often a csh script, although sometimes a compiled binary) is <strong>not reliable</strong> for this purpose. which(1) may not set a useful exit code, and it may not even write errors to stderr. Therefore, in order to have a prayer of successfully using it, one must parse its output (wherever that output may be written).</p>
<p>Note that which(1)&#8217;s output when a command is not found is not consistent across platforms. On HP-UX 10.20, for example, it prints &#8220;no qwerty in /path /path /path &#8230;&#8221;; on OpenBSD 4.1, it prints &#8220;qwerty: Command not found.&#8221;; on Debian (3.1 through 5.0 at least) and SuSE, it prints nothing at all; on Red Hat 5.2, it prints &#8220;which: no qwerty in (/path:/path:&#8230;)&#8221;; on Red Hat 6.2, it writes the same message, but on standard error instead of standard output; and on Gentoo, it writes something on stderr.</p></blockquote>
<p>(Quotation and manpage reference additions mine). So, if which(1) is bad news, then what is the &#8220;proper&#8221; way to determine if a command is in your $PATH? Well POSIX has an answer, and not surprisingly, the command to use is &#8220;command&#8221;:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;"># POSIX</span><br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">command</span> <span style="color: #660033;">-v</span> qwerty <span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty exists<br />
<span style="color: #000000; font-weight: bold;">else</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty does not exist<br />
<span style="color: #000000; font-weight: bold;">fi</span></div></td></tr></tbody></table></div>
<p>The &#8220;command&#8221; built-in also returns true for shell built-ins. If you absolutely must check only PATH, the only POSIX way is to iterate over it:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;"># POSIX</span><br />
IsInPath <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br />
<span style="color: #7a0874; font-weight: bold;">&#40;</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #007800;">$#</span> <span style="color: #660033;">-eq</span> <span style="color: #000000;">1</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #000000; font-weight: bold;">&amp;&amp;</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #ff0000;">&quot;$1&quot;</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #000000; font-weight: bold;">||</span> <span style="color: #7a0874; font-weight: bold;">return</span> <span style="color: #000000;">2</span><br />
&nbsp; <span style="color: #000000; font-weight: bold;">set</span> <span style="color: #660033;">-f</span>; <span style="color: #007800;">IFS</span>=:<br />
&nbsp; <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #c20cb9; font-weight: bold;">dir</span> <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #007800;">$PATH</span>; <span style="color: #000000; font-weight: bold;">do</span><br />
&nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #660033;">-z</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$dir</span>&quot;</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #000000; font-weight: bold;">&amp;&amp;</span> <span style="color: #007800;">dir</span>=. <span style="color: #666666; font-style: italic;"># Legacy behaviour</span><br />
&nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #660033;">-x</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$dir</span>/$1&quot;</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #000000; font-weight: bold;">&amp;&amp;</span> <span style="color: #7a0874; font-weight: bold;">return</span><br />
&nbsp; <span style="color: #000000; font-weight: bold;">done</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">return</span> <span style="color: #000000;">1</span><br />
<span style="color: #7a0874; font-weight: bold;">&#41;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">if</span> IsInPath qwerty; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty exists<br />
<span style="color: #000000; font-weight: bold;">else</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty does not exist<br />
<span style="color: #000000; font-weight: bold;">fi</span></div></td></tr></tbody></table></div>
<p>There are also Bash built-ins that can be used, should you have Bash installed on your system:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;"># Bash using the 'hash' built-in</span><br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">hash</span> qwerty <span style="color: #000000;">2</span><span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty exists<br />
<span style="color: #000000; font-weight: bold;">else</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty does not exist<br />
<span style="color: #000000; font-weight: bold;">fi</span></div></td></tr></tbody></table></div>
<p>Or:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;"># Bash using the 'type' built-in</span><br />
<span style="color: #666666; font-style: italic;"># type -P forces a PATH search, skipping builtins and so on</span><br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">type</span> <span style="color: #660033;">-P</span> qwerty <span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty exists<br />
<span style="color: #000000; font-weight: bold;">else</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty does not exist<br />
<span style="color: #000000; font-weight: bold;">fi</span></div></td></tr></tbody></table></div>
<p>If you prefer the ZSH (my addition not present in the wiki), as I do, then you can look in the $commands associative array:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;"># ZSH using the $commands associative array</span><br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #007800;">$commands</span><span style="color: #7a0874; font-weight: bold;">&#91;</span>qwerty<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null <span style="color: #7a0874; font-weight: bold;">&#93;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty exists<br />
<span style="color: #000000; font-weight: bold;">else</span><br />
&nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> qwerty does not exist<br />
<span style="color: #000000; font-weight: bold;">fi</span></div></td></tr></tbody></table></div>
<p>I like that at the end of the FAQ, he gives a shell script for using which(1) should it be absolutely necessary. Not only do you have to test for exit code, but you also have to test for common strings in the output, seeing as though which(1) doesn&#8217;t always use exit codes properly:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;"># Bourne. &nbsp;Last resort -- using which(1)</span><br />
<span style="color: #007800;">tmpval</span>=<span style="color: #000000; font-weight: bold;">`</span><span style="color: #007800;">LC_ALL</span>=C <span style="color: #c20cb9; font-weight: bold;">which</span> qwerty <span style="color: #000000;">2</span><span style="color: #000000; font-weight: bold;">&gt;&amp;</span><span style="color: #000000;">1</span><span style="color: #000000; font-weight: bold;">`</span><br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">test</span> <span style="color: #007800;">$rc</span> <span style="color: #660033;">-ne</span> <span style="color: #000000;">0</span>; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; <span style="color: #666666; font-style: italic;"># FOR NOW, we'll assume that if this machine's which(1) sets a nonzero</span><br />
&nbsp; <span style="color: #666666; font-style: italic;"># exit status, that it actually failed. &nbsp;I've yet to see any case where</span><br />
&nbsp; <span style="color: #666666; font-style: italic;"># which(1) sets an erroneous failure -- just erroneous &quot;successes&quot;.</span><br />
&nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;qwerty is not installed. &nbsp;Please install it.&quot;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">else</span><br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;"># which returned 0, but that doesn't mean it succeeded. &nbsp;Look for known error strings.</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">case</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$tmpval</span>&quot;</span> <span style="color: #000000; font-weight: bold;">in</span><br />
&nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">*</span>no\ <span style="color: #000000; font-weight: bold;">*</span>\ <span style="color: #000000; font-weight: bold;">in</span>\ <span style="color: #000000; font-weight: bold;">*|*</span>not\ found<span style="color: #000000; font-weight: bold;">*|</span><span style="color: #ff0000;">''</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;qwerty is not installed. &nbsp;Please install it.&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">;;</span><br />
&nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">*</span><span style="color: #7a0874; font-weight: bold;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;Congratulations -- it seems you have qwerty (in <span style="color: #007800;">$tmpval</span>).&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">;;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">esac</span><br />
<span style="color: #000000; font-weight: bold;">fi</span></div></td></tr></tbody></table></div>
<p><strong>CONCLUSION:</strong><br />
You have many options to find whether or not a command exists in your $PATH, some POSIX, some proper built-ins. Regardless, you should be able to build platform-independent scripts using the proper tools, and using which(1) is not the right tool for the job. Hopefully, this has convinced you of that.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/09/26/avoid-using-which1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Setting Up Mutt With S/MIME And PGP/MIME</title>
		<link>http://pthree.org/2011/09/15/setting-up-mutt-with-smime-and-pgpmime/</link>
		<comments>http://pthree.org/2011/09/15/setting-up-mutt-with-smime-and-pgpmime/#comments</comments>
		<pubDate>Fri, 16 Sep 2011 03:52:33 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2027</guid>
		<description><![CDATA[If you have two accounts that you use with Mutt, and one of them you would like to use your OpenPGP key for signing mail (PGP/MIME), and the other you would like to use an OpenSSL certificate for doing the same (S/MIME), then this post is for you. Before beginning, however, you need to have [...]]]></description>
			<content:encoded><![CDATA[<p>If you have two accounts that you use with Mutt, and one of them you would like to use your OpenPGP key for signing mail (PGP/MIME), and the other you would like to use an OpenSSL certificate for doing the same (S/MIME), then this post is for you. Before beginning, however, you need to have the development version of Mutt (1.5.x) and it needs to have S/MIME support compiled in (which is the default for Debian/Ubuntu).</p>
<p>In my &#8220;~/.muttrc&#8221;, I have my accounts separated into two files, so I can set specific options for each account that don&#8217;t affect the other. As a result, here is the relevant parts of my ~/.muttrc:</p>
<pre># ~/.muttrc
folder-hook "personal.mail.tld" "source ~/.mutt/personal.rc"
folder-hook "work.mail.tld" "source ~/.mutt/work.rc"
source ~/.mutt/personal.rc # use this as the default account when Mutt executes</pre>
<p>So, I have two accounts: &#8220;personal.mail.tld&#8221; and &#8220;work.mail.tld&#8221; (those are actual URLs to your accounts, whether they be GMail, Yahoo!, or whatever. Change as necessary). So, let&#8217;s take a loot at the relevant parts of &#8220;~/.mutt/personal.rc&#8221; and &#8220;~/.mutt/work.rc&#8221;:</p>
<pre># ~/.mutt/personal.rc
source ~/.mutt/gpg.rc</pre>
<pre># ~/.mutt/work.rc
source ~/.mutt/smime.rc</pre>
<p>Obviously, I&#8217;m sourcing external files. The personal account sources the gpg.rc config, while the work account sources the smime.rc config. Both of these configs can be found as example files in &#8220;/etc/Muttrc.d/&#8221; on Debian. From there, I made my edits.</p>
<p>Obviously, for both configs, I will have needed to setup cryptographic keys. For GnuPG, I will need to generate and save off my public key pair. There is a wealth of documentation on the internet that discusses this, so I won&#8217;t cover that here. I&#8217;ll just assume you have it created already.</p>
<p>For S/MIME, you will need to generate an OpenSSL certificate, signed by a centralized certificate authority. For myself, I chose <a href="https://secure.comodo.com/products/frontpage?area=SecureEmailCertificate">Comodo</a>. It was easy and quick. I had my SSL cert in less than 5 minutes, and it&#8217;s good for a full year, and it was free.</p>
<p>Once you get the email about the certificate, when you click the link to open it in your browser, it will install your certificate in the browser. No big deal. Just navigate to the certificate using your browser&#8217;s menu, and backup the certificate (both the private and public keys) to some local directory on your machine. It should have &#8220;.p12&#8243; as its extension. Then, copy the certificate to the machine that will be running Mutt.</p>
<p>On the machine that you will be running Mutt from, you will need to issue a few commands to get your environment setup correctly before you can start using the certificate. You will need OpenSSL installed before you can begin:</p>
<pre>$ smime_keys init</pre>
<p>This should create &#8220;~/.smime/&#8221;, &#8220;~/.smime/certificates/&#8221; and &#8220;~/.smime/keys/&#8221;. After which, you will need to copy &#8220;ca-bundle.crt&#8221; to &#8220;~/.smime/&#8221;. &#8220;ca-bundle.crt&#8221; is provided by a number of packages, and you can find it on the internet should it not already be installed. After that is copied, type the following:</p>
<pre>$ smime_keys add_p12 /path/to/your-backed-up-cert.p12</pre>
<p>You will be asked for the passphrase for the private key in the .p12 file, as well as a new passphrase for encrypting the key in your database. It doesn&#8217;t matter if you use the same passphrase both times. <em>This passphrase is not recoverable, so make sure you remember it.</em> You will also be asked what you want to call the certificate; it makes no difference to anyone what you call it, but you can&#8217;t have two certificates with the same nickname.</p>
<p>Make note of the hash of your key. You can retrieve this hash from the line &#8220;added private key&#8221; followed by a path and a hexadecimal number ending in &#8220;.0&#8243;. Find the line in your .muttrc that says:</p>
<pre># ~/.mutt/smime.rc
set smime_default_key="12345678.0"</pre>
<p>and change &#8220;12345678.0&#8243; to the hash for your key that you took note of. The only thing left to do is to install the Comodo root certificates. These are already installed on your system in &#8220;/etc/ssl/certs/Comodo*&#8221; and &#8220;/etc/ssl/certs/COMODO*&#8221;. For each of those root certs (I&#8217;m actually not sure which is needed for your personal cert), you need to issue the following command:</p>
<pre>$ smime_keys add_root /etc/ssl/certs/Comodo_Secure_Services_root.pem</pre>
<p>You have now successfully setup your environment to use S/MIME with Mutt. Now let&#8217;s look at the configs. For the gpg.rc config, I kept everything default except for the following:</p>
<pre># ~/.mutt/gpg.rc
set pgp_good_sign="^gpg: Good signature from"
set crypt_autosign="yes"
set crypt_replysign="yes"
set crypt_replysignencrypted="yes"
set pgp_auto_decode="yes"
unset smime_is_default</pre>
<p>For the smime.rc config, again I kept everything default except for the following:</p>
<pre># ~/.mutt/smime.rc
set smime_is_default="yes"
set crypt_autosmime="yes"
set pgp_autosign="no"
set smime_timeout="300"
set crypt_autosign="yes"
set crypt_replyencrypt="yes"
set crypt_replysign="yes"
set crypt_replysignencrypted="yes"
set crypt_verify_sig="yes"
set smime_default_key="12345678.0" # you should have already changed this</pre>
<p>You are now ready to roll. When you change to your personal account, it should use PGP/MIME by default, and when you change to your work account, it should use S/MIME by default.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/09/15/setting-up-mutt-with-smime-and-pgpmime/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Use Your SSH Client To Help Prevent Stupid Mistakes</title>
		<link>http://pthree.org/2011/09/06/use-your-ssh-client-to-help-prevent-stupid-mistakes/</link>
		<comments>http://pthree.org/2011/09/06/use-your-ssh-client-to-help-prevent-stupid-mistakes/#comments</comments>
		<pubDate>Tue, 06 Sep 2011 15:12:37 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=2007</guid>
		<description><![CDATA[I have chosen the path of system administration for my career. It&#8217;s been very rewarding, and I really love my job. However, there are times when I make stupid mistakes that cost others money. I&#8217;m sure we&#8217;ve all been there. It&#8217;s stressful, embarrassing and can really shake you up, if you mistake is bad enough. [...]]]></description>
			<content:encoded><![CDATA[<p>I have chosen the path of system administration for my career. It&#8217;s been very rewarding, and I really love my job. However, there are times when I make stupid mistakes that cost others money. I&#8217;m sure we&#8217;ve all been there. It&#8217;s stressful, embarrassing and can really shake you up, if you mistake is bad enough. Many times, this happens because you fat-fingered an IP address, hostname, or something else, and your SSH client takes you somewhere you shouldn&#8217;t be. If that&#8217;s the case, hopefully this post can help.</p>
<p>According to the <a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ssh_config&#038;sektion=5">ssh_config(5)</a> manual:</p>
<pre>     <strong>LocalCommand</strong>
             Specifies a command to execute on the local machine after suc‐
             cessfully connecting to the server.  The command string extends
             to the end of the line, and is executed with the user's shell.
             The following escape character substitutions will be performed:
             ‘%d’ (local user's home directory), ‘%h’ (remote host name), ‘%l’
             (local host name), ‘%n’ (host name as provided on the command
             line), ‘%p’ (remote port), ‘%r’ (remote user name) or ‘%u’ (local
             user name).

             The command is run synchronously and does not have access to the
             session of the ssh(1) that spawned it.  It should not be used for
             interactive commands.

             This directive is ignored unless PermitLocalCommand has been
             enabled.</pre>
<p>As mentioned, the used of LocalCommand executes a local command after successfully connecting to the server. I figured this would be a great way to print something to the terminal, letting me know whether or not my client just connected to a production machine, a QA machine, or a development machine.</p>
<p>I wanted to use colors, to make it obvious. I don&#8217;t want to make the same mistake twice, so I want it painfully clear what machine I just went to. As a result, if I go to a development or home machine, use green. If I enter a QA machine, use yellow. If I enter a production, or other serious machine I probably shouldn&#8217;t be on, use red. As a result, I can take advantage of the ANSI escape sequences for color. In case you forgot, here are the colors and modes:</p>
<blockquote><p><strong>Colors</strong><br />
\e[{attr1;&#8230;;{attrN}m</p>
<p><strong>Text attributes</strong><br />
0 Reset<br />
1 Bright<br />
2 Dim<br />
4 Underscore<br />
5 Blink<br />
7 Reverse<br />
8 Hidden</p>
<p><strong>Foreground Colors</strong><br />
30 Black<br />
31 Red<br />
32 Green<br />
33 Yellow<br />
34 Blue<br />
35 Magenta<br />
36 Cyan<br />
37 White</p>
<p><strong>Background Colors</strong><br />
40 Black<br />
41 Red<br />
42 Green<br />
43 Yellow<br />
44 Blue<br />
45 Magenta<br />
46 Cyan<br />
47 White</p></blockquote>
<p>So, if I were about to SSH to a production machine, I probably want to make it as obvious as possible. Thus, I could print to the terminal, in blinking, bold, red text &#8220;PRODUCTION&#8221;. I could use the following command:</p>
<pre>print "\e[1;5;31PRODUCTIONm\e[0;m"</pre>
<p>Notice that at the end of the sequence, I&#8217;m resetting the text attributes. This is because if you don&#8217;t do this, you will keep the text attributes in your terminal, and that may have an affect on how the text is displayed when in your remote SSH connection.</p>
<p>A possible ~/.ssh/config file could look like this:</p>
<div class="codecolorer-container text twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Host development<br />
&nbsp; &nbsp; Hostname dev.domain.tld<br />
&nbsp; &nbsp; LocalCommand print &quot;\e[1;32mDevelopment\e[0;m&quot;<br />
&nbsp; &nbsp; PermitLocalCommand yes<br />
<br />
Host qa<br />
&nbsp; &nbsp; Hostname qa.domain.tld<br />
&nbsp; &nbsp; LocalCommand print &quot;\e[1;33mQuality Assurance\e[0;m&quot;<br />
&nbsp; &nbsp; PermitLocalCommand yes<br />
<br />
Host production<br />
&nbsp; &nbsp; Hostname prod.domain.tld<br />
&nbsp; &nbsp; LocalCommand print &quot;\e[1;5;32mPRODUCTION\e[0;m&quot;<br />
&nbsp; &nbsp; PermitLocalCommand yes</div></td></tr></tbody></table></div>
<p>Here is a screenshot in action (without the blink):</p>
<p><img src="http://pthree.org/wp-content/uploads/2011/09/localcommand.png" alt="" title="localcommand" width="300" height="91" class="aligncenter size-full wp-image-2011" /></p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/09/06/use-your-ssh-client-to-help-prevent-stupid-mistakes/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>My Tmux Hardstatus Theme</title>
		<link>http://pthree.org/2011/08/31/my-tmux-hardstatus-theme/</link>
		<comments>http://pthree.org/2011/08/31/my-tmux-hardstatus-theme/#comments</comments>
		<pubDate>Wed, 31 Aug 2011 20:32:13 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[irssi]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1992</guid>
		<description><![CDATA[Recently, I&#8217;ve made the switch from GNU Screen to Tmux for my remote terminal multiplexor. I still prefer GNU Screen for serial connections, however (something the Tmux developers don&#8217;t seem to think is important). So, when getting it setup, I wanted my hardstatus line to imitate my GNU Screen hardstatus line as closely as possible. [...]]]></description>
			<content:encoded><![CDATA[<p>Recently, I&#8217;ve made the switch from GNU Screen to Tmux for my remote terminal multiplexor. I still prefer GNU Screen for serial connections, however (something the Tmux developers don&#8217;t seem to think is important). So, when getting it setup, I wanted my hardstatus line to imitate my GNU Screen hardstatus line as closely as possible. Well, I got it to that point, then took it even further to work with <a href="http://pthree.org/2010/01/12/more-88-madcows/">my custom Irssi</a> and <a href="http://pthree.org/2009/10/14/more-zsh-prompt-love/">ZSH themes</a> (I want everything to tie in nicely). The only thing left is getting Mutt in order, but that&#8217;s for another post.</p>
<p>At any event, here is the configuration for the theme:</p>
<div class="codecolorer-container text twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"># hardstatus line theme<br />
set-option -g status-fg white<br />
set-option -g status-bg default<br />
set-option -g status-justify centre<br />
set-option -g status-left-length 13<br />
set-option -g status-left '#[fg=green][ #[fg=black,bright]#h #[fg=green,nobright]][#[default]'<br />
set-option -g status-right-length 16<br />
set-option -g status-right '#[fg=green]][ #[fg=black,bright]#(date +&quot;%m/%d %H:%M&quot;) #[fg=green,nobright]]#[fg=default]'<br />
set-window-option -g window-status-current-format '#[fg=red,bg=default]( #[fg=white,bright,bg=default]#I:#W#[fg=red,nobright,bg=default] )#[fg=default]'<br />
set-window-option -g window-status-alert-attr bold<br />
set-window-option -g window-status-alert-fg yellow<br />
set-window-option -g window-status-alert-bg default</div></td></tr></tbody></table></div>
<p>Obviously, this won&#8217;t look that great on a terminal with a white background (or really any color other than black). And here is the screenshot:</p>
<p><a href="http://pthree.org/wp-content/uploads/2011/08/tmux.png"><img src="http://pthree.org/wp-content/uploads/2011/08/tmux.png" alt="" title="tmux" width="572" height="21" class="aligncenter size-full wp-image-1993" /></a></p>
<p>As you can clearly see, the active window you&#8217;re under is bold white with red parentheses around the window name. The previous window you were in is marked with a dash &#8216;-&#8217; (by default). An alert in a terminal will change the text to bold yellow, so long as you&#8217;re not in that window (as you can see with the &#8220;mutt&#8221; window). It ties in nicely with the <a href="http://pthree.org/wp-content/uploads/2011/08/88_madcows.theme">88_madcows.theme</a> file for Irssi, and the ZSH theme I built.</p>
<p>I&#8217;m new to building Tmux hardstatus lines, so if there is something I should be doing differently, let me know.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/08/31/my-tmux-hardstatus-theme/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Announcing Penny Red</title>
		<link>http://pthree.org/2011/03/30/announcing-penny-red/</link>
		<comments>http://pthree.org/2011/03/30/announcing-penny-red/#comments</comments>
		<pubDate>Thu, 31 Mar 2011 02:11:14 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Freedom]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1860</guid>
		<description><![CDATA[This is my first open source project that I&#8217;ve started and maintained, so I&#8217;m pretty excited about it. I was upset at the current offerings of Hashcash for the various MUAs, so I set out to do something about it. You&#8217;ve already read on my blog about my solutions for minting and verifying Hashcash tokens [...]]]></description>
			<content:encoded><![CDATA[<p>This is my first open source project that I&#8217;ve started and maintained, so I&#8217;m pretty excited about it. I was upset at the current offerings of Hashcash for the various MUAs, so I set out to do something about it. You&#8217;ve already read on my blog about my solutions for minting and verifying Hashcash tokens with Mutt. Well, a good friend of mine suggested that I start an open source project out of it, put it into revision control and get my rear in gear. So, I did just that.</p>
<p>Announcing <a href="http://github.com/atoponce/penny-red">Penny Red</a>, a Python solution to integrate Hashcash into Mutt, licensed under the GPLv3. I chose the name &#8220;Penny Red&#8221; for a two reasons:</p>
<ol>
<li>Penny Red was the second British postal stamp which was debuted in 1841, the first being the Penny Black stamp. The color was changed, so the black cancellation mark could be easily seen on a red stamp, versus the previous black stamp. Because the goal of the project is to implement a payment system through minted tokens, and a <a href="http://www.ruddick.com/tim/hashcash_filter.html">previous solution had existed in Perl</a> (without documentation, mind you), this seemed appropriate.</li>
<li>Unfortunately, in the English language, the words &#8220;red&#8221; and &#8220;read&#8221; have the same sound, yet different spelling. After reading your email, you would say &#8220;I have read my mail&#8221;. Because it had postage attached in the headers, you read mail that was paid for. So, &#8220;Penny Red&#8221; is a play on &#8220;Penny Read&#8221;</li>
</ol>
<p>The great thing with the Python scripts in Penny Red is their portability. The &#8220;mint_hashcash.py&#8221; script reads a file as a passed argument, and then writes to the headers. The &#8220;verify_hashcash.py&#8221; script reads the mail from STDIN and prints the message and the verified tokens back to STDOUT. Nothing specific about Mutt is in either script! As a result, as long as the MUA supports STDIN and STDOUT with each message, and modifying the headers, these scripts can be used. I guess what I&#8217;m saying is, I would like to extend these scripts to Pine, Alpine, Gnus (although not really necessary as Gnus supports Hashcash out of the box), Elm, and others, without forking the project. Penny Red should be able to support multiple MUAs.</p>
<p>At any event, I just wanted to get this post out there, seeing as though I just barely setup the project. I&#8217;m pretty excited. Should be fun, and give me something to do after graduation.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/03/30/announcing-penny-red/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Verifying Hashcash Tokens With Mutt</title>
		<link>http://pthree.org/2011/03/29/verifying-hashcash-tokens-with-mutt/</link>
		<comments>http://pthree.org/2011/03/29/verifying-hashcash-tokens-with-mutt/#comments</comments>
		<pubDate>Wed, 30 Mar 2011 03:32:35 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Cryptology]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1842</guid>
		<description><![CDATA[Just five days ago, I blogged about minting Hashcash tokens in Mutt using a Python script (make sure you check that page for any updates to the source if you&#8217;re using it). Well today, I finished writing my verification script. It takes some additional changes to your ~/.muttrc, which I&#8217;ll outline here, and it requires [...]]]></description>
			<content:encoded><![CDATA[<p>Just five days ago, I blogged about <a href="http://pthree.org/2011/03/24/hashcash-and-mutt/">minting Hashcash tokens in Mutt</a> using a Python script (make sure you check that page for any updates to the source if you&#8217;re using it). Well today, I finished writing my verification script. It takes some additional changes to your ~/.muttrc, which I&#8217;ll outline here, and it requires the installation of a Python script. Of course, as previous, I&#8217;m assuming that you&#8217;re running at least Python 2.5 and the latest version of <a href="http://hashcash.org">Hashcash</a>. With that, let&#8217;s get busy.</p>
<p>First, the necessary changes to your &#8220;~/.muttrc&#8221; config. The script relies on the &#8220;X-Hashcash:&#8221; header (as well as the &#8220;Hashcash:&#8221; header- I guess there was some discrepancy or something about X-headers being deprecated, or something) not being weeded out. It must be displayed if present. This way, the script can actually see the token in the header, and process the logic of checking if it&#8217;s valid. If you hid the hashcash headers, then the script won&#8217;t execute, and as a result, won&#8217;t add anything to the token database. We use the $display_filter variable to execute the Python script, and show the results to the pager. Here&#8217;s the changes you will need to make:</p>
<pre># file: ~/.muttrc
ignore *                                    # draconian header weed - recommended
unignore from date subject to cc user-agent # standard headers unignored - recommended
unignore x-hashcash hashcash                # required</pre>
<p>You will also need to set the $display_filter variable. I like having my theme consistent, so I&#8217;ve also added color to my theme to show the Hashcash verification at the top of the mail:</p>
<pre># file: ~/.muttrc
set display_filter="/path/to/verify_hashcash.py"    # required
color body brightyellow default "^\\[--.*Hashcash*" # recommended</pre>
<p>Now with that set, all we need to do is install the Python script, and we&#8217;re ready to go. When looking over the code, you&#8217;ll notice that it&#8217;s creating a database of spent tokens. I&#8217;ve placed the database in ~/.mutt/, seeing as though I only have Mutt working with Hashcash at the moment (and it&#8217;s really the only MUA I use these days). If you have something else that uses Hashcash tokens, in an already existing database, you may want to make the necessary modifications to the Python script, so it&#8217;s pointing to the right file. Also, we&#8217;re only interested in keeping track of tokens minted for us personally, not all tokens we can find in the headers. Lastly, this Python script is only working for one email address. If you have multiple emails, as I do, you&#8217;ll have to either use a primary email to verify the tokens against, or modify the Python script to support checking tokens under multiple accounts.</p>
<p>With that said, here&#8217;s the script:</p>
<div class="codecolorer-container python twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;">#!/usr/bin/env python</span><br />
<span style="color: #808080; font-style: italic;"># Licensed under the public domain</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">rfc822</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">StringIO</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">subprocess</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Change the DB path in COMMAND as needed, and change your email address</span><br />
COMMAND<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;hashcash -cdb '%s' -r '%s' -f /home/user/.mutt/hashcash.db '%s'&quot;</span><br />
EMAILADDR<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;foo@bar.com&quot;</span><br />
<br />
tokens <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
token_status <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># converting a list to a file-type object for parsing rfc822 headers</span><br />
original <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdin</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
emailmsg <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">StringIO</span>.<span style="color: #dc143c;">StringIO</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>original<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
message <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">rfc822</span>.<span style="color: black;">Message</span><span style="color: black;">&#40;</span>emailmsg<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># check for the presence of &quot;X-Hashcash&quot; and &quot;Hashcash&quot; headers</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> message.<span style="color: black;">has_key</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;X-Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> hc_list <span style="color: #ff7700;font-weight:bold;">in</span> message.<span style="color: black;">getheaders</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;X-Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; tokens.<span style="color: black;">append</span><span style="color: black;">&#40;</span>hc_list<span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> message.<span style="color: black;">has_key</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> hc_list <span style="color: #ff7700;font-weight:bold;">in</span> message.<span style="color: black;">getheaders</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; tokens.<span style="color: black;">append</span><span style="color: black;">&#40;</span>hc_list<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># check each token</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> tokens:<br />
&nbsp; &nbsp; token_status.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;[-- Begin Hashcash output --]&quot;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> hc_token <span style="color: #ff7700;font-weight:bold;">in</span> tokens:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> hc_token.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;:&quot;</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: #66cc66;">==</span> EMAILADDR:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hc_bits <span style="color: #66cc66;">=</span> hc_token.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;:&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hc_resource <span style="color: #66cc66;">=</span> hc_token.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;:&quot;</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><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; p <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">Popen</span><span style="color: black;">&#40;</span>COMMAND % <span style="color: black;">&#40;</span>hc_bits<span style="color: #66cc66;">,</span>hc_resource<span style="color: #66cc66;">,</span>hc_token<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; shell<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> stderr<span style="color: #66cc66;">=</span><span style="color: #dc143c;">subprocess</span>.<span style="color: black;">PIPE</span><span style="color: #66cc66;">,</span> stdout<span style="color: #66cc66;">=</span><span style="color: #dc143c;">subprocess</span>.<span style="color: black;">PIPE</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; out <span style="color: #66cc66;">=</span> p.<span style="color: black;">stderr</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; token_status.<span style="color: black;">append</span><span style="color: black;">&#40;</span>out<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; token_status.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;No valid tokens for %s found.&quot;</span> % EMAILADDR<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; token_status.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;[-- End Hashcash output --]&quot;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #66cc66;">&gt;&gt;</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdout</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>message.<span style="color: black;">headers</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">for</span> status <span style="color: #ff7700;font-weight:bold;">in</span> token_status:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #66cc66;">&gt;&gt;</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdout</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>status<span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> tokens:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">''</span><br />
emailmsg.<span style="color: black;">seek</span><span style="color: black;">&#40;</span>message.<span style="color: black;">startofbody</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #66cc66;">&gt;&gt;</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdout</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>emailmsg.<span style="color: black;">readlines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></td></tr></tbody></table></div>
<p>One thing I do find odd about the code above, is the hashcash binary, when checking tokens, prints to STDERR rather than STDOUT. I&#8217;m guessing this could change in the future, so that&#8217;s something that will need to be watched out for.</p>
<p>So far, the Python script has been working flawless for me. I haven&#8217;t noticed a single hiccup, and it&#8217;s fast, which it should be. However, standard disclaimers apply, such as not coming with any warranty, blah, blah, blah, and if you find any bugs, or have any enhancements (such as supporting multiple email addresses), I&#8217;m all ears. At any rate, I hope you find it helpful, should you wish to get into Hashcash with Mutt.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/03/29/verifying-hashcash-tokens-with-mutt/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hashcash and Mutt</title>
		<link>http://pthree.org/2011/03/24/hashcash-and-mutt/</link>
		<comments>http://pthree.org/2011/03/24/hashcash-and-mutt/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 00:09:58 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Cryptology]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1814</guid>
		<description><![CDATA[Introduction I wanted to used Hashcash with Mutt, for nothing more than a curiosity to see if it generates any discussion, and to see if people notice. Further, I&#8217;m a big crypto advocate, and while Hashcash isn&#8217;t exactly crypto, it&#8217;s highly related to it, and uses it. Regardless, I wanted to see if I could [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Introduction</strong></p>
<p>I wanted to used Hashcash with Mutt, for nothing more than a curiosity to see if it generates any discussion, and to see if people notice. Further, I&#8217;m a big crypto advocate, and while Hashcash isn&#8217;t exactly crypto, it&#8217;s highly related to it, and uses it. Regardless, I wanted to see if I could seamlessly tie in Hashcash with Mutt, both for minting and verification.</p>
<p>I make the following assumptions:</p>
<ul>
<li>You have Hashcash installed.</li>
<li>You have Mutt installed.</li>
<li>You have Python 2.5 or later installed.</li>
</ul>
<p>With that, let&#8217;s continue.</p>
<p><strong>What is Hashcash?</strong></p>
<p>Hashcash is a proof-of-work protocol, with the motivation of eliminating SPAM. The idea is simple, although we&#8217;ll cover it in greater detail. The TL;DR version, is using Hashcash is about generating tokens, and attaching them to the header of the message you&#8217;re sending. The recipient checks the token for verification. If it passes, then you must not be SPAM, as you&#8217;ve put your computer through enough strenuous work to prove it. If the token fails, you could be SPAM, or you just did it wrong. Either way, it&#8217;s no guarantee that you&#8217;re not a spammer.</p>
<p>Now, the detailed version.</p>
<p>The premise behind Hashcash is that some certain mathematical results are difficult to discover but easy to verify. Factoring large numbers is one such example. So Hashcash is some sort of challenge for your CPU. For example, I say that you cannot comment on my blog, unless you can find the two prime factors of some large number. Once you&#8217;ve paid the work through CPU cycles, and provide the numbers, I multiply them together to see if it gives the result. The work was difficult for you to find, but simple for me to verify. Hashcash is based on this principle.</p>
<p>Using a lot of CPU cycles to answer a question non-interactively is one way to beat spammers at their own game, which Hashcash hopes to address. Rather than looking for large prime factors though, Hashcash is looking for a SHA1 sum of a unique string where the first set of characters in the sum are zero. Because SHA1 can provide 2^160 possible hashes before a collision is found, it shouldn&#8217;t take too much work to find such a string. In other words, the search space is large enough for the work to be reasonable. However, the search space is also large enough, that given a certain criteria, it can be difficult to find the requested string.</p>
<p>Let&#8217;s look at an example. Consider the SHA1 string:</p>
<pre>00000528ba02c4da777839db269fde97de56ddf7</pre>
<p>The first 20 bits of the string are zero. So, this is one such SHA1 hash that would work. The questions are, how did we find it, and what string did we use to generate it? Well, because the first 20 bits of the string are zero, this means that I should only have to search up to 2^20 possible hashes to find the string I&#8217;m looking for. On a moderate system within the last 10 years or so, this should take under a second. Indeed, the SHA1 hash string above was done on an AMD Athlon(tm) XP 1800+ with only 384 MB of PC133 RAM, and it was calculated in 940 milliseconds. The string, or in this case, our &#8220;Hashcash token&#8221; that generated that SHA1 hash is:</p>
<pre>1:20:110321:&#97;&#97;&#114;&#111;&#110;&#46;<u></u>&#116;<i></i>&#111;&#112;&#111;&#110;&#99;&#101;<b></b>&#64;<u></u>&#103;<u></u>&#109;&#97;&#105;<i></i>&#108;&#46;&#99;<i></i>&#111;&#109;<i></i>::Ci2v7/gsBbYY/dhk:0BTS</pre>
<p>You can verify this easily enough:</p>
<pre>echo -n 1:20:110321:&#97;&#97;&#114;&#111;&#110;&#46;<u></u>&#116;<i></i>&#111;&#112;&#111;&#110;&#99;&#101;<b></b>&#64;<u></u>&#103;<u></u>&#109;&#97;&#105;<i></i>&#108;&#46;&#99;<i></i>&#111;&#109;<i></i>::Ci2v7/gsBbYY/dhk:0BTS | sha1sum
00000528ba02c4da777839db269fde97de56ddf7  -</pre>
<p>So, if that is indeed a Hashcash token, then what are all the fields, and how are they used when searching for a SHA1 hash that starts with zeros? Well, the breakdown is thus:</p>
<ol>
<li>The version number.</li>
<li>The claimed bit value.</li>
<li>The date (and time) a stamp was minted.</li>
<li>The resource for which a stamp is minted.</li>
<li>Extensions that a specialized application may want.</li>
<li>A random salt.</li>
<li>The suffix.</li>
</ol>
<p>Hashcash has undergone two revisions thus far. The version number shows the claimed version of the protocol being used. With the latest release of Hashcash, this is &#8220;1&#8243;. &#8220;0&#8243; has shown to have some limitations.</p>
<p>The claimed bit value it telling the verifier of the token how many zeros the resulting SHA1 hash should have. The default is 20-bits, but this can be changed. If the bit value is too low, then it becomes trivial for spammers to reproduce with minimal effort. If the bit value is too high, it may take some considerable time for your CPU to mint the token.</p>
<p>The timestamp is the date when the token was minted. We can take advantage of this for expiry. When the verifier downloads the token, he can keep it in a database. This is useful to hopefully prevent someone else from claiming the same token. However, holding on to tokens indefinitely could be cumbersome, so we can set expiration dates on when the tokens expire, to help manage the database. The default expiration is 28 days.</p>
<p>The resource for which the token is minted is generally the email address of the recipients. However, it&#8217;s flexible enough to be anything. It could be a URL to a webpage, some string of text, or anything that uniquely identifies a certain resource. We&#8217;ll be using email addresses as our resource.</p>
<p>The fifth field is generally left blank, but the protocol specifies that this is used for extensions that any specialized application may want.</p>
<p>The salt is used just like it is used anywhere else. Here, we wish to make our token unique for our resource. The email address and timestamp might not be unique enough to prevent two minted tokens from being the same. So, we add a random, hopefully unique salt here to the token. Once the salt is chosen, just like the timestamp and the resource, it won&#8217;t change for that token. The real grunt work is done with the next field. But, the goal with the salt is to just eliminate the possibility that two people send me an email on the same day, and the minted token is the same. If each salt is randomly chosen, then the tokens will differ.</p>
<p>As stated, this last field is where the real grunt work of your CPU lies. Every previous field is statically set upon instantiation. It is only this field that changes until we find the SHA1 sum that produces the correct number of zeros that we&#8217;ve asked for. Fortunately, the suffix is sequential, starting at zero, and working it&#8217;s way up, base 64, until the appropriate suffix attached to this string gives us the SHA1 we want. As mentioned, if our bit size is 20 bits, by default, then we will only have to work our way through 2^20 possible suffixes, on average, to find the right string that gives us the SHA1 with 20 bits of leading zeros.</p>
<p>The security of the token comes from the fact that there should only be one collision for every 2^160 possible strings in SHA1. Let&#8217;s not get tied up in the cryptanalysis. Suffice it to say, that SHA1 is still holding well in cryptographic circles, and for all practical purposes, we are interested in the amount of work it takes for the CPU to find the right token. When SHA1 becomes broken, and cryptanalysis shows it&#8217;s no longer secure enough for today&#8217;s applications, Hashcash is flexible enough to move to a different cryptographic hashing algorithm by just changing the protocol version. In the meantime, SHA1 works.</p>
<p>Also, given today&#8217;s multicore CPUs, and Moore&#8217;s Law, 20 bits might not be enough work for the CPU to crunch through. Remember, the idea is to beat spammers at their own game. If they can produce mass valid tokens for each spam mail they send, then we should make sure that our bit strength is stronger. Right now, 20 bits seems to be strong enough, but maybe moving it to 24 or 28 bits in the future might be necessary. Just remember the amount of time it takes for your CPU to calculate the work needed for the token.</p>
<p><strong>How Hashcash works with email</strong></p>
<p>When the token is minted, we wish to add the token to our mail header. We can add a token for each recipient in the &#8220;To:&#8221; and &#8220;Cc:&#8221; lines (with special care on how the headers are modified with Bcc: recipients (generally solved by sending separate emails with individual tokens)). What is added to the mail header is the following:</p>
<pre>X-Hashcash: 1:20:110321:&#97;&#97;&#114;&#111;&#110;&#46;<u></u>&#116;<i></i>&#111;&#112;&#111;&#110;&#99;&#101;<b></b>&#64;<u></u>&#103;<u></u>&#109;&#97;&#105;<i></i>&#108;&#46;&#99;<i></i>&#111;&#109;<i></i>::Ci2v7/gsBbYY/dhk:0BTS</pre>
<p>For those using Hashcash, including antispam utilities such as SpamAssassin, looking for the X-Hashcash header in the mail, then verifying that the minted token is valid takes a fraction of a second. But, as we discussed earlier, minting the token takes some time. 20 bits for me takes just under a second. So, now imagine a SPAM ring with control of thousands of zombie computers infected with trojans. There are only 86400 seconds in a day, so if it takes one second to mint a valid token, then a single computer could only send out at most 86400 mails in a day, a far cry from the millions it wishes to send out. So, while it doesn&#8217;t completely eliminate the zombie nets from sending mass emails, it does slow them down considerably.</p>
<p>Yet, for those of us who only send a couple dozen mails per day, it&#8217;s trivial for our CPU to do the work, and doesn&#8217;t slow us down any. Further, those receiving our messages can verify the token is valid in a fraction of the time it took to mint it. As the receiver, you set the price of the token you wish to validate as antispam. Again, 20 bits seems sufficient enough for today, but 24 or 28 bits might need to be your standard in the future.</p>
<p>For those of you who are not using Hashcash, you can simply ignore the header, and move on with your life. It won&#8217;t slow your mail experience down any, and shouldn&#8217;t get in the way of reading or replying to it.</p>
<p><strong>Mutt Configuration</strong></p>
<p>Now that we have a decent understanding of how the Hashcash protocol works, let&#8217;s see how we set this up with Mutt. Thankfully, this is rather straight forward. All you need to add to your ~/.muttrc is:</p>
<pre>set edit_headers="yes"                 # required
set editor="/path/to/mint_hashcash.py" # required
set askcc="yes"                        # recommended, but not required</pre>
<p>This will allow you to edit the mail headers when composing your message with the editor of your choice. The editor is chosen for you in the Python script. I use Vim, so it&#8217;s hardcoded to that. Feel free to change it to your preferred editor of choice. I should also mention at this point that I have only found a way to automate minting tokens with Mutt. I haven&#8217;t found a way yet, although I&#8217;m working on it, to automate the process of verifying tokens, and storing tokens in a database, with Mutt. Hopefully, a followup post can come to fruition when I figure it out (after all, what&#8217;s the point of minting tokens if you can&#8217;t verify others&#8217; tokens yourself?).</p>
<p>The script is here:</p>
<div class="codecolorer-container python twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;">#!/usr/bin/env python</span><br />
<span style="color: #808080; font-style: italic;"># Licensed under the public domain</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">fileinput</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">rfc822</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">subprocess</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span><br />
<br />
<span style="color: #dc143c;">subprocess</span>.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;vim %s&quot;</span> % <span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> shell<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #008000;">file</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'r'</span><span style="color: black;">&#41;</span><br />
headers <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">rfc822</span>.<span style="color: black;">Message</span><span style="color: black;">&#40;</span><span style="color: #008000;">file</span><span style="color: black;">&#41;</span><br />
<br />
to_emails <span style="color: #66cc66;">=</span> headers.<span style="color: black;">getaddrlist</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;To&quot;</span><span style="color: black;">&#41;</span><br />
cc_emails <span style="color: #66cc66;">=</span> headers.<span style="color: black;">getaddrlist</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Cc&quot;</span><span style="color: black;">&#41;</span><br />
<br />
email_addrs <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
tokens <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Harvest all email addresses from the header</span><br />
<span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #dc143c;">email</span> <span style="color: #ff7700;font-weight:bold;">in</span> to_emails:<br />
&nbsp; &nbsp; email_addrs.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">email</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #dc143c;">email</span> <span style="color: #ff7700;font-weight:bold;">in</span> cc_emails:<br />
&nbsp; &nbsp; email_addrs.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">email</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Remove duplicate emails from the list, requires Python 2.5 and later</span><br />
email_addrs <span style="color: #66cc66;">=</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #008000;">set</span><span style="color: black;">&#40;</span>email_addrs<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Check if an appropriate token is already generated for the mail</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> headers.<span style="color: black;">has_key</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;X-Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #008000;">list</span> <span style="color: #ff7700;font-weight:bold;">in</span> headers.<span style="color: black;">getheaders</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;X-Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; email_addrs.<span style="color: black;">remove</span><span style="color: black;">&#40;</span><span style="color: #008000;">list</span>.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;:&quot;</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: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> headers.<span style="color: black;">has_key</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #008000;">list</span> <span style="color: #ff7700;font-weight:bold;">in</span> headers.<span style="color: black;">getheaders</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Hashcash&quot;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; email_addrs.<span style="color: black;">remove</span><span style="color: black;">&#40;</span><span style="color: #008000;">list</span>.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;:&quot;</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: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Call the hashcash function from the operating system to mint tokens</span><br />
<span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #dc143c;">email</span> <span style="color: #ff7700;font-weight:bold;">in</span> email_addrs:<br />
&nbsp; &nbsp; t <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">Popen</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;hashcash -m %s -X -Z 2&quot;</span> % <span style="color: #dc143c;">email</span><span style="color: #66cc66;">,</span> shell<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> stdout<span style="color: #66cc66;">=</span><span style="color: #dc143c;">subprocess</span>.<span style="color: black;">PIPE</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; tokens.<span style="color: black;">append</span><span style="color: black;">&#40;</span>t.<span style="color: black;">stdout</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Write the newly minted tokens to the header</span><br />
f <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">fileinput</span>.<span style="color: black;">FileInput</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> inplace<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> f:<br />
&nbsp; &nbsp; line <span style="color: #66cc66;">=</span> line.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> f.<span style="color: black;">lineno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">1</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #dc143c;">token</span> <span style="color: #ff7700;font-weight:bold;">in</span> tokens:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #dc143c;">token</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> line<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">continue</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> line<br />
<br />
<span style="color: #008000;">file</span>.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></td></tr></tbody></table></div>
<p>The code is fairly straight forward. Parse the email headers using RFC 822, grab the necessary email addresses, and mint the tokens as necessary. You&#8217;ll quickly note that &#8220;Bcc:&#8221; addresses aren&#8217;t supported, as minting tokens for those addresses would give them away in the header. I could send separate emails for each Bcc recipient, putting their own token in the header, but I&#8217;m not motivated enough to write that code, and I rarely, if ever, use blind carbon copy.</p>
<p>I&#8217;ve been running the above code now for 2-3 days, trying to break it as best I can, and I haven&#8217;t come across anything. If you do notice a bug, or something sloppy in the code, let me know, and I&#8217;ll make a best attempt at addressing it. However, it seems to be working quite well. The only thing I would mention, is if you have a lot of emails in the &#8220;To:&#8221; or &#8220;Cc:&#8221; fields, it may take some time to mint all those tokens. Just let it do its thing. It will get you back to Mutt. I promise.</p>
<p> <img src='http://pthree.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2011/03/24/hashcash-and-mutt/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>My ZSH, Irssi and Screen Themes On White And Black Backgrounds</title>
		<link>http://pthree.org/2010/04/29/my-zsh-irssi-and-screen-themes-on-white-and-black-backgrounds/</link>
		<comments>http://pthree.org/2010/04/29/my-zsh-irssi-and-screen-themes-on-white-and-black-backgrounds/#comments</comments>
		<pubDate>Fri, 30 Apr 2010 06:43:38 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[irssi]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1359</guid>
		<description><![CDATA[Now that school is out for the summer, I have a bit of time to work on some of the themes I&#8217;ve created for my ZSH prompt, Irssi and GNU Screen. The first focus of mine was to get all themes working well with both white text on black backgrounds, and black text on white [...]]]></description>
			<content:encoded><![CDATA[<p>Now that school is out for the summer, I have a bit of time to work on some of the themes I&#8217;ve created for my ZSH prompt, Irssi and GNU Screen. The first focus of mine was to get all themes working well with both white text on black backgrounds, and black text on white backgrounds. I&#8217;ve been leaning more and more to black text on white background colors for my default terminals, so I wanted to make sure that everything I was using day-to-day looked good with that setup.</p>
<p>First, my ZSH prompt was <a href="http://pthree.org/2009/10/14/more-zsh-prompt-love/">already developed</a> from the outset with that in mind. So, no additional hacking has been needed on that. There are some elements that I&#8217;m not too terribly excited about. I don&#8217;t care for the dark blue directories on a black background, and I don&#8217;t care for the yellow character devices on a white background. Using the <a href="http://tango.freedesktop.org/Tango_Desktop_Project">Tango scheme</a> for gnome-terminal makes both of those scenarios much more tolerable. However, I do have additional items that I want to put in my prompt, but that will be for a later post. Also, my GNU Screen hardstatus line also needed to be compatible. This wasn&#8217;t that big of a deal, as I only needed to apply some colors to a few elements. Hhere&#8217;s a couple screenshots showing both black and white backgrounds, and how the ZSH prompt inside looks in each. Note the GNU Screen hardstatus line is at the bottom of the terminal.</p>
<table style="width:auto;">
<tr>
<td><a href="http://picasaweb.google.com/lh/photo/VEs4lOWonaHjJUfZhvVAuA?feat=embedwebsite"><img src="http://lh6.ggpht.com/_UjZEx7iINYc/S9pzPJlvNQI/AAAAAAAAAx4/vBb1Xij848s/s144/zsh-screen-white.png" /></a></td>
</tr>
<tr>
<td style="font-family:arial,sans-serif; font-size:11px; text-align:right">From <a href="http://picasaweb.google.com/aaron.toponce/DesktopScreenshots?feat=embedwebsite">Desktop Screenshots</a></td>
</tr>
</table>
<table style="width:auto;">
<tr>
<td><a href="http://picasaweb.google.com/lh/photo/L0SeomtjbteBgH95hyKdCA?feat=embedwebsite"><img src="http://lh3.ggpht.com/_UjZEx7iINYc/S9pzPPgsL8I/AAAAAAAAAx0/CjszuCg_Hyw/s144/zsh-screen-black.png" /></a></td>
</tr>
<tr>
<td style="font-family:arial,sans-serif; font-size:11px; text-align:right">From <a href="http://picasaweb.google.com/aaron.toponce/DesktopScreenshots?feat=embedwebsite">Desktop Screenshots</a></td>
</tr>
</table>
<p>Second, my Irssi theme also needed to work well with both. As I&#8217;ve already blogged before, I was really, <i>really</i> <a href="http://pthree.org/2010/01/12/more-88-madcows/">impressed with the madcows theme</a>. However, I didn&#8217;t care for a few elements, so I started hacking it, making my own changes. I&#8217;ve tried keeping the true nature of the theme, while still adding my own style. The theme was already largely compatible with my ZSH prompt colors, it just needed some adjustments here and there, before I was totally satisfied. Further, it looked like crap using a white background, so this needed some hacking as well. I think I&#8217;m overall happy with the result, although I&#8217;m sure I&#8217;ve missed many things (like DCC, or CTCP), so there&#8217;s likely much more hacking to go before it&#8217;s perfect. However, for the general day-to-day chat, it&#8217;s 95% there. Screenshots for both backgrounds below:</p>
<table style="width:auto;">
<tr>
<td><a href="http://picasaweb.google.com/lh/photo/0DvCb_vsNGWKN3j6vi7uNg?feat=embedwebsite"><img src="http://lh5.ggpht.com/_UjZEx7iINYc/S9pzOj281fI/AAAAAAAAAxw/YL-NyubDrb8/s144/irssi-white.png" /></a></td>
</tr>
<tr>
<td style="font-family:arial,sans-serif; font-size:11px; text-align:right">From <a href="http://picasaweb.google.com/aaron.toponce/DesktopScreenshots?feat=embedwebsite">Desktop Screenshots</a></td>
</tr>
</table>
<table style="width:auto;">
<tr>
<td><a href="http://picasaweb.google.com/lh/photo/Hdxl0nvnQ3wHCfRKiyQWwQ?feat=embedwebsite"><img src="http://lh4.ggpht.com/_UjZEx7iINYc/S9pzOaOQ0uI/AAAAAAAAAxs/iC70wR8vCcI/s144/irssi-black.png" /></a></td>
</tr>
<tr>
<td style="font-family:arial,sans-serif; font-size:11px; text-align:right">From <a href="http://picasaweb.google.com/aaron.toponce/DesktopScreenshots?feat=embedwebsite">Desktop Screenshots</a></td>
</tr>
</table>
<p>In a nutshell, the themes are compatible with xterm-color support on most terminals. Mainly, I&#8217;m using bold and normal weights on red, green, yellow, black, white and blue. Anything else takes the default color of the terminal itself, whether it be the foreground text or the background. So, as long as your TERM variable is set to &#8220;xterm-color&#8221; or something better, you should be okay.</p>
<p>This post wouldn&#8217;t be complete without the source for you to try it out. <a href="http://pthree.org/wp-content/uploads/2010/04/all-themes.tar.gz">Here&#8217;s a compressed tarball</a> for giving it a shot, and reporting anything you find in the comments, if you like.</p>
<p>Cheers!  </p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2010/04/29/my-zsh-irssi-and-screen-themes-on-white-and-black-backgrounds/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Add Colors To Your ZSH Scripts</title>
		<link>http://pthree.org/2009/12/18/add-colors-to-your-zsh-scripts/</link>
		<comments>http://pthree.org/2009/12/18/add-colors-to-your-zsh-scripts/#comments</comments>
		<pubDate>Fri, 18 Dec 2009 22:58:29 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1201</guid>
		<description><![CDATA[I was writing some scripts this morning to help me keep the Unix and Linux server I administer at work up to date with their NTP time synchronization. As I was going along, I thought to myself, &#8220;I&#8217;d like to see some color in the output.&#8221; Thankfully, I already had the code in my ZSH [...]]]></description>
			<content:encoded><![CDATA[<p>I was writing some scripts this morning to help me keep the Unix and Linux server I administer at work up to date with their NTP time synchronization. As I was going along, I thought to myself, &#8220;I&#8217;d like to see some color in the output.&#8221; Thankfully, <a href="http://pthree.org/2009/10/14/more-zsh-prompt-love/">I already had the code in my ZSH prompt</a>. All I needed to do was remove some sigils, and I was up and running. If you want to add color to the output of your ZSH scripts, here&#8217;s what you need to add:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">autoload colors<br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$terminfo</span>[colors]&quot;</span> <span style="color: #660033;">-gt</span> <span style="color: #000000;">8</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>; <span style="color: #000000; font-weight: bold;">then</span><br />
&nbsp; &nbsp; colors<br />
<span style="color: #000000; font-weight: bold;">fi</span><br />
<span style="color: #000000; font-weight: bold;">for</span> COLOR <span style="color: #000000; font-weight: bold;">in</span> RED GREEN YELLOW BLUE MAGENTA CYAN BLACK WHITE; <span style="color: #000000; font-weight: bold;">do</span><br />
&nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">eval</span> <span style="color: #007800;">$COLOR</span>=<span style="color: #ff0000;">'$fg_no_bold[${(L)COLOR}]'</span><br />
&nbsp; &nbsp; <span style="color: #7a0874; font-weight: bold;">eval</span> BOLD_<span style="color: #007800;">$COLOR</span>=<span style="color: #ff0000;">'$fg_bold[${(L)COLOR}]'</span><br />
<span style="color: #000000; font-weight: bold;">done</span><br />
<span style="color: #7a0874; font-weight: bold;">eval</span> <span style="color: #007800;">RESET</span>=<span style="color: #ff0000;">'$reset_color'</span></div></td></tr></tbody></table></div>
<p>You now have the following variables available in the shell script namepace: RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, BLACK, WHITE, BOLD_RED, BOLD_GREEN, BOLD_YELLOW, BOLD_BLUE, BOLD_MAGENTA, BOLD_CYAN, BOLD_BLACK, BOLD_WHITE, RESET. Using these variables, you can manipulate the output from &#8220;echo&#8221; and &#8220;printf&#8221; for your script. For example, <a href="http://picasaweb.google.com/lh/photo/5dfKS0uddBu57qpaAhIhtg?feat=directlink">here&#8217;s a screenshot using &#8220;echo&#8221; to print red, green and blue text to the screen</a>. Notice that I&#8217;m using the &#8220;RESET&#8221; variable after the blue text to reset my prompt text back to normal. This may or may not be necessary, depending on how you configured your prompt, but it&#8217;s not a bad habit to get into.</p>
<p>Thought this might be helpful to the larger scripting community or for those sysadmins, such as myself, who would like a little variety added to their script output.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2009/12/18/add-colors-to-your-zsh-scripts/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>More ZSH Prompt Love</title>
		<link>http://pthree.org/2009/10/14/more-zsh-prompt-love/</link>
		<comments>http://pthree.org/2009/10/14/more-zsh-prompt-love/#comments</comments>
		<pubDate>Wed, 14 Oct 2009 14:51:24 +0000</pubDate>
		<dc:creator>Aaron Toponce</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://pthree.org/?p=1148</guid>
		<description><![CDATA[Ever since discovering ZSH 3 years ago, I&#8217;ve been addicted, but it wasn&#8217;t until a good 2 years into using the prompt on a daily basis that I decided to do some radical work with my prompt. I&#8217;ve blogged about this before a couple times, making improvements along the way: post 0, post 1, post [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since discovering ZSH 3 years ago, I&#8217;ve been addicted, but it wasn&#8217;t until a good 2 years into using the prompt on a daily basis that I decided to do some radical work with my prompt. I&#8217;ve blogged about this before a couple times, making improvements along the way: <a href="http://pthree.org/2008/01/31/my-zsh-prompt/">post 0</a>, <a href="http://pthree.org/2008/03/29/my-zsh-prompt-improved/">post 1</a>, <a href="http://pthree.org/2008/11/23/727/">post 2</a>, <a href="http://pthree.org/2009/03/28/add-vim-editing-mode-to-your-zsh-prompt/">post 3</a>. Check out those posts if you&#8217;re interested in what I&#8217;ve done to the prompt, and extra screenshots.</p>
<p>At the Utah Open Source Conference, I gave a BOF on Unix shells. The turnout was good, and we had a great discussion. I presented on my default prompt for ZSH, showing all the hidden features of the prompt. However, I had forgotten that I had removed battery status from my prompt, because I was depending on APM, which is no longer compiled in the kernel. A couple people have asked me since then why I&#8217;m depending on APM and not ACPI. I don&#8217;t have an answer, other than that was just what I coded. So, last night, I put up an ACPI implementation, and it works great. As with the APM implementation, if the battery percentage is less than 15%, the percentage display is red. If it&#8217;s less than 50% but greater than 14%, it&#8217;s yellow, and if it&#8217;s less than 100% but greater than 49%, it&#8217;s blue. If it&#8217;s 100%, or the tool &#8220;acpi&#8221; is not installed, then it doesn&#8217;t show up. Here&#8217;s a screenshot below:</p>
<p><img src="http://pthree.org/wp-content/uploads/2009/10/battery-34percent.png" alt="Battery Percentage in ZSH prompt" title="Battery Percentage in ZSH prompt" width="570" height="388" class="aligncenter size-full wp-image-1150" /></p>
<p>While hanging out in our local LUG channel for the Ogden Area Linux Users Group, I got talking with Seth about prompts. He decided to change his, including adding the dog from Nethack randomly &#8220;moving&#8221; in the prompt. He also mentioned changing the color of the path if the present working directory was not writable. I really liked this idea, and decided to implement it in my prompt. Here&#8217;s a screenshot of that in action:</p>
<p><img src="http://pthree.org/wp-content/uploads/2009/10/path-color-change.png" alt="Path color change in ZSH prompt" title="Path color change in ZSH prompt" width="570" height="388" class="aligncenter size-full wp-image-1151" /></p>
<p>I change the path color to yellow if the present working directory is not writable, as it&#8217;s noticeable enough to catch your attention, but subtle enough to not get in the way, and be distracting.</p>
<p>As usual, if you want the source, <a href="http://pthree.org/wp-content/uploads/2009/10/zsh-prompt.txt">here it is</a>. Yes, it&#8217;s public domain, as mentioned in the code, so have at it.</p>
]]></content:encoded>
			<wfw:commentRss>http://pthree.org/2009/10/14/more-zsh-prompt-love/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

