I finally got tired of using pine on remote machines to deal with my email. I've had a DSL for several months now, so there was no reason not to switch to reading it localy. Being a general fan of emacs, I turned to Gnus to deal with my email on my home machine Hesse. It looked like a very powerful and extremely customizable environment. It is. However, it is also an incredible pain to get working for anything other than a vanilla setup. The online documents, while verbose, are heavily geared towards news instead of mail and (IMHO) poorly organized. For my own quick introduction, read this.

Gnus can read mail from the local spool, read it from its own archives, or use pop or imap to grab it from a remote server. I recommend you use Fetchmail to get the mail and have Gnus read from the local spool. While the Gnus pop and imap work, they're harder to setup than Fetchmail, which was quite easy.

I lean towards paranoia when it comes to network connections, so I wanted all the connections to be secure. Fetchmail does support secured authentication, but I decided to tunnel the connection over ssh anyway. As you'll see, I had to set up tunneling for Gnus anyway, so doing it for Fetchmail wasn't much more work. To do this I have my .fetchmailrc call a script tunnel-saul-imap which creates a temporary tunnel:

ssh -f -L 1143:gfish.deskmail.washington.edu:143 -l gfish saul.u.washington.edu sleep 60

This connects to saul (using a key with no passphrase so it can be run from a daemon) via ssh and creates a tunnel from localhost:1143 to gfish.mailhost:143. 143 is the IMAP port, use 110 if you want POP instead. The sleep 60 is run on the far side. When it finishes (in 60 seconds) the connection closes and the tunnel disappears. I have a similiar script for connecting to orcas.cs.washington.edu.

My .fetchmailrc looks something like this:

poll gfish.deskmail.washington.edu via localhost port 1143
proto imap user "gfish" password "****" is gfish
preconnect "tunnel-saul-imap";

poll mrd.mail.cs.washington.edu via localhost port 1144
proto imap user "mrd" password "****" is gfish
preconnect "tunnel-orcas-imap";

Before it tries to connect to the imap servers, it runs the tunneling scripts. Because the connections are via localhost port 114x, it connects through the tunnel the scripts set up. All invisibly. Pretty cool, I think. Read the Fetchmail man page for more details.

So getting the mail to Hesse was fairly easy, and would be very easy if I hadn't been so anal about it. And getting Gnus to read the mail was very easy. A .gnus of just (setq gnus-select-method '(nnml "")) would do that. Sending, on the other hand, was a problem.

Sendmail (or other smtp daemon) already had to be running for Fetchmail to work. So sending mail as gfish@hesse.grendel.cx was trivial. But no one knows me as that. All my mail goes to either gfish@u.washington.edu or mrd@cs.washington.edu. To use the local mailhost would be a large pain, telling everyone to use my new address. And even telling sendmail to pretend to be u.washington.edu wouldn't work, because several mailing lists I'm on will bounce mail that looks forged. So I had to send through the U's server.

The following code in your .gnus will get it to use direct SMTP instead of sendmail:

(setq user-full-name "Matthew Dockrey")
(setq user-mail-address "gfish@u.washington.edu")
(setq smtpmail-default-smtp-server "localhost")
(setq smtpmail-local-domain nil)
(setq send-mail-function 'smtpmail-send-it)
(setq message-send-mail-function 'smtpmail-send-it)

Unfortunately, we live in an age of spam and network abuse, so you can't just set smtpmail-default-smtp-server to "u.washington.edu". It will not let you relay mail through it from outside the network (which I am, thanks to the DSL) as this is a common way to send spam. It will only accept connections from the UW network. If you think this sounds like a job for tunneling, you are correct. By adding (setq smtpmail-smtp-service 2525) you can make Emacs use port 2525 (or whatever) instead of 25. With another tunneling script I can tunnel localhost:2525 > saul:25.

This is trickier, though, because I could be sending mail at any time. And while for testing purposes I created scripts with sleep 1209600 I couldn't accept that as an elegant solution. So I had to add hooks in .gnus to create a temporary tunnel before I send each message, as Fetchmail does. At the same time, I wanted to send mail to three different servers (gfish.deskmail.u.washington.edu, mrd.mail.cs.washington.edu and hesse.grendel.cx) depending on the context (mrd if I'm in the mrd@cs folder, hesse if I'm in the gfish@hesse folder and saul as a default). I already had the infrastructure to use these servers, so it was just a matter of writing hooks to change settings depending on what folder I was in. The entire section from my .gnus:

(setq gfish-tunnel-method "saul")

;; localhost:2525 -> mailhost.u.washington.edu:25
(defun gfish-tunnel-saul-smtp ()
  (call-process "/home/gfish/bin/tunnel-saul-smtp" nil 0)
  (sleep-for 2)
)

;; localhost:2526 -> cs.washington.edu:25
(defun gfish-tunnel-orcas-smtp ()
  (call-process "/home/gfish/bin/tunnel-orcas-smtp" nil 0)
  (sleep-for 2)
)

;; Create the correct tunnel for sending the message
(add-hook 'message-send-hook
          (function (lambda ()
		      (cond ((string-match "saul" gfish-tunnel-method)
			     (gfish-tunnel-saul-smtp))
			    ((string-match "orcas" gfish-tunnel-method)
			     (gfish-tunnel-orcas-smtp)))))
)

;; Put everything back to the default send method
(defun gfish-send-default ()
  (setq smtpmail-smtp-service 2525)
  (setq send-mail-function 'smtpmail-send-it)
  (setq message-send-mail-function 'smtpmail-send-it)
  (setq gfish-tunnel-method "saul")
  (setq user-mail-address "gfish@u.washington.edu")
  (setq message-signature-file "~/.signature")
)

;; Change the mail settings to send in different ways depending on group
(add-hook 'gnus-select-group-hook
          (function (lambda ()
	       (cond ((string-match "mrd@cs" gnus-newsgroup-name)
		      ;; Send through orcas smtp
		      (setq smtpmail-smtp-service 2526)
		      (setq send-mail-function 'smtpmail-send-it)
		      (setq message-send-mail-function 'smtpmail-send-it)
		      (setq gfish-tunnel-method "orcas")
		      (setq user-mail-address "mrd@cs.washington.edu")
                      (setq message-signature-file "~/.mrd-signature"))
		     ((string-match "gfish@hesse" gnus-newsgroup-name)
		      ;; Send through hesse sendmail
		      (setq send-mail-function
			    'message-send-mail-with-sendmail)
		      (setq message-send-mail-function 
			    'message-send-mail-with-sendmail)
		      (setq gfish-tunnel-method "nil")
		      (setq user-mail-address "gfish@hesse.grendel.cx")
		      (setq message-signature-file "~/.signature"))
		     (t
		      ;; Default to saul smtp
		      (gfish-send-default)
		      ))))
)

(add-hook 'gnus-exit-group-hook 'gfish-send-default)

Yeah, its a mess. But it works. Basicaly, the gnus-select-group-hook is set to a lambda function that tweaks all the send method variables depending on what group is being entered. gnus-exit-group-hook sets everything back to the default. The variable gfish-tunnel-method holds a string describing the send method desired. message-send-hook calls the tunnel creation appropriate to the send method.

All in all it works. Sometimes the tunnel isn't started in time when I send a message, but if I send it again immediately it always works. I'll play with it some more eventually. But for now I'm pretty happy with it. Here is my compelte .gnus file. It does a lot of other things, but most of them are costmetic and just my personal tastes.