source: ProjectBuilder/projects/md2mb/devel/md2mb/bin/md2mb.pl

Last change on this file was 1488, checked in by Bruno Cornec, 12 years ago
  • Add licenses references for md2mb and disclaimer
File size: 12.7 KB
RevLine 
[1141]1#!/usr/bin/perl -w
2
[1445]3=head1 NAME
4
5md2mb.pl - Import a maildir kmail environment into a thunderbird one
6
7=cut
8
[1488]9# Released under the GPLv2 or the Artistic license at your will.
10# Cf: http://www.gnu.org/licenses/gpl2.html
11# Remember that:
12# 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
13#
14# 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
15
[1141]16use strict;
17use File::Find;
18use File::Copy;
19use File::Basename;
20use File::Path;
[1389]21use File::Glob ':glob';
[1445]22use Getopt::Long;
23use Pod::Usage;
[1447]24use List::Util qw(first);
[1141]25
[1445]26# settings
[1453]27my $cmd = 'formail';
[1141]28my $debug = 0;
[1450]29my $file = 0; # is the newroot a file (1) or a dir (0) ?
[1445]30my $help = 0;
31my $man = 0;
[1449]32my $oldroot = first { -d } "$ENV{HOME}/.Mail", "$ENV{HOME}/Mail";
33my @default_profiles = bsd_glob("$ENV{HOME}/.thunderbird/*.default");
34my $profile = shift @default_profiles;
35# if there is more than one default profile (quite unlikely, but you never
36# know), unset $profile again and let the user resolve this
37if (@default_profiles) { undef $profile }
38my $subdir;
[1458]39my $touch = 1;
[1141]40
[1445]41# parse command-line options
42GetOptions(
[1456]43 'cmd|c=s' => \$cmd,
[1454]44 'debug|d+' => \$debug,
45 'file|f' => \$file,
46 'help|h' => \$help,
47 'man|m' => \$man,
[1458]48 'notouch|n' => sub { $touch = 0 },
[1454]49 'oldroot|o=s' => \$oldroot,
50 'profile|p=s' => \$profile,
51 'subdir|s=s' => \$subdir,
[1445]52) or pod2usage(2);
53pod2usage(1) if ($help);
54pod2usage(-exitstatus => 0, -verbose => 2) if ($man);
[1449]55defined $subdir or pod2usage("Error: -subdir is a mandatory argument\n");
[1445]56
[1449]57# the new root is constructed from the profile and the chosen subdirectory name
[1455]58my $newroot = "$profile/$subdir" if defined $profile;
[1449]59
[1446]60# debug mode overview
61if ($debug) {
62 print <<EOF;
63DEBUG MODE, not doing anything, just printing
64--- current state ---
[1454]65debug = $debug
[1446]66cmd = $cmd
67oldroot = $oldroot
[1449]68profile = $profile
69subdir = $subdir
70newroot = $newroot (constructed)
[1446]71--- end ---
72EOF
73}
74
[1448]75# some sanity checks before proceeding
[1453]76system("$cmd </dev/null >/dev/null 2>/dev/null") == 0 or die <<EOF;
77Cannot run `$cmd' or locate it in your \$PATH!
78
79I need the program `formail' to process your mailboxes. You can provide the
80full path to the formail utility using the `-cmd' command-line switch.
81
82Perhaps `formail' is not installed on your system. Try installing the `formail'
83package using your distribution's package manager. The formail utility might be
84contained in the `procmail' package (e.g., Ubuntu 11.10), so you may need to
85look for the latter instead.
86
87Aborting ...
88EOF
[1457]89defined $oldroot or pod2usage(<<EOF);
90Cannot locate KMail mailboxes automatically. Please specify their location by
91rerunning the program with the -oldroot option.
92EOF
[1448]93-d $oldroot or die "cannot find mailbox root `$oldroot'";
[1457]94defined $profile or pod2usage(<<EOF);
95Cannot locate the default Thunderbird profile directory automatically. Please
96specify its location by rerunning the program with the -profile option.
97EOF
[1449]98-d $profile or die "cannot find Thunderbird profile directory `$profile'";
[1448]99
[1446]100# create destination path
[1141]101if ($debug) {
[1450]102 print "TARGET DIR: mkdir -p $newroot\n" if ((not -d "$newroot") && (not $file));
103 print "CMD: mkdir -p $newroot\n" if ((not -d "$newroot") && (not $file));
[1141]104} else {
[1450]105 mkpath("$newroot",0, 0755) if ((not -d "$newroot") && (not $file));
[1141]106}
107
[1446]108# the main work is done here
109if ($debug) {
110 print "DESCENDING INTO oldroot($oldroot)\n";
111}
[1141]112find(\&md2mb,($oldroot));
113
[1451]114# now go back again and create empty files corresponding to the `.sbd'
115# directories
116if ($touch) {
117 print "DESCENDING AGAIN INTO oldroot($oldroot)\n" if ($debug);
118 find(sub {
119 return if (! -d); # consider only directories ...
120 return if (! /\.sbd$/); # ... with the right name
121 s/\.sbd//; # strip .sbd suffix
122 if ($debug) {
123 print "WOULD CREATE file($_) IF NEEDED\n";
124 } else {
125 open my $fh, '>>', $_ or die "cannot open $_: $!";
126 }
127 }, $newroot);
128}
129
[1446]130# rename `inbox' to `Inbox'
[1390]131if ((-z "$newroot/Inbox") || (! -e "$newroot/Inbox")) {
[1446]132 if ($debug) {
133 print "RENAMING inbox($newroot/inbox) INTO Inbox($newroot/Inbox)\n" if (-e "$newroot/inbox");
134 } else {
135 print "Renaming inbox into Inbox\n";
136 move("$newroot/inbox","$newroot/Inbox") if (-e "$newroot/inbox");
137 }
[1390]138}
139
[1141]140sub md2mb {
141
142if (-f $File::Find::name) {
[1389]143 if (($File::Find::name =~ /\.ids$/) ||
[1141]144 ($File::Find::name =~ /\.sorted$/) ||
[1389]145 ($File::Find::name =~ /\.index$/)) {
146 print "SKIP FILE: $File::Find::name\n" if ($debug);
147 return;
148 }
[1141]149}
150if (-d $File::Find::name) {
[1389]151 if (($File::Find::name =~ /\/cur$/) ||
[1141]152 ($File::Find::name =~ /\/new$/) ||
[1389]153 ($File::Find::name =~ /\/tmp$/)) {
154 print "SKIP DIR: $File::Find::name\n" if ($debug);
155 return;
156 }
[1141]157}
158my $destname = $File::Find::name;
[1389]159# Target name is under a different root dir
[1141]160$destname =~ s|^$oldroot||;
[1389]161# Target name is not under a .directory dir but under a .sdb one
[1390]162$destname =~ s|\.([^/]+)\.directory/|$1.sbd/|g;
[1389]163# Here we create the target dir and target name
[1141]164my $outputfile="$newroot/$destname";
[1412]165my $cdir = dirname("$outputfile");
[1389]166# Handle case where target file name is empty
[1141]167$outputfile="$newroot" if ($destname =~ /^\s*$/);
[1389]168# When we treat a dir, we will have to handle what it has below
[1141]169if (-d $File::Find::name) {
[1328]170 if ($debug) {
[1389]171 print "DIR SRC: $File::Find::name\n";
[1328]172 }
[1389]173 my @files = (bsd_glob("$File::Find::name/cur/*"),bsd_glob("$File::Find::name/new/*"));
[1141]174 if (@files) {
175 if ($debug) {
[1389]176 print "DIR ($File::Find::name) DIR TARGET: mkdir -p $cdir\n" if (not -d "$cdir");
[1141]177 } else {
178 mkpath("$cdir",0, 0755) if (not -d "$cdir");
179 }
180 }
181 foreach my $file (@files) {
182 next unless -f $file; # skip non-regular files
183 next unless -s $file; # skip empty files
184 next unless -r $file; # skip unreadable files
[1389]185 $file =~ s/'/'"'"'/g; # escape ' (single quote)
[1141]186 # NOTE! The output file must not contain single quotes (')!
187 my $run = "cat '$file' | $cmd >> '$outputfile'";
188 if ($debug) {
[1389]189 print "COPYING CONTENT maildir($file) to $outputfile\n";
190 print "CMD: $run\n";
[1141]191 } else {
[1389]192 print "Copying maildir content from $file to $outputfile\n";
[1141]193 system($run) == 0 or warn "cannot run \"$run\".";
194 }
195 }
196}
197if (-f $File::Find::name) {
[1389]198 if (($File::Find::name =~ /\/cur\//) ||
199 ($File::Find::name =~ /\/new\//) ||
200 ($File::Find::name =~ /\/tmp\//)) {
201 print "SKIP FILE: $File::Find::name\n" if ($debug);
202 return;
203 }
[1141]204 if ($debug) {
[1389]205 print "FILE ($File::Find::name) TARGET DIR: mkdir -p $cdir\n" if (not -d "$cdir");
206 print "CMD: cp $File::Find::name $cdir\n";
[1141]207 } else {
[1389]208 print "Copying mailbox content from $File::Find::name to $cdir\n";
[1141]209 mkpath("$cdir",0, 0755) if (not -d "$cdir");
210 copy($File::Find::name,$cdir);
211 }
212}
213}
[1445]214__END__
215
216=head1 SYNOPSIS
217
[1449]218md2mb.pl [options] -s name
[1445]219
220 Options:
[1457]221 -cmd | -c <cmd> command to process mail
222 -debug | -d debug mode
223 -file | -f whether $newroot is a file or a directory
224 -help | -h brief help message
225 -man | -m full documentation
[1458]226 -notouch | -n do not create empty files corresponding to .sdb
227 dirs
[1457]228 -oldroot | -o <path> location of the KMail mailboxes
229 -profile | -p <path> path to the Thunderbird profile to install to
230 -subdir | -s <name> subdir that will be created to hold the
231 imported mails
[1445]232
233=head1 OPTIONS
234
235=over 4
236
[1457]237=item B<-cmd> I<cmd>
[1453]238
[1457]239Use I<cmd> to process your maildirs. By default uses B<formail> in
[1453]240your C<$PATH>, but you can use this option to specify the full path to the
241executable to use instead.
242
[1446]243=item B<-debug>
244
245Enter debug mode. This will print what would be done. No commands are executed,
246so this is safe to use when testing.
247
[1450]248=item B<-file>
249
250Specifies that $newroot is a file. If this option is omitted, it is assumed
251that $newroot is a directory instead.
252
[1445]253=item B<-help>
254
255Print a brief help message and exits.
256
257=item B<-man>
258
259Prints the manual page and exits.
260
[1458]261=item B<-notouch>
262
263Do not create empty files corresponding to the F<.sbd> dirs created by this
264script. By default, those empty files are created to make KMail's subfolders
265show up in Thunderbird. You probably don't need to use this option, but it is
266here nevertheless in case you need it.
267
[1449]268=item B<-oldroot> I<path>
[1448]269
270Specify where your B<KMail> mailboxes are to be found. By default assumes a
271folder called F<.Mail> or F<Mail> in your homedir, preferring F<.Mail> if it
272exists.
273
[1449]274=item B<-profile> I<path>
275
276Specify the path to the Thunderbird profile. Your imported mails will go inside
277a directory in this profile. By default uses the default profile in the
278F<.thunderbird> directory in your home directory, if it is unique.
279
280=item B<-subdir> I<name>
281
282Specify the subdirectory I<name> that will be created inside the Thunderbird
283profile to hold the imported mails. This option is mandatory; there is no
284default.
285
[1445]286=back
287
[1449]288=head1 EXAMPLES
289
290 # this will fetch all mails from the folder .Mail or Mail in your home
291 # directory, and import them into your default Thunderbird profile, in
292 # a directory called Mail/pop.home.musique-ancienne.org/
293 #
294 # usually, this is all you need to specify:
295
296 perl md2mb.pl -s Mail/pop.home.musique-ancienne.org/
297
298 # on your computer, the mails may end up in
299 # /users/segolene/.thunderbird/qk2f4dl6.default/Mail/pop.home.musique-ancienne.org/
300
301 # if md2mb.pl cannot figure out where your default Thunderbird profile
302 # is, use the following:
303
304 perl md2mb.pl -p ~/.thunderbird/qk2f4dl6.default -s Mail/pop.home.musique-ancienne.org/
305
[1445]306=head1 DESCRIPTION
307
308B<md2mb.pl> will import a B<kmail> maildir environment, and transform it into a
[1453]309B<thunderbird> one. It relies on B<formail> (or an equivalent utility) being
310available on your system.
[1445]311
[1448]312By default, B<md2mb.pl> assumes that your B<kmail> mailboxes are stored in a
313folder called F<.Mail> or F<Mail> in your homedir. If this assumption is
314incorrect, you need to specify the correct folder with B<-oldroot>.
315
[1449]316The mails will be imported into the Thunderbird profile that is either
317specified with B<-profile> or determined automatically. The script will try to
318determine the default Thunderbird profile automatically. If there is a folder
319that ends in F<.default> in the F<.thunderbird> directory in your home dir, and
320if it is unique, that one will be used.
321
322The mails finally end up in a subdirectory below the profile. You must specify
323the subdirectory on the command line with B<-subdir>, as shown in the examples
324above.
325
[1451]326If you have used a structure with subfolders in KMail, mails in subfolders
[1458]327should show up correctly in Thunderbird.
[1451]328
[1445]329=head1 AUTHOR
330
[1448]331=over 4
332
333=item *
334
[1445]335Bruno Cornec, http://brunocornec.wordpress.com
336
[1448]337=item *
338
339Edward Baudrez, C<< ebaudrez@cpan.org >>
340
341=back
342
[1445]343=head1 LICENSE
344
345Released under the GPLv2 or the Artistic license at your will.
[1488]346Cf: http://www.gnu.org/licenses/gpl2.html
347Cf: http://www.perlfoundation.org/artistic_license_1_0
348Remember that:
349BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
[1445]350
[1488]351IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
352
353
[1445]354=cut
Note: See TracBrowser for help on using the repository browser.