source: projects/md2mb/devel/md2mb/bin/md2mb.pl @ 1451

Last change on this file since 1451 was 1451, checked in by ebaudrez, 8 years ago

create empty files for subdirs

Subfolders in KMail lead to directories ending in .sbd, which might not
show up in Thunderbird. This commit automates the process of creating
corresponding empty files to indicate the presence of the subfolders to
Thunderbird.

File size: 9.1 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
[1141]9use strict;
10use File::Find;
11use File::Copy;
12use File::Basename;
13use File::Path;
[1389]14use File::Glob ':glob';
[1445]15use Getopt::Long;
16use Pod::Usage;
[1447]17use List::Util qw(first);
[1141]18
[1445]19# settings
[1447]20my $cmd = first {
21        system("$_ </dev/null >/dev/null 2>/dev/null") == 0
22    } qw(formail procmail);
23$cmd or die <<EOF;
24cannot find a usable program to manipulate your mailboxes in your \$PATH!
25Try installing `formail' or `procmail'
26Aborting
27EOF
[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;
[1451]39my $touch = 0;
[1141]40
[1445]41# parse command-line options
42GetOptions(
[1449]43    'debug|d'         => \$debug,
[1450]44    'file|f'          => \$file,
[1449]45    'help|h'          => \$help,
46    'man|m'           => \$man,
47    'oldroot|old|o=s' => \$oldroot,
48    'profile|p=s'     => \$profile,
49    'subdir|s=s'      => \$subdir,
[1451]50    'touch|t'         => \$touch,
[1445]51) or pod2usage(2);
52pod2usage(1) if ($help);
53pod2usage(-exitstatus => 0, -verbose => 2) if ($man);
[1449]54defined $subdir or pod2usage("Error: -subdir is a mandatory argument\n");
[1445]55
[1449]56# the new root is constructed from the profile and the chosen subdirectory name
57my $newroot = "$profile/$subdir";
58
[1446]59# debug mode overview
60if ($debug) {
61    print <<EOF;
62DEBUG MODE, not doing anything, just printing
63--- current state ---
64cmd     = $cmd
65oldroot = $oldroot
[1449]66profile = $profile
67subdir  = $subdir
68newroot = $newroot (constructed)
[1446]69--- end ---
70EOF
71}
72
[1448]73# some sanity checks before proceeding
74-d $oldroot or die "cannot find mailbox root `$oldroot'";
[1449]75-d $profile or die "cannot find Thunderbird profile directory `$profile'";
[1448]76
[1446]77# create destination path
[1141]78if ($debug) { 
[1450]79    print "TARGET DIR: mkdir -p $newroot\n" if ((not -d "$newroot") && (not $file));
80    print "CMD: mkdir -p $newroot\n" if ((not -d "$newroot") && (not $file));
[1141]81} else {
[1450]82    mkpath("$newroot",0, 0755) if ((not -d "$newroot") && (not $file));
[1141]83}
84
[1446]85# the main work is done here
86if ($debug) {
87    print "DESCENDING INTO oldroot($oldroot)\n";
88}
[1141]89find(\&md2mb,($oldroot));
90
[1451]91# now go back again and create empty files corresponding to the `.sbd'
92# directories
93if ($touch) {
94    print "DESCENDING AGAIN INTO oldroot($oldroot)\n" if ($debug);
95    find(sub {
96            return if (! -d);   # consider only directories ...
97            return if (! /\.sbd$/); # ... with the right name
98            s/\.sbd//;      # strip .sbd suffix
99            if ($debug) {
100                print "WOULD CREATE file($_) IF NEEDED\n";
101            } else {
102                open my $fh, '>>', $_ or die "cannot open $_: $!";
103            }
104        }, $newroot);
105}
106
[1446]107# rename `inbox' to `Inbox'
[1390]108if ((-z "$newroot/Inbox") || (! -e "$newroot/Inbox")) {
[1446]109    if ($debug) {
110        print "RENAMING inbox($newroot/inbox) INTO Inbox($newroot/Inbox)\n" if (-e "$newroot/inbox");
111    } else {
112        print "Renaming inbox into Inbox\n";
113        move("$newroot/inbox","$newroot/Inbox") if (-e "$newroot/inbox");
114    }
[1390]115}
116
[1141]117sub md2mb {
118
119if (-f $File::Find::name) {
[1389]120    if (($File::Find::name =~ /\.ids$/) ||
[1141]121        ($File::Find::name =~ /\.sorted$/) ||
[1389]122        ($File::Find::name =~ /\.index$/)) {
123        print "SKIP FILE: $File::Find::name\n" if ($debug);
124        return;
125    }
[1141]126}
127if (-d $File::Find::name) {
[1389]128    if (($File::Find::name =~ /\/cur$/) ||
[1141]129        ($File::Find::name =~ /\/new$/) ||
[1389]130        ($File::Find::name =~ /\/tmp$/)) {
131        print "SKIP DIR: $File::Find::name\n" if ($debug);
132        return;
133    }
[1141]134}
135my $destname = $File::Find::name;
[1389]136# Target name is under a different root dir
[1141]137$destname =~ s|^$oldroot||;
[1389]138# Target name is not under a .directory dir but under a .sdb one
[1390]139$destname =~ s|\.([^/]+)\.directory/|$1.sbd/|g;
[1389]140# Here we create the target dir and target name
[1141]141my $outputfile="$newroot/$destname";
[1412]142my $cdir = dirname("$outputfile");
[1389]143# Handle case where target file name is empty
[1141]144$outputfile="$newroot" if ($destname =~ /^\s*$/);
[1389]145# When we treat a dir, we will have to handle what it has below
[1141]146if (-d $File::Find::name) {
[1328]147    if ($debug) { 
[1389]148        print "DIR SRC: $File::Find::name\n";
[1328]149    }
[1389]150    my @files = (bsd_glob("$File::Find::name/cur/*"),bsd_glob("$File::Find::name/new/*"));
[1141]151    if (@files) {
152        if ($debug) { 
[1389]153            print "DIR ($File::Find::name) DIR TARGET: mkdir -p $cdir\n" if (not -d "$cdir");
[1141]154        } else {
155            mkpath("$cdir",0, 0755) if (not -d "$cdir");
156        }
157    }
158    foreach my $file (@files) {
159        next unless -f $file; # skip non-regular files
160        next unless -s $file; # skip empty files
161        next unless -r $file; # skip unreadable files
[1389]162        $file =~ s/'/'"'"'/g;  # escape ' (single quote)
[1141]163        # NOTE! The output file must not contain single quotes (')!
164        my $run = "cat '$file' | $cmd >> '$outputfile'";
165        if ($debug) { 
[1389]166            print "COPYING CONTENT maildir($file) to $outputfile\n";
167            print "CMD: $run\n";
[1141]168        } else {
[1389]169            print "Copying maildir content from $file to $outputfile\n";
[1141]170            system($run) == 0 or warn "cannot run \"$run\".";
171        }
172    }
173}
174if (-f $File::Find::name) {
[1389]175    if (($File::Find::name =~ /\/cur\//) ||
176        ($File::Find::name =~ /\/new\//) ||
177        ($File::Find::name =~ /\/tmp\//)) {
178        print "SKIP FILE: $File::Find::name\n" if ($debug);
179        return;
180    }
[1141]181    if ($debug) { 
[1389]182        print "FILE ($File::Find::name) TARGET DIR: mkdir -p $cdir\n" if (not -d "$cdir");
183        print "CMD: cp $File::Find::name $cdir\n";
[1141]184    } else {
[1389]185        print "Copying mailbox content from $File::Find::name to $cdir\n";
[1141]186        mkpath("$cdir",0, 0755) if (not -d "$cdir");
187        copy($File::Find::name,$cdir);
188    }
189}
190}
[1445]191__END__
192
193=head1 SYNOPSIS
194
[1449]195md2mb.pl  [options]  -s name
[1445]196
197 Options:
[1448]198   -debug   | -d        debug mode
[1450]199   -file    | -f                whether $newroot is a file or a directory
[1448]200   -help    | -h        brief help message
201   -man     | -m        full documentation
202   -oldroot | -old | -o     location of the KMail mailboxes
[1449]203   -profile | -p        path to the Thunderbird profile to install to
204   -subdir  | -s        subdir that will be created to hold the
205                imported mails
[1451]206   -touch   | -t        create empty files corresponding to .sdb dirs
[1445]207
208=head1 OPTIONS
209
210=over 4
211
[1446]212=item B<-debug>
213
214Enter debug mode. This will print what would be done. No commands are executed,
215so this is safe to use when testing.
216
[1450]217=item B<-file>
218
219Specifies that $newroot is a file. If this option is omitted, it is assumed
220that $newroot is a directory instead.
221
[1445]222=item B<-help>
223
224Print a brief help message and exits.
225
226=item B<-man>
227
228Prints the manual page and exits.
229
[1449]230=item B<-oldroot> I<path>
[1448]231
232Specify where your B<KMail> mailboxes are to be found. By default assumes a
233folder called F<.Mail> or F<Mail> in your homedir, preferring F<.Mail> if it
234exists.
235
[1449]236=item B<-profile> I<path>
237
238Specify the path to the Thunderbird profile. Your imported mails will go inside
239a directory in this profile. By default uses the default profile in the
240F<.thunderbird> directory in your home directory, if it is unique.
241
242=item B<-subdir> I<name>
243
244Specify the subdirectory I<name> that will be created inside the Thunderbird
245profile to hold the imported mails. This option is mandatory; there is no
246default.
247
[1451]248=item B<-touch>
249
250Create empty files corresponding to the F<.sbd> dirs created by this script.
251Use this hack if your mails don't show up in Thunderbird.
252
[1445]253=back
254
[1449]255=head1 EXAMPLES
256
257    # this will fetch all mails from the folder .Mail or Mail in your home
258    # directory, and import them into your default Thunderbird profile, in
259    # a directory called Mail/pop.home.musique-ancienne.org/
260    #
261    # usually, this is all you need to specify:
262
263    perl md2mb.pl -s Mail/pop.home.musique-ancienne.org/
264
265    # on your computer, the mails may end up in
266    # /users/segolene/.thunderbird/qk2f4dl6.default/Mail/pop.home.musique-ancienne.org/
267
268    # if md2mb.pl cannot figure out where your default Thunderbird profile
269    # is, use the following:
270
271    perl md2mb.pl -p ~/.thunderbird/qk2f4dl6.default -s Mail/pop.home.musique-ancienne.org/
272
[1445]273=head1 DESCRIPTION
274
275B<md2mb.pl> will import a B<kmail> maildir environment, and transform it into a
[1447]276B<thunderbird> one. It relies on either B<formail> or B<procmail> being
277available in your search path, and will preferentially use B<formail>.
[1445]278
[1448]279By default, B<md2mb.pl> assumes that your B<kmail> mailboxes are stored in a
280folder called F<.Mail> or F<Mail> in your homedir. If this assumption is
281incorrect, you need to specify the correct folder with B<-oldroot>.
282
[1449]283The mails will be imported into the Thunderbird profile that is either
284specified with B<-profile> or determined automatically. The script will try to
285determine the default Thunderbird profile automatically. If there is a folder
286that ends in F<.default> in the F<.thunderbird> directory in your home dir, and
287if it is unique, that one will be used.
288
289The mails finally end up in a subdirectory below the profile. You must specify
290the subdirectory on the command line with B<-subdir>, as shown in the examples
291above.
292
[1451]293If you have used a structure with subfolders in KMail, mails in subfolders
294might not show up in Thunderbird. Remove the import, and run again with
295B<-touch>.
296
[1445]297=head1 AUTHOR
298
[1448]299=over 4
300
301=item *
302
[1445]303Bruno Cornec, http://brunocornec.wordpress.com
304
[1448]305=item *
306
307Edward Baudrez, C<< ebaudrez@cpan.org >>
308
309=back
310
[1445]311=head1 LICENSE
312
313Released under the GPLv2 or the Artistic license at your will.
314
315=cut
Note: See TracBrowser for help on using the repository browser.