Switch between SMTP mail servers in Gnus using smtpmail

Tagged:  

OK, it has happened - one of the domains I frequently write mails under has published a SPF record, so I cannot post through my own mail server anymore (well, I can, but I have a good chance the mail will not be accepted or be flagged as spam).

I have struggled with SPF before, and I am still not convinced it is worth the hustle, but that's another topic. Anyway, as a result, I have to switch between mail servers, dependend on the address I am writing under. I guess I could configure my mail server to somehow relay the mail to the proper host who is responsible for the domain, but I thought it would be more straightforward to do in Emacs/Gnus.

Turns out it is really not that straightforward… but here's how you can do it:

I use the smtpmail package, which ships with Emacs, since I regularly switch between machines and don't necessarily have a proper MTA installed on all of them (that's also the reason why I don't use msmtp). If you also use smptmail, you should already have a basic setup which probably looks similar to mine:

(setq smtpmail-default-smtp-server "primary-mailer.default.invalid") ; needs to be specified before the (require)
(require 'smtpmail)
(setq smtpmail-mail-address "primary-address@domain.invalid")
(setq send-mail-function 'smtpmail-send-it)
(setq message-send-mail-function 'smtpmail-send-it)
;; enable debugging output
(setq smtpmail-debug-info t)
;; use encrypted authinfo file
(setq smtpmail-auth-credentials (expand-file-name "~/accounts.gpg"))
(setq smtpmail-starttls-credentials
      '(("primary-mailer.domain.invalid" 25 nil nil)
        ("another-mailer.domain.invalid" 25 nil nil)
        ("yet-another-one.domain.invalid" 465 nil nil)))
(setq starttls-use-gnutls t)
(setq starttls-gnutls-program "gnutls-cli")
(setq starttls-extra-arguments nil)

As you can see, this setup uses gnutls for encrypting the connections to the server. Also, I use an encrypted authinfo file, which has the following structure:

machine primary-mailer.domain.invalid login NAME port 25 password SECRET
machine another-mailer.domain.invalid login NAME port 25 password SECRET
machine yet-another-one.domain.invalid login NAME port 465 password SECRET

Note that you absolutely have to specify the correct ports here, otherwise the login credentials won't be used!

The smptmail package does not directly support server switching, so I use the following code:

(setq DE-smtpmailer-alist 
      '((".*@\\(firstdomain.invalid\\|seconddomain.invalid\\)" "primary-mailer.domain.invalid")
        ("name@work.invalid" "another-mailer.domain.invalid")
        ("name@otherwork.invalid" "yet-another-one.domain.invalid")))

(defun DE-set-smtp-server ()
  (let* ((from (cadr
                (mail-extract-address-components
                 (message-field-value "from"))))
         (mailer (car (assoc-default from DE-smtpmailer-alist
                                     'string-match smtpmail-default-smtp-server))))
    ;; set mailer address and port
    (setq smtpmail-smtp-server mailer
          smtpmail-smtp-service 
          (nth 1 (assoc mailer smtpmail-starttls-credentials)))
    ;; if wanted, set envelope address to 'from'
    (when mail-specify-envelope-from
      (setq mail-envelope-from from))))

;; manually set envelope address
(setq mail-specify-envelope-from t)

(add-hook 'message-send-hook 'DE-set-smtp-server)

The variable DE-smtpmailer-alist contains lists with each having a regular expression and the mailer for matching addresses. The code sets the name and port of the mailer (it gets the latter from smtpmail-starttls-credentials, so make sure it is correct). If mail-specify-envelope-from is t, it also sets the envelope from address.

About a year ago I had uploaded some of my similar scripts to emacswiki[1]. HTH. [1] http://www.emacswiki.org/emacs/MultipleSMTPAccounts
Damn, why didn't Google find that? I should have spend more time optimizing my search... Additionally, EmacsWiki was down yesterday, so I couldn't browse it. Anyway, thanks for the pointer!