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.

Comments §

Comments should load when you scroll to here...