GnusSolution
GnusSolution copied to clipboard
A complete working solution of gnus+offlineimap+dovecot+msmtp+cron
#+TITLE: Complete Gnus
SWITCHED TO mu4e, THE CODE IS PROVIDED AS IT-IS, MAY NOT UPDATE IT ANYMORE.
This repo demonstrate one complete and working solution to reading/writing mails in Gnus, with the help of =offlineimap=, =dovecot=, =msmtp= and =cron=. Concretely, this repo contains
- necessary configuration files for each component,
- a brief summary of the big picture and detailed explanation on how the components work together, and
- helpful resource links.
/Note: The conf is tested under Ubuntu, it should work fine for all Linux distributions provided each component be properly installed./
- Dependencies :PROPERTIES: :CUSTOM_ID: sec:dependencies :END:
- gnus, http://www.gnus.org/
- offlineimap, http://www.offlineimap.org/
- dovecot, https://www.dovecot.org/
- msmtp, http://msmtp.sourceforge.net/
- cron, https://directory.fsf.org/wiki/Vixie-cron
- Requirements :PROPERTIES: :CUSTOM_ID: sec:requirements :END:
- Basic knowledge of Linux environment
- Comfortable with command line.
- Good knowledge of =emacs= and =gnus=.
- How to Use :PROPERTIES: :CUSTOM_ID: sec:how-to-use :END:
Make sure all dependencies are installed properly and stop =dovecot= service if it is running in background.
-
/Edit/ and copy =msmtprc=, =netrc= and =offlineimaprc= to =~/.msmtprc=, =~/.netrc= and =~/.offineimaprc=. Note that ~.netrc~ need to be set to mode ~600~, otherwise ~msmtp~ may complain.
-
Edit =10-mail.conf= (detailed below).
-
Invoke =offlineimap= manually. Sit back and have a cup of coffee, it will takes quite some time to download all the mails for the first time. Once finished, you should be able to see a similar folder structure.
#+BEGIN_EXAMPLE ~/Mail/ ├── archive ├── cur ├── drafts ├── Gmail │ ├── archive │ │ ├── cur │ │ ├── new │ │ └── tmp │ ├── flagged │ │ ├── cur │ │ ├── new │ │ └── tmp │ ├── important │ │ ├── cur │ │ ├── new │ │ └── tmp │ ├── inbox │ │ ├── cur │ │ ├── new │ │ └── tmp │ ├── sent │ │ ├── cur │ │ ├── new │ │ └── tmp │ └── trash │ ├── cur │ ├── new │ └── tmp ├── new └── tmp #+END_EXAMPLE
-
Start =dovecot= by =sudo service dovecot start=.
-
Register the =cron= job by =crontab cron-jobs=.
-
Open =emacs= and fire up =gnus=. And that's it!
- The Big Picture :PROPERTIES: :CUSTOM_ID: sec:the-big-picture :END:
This section gives a brief summary of what each component does, and does not if necessary.
-
offlineimap :: It does mainly two things:
- retrive mail from server to local folder, and
- sync flags on local mails to server. For example, when you finish reading a mail in =gnus=, the mail is marked as /read/; =offlineimap= will sync this /read/ flag to the server to make sure that the mail on the server is also marked as /read/.
It does NOT do two things,
- send mails, which is done by =msmtp=;
- invoke itself, which is done by =cron=.
-
msmtp :: It sends mails. It will be automatically invoked by =gnus=. You never need to manually start the program yourself. Actually most of the time you are not even aware of its existence.
-
dovecot :: It serves mails. =dovecot= is a simple yet powerful mail server. Wait, why do we ever need a local server? =offlineimap= already downloads all the mails to local folders, and =gnus= /can/ directly read local folders, why bother with another mail server? Because =gnus= sometimes messes up the IMAP mails. =gnus= is known to have problems properly handling flags, e.g., /read/, on mails. Instead of using /gnus/ to directly messing up with mail, we use a delicate mail server, =dovecot= to interact with mails professionally. In this case, =gnus= only tells =dovecot= what it wants to do and =dovecot= does it accordingly. For example, when finishing reading a mail in =gnus=, =gnus= notifies =dovecot= that this mail should be marked as /read/, =dovecot= does it accordingly. And as you already know it, this /read/ flag with be synced by =offlineimap=.
-
cron :: Invoke
offlineimap
periodically. =offlineimap= does not run by itself magically, we need to invoke =offlineimap= periodically to sync between local and remote. You may also invoke =offlineimap= manually if that's how want it LOL. -
gnus :: You read/write/reply/... mails in it. Basically it is a interface where you interact with mails. Although =gnus= can actually finished all aforementioned jobs by itself (woooof), I decide to use professional utilities to handle what it is best at.
-
netrc :: The
netrc
file stores the password. It has to be set to mode600
(read/write by current login user only) to work properly.
- Devil in the detail :PROPERTIES: :CUSTOM_ID: sec:devil-in-the-detail :END:
This section explains in detail configuration of each component. Note that there are many possible working configurations available, what's outlined here is just one of them (as a result of my years of frustration).
** offlineimap :PROPERTIES: :CUSTOM_ID: subsec:offlineimap :END:
Full configuration in [[file:offlineimaprc]].
The configuration is minimum yet fully functional. Most of the configuration are self-evident except for, perhaps, the =nametrans=.
According to the official [[http://www.offlineimap.org/doc/versions/v6.5.6/nametrans.html#nametrans][document on =nametrans=]], it allows you to have different folder name other than the names on the remote server. For example, when login in Gmail, you will see folders [fn:1], e.g., =Sent Mail= for sent mails, =Trash= for deleted mails, etc. Different mail server may name these folders differently. If you want a unified names locally, you can use =nametrans= features to map a remote folder to the local folder with a different name, e.g., =sent= for sent mails that syncs with =Sent Mail=, =trash= for deleted mails that syncs with =Trash=, etc.
In my configuration, =nametrans= takes a Python function, with sole parameter =folder=, i.e., the remote folder name in string.
=folderfilter= controls which folders to sync. Please refer to [[http://www.offlineimap.org/doc/versions/v6.5.6/nametrans.html#folderfilter][document on =folderfilter=]] for more details.
** msmtp :PROPERTIES: :CUSTOM_ID: subsec:msmtp :END:
Full configuration in [[file:msmtprc]].
The configuration is just standard. I copied the configuration from online.
** dovecot :PROPERTIES: :CUSTOM_ID: subsec:dovecot :END:
Full [fn:2] configuration in [[file:10-mail.conf]].
There is only one change made in =10-mail.conf= which usually resides in =/etc/dovecot/conf.d/=.
** cron :PROPERTIES: :CUSTOM_ID: subsec:cron :END:
The cron job to invoke =offlineimap= periodically is listed in [[file:cron-jobs]].
Register this cron job, the =cron= will invoke =offlineimap= periodically.
** gnus :PROPERTIES: :CUSTOM_ID: subsec:gnus :END:
Full configuration in [[file:gnus-conf.el]].
This is the most /frustrating/ part. I copied my full configuration here just for your information. However, the essential part that makes it /just work/ are detailed as follows.
*** Connect to =dovecot=
#+BEGIN_SRC emacs-lisp (setq gnus-select-method '(nnimap "LocalMail" (nnimap-address "localhost") (nnimap-stream network) (nnimap-server-port 143))) #+END_SRC
Remember that we have a local mail server, =dovecot=, running? The above code connects =gnus= to =dovecot=. Whenever we/gnus want to read mails, =gnus= notifies =dovecot= which retrieves mails from local folder and send it to =gnus=.
*** Read mails
#+BEGIN_SRC emacs-lisp (setq mail-user-agent 'gnus-user-agent) (setq read-mail-command 'gnus) #+END_SRC
The above code notifies =emacs= that we want to use =gnus= to handle mails, since there are other options, e.g., =rmail=.
*** Send mails
#+BEGIN_SRC emacs-lisp (setq send-mail-function 'message-send-mail-with-sendmail) (setq sendmail-program "msmtp") #+END_SRC
The above code specifies that we want to use =msmtp= to send mails. Basically when finish editing a mail in =gnus=, you hit C-c C-c, the =gnus= automatically invoke =msmtp= to send mails.
**** Where to store sent mails
#+BEGIN_SRC emacs-lisp (setq gnus-message-archive-group (("Tiger" "nnimap+tiger:Tiger/sent") ("Gmail" "nnimap+gmail:Gmail/sent") (".*" ,(format-time-string "sent/%Y-%m")))) #+END_SRC
The above code specifies where the sent mails are stored. Note that =Tiger/sent= is the actual folder where sent mails are stored. Remember that we use =nametrans= to map remote folder to local folder with a different name? If you don't use =nametrans= feature, then this =Tiger/sent= might be =Tiger/Sent Mails=, =Tiger/Sent Items= or some other names.
**** Email account configuration
#+BEGIN_SRC emacs-lisp (setq gnus-parameters '(("Tiger." (charset . utf-8) (posting-style (address "[email protected]") (gcc "nnimap+tiger:Tiger/sent") (name "Zhitao Gong") (signature-file "tiger") (organization "Auburn CSSE"))) ("Gmail." (charset . utf-8) (posting-style (address "[email protected]") (gcc "nnimap+gmail:Gmail/sent") (name "Zhitao Gong") (signature-file "gmail") (organization "Auburn University"))))) #+END_SRC
This above code configures each account separately, e.g, signatures, charset, etc.
- Miscellaneous :PROPERTIES: :CUSTOM_ID: sec:miscellaneous :END:
All other configurations are just for personal preference. You could easily find their document online or through =emacs= inline manual.
- Conclusion :PROPERTIES: :CUSTOM_ID: sec:conclusion :END:
Now it works.
This repo shows a /complete/ and /working/ solution of =gnus=, another step towards living in emacs. It took me years to get used to =emacs= and =gnus=, and I never regret the effort.
- Footnotes
[fn:1] Actually virtual folders, since all mails in Gmail are stored in All Mail folder, other folder names are just tags despite that they are visually displayed as folders.
[fn:2] Not full configuration actually, there are lots of configuration files for =dovecot=, most of which, however, work out of the box.