Converting from mbox to Maildir
Introduction
This article describes how to convert from mbox to Maildir format. Maildirs have quite a few advantages over mbox format, especially when you have a lot of users with large mailboxes. Another big advantage is that with dovecot and Maildirs, users can create sub-folders.
These notes are specific to my situation. I wasn’t able to find a complete guide to doing this anywhere, so hopefully it’ll serve as a good guide to others. My system uses:
- Debian GNU/Linux 3.1r2
- dovecot (POP3 and IMAPS)
- exim4 (exim4-daemon-heavy)
- procmail (some users have a ~/.procmailrc and there’s a global /etc/procmailrc)
- Some users SSH in and use mutt.
Before the conversion
Before the conversion, everyone used the mbox mail format. Everyone’s mail spool was /var/mail/$USER
. In addition, dovecot was setup with default_mail_env = mbox:%h/Mail/:INBOX=/var/mail/%u
. So people also had mail in $HOME/Mail/
, such as $HOME/Mail/Spam
, $HOME/Mail/Sent
etc.
Not every user on my system has a valid shell. Some users are mail only, so have a shell of /bin/false. Others are webspace only so have a shell of /usr/sbin/scponlyc. This makes the conversion process a little more complicated because I wasn’t able to su to these users to convert their mailboxes.
Preparation
Before doing on work on converting everything, I stopped dovecot and exim from running and then changed my firewall so only I had access to these services. I didn’t want any mail coming in or users checking their mail during the conversion. I also made sure all mail queues were cleared down.
It goes without saying that a full backup should be done and if possible, run a test restore. Just in case something goes horribly wrong.
Configuring exim
The first task was to reconfigure exim. By default it delivers mail to /var/mail/$USER
, in mbox format. To tell exim that it should now deliver email to a Maildir you should change the local_delivery
transport so that it looks like:
local_delivery: driver = appendfile create_directory = true directory = ${home}/Maildir/ mode = 0600 directory_mode = 0700 maildir_format delivery_date_add envelope_to_add return_path_add
Also check the rest of your exim configuration, to ensure nothing is configured to look at /var/mail/$USER
or $HOME/Mail
. One pitfall I had was an overcomplicated and poorly written procmail transport.
Remember to restart exim.
Configuring dovecot
dovecot is very easy to reconfigure. Simply change the default_mail_env line so it looks like:
default_mail_env = maildir:%h/Maildir
Remember to restart dovecot.
Check the global procmailrc file
This one caused me a headache for quite a while. If you have a global procmailrc file (/etc/procmailrc) then you should to add the following to the beginning:
DEFAULT=$HOME/Maildir/
Run a test conversion
To do this you need to install mb2md which is a Perl script that will convert any mail in an mbox format to in to a Maildir format.
Before attempting a “mass conversion”, I’d strongly suggest you sacrifice your own account as a test. Backup your /var/mail/$USER
spool and your $HOME/Mail
somewhere (just in case). Now run:
$ makemaildir.dovecot $HOME/Maildir $ mb2md -s /var/mail/$USER -d $HOME/Maildir $ mb2md -s $HOME/Mail -R -d $HOME/Maildir $ cp $HOME/Mail/.subscriptions $HOME/Maildir/ $ mv /var/mail/$USER /var/mail/$USER.preMaildir $ mv $HOME/Mail $HOME/Mail.preMaildir
Now you should have your mail in Maildir format. Give it a test by loading your mail client and making sure everything still works (all your mail is there, IMAP subscriptions are there and you can recieve a test email from yourself).
If something isn’t quite right, check:
- Permissions
- Your .forward or .procmailrc is OK (maybe mv them out of the way as a test)
- You configured exim and dovecot correctly and restarted them.
Once everything is working, undo the work you just did (because next we’ll be converting everyone, at once):
$ rm -rf $HOME/Maildir $ mv /var/mail/$USER.preMaildir /var/mail/$USER $ mv $HOME/Mail.preMaildir $HOME/Mail
Converting everyone at once
This is the scary bit. Ensure you do have a working backup. As I mentioned previously, not all my users had a valid shell so it made automating the conversion a bit more tricky. So I decided to run the conversions as root and then use chown afterwards. All of this was scripted and the script I wrote and used is as follows:
#!/bin/sh # *** README *** README *** README *** README *** README *** README *** # This conversion script worked fine for me (tm). YMMV. # # Things to look out for: # 1. On my system, mail in mbox format was also stored in ~/Mail. Your # system might be different (~/mail/ or just ~/mbox). # 2. The maildirmake.dovecot may be called something else on your system. # Things you should do before executing this: # 1. Add an "echo" before any of the commands, so they don't execute # which will allow you to make sure everything looks OK. # 2. Change line 29 so that it only processes a test account, not # everyone (i.e. change "-ge 500" to "-eq 500" to only process the # user account with UID 500). # *** README *** README *** README *** README *** README *** README *** IFS=$' ' # Cycle through users. for line in "$(getent passwd)"; do # Only attempt to process a user with a UID > 500. if [[ $(echo|"$line" | awk -F: '{print $3;}') -ge 500 ]]; then # Get some information about the user. username=$(echo $line | awk -F: '{print $1}') uid=$(echo "$line" | awk -F: '{print $3}') gid=$(echo "$line" | awk -F: '{print $4}') homedir=$(echo "$line" | awk -F: '{print $6}') # Do they have a mail spool or mbox directory? if [[ -f|/var/mail/$username ]] || [[ -d|$homedir/Mail ]]; then # For whatever strange reason, they may already have a # ~/Maildir, when they probably shouldn't. if [[ -d|$homedir/Maildir ]]; then mv $homedir/Maildir $homedir/Maildir.preMaildir fi # Setup the Maildir structure. maildirmake.dovecot $homedir/Maildir # Convert their mail spool. if [[ -f|/var/mail/$username ]]; then mb2md -s /var/mail/$username -d $homedir/Maildir mv /var/mail/$username /var/mail/$username.preMaildir fi # Convert their old mbox directory. if [[ -d|$homedir/Mail ]]; then mb2md -s $homedir/Mail/ -R -d $homedir/Maildir cp -p $homedir/Mail/.subscriptions $homedir/Maildir/ mv $homedir/Mail $homedir/Mail.preMaildir fi # Uncomment the following to attempt to generate a # subscriptions file (probably a bad hack). #find $homedir/Maildir -type d -iname ".*" > $homedir/Maildir/.subscriptions # Give the user back their rights. chown -R $uid:$gid $homedir/Maildir chmod 600 $homedir/Maildir/.subscriptions if [[ -f|$homedir/.procmailrc ]] || [[ -f|$homedir/.forward ]]; then echo "*** $username has a .procmail or .forward!" fi echo "*** $username has been converted." fi echo fi done
This script worked perfectly and converted all the users in one go without a problem. Everyone’s mail spool in /var/mail
will be mv’d out of the way and have “.preMaildir” appended to it. The same applies for $HOME/Mail
. Remember to investigate people’s .procmailrc
or .forward
files to make sure they’re compatible.
Changing the shell environment
The final part of the process is to change the shell environment. Without doing this, pam_mail
(the PAM module that tells you if you’ve got new mail when you login) won’t work and neither will mutt.
First edit /etc/pam.d/login
and /etc/pam.d/ssh
and add dir=~/Maildir
to the pam_mail.so
line:
session optional pam_mail.so standard noenv dir=~/Maildir
Now edit /etc/login.defs
and uncomment the QMAIL_DIR
line.
I’d also suggest running makemaildir.dovecot /etc/skel/Maildir
Lastly, if you use mutt, edit /etc/Muttrc
and add/edit the following:
set mbox_type=Maildir set folder="~/Maildir/" set mask="."
Open the flood gates
Do a final check. Make sure everything looks correct and then unfirewall the exim and dovecot services. Finally keep your fingers crossed that no one notices any changes.
Page written by DavidRamsden
Leave a Reply
You must be logged in to post a comment.