Moving Off of Gmail? What About All Those Filter Rules?

So, for one reason or another, I decided that I was going to move away from gmail.  It’s easy to underestimate the massive pain that is running a mail server, especially if you expect anti-spam, ant-virus, and the thousand (seemingly so at least) other features that are offered for free from most web mail providers. There’s a good chance that people doing the same thing (abandoning gmail that is,) will eventually find that it has been decided that dovecot and sieve will feature heavily in whatever setup they decide upon.  And if they are anything like me, they filter a significant portion of their mail into gmail’s equivalent of IMAP folders (tags.) In my case, I didn’t want (despite the probability that most of the rules are stale) to recreate all of these rules by hand.  It’s fortunate that Google allows the rules to be exported as XML (though actual sieve rules would have been better!)  A quick search didn’t turn up any tools to convert from one to the other, so I threw together a (admittedly poorly written) PERL script.  And to think, I had promised to stop using PERL and move to Ruby for one-off little scripts like this, perhaps a decent new-years resolution?
(googleMailFilters.pl) download
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
#!perl
use XML::Simple;
use Data::Dumper;
$xml = new XML::Simple;
$data = $xml->XMLin("mailFilters.xml");
# Build hashes of arrays one for each type (to, from, subject) with the destination as key, and search values as array values
%from = ();
foreach $filter (keys %{$data->{entry}}){
	push @{$from{$data->{entry}->{$filter}->{"apps:property"}->{"label"}->{"value"}}}, $data->{entry}->{$filter}->{"apps:property"}->{"from"}->{"value"}
	if ( length($data->{entry}->{$filter}->{"apps:property"}->{"from"}->{"value"}) > 0 && length($data->{entry}->{$filter}->{"apps:property"}->{"label"}->{"value"}) > 0);
}
%to = ();
foreach $filter (keys %{$data->{entry}}){
        push @{$to{$data->{entry}->{$filter}->{"apps:property"}->{"label"}->{"value"}}}, $data->{entry}->{$filter}->{"apps:property"}->{"to"}->{"value"}
           if ( length($data->{entry}->{$filter}->{"apps:property"}->{"to"}->{"value"}) > 0 && length($data->{entry}->{$filter}->{"apps:property"}->{"label"}->{"value"}) > 0);
}
%subject = ();
foreach $filter (keys %{$data->{entry}}){
        push @{$subject{$data->{entry}->{$filter}->{"apps:property"}->{"label"}->{"value"}}}, $data->{entry}->{$filter}->{"apps:property"}->{"subject"}->{"value"}
           if ( length($data->{entry}->{$filter}->{"apps:property"}->{"subject"}->{"value"}) > 0 && length($data->{entry}->{$filter}->{"apps:property"}->{"label"}->{"value"}) > 0);
}
# Print out sieve rules from our hashes:
print "require [\"fileinto\"];\n";
foreach $rule (keys %from){
	print "\n# rule:[from-$rule]\n";
	print "if any of (";
	$i=0;
	foreach $value (@{$from{$rule}}){
		print ",\n\t" if $i>0;
		print "header :contains \"From\" \"$value\"";
		$i++;
	}
	print " )\n\t{\n\t\tfileinto \"$rule\";\n\t}\n"
}
foreach $rule (keys %to){
        print "\n# rule:[to-$rule]\n";
        print "if any of (";
        $i=0;
        foreach $value (@{$to{$rule}}){
                print ",\n\t" if $i>0;
                print "header :contains \"To\" \"$value\"";
                $i++;
        }
        print " )\n\t{\n\t\tfileinto \"$rule\";\n\t}\n"
}
foreach $rule (keys %subject){
        print "\n# rule:[subject-$rule]\n";
        print "if any of (";
        $i=0;
        foreach $value (@{$subject{$rule}}){
                print ",\n\t" if $i>0;
                print "header :contains \"Subject\" \"$value\"";
                $i++;
        }
        print " )\n\t{\n\t\tfileinto \"$rule\";\n\t}\n"
}