diff options
Diffstat (limited to 'doc/wiki/Migration.BincIMAP.txt')
-rw-r--r-- | doc/wiki/Migration.BincIMAP.txt | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/doc/wiki/Migration.BincIMAP.txt b/doc/wiki/Migration.BincIMAP.txt new file mode 100644 index 0000000..6a42ba3 --- /dev/null +++ b/doc/wiki/Migration.BincIMAP.txt @@ -0,0 +1,349 @@ +Binc IMAP +========= + +*WARNING: Badly done migration will cause your IMAP and/or POP3 clients to +re-download all mails. Read <Migration.txt> page first carefully.* + +If you're using only Binc IMAP, it's possible to do a transparent Dovecot +migration. + +Binc IMAP v1.2 and later +------------------------ + +binc2dovecot.pl attempts to do as perfect migration as possible. Basically it +reads Binc's uidlist files from the specified maildir and it's (sub-)folders +and generates 'dovecot-uidlist' files out of them. It also converts Binc's +subscription file. This script hasn't been tested with Binc IMAP< 1.2. + +If Binc has been used with the IMAPdir depot format, it need to be converted to +Maildir++ with the script IMAPdir2Maildir++ before running binc2dovecot.pl. + +IMAPdir2Maildir++ + +---%<------------------------------------------------------------------------- +#!/bin/bash +# BINC mailbox definition for the example parameters below +# Mailbox { +# depot = "IMAPdir", +# type = "Maildir", +# path = "Maildir", +# } + +# +# Parameters: set according to your local system settings +# + +# Path to the IMAPdir +IMAPdirName="${1}/Maildir" + +# Path to the new Maildir++ directory +maildirName="${1}/Maildir" + +# Name used for the inbox with IMAPdir +inboxName="INBOX" + +# the character . is invalid for Maildir++ +# What string shall it be replaced with? +dotReplacementString="_cdot_" + +# +# Below here nothing should need to be adjusted +# + +# Initialise some variables and settings +shopt -s dotglob +orgDir=$(pwd) + +# loop through all file names according to the pattern in $FILES +cd $IMAPdirName +FILES="*" +for file in $FILES +do + # Skip over Maildir++ directories + if [[ ${file:0:1} = "." && -e ${file}/maildirfolder ]] + then + continue + fi + # Skip over non-directory file names + if [[ ! -d $file ]] + then + continue + fi + + # Move INBOX contents according to Maildir++ specification + if [[ $file = $inboxName ]] + then + mv ${file}/* $maildirName + rmdir $file + continue + fi + + # create Maildir++ compliant new folder name + newFile=${file//\\\\/\\} + newFile=${newFile//\\./$dotReplacementString} + newFile=${newFile/#./$dotReplacementString} + newFile=.$newFile + + # rename folder name according to Maildir++ specification & add maildirfolder +file + mv "$file" "${maildirName}/$newFile" + owner=$(stat -c %u "${maildirName}/$newFile") + group=$(stat -c %g "${maildirName}/$newFile") + touch "${maildirName}/$newFile/maildirfolder" + chown $owner:$group "${maildirName}/$newFile/maildirfolder" + chmod 600 "${maildirName}/$newFile/maildirfolder" +done + +# Adapt subscriptions file +mv .bincimap-subscribed "${maildirName}/.bincimap-subscribed" +sed -i "s/\./$dotReplacementString/g" "${maildirName}/.bincimap-subscribed" + +# Return to original working directory +cd $orgDir +---%<------------------------------------------------------------------------- + +Usage: ./IMAPdir2Maildir++ /path/to/user + +binc2dovecot.pl + +---%<------------------------------------------------------------------------- +#!/usr/bin/perl + +use IO::File; +use IO::Dir; +use File::stat; +use File::Basename; +use strict; + +### Parameters to adapt to local cofiguration +# Name of the Maildir++ directory relative to the path passed as argument +my $mailbox = $ARGV[0]."/Maildir"; +# Name space used by BINC for private folders, with IMAPdir often = "" +our $namespace = "INBOX."; + +### Nothing should need to be changed below here +our $indent = 0; + +die("Mailbox doesn't exist") + if (!-d $mailbox); +parse_mailbox($mailbox); + +# Sanity check for namespace +my $subscriptionsSize = -s $mailbox.'/subscriptions'; +if ($subscriptionsSize == 0) { + print $/; + print $/; + print "WARNING: Your new subscriptions file is empty. Are you using the +correct namespace? If not re-run script with correct namespace parameter.", $/; +} + +sub parse_mailbox +{ + my ($mailbox) = @_; + print " " x $indent, "Parsing ", $mailbox, " ...", $/; + $indent += 2; + + my $mb = IO::Dir->new($mailbox) + or die("Unable to open mailbox $mailbox"); + while(my $file = $mb->read()) + { + my $absfile = $mailbox."/".$file; + next + if ($file eq "." || $file eq ".."); + if ($file eq ".bincimap-subscribed" && -f $absfile) + { + convert_subscribtions($absfile); + } + elsif ($file eq "bincimap-cache" && -f $absfile) + { + convert_cache($absfile); + } + elsif (substr($file, 0, 1) eq "." && -d $absfile && -e +$absfile."/maildirfolder") + { + parse_mailbox($absfile); + } + } + $mb->close(); + + $indent -= 2; + return 1; +} + +sub convert_cache +{ + my ($infile) = @_; + my $dir = dirname($infile); + my %uids = (); + + print " " x $indent, "Converting cache...", $/; + my $in = IO::File->new("<".$infile) + or die("Unable to open cache file $infile"); + my ($blockopen, $uid) = (0, 0); + my $id = ""; + while(my $line = $in->getline()) + { + if ($line =~ /^\d+\s{$/) + { + $blockopen = 1; + $uid = 0; + $id = ""; + next; + } + elsif ($blockopen && $line =~ /^}$/) + { + $blockopen = 0; + next; + } + elsif ($blockopen && $line =~ /^\t_UID\s=\s(\d+),?$/) + { + $uid = $1; + } + elsif ($blockopen && $line =~ /^\t_ID\s=\s"?(.*?)"?,?$/) + { + $id = $1; + } + if ($uid > 0 && length($id) > 0) + { + $uids{$uid} = $id; + $uid = 0; + $id = ""; + next; + } + } + $in->close(); + + if (scalar(keys(%uids)) <= 0) + { + print " " x $indent, "Empty uidlist. Skipping...", $/; + return 1; + } + + my $uidvalfile = $dir."/bincimap-uidvalidity"; + my ($uidvalidity, $uidnext) = (0, 0); + die("Error: File $uidvalfile doesn't exist") + if (!-f $uidvalfile); + $in = IO::File->new("<".$uidvalfile) + or die("Unable to open file: $uidvalfile"); + while(my $line = $in->getline()) + { + if ($line =~ /^\t_uidvalidity\s=\s(\d+),?$/) + { + $uidvalidity = $1; + } + elsif ($line =~ /^\t_uidnext\s=\s(\d+),?$/) + { + $uidnext = $1; + } + } + $in->close(); + + die("Error: either uidnext ($uidnext) or uidvalidity ($uidvalidity) is +invalid") + if ($uidnext <= 0 || $uidvalidity <= 0); + + my $version = 1; + my $outfile = $dir."/dovecot-uidlist";; + my $out = IO::File->new(">".$outfile) + or die("Unable to create cache file $outfile"); + $out->print($version, " ", $uidvalidity, " ", $uidnext, $/); + foreach my $uid (sort{$a <=> $b} (keys(%uids))) + { + $out->print($uid, " ", $uids{$uid}, $/); + } + $out->close(); + + my $stat = stat($infile); + chown($stat->uid, $stat->gid, $outfile); + chmod(0600, $outfile); + + return 1; +} + +sub convert_subscribtions +{ + my ($infile) = @_; + my $dir = dirname($infile); + my @cache = (); + + print " " x $indent, "Converting subscriptions...", $/; + my $in = IO::File->new("<".$infile) + or die("Unable to open file: $infile"); + while(my $line = $in->getline()) + { + next + if ($line !~ /^$namespace/); + $line =~ s/^$namespace?//; + $line =~ s/\n$//; + $line =~ s/\r$//; + $line =~ s/\//\./g; + next + if (length($line) <= 0); + next + if (!-d $dir."/.".$line); + push(@cache, $line) + if (scalar(grep{$_ eq $line}(@cache)) <= 0); + } + $in->close(); + + my $outfile = $dir."/subscriptions"; + my $out = IO::File->new(">".$outfile) + or die("Unable to create subscriptions file: $outfile"); + foreach my $subscription (@cache) + { + $out->print($subscription, $/); + } + $out->close(); + + my $stat = stat($infile); + chown($stat->uid, $stat->gid, $outfile); + chmod(0600, $outfile); + + return 1; +} +---%<------------------------------------------------------------------------- + +Usage: ./binc2dovecot.pl /path/to/user + +NOTE: /path/to/user/Maildir MUST exist. If "./Maildir" isn't your default +maildir-name, you can edit this at the top of the script. + +---%<------------------------------------------------------------------------- +Example 1: +# find /var/pop -mindepth 1 -maxdepth 1 -type d -exec +/path/to/binc2dove.pl {} \; + +Example 2: +# find /usr/local/vpopmail/domains -mindepth 2 -maxdepth 2 -type d -exec +/path/to/binc2dove.pl {} \; +---%<------------------------------------------------------------------------- + +Dovecot configuration +--------------------- + +Binc IMAP by default uses "INBOX/" as the IMAP namespace for private mailboxes. +If you want a transparent migration, you'll need to configure Dovecot to use a +namespace with "INBOX/" prefix as well. + +---%<------------------------------------------------------------------------- +mail_location = maildir:~/Maildir +namespace { + separator = / + prefix = INBOX/ + inbox = yes +} +---%<------------------------------------------------------------------------- + +Manual conversion +----------------- + + * Binc's '.bincimap-subscribed' file is compatible with Dovecot's + 'subscriptions' file, but you need to remove the "INBOX/" prefixes from the + mailboxes. + * Binc's 'bincimap-cache + bincimap-uidvalidity' are NOT compatible with + Dovecot's 'dovecot-uidlist' file. See file format documention or above + script for conversion. + * Binc's message flags are compatible with Dovecot (as they are specified by + the Maildir specification) + +(This file was created from the wiki on 2019-06-19 12:42) |