summaryrefslogtreecommitdiffstats
path: root/doc/wiki/Migration.BincIMAP.txt
blob: 6a42ba3da8c146bb976c165f63f719d0d7e9ed1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
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)