-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdovecot-maildir2mbox.pl
executable file
·129 lines (94 loc) · 3.09 KB
/
dovecot-maildir2mbox.pl
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
#!/usr/bin/perl
use 5.012;
use warnings;
use autodie;
use File::Basename;
use Getopt::Long;
use Pod::Usage;
my $formatter = 'formail'; # installed with procmail
pod2usage(2) if scalar @ARGV != 2;
my $in_mdir = $ARGV[0];
my $out_mbox = $ARGV[1];
if ( -f $out_mbox ) {
die "$out_mbox already exists\n";
}
my $keywords = load_keywords("$in_mdir/dovecot-keywords");
my @in_files;
for my $subdir ("$in_mdir/cur", "$in_mdir/new") {
opendir(my $dh, $subdir);
while (readdir $dh) {
next if /^\./ || ! -f "$subdir/$_";
push @in_files, "$subdir/$_";
}
closedir $dh;
}
# sort by timestamp in filename
@in_files = sort {
my $aa = basename($a);
$aa =~ s/^(\d+)\..*$/$1/;
my $bb = basename($b);
$bb =~ s/^(\d+)\..*$/$1/;
$aa <=> $bb
} @in_files;
foreach my $in_file (@in_files) {
# extract flags from filename
warn "bad filename format: $in_file"
unless $in_file =~ /:2,([A-Z]*)([a-z]*)$/;
my %flags = map { $_ => 1 } split //, $1;
my @kw = split //, $2;
my $kw_header = 'X-Keywords:';
foreach (@kw) {
$kw_header .= ' '. %$keywords{$_};
}
# XXX non-\Recent flag is always added, I'm lazy
my $status_header = 'Status: ' .
( $flags{S} ? 'RO' : 'O' );
my $x_status_header = 'X-Status: ';
# Maildir flags: http://cr.yp.to/proto/maildir.html
# dovecot flags: http://wiki.dovecot.org/MailboxFormat/mbox
$x_status_header .= 'A' if $flags{R}; # \Answered - replied
$x_status_header .= 'F' if $flags{F}; # \Flagged - flagged
$x_status_header .= 'T' if $flags{D}; # \Draft - draft
$x_status_header .= 'D' if $flags{T}; # \Deleted - trashed
# There is "P" (passed) flag in maildir, but I have no messages to test
$in_file =~ s/'/'"'"'/g; # escape ' (single quote)
my $cmd = "$formatter -A '$status_header' -A '$x_status_header' -A '$kw_header' < $in_file >> $out_mbox";
system($cmd) == 0 or die "system $cmd filed: $?";
}
##############################################################################
sub load_keywords {
my $f = shift;
my %k; # letter -> label, e. g. a -> NonJunk
# in dovecot-keywords file labels numbered by 0, 1, 2 ...
# in maildir filename flags letters a, b, c, ... z are used
open my $fh, '<', $f;
while (<$fh>) {
chomp;
die "bad line: $_" unless /^(\d+)\s+(\S+)$/;
# 0 -> a, 1 -> b, 2 -> c, e. t. c.
my $letter = chr(ord('a') + $1);
$k{$letter} = $2;
}
close $fh;
return \%k;
}
__END__
=head1 NAME
dovecot-maildir2mbox.pl - convert maildir to mbox
=head1 SYNOPSIS
dovecot-maildir2mbox.pl maildir/.Sent mailbox/Sent
=head1 DESCRIPTION
dovecot-maildir2mbox.pl is a script to convert maildir with dovecot extensions
to mbox (with dovecot's metadata).
To convert maildir run the script with two arguments:
=over 2
=item *
path to directory with maildir (with cur and new subdirs inside)
=item *
path to mailbox file to be created
=back
Script converts one folder at time, to convert whole mailbox run it for each folder.
See L<http://wiki2.dovecot.org/MailboxFormat/Maildir> and
L<http://wiki.dovecot.org/MailboxFormat/mbox> for formats description.
UIDVALIDITY and UIDs are not converted by this script.
Recent flag is not preserved (all messages marked as non-recent).