<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Tobin Industries]]></title>
  <link href="http://tobinindustries.com/atom.xml" rel="self"/>
  <link href="http://tobinindustries.com/"/>
  <updated>2013-09-09T22:30:20-07:00</updated>
  <id>http://tobinindustries.com/</id>
  <author>
    <name><![CDATA[Brian Tobin]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Inspect IMAP traffic using a Node.js proxy]]></title>
    <link href="http://tobinindustries.com/blog/2013/09/09/inspect-imap-traffic-using-a-nodejs-proxy/"/>
    <updated>2013-09-09T08:46:00-07:00</updated>
    <id>http://tobinindustries.com/blog/2013/09/09/inspect-imap-traffic-using-a-nodejs-proxy</id>
    <content type="html"><![CDATA[<p>At <a href="http://www.handle.com">Handle</a> we talk to a lot of email servers, mostly via IMAP. Something most engineers don&rsquo;t realize is how crazy IMAP has become, in large part due to fragmentation and providers who add functionality on top of the protocol. Talking to Yahoo? No IMAP IDLE. Talking to Gmail? You may or may not be blessed with an &ldquo;All Mail&rdquo; folder. It might also be localized to the user&rsquo;s language (e.g. &ldquo;Alle Nachrichten&rdquo; for German users). There&rsquo;s also COMPRESS, MODESEQ, Gmail MSGID &amp; THRID – the list goes on. How does a programmer begin to learn the nuances?</p>

<!--more-->


<p>Open source clients like <a href="http://www.mozilla.org/en-US/thunderbird/">Thunderbird</a> or
<a href="http://www.zimbra.com">Zimbra</a> are a good place to start. But sometimes it&rsquo;s faster to watch an exchange than
trace code. What about a TCP dump? Sometimes the server only responds to SSL and it&rsquo;s not possible to sniff the
handshake. Or even if you record a session, the IMAP server might have compressed parts of it. Annoyances such as
these are what prompted me to write an IMAP proxy, making it easy to inspect any IMAP conversation.</p>

<p>To run the script you&rsquo;ll need node.js and <a href="https://npmjs.org/package/coffee-script">coffee-script</a> (via npm). To
start the script, just run <code>coffee imap_proxy.coffee</code>. Then configure your email client to connect to your machine
on port 3737. Disable SSL. Save your settings and watch the conversation unfold!</p>

<figure class='code'><figcaption><span> (imap_proxy.coffee)</span> <a href='http://tobinindustries.com/downloads/code/imap_proxy.coffee'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="c1">#!/usr/bin/env coffee</span>
</span><span class='line'>
</span><span class='line'><span class="nv">tls = </span><span class="nx">require</span><span class="p">(</span><span class="s">&quot;tls&quot;</span><span class="p">)</span>
</span><span class='line'><span class="nv">net = </span><span class="nx">require</span><span class="p">(</span><span class="s">&quot;net&quot;</span><span class="p">)</span>
</span><span class='line'><span class="nv">Socket = </span><span class="nx">require</span><span class="p">(</span><span class="s">&quot;net&quot;</span><span class="p">).</span><span class="nx">Socket</span>
</span><span class='line'><span class="nv">state = </span><span class="p">{}</span>
</span><span class='line'>
</span><span class='line'><span class="nv">IMAP_SERVER = </span><span class="s">&quot;imap.gmail.com&quot;</span>
</span><span class='line'><span class="nv">GREEN_TEXT_CODE = </span><span class="s">&#39;\x1b[0;32m&#39;</span>
</span><span class='line'><span class="nv">RED_TEXT_CODE = </span><span class="s">&#39;\x1b[0;31m&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="nv">clientListener = </span><span class="nf">(connectionToClient) -&gt;</span>
</span><span class='line'>  <span class="c1"># This callback is run when the server gets a connection from a client.</span>
</span><span class='line'>  <span class="nv">isConnected = </span><span class="kc">true</span>
</span><span class='line'>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;Connected to mail client&quot;</span>
</span><span class='line'>
</span><span class='line'>  <span class="nx">connectionToClient</span><span class="p">.</span><span class="nx">on</span> <span class="s">&quot;data&quot;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span>
</span><span class='line'>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;</span><span class="si">#{</span><span class="nx">RED_TEXT_CODE</span><span class="si">}</span><span class="s"> Mail client: &quot;</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span>
</span><span class='line'>    <span class="nx">connectionToImapServer</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span><span class='line'>  <span class="nx">connectionToClient</span><span class="p">.</span><span class="nx">on</span> <span class="s">&quot;end&quot;</span><span class="p">,</span> <span class="nf">-&gt;</span>
</span><span class='line'>    <span class="nv">isConnected = </span><span class="kc">false</span>
</span><span class='line'>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;Disconnected from </span><span class="si">#{</span><span class="nx">IMAP_SERVER</span><span class="si">}</span><span class="s">&quot;</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1"># Now that we have a client on the line, make a connection to the IMAP server.</span>
</span><span class='line'>  <span class="nv">state.conn = </span><span class="k">new</span> <span class="nx">Socket</span><span class="p">()</span>
</span><span class='line'>  <span class="nv">connectionToImapServer = </span><span class="nx">tls</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span>
</span><span class='line'>    <span class="nv">socket: </span><span class="nx">state</span><span class="p">.</span><span class="nx">conn</span><span class="p">,</span>
</span><span class='line'>    <span class="c1"># Skip TLS certificate verification. Don&#39;t use this script on sensitive data!</span>
</span><span class='line'>    <span class="nv">rejectUnauthorized: </span><span class="kc">false</span>
</span><span class='line'>  <span class="p">,</span> <span class="nf">-&gt;</span>
</span><span class='line'>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;Client connected&quot;</span>
</span><span class='line'>    <span class="nv">state.conn = </span><span class="nx">connectionToImapServer</span>
</span><span class='line'>  <span class="p">)</span>
</span><span class='line'>  <span class="nx">connectionToImapServer</span><span class="p">.</span><span class="nx">on</span> <span class="s">&quot;data&quot;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span>
</span><span class='line'>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;</span><span class="si">#{</span><span class="nx">GREEN_TEXT_CODE</span><span class="si">}</span><span class="s"> </span><span class="si">#{</span><span class="nx">IMAP_SERVER</span><span class="si">}</span><span class="s">: &quot;</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span>
</span><span class='line'>    <span class="k">return</span> <span class="k">if</span> <span class="o">!</span><span class="nx">isConnected</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1"># Do something sneaky during the CAPABILITY exchange. Remove &quot;DEFLATE&quot; from</span>
</span><span class='line'>    <span class="c1"># the list (if present) so this proxy doesn&#39;t have to decompress messages.</span>
</span><span class='line'>    <span class="k">if</span> <span class="nx">data</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">match</span><span class="p">(</span><span class="sr">/CAPABILITY.*COMPRESS=DEFLATE/</span><span class="p">)</span>
</span><span class='line'>      <span class="nv">minusCompress = </span><span class="nx">data</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">replace</span> <span class="s">&quot;COMPRESS=DEFLATE &quot;</span><span class="p">,</span> <span class="s">&quot;&quot;</span>
</span><span class='line'>      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;Proxy substitution: &quot;</span><span class="p">,</span> <span class="nx">minusCompress</span>
</span><span class='line'>      <span class="nx">connectionToClient</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">minusCompress</span><span class="p">)</span>
</span><span class='line'>    <span class="k">else</span>
</span><span class='line'>      <span class="nx">connectionToClient</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="nx">state</span><span class="p">.</span><span class="nx">conn</span><span class="p">.</span><span class="nx">connect</span> <span class="mi">993</span><span class="p">,</span> <span class="nx">IMAP_SERVER</span>
</span><span class='line'>
</span><span class='line'><span class="nv">server = </span><span class="nx">net</span><span class="p">.</span><span class="nx">createServer</span><span class="p">(</span><span class="nx">clientListener</span><span class="p">)</span>
</span><span class='line'><span class="nx">server</span><span class="p">.</span><span class="nx">listen</span> <span class="mi">3737</span><span class="p">,</span> <span class="nf">-&gt;</span>
</span><span class='line'>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;IMAP proxy is listening on port 3737&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>I hope this helps on your next IMAP adventure. And if you&rsquo;re passionate about IMAP, <a href="http://handl.theresumator.com/">Handle is hiring</a>!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Using GMail as a Cheap, Multi-User Relational Database]]></title>
    <link href="http://tobinindustries.com/blog/2009/10/18/using-gmail-as-a-cheap/"/>
    <updated>2009-10-18T22:08:00-07:00</updated>
    <id>http://tobinindustries.com/blog/2009/10/18/using-gmail-as-a-cheap</id>
    <content type="html"><![CDATA[<p>After many years of calling 849 Fell Street their home, my roommates Tom and Ted are moving out at the end of the month.
Jeremy and I have been relying principally on Craigslist to find new roommates. Rather than direct incoming applications
to either (or both) of our personal inboxes, we opted to create a new, special-use Gmail account (appropriately
named 849<a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#x3a;&#102;&#101;&#x6c;&#108;&#115;&#x74;&#x72;&#x65;&#101;&#x74;&#64;&#103;&#109;&#x61;&#105;&#x6c;&#46;&#99;&#111;&#109;">&#x66;&#x65;&#108;&#108;&#x73;&#x74;&#114;&#101;&#x65;&#x74;&#x40;&#103;&#x6d;&#97;&#105;&#108;&#x2e;&#99;&#x6f;&#109;</a>). That much isn&rsquo;t novel, but we&rsquo;ve also developed a quick and easy system
for categorizing and responding to applicants that I want to share.</p>

<!--more-->


<p>As a Rails app, it would be natural to break the data into a few tables: &ldquo;roommates&rdquo;, &ldquo;applicants&rdquo;, &ldquo;ratings&rdquo;,
&ldquo;appointments&rdquo;, and &ldquo;correspondences&rdquo;. In our case there are only two rows in the &ldquo;roommates&rdquo; table: Jeremy and me.
Every reply to our Craigslist ad creates rows in &ldquo;applicants&rdquo; and &ldquo;correspondences&rdquo;. Assuming Jeremy likes an
application, it creates a row in the &ldquo;ratings&rdquo; table (e.g. roommate: 1, applicant: 37, rating: &ldquo;approve&rdquo;). One of us
replies, creating a new row in &ldquo;appointments&rdquo;, etc. That&rsquo;s a lot of work, which is why I&rsquo;m happy to have found a way
to do the same thing inside Gmail.</p>

<p><img src="http://tobinindustries.com/images/using-gmail-as-a-cheap/list.png" title="Sample mailbox" >
<img class="right" src="http://tobinindustries.com/images/using-gmail-as-a-cheap/quick-links.png" title="Quick links" ></p>

<p>Because Craigslist correspondences happen over email anyway, every Gmail thread is a row in &ldquo;applicants&rdquo;, plus >1
row in &ldquo;correspondences&rdquo;. Instead of tables for &ldquo;ratings&rdquo; and &ldquo;appointments&rdquo;, labels can be used to track the status
of applicants (e.g. <code>JeremyLikes</code>, <code>JeremyPass</code>, <code>InterviewScheduled</code>, <code>RejectionSent</code>).</p>

<p>Generous labeling affords other useful tricks. Add the &ldquo;Quick Links&rdquo; lab and save custom, frequently used queries.
Here are some that we find useful:</p>

<ul>
<li><strong>Brian likes, Jeremy hasn&rsquo;t decided:</strong> <code>label:brianlikes AND -{label:jeremylikes label:jeremypass}</code></li>
<li><strong>Jeremy likes, Brian hasn&rsquo;t decided:</strong> <code>label:jeremylikes AND -{label:brianlikes label:brianpass}</code></li>
<li><strong>Needs &ldquo;thanks but no thanks&rdquo; note:</strong> <code>label:brianpass AND label:jeremypass AND -{label:rejectionsent}</code></li>
<li><strong>Let&rsquo;s set up an interview:</strong> <code>label:brianlikes label:jeremylikes -{label:interviewscheduled}</code></li>
<li><strong>Needs attention:</strong> <code>label:requiresfollowup OR -{label:brianlikes label:brianpass label:jeremylikes label:jeremypass}</code></li>
</ul>


<p>I hope this trick comes in handy the next time you need to collaboratively categorize and search some data. One
word of caution: as of this writing, there is a bug in Gmail involving labels containing dashes (I switched to
camel case after many frustrating and fruitless attempts to fix my search queries). Good luck!</p>
]]></content>
  </entry>
  
</feed>
