This shows you the differences between two versions of the page.
email:better_mobile_email [2016/02/01 21:13] phil created |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Better Mobile Email ====== | ||
- | |||
- | [[http:// | ||
- | notebook' | ||
- | differences are: | ||
- | |||
- | * I moved from '' | ||
- | * Instead of having a full-fledged MTA running on my notebook, I prefer a small send-only MTA for the sake of small footprint and ease of configuration. | ||
- | |||
- | Here's how the setup is done in particular: | ||
- | |||
- | ===== The Big Picture ===== | ||
- | |||
- | When it comes to using the same email account on multiple machines, IMAP is a | ||
- | great benefit. It allows for every box to see every incoming mail and | ||
- | housekeeping tasks such as deleting old mails or reorganizing them into | ||
- | subfolders will automatically be visible from the other machines, as well. By | ||
- | storing sent mail in an IMAP folder the same applies to outgoing mail, too. | ||
- | |||
- | The only downside with typical IMAP usage is that the task of email | ||
- | synchronization and caching has to be done by the email client (MUA). This | ||
- | naturally involves delays during client usage, and often clients are not | ||
- | really usable without direct connectivity to the IMAP server. | ||
- | |||
- | My personal MUA of choice is '' | ||
- | since handling mail in local maildirs is strikingly fast, the added delay of | ||
- | having to sync with IMAP feels much higher than with other email clients. | ||
- | |||
- | In order to overcome this, an asynchronous approach serves well: A dedicated | ||
- | instance covers the task of bidirectional synchronization between remote IMAP | ||
- | and local maildirs, while '' | ||
- | |||
- | Using '' | ||
- | connectivity, | ||
- | be directly experienced by '' | ||
- | with an error message). But even if connectivity is given, any delay and/or | ||
- | bandwidth limitation on the line to the MTA is directly appreciable. | ||
- | |||
- | Here, an asynchronous approach is chosen as well: A dedicated instance takes | ||
- | care of queueing and ultimately delivering the email to be sent and '' | ||
- | returns control to the user as soon as it has dispatched the job. | ||
- | |||
- | ===== Bidirectional IMAP Synchronization ===== | ||
- | |||
- | There are a number of tools to achieve this, but the only two which proved | ||
- | usable to me were '' | ||
- | superior, not least due to the fact that it is written in C. | ||
- | |||
- | ==== Mbsync Configuration File ==== | ||
- | |||
- | '' | ||
- | to understand it's semantics, a number of concepts have to be known by the | ||
- | user: | ||
- | |||
- | * A '' | ||
- | |||
- | * A '' | ||
- | |||
- | With these two in mind, let's start by defining two stores. The first one | ||
- | defines the local maildir: | ||
- | |||
- | < | ||
- | MaildirStore local | ||
- | Path ~/ | ||
- | Inbox ~/Maildir/ | ||
- | SubFolders Maildir++ | ||
- | </ | ||
- | |||
- | The way this is defined is a bit unintuitive: | ||
- | the actual maildir path, it is set to it's parent and '' | ||
- | point to the top-most maildir. I don't really remember why, but this was | ||
- | important to get my setup working. The second store defines the remote IMAP: | ||
- | |||
- | < | ||
- | IMAPStore remote | ||
- | Host mail.nwl.cc | ||
- | User myself | ||
- | SSLType IMAPS | ||
- | </ | ||
- | |||
- | Obviously, this store defines the remote host running the IMAP service, the | ||
- | user name to use for authentication and that IMAPS should be used. If not | ||
- | using SASL authentication, | ||
- | using the '' | ||
- | |||
- | With both endpoints being defined, channels can be declared. The following | ||
- | defines two channels between those peers: One for the inbox only, the other | ||
- | for all subfolders. This way synchronisation of the INBOX can be triggered | ||
- | separately from the subfolders, which becomes useful later on: | ||
- | |||
- | < | ||
- | Channel inbox | ||
- | Master : | ||
- | Slave : | ||
- | |||
- | Channel folders | ||
- | Master :remote: | ||
- | Slave : | ||
- | Patterns * !INBOX !Chats !Contacts !Emailed\ Contacts !Junk | ||
- | </ | ||
- | |||
- | A few things are noteworthy about the used semantics: | ||
- | |||
- | * Each channel defines which of the peers is master and which is slave. This is important when defining how synchronisation should take place. Apart from that, these two keywords are interchangeable. | ||
- | |||
- | * The IMAP server exposes the inbox as a subfolder on it's own, therefore the first channel specifies this explicitly. | ||
- | |||
- | * The second channel makes use of the '' | ||
- | - The asterisk ('' | ||
- | - The exclamation mark ('' | ||
- | |||
- | Finally, the above store and channel definitions are prepended by a few global | ||
- | statements: | ||
- | |||
- | < | ||
- | Expunge Both | ||
- | Create Both | ||
- | Remove Both | ||
- | |||
- | SyncState * | ||
- | </ | ||
- | |||
- | The '' | ||
- | synchronization process. Instead of '' | ||
- | '' | ||
- | recommended measure against unwanted loss of data during testing. | ||
- | |||
- | '' | ||
- | asterisk ('' | ||
- | itself. | ||
- | |||
- | ==== Calling Mbsync ==== | ||
- | |||
- | To manually trigger synchronization of a defined channel, a direct call to | ||
- | '' | ||
- | |||
- | < | ||
- | mbsync < | ||
- | </ | ||
- | |||
- | ==== Looping Over Mbsync Calls ==== | ||
- | |||
- | To integrate this nicely into the rest of my setup, I have written a shell | ||
- | script which provides a few more goodies: | ||
- | |||
- | * Run indefinitely. | ||
- | * Before every round of synchronization, | ||
- | * Synchronize the inbox every 10sec, the IMAP folders just once every 100sec. | ||
- | * After every folder sync run, fetch a list of existing maildir subfolders into // | ||
- | |||
- | <file bash mbsyncloop> | ||
- | #!/bin/bash | ||
- | mailboxes=" | ||
- | |||
- | do_mbsync() { # (channel) | ||
- | echo -e " | ||
- | mbsync $1 && echo -e "... done" || echo -e "... failed" | ||
- | } | ||
- | |||
- | get_mailboxes() { # (maildir) | ||
- | echo -n " | ||
- | find " | ||
- | mbox=" | ||
- | [[ $mbox == " | ||
- | mbox=" | ||
- | echo -n " \" | ||
- | done | ||
- | echo "" | ||
- | } | ||
- | |||
- | update_mailboxes() { | ||
- | mboxes=" | ||
- | [[ -f $mailboxes ]] || { | ||
- | echo " | ||
- | echo -n $mboxes > " | ||
- | return | ||
- | } | ||
- | old_mboxes=" | ||
- | [[ " | ||
- | echo " | ||
- | echo -n $mboxes > " | ||
- | } | ||
- | } | ||
- | |||
- | imaphost=" | ||
- | |||
- | # sync INBOX every 10 seconds, | ||
- | # sync subfolders every minute | ||
- | cnt=0 | ||
- | while true; do | ||
- | ping -c 1 $imaphost >/ | ||
- | echo "Host $imaphost not responding" | ||
- | sleep 10 | ||
- | continue | ||
- | } | ||
- | do_mbsync inbox | ||
- | [[ $((cnt % 10)) -eq 0 ]] && { | ||
- | cnt=0 | ||
- | do_mbsync folders | ||
- | update_mailboxes " | ||
- | } | ||
- | ((cnt++)) | ||
- | sleep 10 | ||
- | done | ||
- | </ | ||
- | |||
- | ===== Relay-Only MTA ===== | ||
- | |||
- | My notebook does not receive email from external hosts. For local delivery to | ||
- | maildirs, '' | ||
- | for an MTA on my notebook to do is to get my user's email out to the right MX | ||
- | using appropriate credentials. And all that in background, without disturbing | ||
- | my workflow in case something (Wifi, uplink, VPN, MX, etc.) does not work as | ||
- | it should. | ||
- | |||
- | Back then when I was searching for a relay-only MTA with queueing support, I | ||
- | didn't find any which felt comfortable. Therefore I sticked to '' | ||
- | using already and wrote a little shell-wrapper implementing the desired | ||
- | enhancement. This shell wrapper has been accepted into the official code | ||
- | repository back in 2007, but sadly the whole project was orphaned in 2011. | ||
- | Though, Gentoo luckily still ships it. Probably I should fork the project and | ||
- | revive it by implementing queueing support in C. Anyway, here's how to set | ||
- | things up using the latest release 1.2: | ||
- | |||
- | ==== Esmtp Configuration File ==== | ||
- | |||
- | The configuration file is quite straightforward. The only thing worth | ||
- | mentioning in beforehand is '' | ||
- | choose a different SMTP upstream based on sending address. Here's the config I | ||
- | use: | ||
- | |||
- | <file esmtprc> | ||
- | mda = '/ | ||
- | |||
- | hostname = smtp.corp.example.com | ||
- | force reverse_path = myself@example.com | ||
- | |||
- | identity = myself@nwl.cc | ||
- | preconnect = "ssh -f -L 25025: | ||
- | hostname = localhost: | ||
- | force reverse_path = myself@nwl.cc | ||
- | </ | ||
- | |||
- | The first line ('' | ||
- | recipients not containing an @-sign. Piping through '' | ||
- | useful '' | ||
- | |||
- | The default delivery method is via '' | ||
- | authentication (only reachable via VPN). Whatever '' | ||
- | '' | ||
- | |||
- | A second identity defines my private mail setup. It establishes an SSH tunnel | ||
- | prior to connecting, which is convenient for me as it relieves me from having | ||
- | to specify login credentials here and SSH Agent is running anyway. | ||
- | |||
- | ==== Esmtp Queueing Wrapper ==== | ||
- | |||
- | Now for queueing support: The whole magic is implemented by my shell wrapper, | ||
- | '' | ||
- | symlinks (it changes it's operation mode depending the name by which it was | ||
- | called): | ||
- | |||
- | < | ||
- | % find bin -lname esmtp-wrapper | ||
- | bin/mailq | ||
- | bin/deliver | ||
- | bin/ | ||
- | </ | ||
- | |||
- | The commands to what their name suggests: '' | ||
- | queue, '' | ||
- | '' | ||
- | |||
- | Upon invocation, '' | ||
- | with the mail body passed via '' | ||
- | // | ||
- | |||
- | Upon invocation, '' | ||
- | '' | ||
- | to catch mass mail delivery like e.g. git-send-email typically does). Then it | ||
- | continues to browse through any directories below // | ||
- | calls '' | ||
- | succeeds, the temporary directory is deleted, otherwise it's kept for a later | ||
- | try. | ||
- | |||
- | ==== Periodic Mail Queue Flushing ==== | ||
- | |||
- | If initial mail delivery fails, it has to be retried later. Since '' | ||
- | does not run as a daemon, there is no automatic way to achieve this. Instead, | ||
- | a simple cron job serves well: | ||
- | |||
- | < | ||
- | */10 * * * * /bin/ping -c1 smtp.corp.example.com >/ | ||
- | </ | ||
- | |||
- | This checks connectivity every 10min and calls '' | ||
- | |||
- | ===== Wrapping Things Up ===== | ||
- | |||
- | The last piece of the puzzle is '' | ||
- | actually does not know about '' | ||
- | is to specify is the path to '' | ||
- | |||
- | < | ||
- | set sendmail=~/ | ||
- | </ | ||
- | |||
- | What is more interesting is how to conveniently run '' | ||
- | '' | ||
- | |||
- | <file screenrc.mutt> | ||
- | source .screenrc | ||
- | screen -t SYNC 0 mbsyncloop | ||
- | screen -t MUTT -h 0 1 mutt_then_killall_mbsyncloop | ||
- | bind c screen mutt | ||
- | bind ^c screen mutt | ||
- | </ | ||
- | |||
- | When screen is invoked passing this config via it's '' | ||
- | automatically create two windows: | ||
- | |||
- | * Window 0 runs '' | ||
- | * Window 1 has no scrollback buffer ('' | ||
- | |||
- | Finally, it remaps the '' | ||
- | new screen window to do just the same but automatically run another instance | ||
- | of '' | ||
- | '' | ||
- | (e.g. with composing an email). | ||
- | |||
- | Finally, the last puzzle piece' | ||
- | does just what it's name (subtly) suggests: | ||
- | |||
- | <file bash mutt_then_killall_mbsyncloop> | ||
- | #!/bin/bash | ||
- | |||
- | mutt; killall mbsyncloop | ||
- | </ | ||
- | |||
- | The idea here is that once the initial instance of '' | ||
- | kills the running '' | ||
- | '' | ||
- | |||
- | ===== Links ===== | ||
- | |||
- | * [[http:// | ||
- | * [[http:// | ||