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

Last change on this file since 1451 was 1451, checked in by ebaudrez, 12 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
Line 
1#!/usr/bin/perl -w
2
3=head1 NAME
4
5md2mb.pl - Import a maildir kmail environment into a thunderbird one
6
7=cut
8
9use strict;
10use File::Find;
11use File::Copy;
12use File::Basename;
13use File::Path;
14use File::Glob ':glob';
15use Getopt::Long;
16use Pod::Usage;
17use List::Util qw(first);
18
19# settings
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
28my $debug = 0;
29my $file = 0; # is the newroot a file (1) or a dir (0) ?
30my $help = 0;
31my $man = 0;
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;
39my $touch = 0;
40
41# parse command-line options
42GetOptions(
43 'debug|d' => \$debug,
44 'file|f' => \$file,
45 'help|h' => \$help,
46 'man|m' => \$man,
47 'oldroot|old|o=s' => \$oldroot,
48 'profile|p=s' => \$profile,
49 'subdir|s=s' => \$subdir,
50 'touch|t' => \$touch,
51) or pod2usage(2);
52pod2usage(1) if ($help);
53pod2usage(-exitstatus => 0, -verbose => 2) if ($man);
54defined $subdir or pod2usage("Error: -subdir is a mandatory argument\n");
55
56# the new root is constructed from the profile and the chosen subdirectory name
57my $newroot = "$profile/$subdir";
58
59# debug mode overview
60if ($debug) {
61 print <<EOF;
62DEBUG MODE, not doing anything, just printing
63--- current state ---
64cmd = $cmd
65oldroot = $oldroot
66profile = $profile
67subdir = $subdir
68newroot = $newroot (constructed)
69--- end ---
70EOF
71}
72
73# some sanity checks before proceeding
74-d $oldroot or die "cannot find mailbox root `$oldroot'";
75-d $profile or die "cannot find Thunderbird profile directory `$profile'";
76
77# create destination path
78if ($debug) {
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));
81} else {
82 mkpath("$newroot",0, 0755) if ((not -d "$newroot") && (not $file));
83}
84
85# the main work is done here
86if ($debug) {
87 print "DESCENDING INTO oldroot($oldroot)\n";
88}
89find(\&md2mb,($oldroot));
90
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
107# rename `inbox' to `Inbox'
108if ((-z "$newroot/Inbox") || (! -e "$newroot/Inbox")) {
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 }
115}
116
117sub md2mb {
118
119if (-f $File::Find::name) {
120 if (($File::Find::name =~ /\.ids$/) ||
121 ($File::Find::name =~ /\.sorted$/) ||
122 ($File::Find::name =~ /\.index$/)) {
123 print "SKIP FILE: $File::Find::name\n" if ($debug);
124 return;
125 }
126}
127if (-d $File::Find::name) {
128 if (($File::Find::name =~ /\/cur$/) ||
129 ($File::Find::name =~ /\/new$/) ||
130 ($File::Find::name =~ /\/tmp$/)) {
131 print "SKIP DIR: $File::Find::name\n" if ($debug);
132 return;
133 }
134}
135my $destname = $File::Find::name;
136# Target name is under a different root dir
137$destname =~ s|^$oldroot||;
138# Target name is not under a .directory dir but under a .sdb one
139$destname =~ s|\.([^/]+)\.directory/|$1.sbd/|g;
140# Here we create the target dir and target name
141my $outputfile="$newroot/$destname";
142my $cdir = dirname("$outputfile");
143# Handle case where target file name is empty
144$outputfile="$newroot" if ($destname =~ /^\s*$/);
145# When we treat a dir, we will have to handle what it has below
146if (-d $File::Find::name) {
147 if ($debug) {
148 print "DIR SRC: $File::Find::name\n";
149 }
150 my @files = (bsd_glob("$File::Find::name/cur/*"),bsd_glob("$File::Find::name/new/*"));
151 if (@files) {
152 if ($debug) {
153 print "DIR ($File::Find::name) DIR TARGET: mkdir -p $cdir\n" if (not -d "$cdir");
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
162 $file =~ s/'/'"'"'/g; # escape ' (single quote)
163 # NOTE! The output file must not contain single quotes (')!
164 my $run = "cat '$file' | $cmd >> '$outputfile'";
165 if ($debug) {
166 print "COPYING CONTENT maildir($file) to $outputfile\n";
167 print "CMD: $run\n";
168 } else {
169 print "Copying maildir content from $file to $outputfile\n";
170 system($run) == 0 or warn "cannot run \"$run\".";
171 }
172 }
173}
174if (-f $File::Find::name) {
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 }
181 if ($debug) {
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";
184 } else {
185 print "Copying mailbox content from $File::Find::name to $cdir\n";
186 mkpath("$cdir",0, 0755) if (not -d "$cdir");
187 copy($File::Find::name,$cdir);
188 }
189}
190}
191__END__
192
193=head1 SYNOPSIS
194
195md2mb.pl [options] -s name
196
197 Options:
198 -debug | -d debug mode
199 -file | -f whether $newroot is a file or a directory
200 -help | -h brief help message
201 -man | -m full documentation
202 -oldroot | -old | -o location of the KMail mailboxes
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
206 -touch | -t create empty files corresponding to .sdb dirs
207
208=head1 OPTIONS
209
210=over 4
211
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
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
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
230=item B<-oldroot> I<path>
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
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
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
253=back
254
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
273=head1 DESCRIPTION
274
275B<md2mb.pl> will import a B<kmail> maildir environment, and transform it into a
276B<thunderbird> one. It relies on either B<formail> or B<procmail> being
277available in your search path, and will preferentially use B<formail>.
278
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
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
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
297=head1 AUTHOR
298
299=over 4
300
301=item *
302
303Bruno Cornec, http://brunocornec.wordpress.com
304
305=item *
306
307Edward Baudrez, C<< ebaudrez@cpan.org >>
308
309=back
310
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.