3
# RFCs important for this script:
5
# RFC 2822 - Internet Message Format
6
# RFC 2047 - MIME (Multipurpose Internet Mail Extensions) Part Three:
7
# Message Header Extensions for Non-ASCII Text
12
# This ugly trick lets the script work even if gettext support is missing.
13
# We did so because Locale::gettext doesn't ship with the standard perl
16
if (eval { require Locale::gettext }) {
17
import Locale::gettext;
19
import POSIX, qw(setlocale);
22
use constant LC_MESSAGES => 0;
24
sub bindtextdomain($$) { }
26
sub gettext($) { shift }
31
setlocale(LC_MESSAGES, "");
32
bindtextdomain("quilt", "@LOCALEDIR@");
36
return gettext(shift);
39
my (%append_name, %append_value, $remove_empty_headers, %remove_header,
40
%extract_recipients_from, %replace_name, %replace_value, $charset);
41
GetOptions('add-recipient:s%' =>
43
$append_name{lc $_[1]} = $_[1];
44
$append_value{lc $_[1]} .= ",\n " . $_[2];
46
'remove-header:s' => sub { $remove_header{lc $_[1]}++ },
47
'remove-empty-headers' => \$remove_empty_headers,
48
'replace-header:s%' =>
50
$replace_name{lc $_[1]} = $_[1];
51
$replace_value{lc $_[1]} = $_[2];
53
'extract-recipients:s' => sub { $extract_recipients_from{lc $_[1]} = 1 },
54
'charset:s' => \$charset)
56
my %recipient_headers = map {lc $_ => 1} (@ARGV, keys %append_name);
58
sub encode_header($) {
60
$word =~ s{[^\t\41-\76\100-\176]}{sprintf "=%02X", ord($&)}ge;
61
return "=?$charset?q?$word?=";
64
my $special = '()<>\[\]:;@\\,"'; # special characters
65
my $special_dot = "$special."; # special characters + dot
67
# Check for a valid display name
68
sub check_display_name($) {
71
if ($display =~ /^"((?:[^"\\]|\\[^\n\r])*)"/) {
73
if ($quoted =~ /[^\t\40-\176]/) {
76
return encode_header($display);
80
# The value is not (properly) quoted. Check for invalid characters.
81
while (/\(/ or /\)/) {
83
_("Display name `%s' contains unpaired parentheses\n"), $display)
84
unless s/\(([^()]*)\)/$1/;
86
if ($display =~ /[^\t\40-\176]/ || $display =~ /[$special_dot]/) {
87
if ($display =~ /[^\1-\10\13\14\16-\37\40\41\43-\133\135-\177]/) {
88
return encode_header($display);
89
} elsif ($display =~ /[$special_dot]/) {
90
return "\"$display\"";
97
# Check for a valid delivery address
98
sub check_delivery_address($) {
101
die sprintf(_("Delivery address `%s' is invalid\n"), $deliver)
102
if $deliver =~ /[ \t]/ or
103
$deliver =~ /[^ \t\40-\176]/ or
104
$deliver !~ /^[^$special]+@(\[?)[^$special_dot]+(?:\.[^$special_dot]+)*(\]?)$/ or
109
sub check_recipient($) {
110
my ($recipient) = @_;
112
if ($recipient =~ /^(.*?)\s*<(.+)>$/) {
113
my $deliver = check_delivery_address($2);
114
return ( check_display_name($1) . " <" . $deliver . ">", $deliver );
115
} elsif ($recipient =~ /^(\S*)\s*\((.*)\)$/) {
116
my $deliver = check_delivery_address($1);
117
return ( $deliver . " (" . check_display_name($2) . ")", $deliver );
119
my $deliver = check_delivery_address($recipient);
120
return ( $deliver, $deliver );
125
sub process_header($) {
129
return unless defined $_;
130
unless (($name, $value) = /^([\41-\176]+):\s*(.*)\s*/s) {
134
if (%extract_recipients_from) {
135
if (exists $extract_recipients_from{lc $name}) {
137
$value =~ s/^\s*//; $value =~ s/\s*$//;
138
foreach my $recipient (split /\s*,\s*/s, $value) {
139
next if $recipient =~ /^\s*$/;
140
#print "<<$recipient>>";
142
($recipient, $deliver) = check_recipient($recipient);
148
return if exists $remove_header{lc $name};
149
if (exists $replace_name{lc $name}) {
150
if (exists $replace_value{lc $name}) {
151
print "$replace_name{lc $name}: $replace_value{lc $name}\n";
152
delete $replace_value{lc $name};
156
if (exists $recipient_headers{lc $1}) {
157
if (exists $append_name{lc $name}) {
158
$value .= $append_value{lc $name};
159
delete $append_name{lc $name};
162
# This is a recipients field. Split out all the recipients and
163
# check the addresses. Suppress duplicate recipients.
164
$value =~ s/^\s*//; $value =~ s/\s*$//;
165
foreach my $recipient (split /\s*,\s*/, $value) {
166
next if $recipient =~ /^\s*$/;
168
($recipient, $deliver) = check_recipient($recipient);
169
unless (exists $recipients{$deliver}) {
170
push @recipients, $recipient;
171
$recipients{$deliver} = $deliver;
174
print "$name: ", join(",\n ", @recipients), "\n"
175
if @recipients || !$remove_empty_headers;
177
print if $value ne "" || !$remove_empty_headers;
185
process_header $header;
190
process_header $header;
191
foreach my $name (keys %append_name) {
192
process_header $append_name{$name} . ': ' . $append_value{$name};
194
unless (%extract_recipients_from) {
195
# Copy the message body to standard output
196
# FIXME check for 7-bit clean, else assume $charset
197
# FIXME if UTF-8, check for invalid characters!
198
# FIXME must make sure that all messages are written in
199
# either 7-bit or $charset => mbox !!!
201
# Content-Transfer-Encoding: 7bit
202
# Content-Transfer-Encoding: 8bit
203
# Content-Type: text/plain; charset=ISO-8859-15
204
# Content-Type: text/plain; charset=UTF-8