Working with email offline

Posted in:

I'm currently without internet connection to my own computer, and I've been working out ways to make send and receiving e-mail as seamless as possible. I thought I'd document my procedure, with scripts, for those who find themselves in the same predicament.

Thankfully, although the machines I'm using to connect to the net are Windows machines, I do have access to a Linux box that is connected to the net with an ssh account I can log in to. Here is my setup for e-mail:

Sending

I'm still using KMail for composing and sending e-mail. I've set up a 'sendmail' item for sending, but pointed it at my own python script, which, instead of attempting to send the e-mail, saves it as a file on a folder in my USB stick (or complains if I've forgotten to plug it in). Here is the script:

#!/usr/bin/python

# Dump stdin in a file with unique name

from datetime import datetime
import operator
import sys
import os
import random
from copy import deepcopy
import itertools

DUMPDIR = '/media/sda1/mail/outgoing'

def mk_filename():
    "Creates a unique filename"
    return (reduce(operator.add,
                  [chr(random.randint(ord('A'),
                  ord('Z'))) for i in xrange(0,10)]) \
                  + '.' +
                  datetime.isoformat(datetime.now())).replace(':','_')

def dump_file(recips):
    if len(recips) == 0:
        print "No recipients found."
        sys.exit(1)

    fname = mk_filename()
    fn = os.path.join(DUMPDIR, fname + ".msg")
    fp = open(fn, 'w')
    for line in sys.stdin:
        fp.write(line)
    fp.close()

    fn2 = os.path.join(DUMPDIR, fname + ".recips")
    fp2 = open(fn2, 'w')
    fp2.write(' '.join(recips))
    fp2.close()

def prompt_for_continue():
    prompt = "Cannot find directory %s for saving mail.  " \
             "Create directory or mount device and press
             'Continue', " \
             "or cancel."  % (DUMPDIR,)
    exit_status = os.system('kdialog --warningcontinuecancel
    "%s"' % prompt)
    return exit_status == 0

def check_dir_and_dump(recips):
    if not os.path.isdir(DUMPDIR):
        if prompt_for_continue():
            check_dir_and_dump()
        else:
            sys.stderr.write("Cancelled.")
            sys.exit(1)
    else:
        dump_file(recips)

# For BCC, have to read recipients from command line,
# and then for simplicity create separate files for each, with a
# 'To' header added

def get_recipients():

    recipients = deepcopy(sys.argv)
    recipients = recipients[1:] # name of this shell script

    cont = True
    i = 0
    while (cont):
        if i >= len(recipients):
            cont = False
        else:
            if recipients[i] == '-i':
                recipients.pop(i)
            else:
                if recipients[i] == '-f':
                    recipients.pop(i)
                    recipients.pop(i)
                else:
                    i += 1
    return recipients

check_dir_and_dump(get_recipients())
os.system('sync')

Then, on the machine that is connected it to the internet, which I'll use perhaps once a day, I transfer the files from my USB stick to my Linux box on the net via ftp. I also log in via ssh, using PuTTy, and run a script with sends all the emails in the 'outgoing' folder, deleting them if successful. To send these e-mails, I use 'msmtp', which can easily be downloaded, compiled and installed locally:

./configure --prefix=~/local && make && make install

(Needs ~/local to exist, probably, and needs ~/local/bin on your path to use the installed binary.)

Then, the script to send all the e-mails is just something like:

#!/bin/bash
cd $HOME
for TMP in ~/lp/mail/outgoing/*.msg;
do
  echo $TMP
  RECIPFILE=${TMP%%.msg}.recips
  RECIPS=$(cat $RECIPFILE);
  msmtp $RECIPS < $TMP || exit 1;
  rm $TMP $RECIPFILE
done

(once you've created a config file for msmtp).

Receiving

Currently, I check my e-mail using Fastmail's web interface, and at that point deal with all the spam, and delete other e-mail that there is no point transfering, and answer some e-mails. What needs to be transfered goes into my 'received' folder, and you can then use Fastmail's 'Archive' feature to take all the e-mails in a folder and download them as a zip file. This zip file is saved back onto my USB stick in a specific folder, and taken back to my computer.

Once on my computer, I have another script which imports all the e-mail in that folder into KMail, removing them from the USB stick.

#!/bin/bash

# Attempt to import all files on removable device into KMail

INCOMING_DIR=/media/sda1/mail/incoming
EXTRACT_DIR=/home/luke/download/mail

die_loudly() {
    # kdialog --error "$1"
    echo "$1"
    echo
    exit 1
}

ps ax | egrep 'kontact|kmail' > /dev/null || { die_loudly "KMail isn't running, can't import messages" ; }

if [ \! -d "$INCOMING_DIR" ]
then
    die_loudly "$INCOMING_DIR cannot be found"
fi

if [ \! -d "$EXTRACT_DIR" ]
then
    die_loudly "$EXTRACT_DIR cannot be found"
fi

mv $INCOMING_DIR/*.zip $EXTRACT_DIR || die_loudly "Can't move files from device"
cd $EXTRACT_DIR
unzip -o *.zip
for FILE in *.eml
do
  echo dcop kmail KMailIface dcopAddMessage "inbox" \"file://$PWD/$FILE\" ""
  retval=`dcop kmail KMailIface dcopAddMessage "inbox" "file://$PWD/$FILE" ""`
  if [ $? -ne 0 ]
  then
      die_loudly "Failed to import $FILE"
  fi
  if [ $retval -ne 1 ]
  then
      die_loudly "Failed to import $FILE"
  fi
  rm "$FILE"
done

rm $EXTRACT_DIR/*.zip

I normally run this from a console (Yakuake, to be precise, which is only ever 'F12' away), so I can see any error messages. Otherwise I'd change the 'die_loudly' function to use kdialog.

This is of course quite a bit of a faff, but it's doable. The methods and scripts are robust against forgetting to do it some days. If I wasn't using Windows boxes for connecting to the net, or if it was always the same box and I was allowed to install any software on, things would be better. As it is, 'PuTTy', which is a single, small executable, is the only thing I have to carry around with me. Also, if for whatever reason I'm reduced to only the web interface of Fastmail, I'm OK – the Linux box isn't periodically retrieving my mails by POP or anything like that, and I only need it for sending e-mails I've prepared on my own machine.

UPDATED: Fixed scripts to handle BCC and other recipients that are passed only on the sendmail commandline.

This is my personal blog, and does not necessarily reflect the opinions of my clients, my employer or my church.

Comments §

Comments should load when you scroll to here...