Image of the glider from the Game of Life by John Conway
Skip to content

Verifying Hashcash Tokens With Mutt

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're using it). Well today, I finished writing my verification script. It takes some additional changes to your ~/.muttrc, which I'll outline here, and it requires the installation of a Python script. Of course, as previous, I'm assuming that you're running at least Python 2.5 and the latest version of Hashcash. With that, let's get busy.

First, the necessary changes to your "~/.muttrc" config. The script relies on the "X-Hashcash:" header (as well as the "Hashcash:" 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's valid. If you hid the hashcash headers, then the script won't execute, and as a result, won'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's the changes you will need to make:

# file: ~/.muttrc
ignore *                                    # draconian header weed - recommended
unignore from date subject to cc user-agent # standard headers unignored - recommended
unignore x-hashcash hashcash                # required

You will also need to set the $display_filter variable. I like having my theme consistent, so I've also added color to my theme to show the Hashcash verification at the top of the mail:

# file: ~/.muttrc
set display_filter="/path/to/verify_hashcash.py"    # required
color body brightyellow default "^\$$!--.*Hashcash*" # recommended

Now with that set, all we need to do is install the Python script, and we're ready to go. When looking over the code, you'll notice that it's creating a database of spent tokens. I've placed the database in ~/.mutt/, seeing as though I only have Mutt working with Hashcash at the moment (and it'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's pointing to the right file. Also, we'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'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.

With that said, here's the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env python
# Licensed under the public domain

import rfc822
import StringIO
import subprocess
import sys

# Change the DB path in COMMAND as needed, and change your email address
COMMAND="hashcash -cdb '%s' -r '%s' -f /home/user/.mutt/hashcash.db '%s'"
EMAILADDR="foo@bar.com"

tokens = []
token_status = []

# converting a list to a file-type object for parsing rfc822 headers
original = sys.stdin.read()
emailmsg = StringIO.StringIO(''.join(original))
message = rfc822.Message(emailmsg)

# check for the presence of "X-Hashcash" and "Hashcash" headers
if message.has_key("X-Hashcash"):
    for hc_list in message.getheaders("X-Hashcash"):
        tokens.append(hc_list)
if message.has_key("Hashcash"):
    for hc_list in message.getheaders("Hashcash"):
        tokens.append(hc_list)

# check each token
if tokens:
    token_status.append("[-- Begin Hashcash output --]")
    for hc_token in tokens:
        if hc_token.split(":")[3] == EMAILADDR:
            hc_bits = hc_token.split(":")[1]
            hc_resource = hc_token.split(":")[3]
            p = subprocess.Popen(COMMAND % (hc_bits,hc_resource,hc_token),
                shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
            out = p.stderr.read().strip()
            token_status.append(out)
        else:
            token_status.append("No valid tokens for %s found." % EMAILADDR)
    token_status.append("[-- End Hashcash output --]")

print >> sys.stdout, ''.join(message.headers)
for status in token_status:
    print >> sys.stdout, ''.join(status)
if tokens:
    print ''
emailmsg.seek(message.startofbody)
print >> sys.stdout, ''.join(emailmsg.readlines())

One thing I do find odd about the code above, is the hashcash binary, when checking tokens, prints to STDERR rather than STDOUT. I'm guessing this could change in the future, so that's something that will need to be watched out for.

So far, the Python script has been working flawless for me. I haven't noticed a single hiccup, and it'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'm all ears. At any rate, I hope you find it helpful, should you wish to get into Hashcash with Mutt.

{ 3 } Comments