Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 320 lines (265 sloc) 7.089 kb
6fdf249 Greg Kroah-Hartman initial import from old git tree
authored
1 #!/usr/bin/perl
2
3 #
4 # This is a simple script written to fit into Greg K-H's x.sh
5 # script. It takes an email message, and spits out headers and
6 # the body, as well as the decoded portion of any text-type
7 # attachments.
8 #
9 # Written by Phil Dibowitz <phil@ipom.com> on 10/19/06
10 #
11 # $Id: mime_dump,v 1.6 2006/11/03 06:45:15 phil Exp phil $
12 #
13
14 use strict;
15 use warnings;
16 use Getopt::Long qw(:config bundling);
17 use Email::MIME;
18 use File::HomeDir;
19 use Data::Dumper;
20
21 use constant VERSION => "1.2";
22 use constant DEFAULT_SEPARATOR => "\n\n";
23 use constant METHOD_FILE => 0;
24 use constant METHOD_STDIN => 1;
25 use constant SECTION_BEGIN => 0;
26 use constant SECTION_END => 1;
27 use constant GLOBAL_CONFIG => "/etc/mime_dump.conf";
28 use constant LOCAL_CONFIG_FILE => ".mime_dump.conf";
29
30 my $opts = {};
31 GetOptions($opts,
32 'debug|d',
33 'file|f=s',
34 'help|h',
35 'html|H',
36 'indent|i',
37 'separator|s=s',
38 'verbose|v')
39 || die();
40
41 if (exists($opts->{'help'})) {
42 help();
43 exit 0;
44 }
45
46
47 #
48 # Read our config files, and combine them with the options
49 # to build our settings
50 #
51 my $settings = build_settings();
52
53 #
54 # Pull in message
55 #
56 my @lines;
57 if ($settings->{'method'} == METHOD_STDIN) {
58 @lines = <STDIN>;
59 } else {
60 open(INPUT,$opts->{'file'})
61 || die("ERROR: Couldn't open $opts->{'file'}");
62 @lines = <INPUT>;
63 close(INPUT)
64 || die("ERROR: Couldn't close $opts->{'file'}");
65 }
66
67 if (scalar(@lines) == 0) {
68 print "ERROR: 0 lines read!\n";
69 exit 1;
70 }
71
72 #
73 # Get our message in one scalar for Email::MIME
74 #
75 my $msg = join('',@lines);
76
77 #
78 # Parse the message
79 #
80 my $parsed = Email::MIME->new($msg);
81 my @parts = $parsed->parts;
82
83 print Dumper($parsed);
84 exit;
85
86 #
87 # Print the headers
88 #
89 #foreach my $hdr (@{$parsed->{'order'}}) {
90 foreach my $hdr ($parsed->header_names()) {
91 print "$hdr: " . $parsed->header($hdr) . "\n";
92 }
93
94 #
95 # Separator
96 #
97 print $settings->{'separator'};
98
99 #
100 # For debugging
101 #
102 #print Dumper(\@parts);
103 #exit;
104
105 #
106 # Loop through all MIME parts. If it's plain text, print it.
107 #
108 my $indent=0;
109 foreach my $part (@parts) {
110 parse_part($part,$indent);
111 }
112
113 sub parse_part
114 {
115 my ($part,$indent) = @_;
116
117 debug("In parse_part");
118
119 #
120 # Setup padding
121 #
122 my $pad = '';
123 if ($settings->{'indent'} == 1) {
124 for (my $i = 0; $i < $indent;$i++) {
125 $pad .= "\t";
126 }
127 debug("Pad is \'$pad\'");
128 }
129
130 #
131 # If we're in verbose mode, print headers
132 #
133
134 #my $type = $part->header("Content-Type");
135 my $type = $part->content_type();
136
137 print_sec(SECTION_BEGIN,\$pad,$indent) if ($settings->{'verbose'} == 1);
138
139 if ($type =~ /multipart\//) {
140 foreach my $subpart ($part->parts()) {
141 parse_part($subpart,$indent+1);
142 }
143 } elsif ($type =~ /text\/html/ && $settings->{'html'} != 1) {
144 return;
145 } elsif ($type =~ /text\//) {
146 debug("type: $type");
147 print_pad(\$pad,$part->body);
148 print_pad(\$pad,$settings->{'separator'})
149 if (!exists($opts->{'mark-sections'}));
150 } else {
151 print_pad(\$pad,"Skipping part of type $type\n")
152 if ($settings->{'verbose'} == 1);
153 }
154
155 print_sec(SECTION_END,\$pad,$indent) if ($settings->{'verbose'} == 1);
156
157 return;
158
159 }
160
161 sub help
162 {
163 print "mime_dump " . VERSION . "\n\n";
164 print "Usage: $0 [<options>]\n\n";
165
166 print "mime_dump takes in a MIME-encoded message, recurses through\n";
167 print "the MIME parts (including MIME nested in MIME) and prints all\n";
168 print "parts that are of type text/* EXCEPT for text/html.\n\n";
169
170 print "Options are as follows:\n";
171 print " -d, --debug\n";
172 print "\tTurn on debugging output\n";
173 print " -f, --file <file>\n";
174 print "\tRather than read from STDIN, read the email from";
175 print " <file>.\n";
176 print " -h, --help\n";
177 print "\tThis message.\n";
178 print " -H, --html\n";
179 print "\tInclude HTML\n";
180 print " -i, --indent\n";
181 print "\tIndent enclosure one tab\n";
182 print " -s, --separator <sep>\n";
183 print "\tUse <sep> to separate various MIME parts.\n";
184 print " -v, --verbose\n";
185 print "\tVerbose. This option prints headers around each enclosure"
186 . "and\n\timplies -i\n\n";
187
188 print "Note: You may also give mime_dump input on STDIN. For";
189 print " example:\n\techo email.txt | mime_dump\n";
190 print "Specifying the -f option tells mime_dump to ignore anything";
191 print " on STDIN.\n\n";
192
193 print "A global config file can be defined in /etc/mime_dump.conf,\n";
194 print "and local ones in ~/.mime_dump.conf. This file has\n";
195 print "'key = value' pairs with the same name as long options listed\n";
196 print "above. All options _except_ \'file\' and \'help\' are allowed\n";
197 print "in the config file. A sample config file looks like:\n\n";
198
199 print "\tverbose=1\n\tdebug=0\n\n";
200
201 print "Obviously, you don't need to define all config options in the\n";
202 print "file. Note that command-line options override config files.\n\n";
203 }
204
205 sub print_pad
206 {
207 my ($pad_ptr,$msg) = @_;
208 my $pad = $$pad_ptr;
209 my @lines = split(/\n/,$msg);
210 foreach my $line (@lines) {
211 print "${pad}${line}\n";
212 }
213 }
214
215 sub print_sec
216 {
217 my ($pos,$pad_ptr,$indent) = @_;
218
219 $msg = ($pos) ? "== END" : " BEGIN";
220
221 print_pad($pad_ptr,"==================$msg ENCLOSURE LEVEL $indent"
222 . " =========================\n")
223 if ($settings->{'verbose'} == 1);
224
225 }
226
227 #
228 # This sub assumes you've already checked for the existance of a file
229 #
230 sub read_config
231 {
232 my ($file,$conf) = @_;
233
234 open(CONF,"<$file") || die("ERROR: Couldn't open $file");
235
236 foreach my $line (<CONF>) {
237 chomp($line);
238 my ($key,$val) = split(/\s*=\s*/,$line,2);
239 $conf->{$key} = $val;
240 }
241
242 }
243
244 sub debug
245 {
246 my $msg = shift;
247
248 print "DEBUG: $msg\n" if ($settings->{'debug'} == 1);
249 }
250
251 sub build_settings
252 {
253
254 #
255 # Set defaults
256 #
257 my $settings = {
258 'debug' => 0,
259 'html' => 0,
260 'indent' => 0,
261 'separator' => DEFAULT_SEPARATOR,
262 'verbose' => 0,
263 'method' => METHOD_FILE,
264 };
265
266
267 #
268 # Grab config file
269 #
270 my $config = {};
271 read_config(GLOBAL_CONFIG,$config) if (-r GLOBAL_CONFIG);
272 my $local_config = home() . '/' . LOCAL_CONFIG_FILE;
273 read_config($local_config,$config) if (-r $local_config);
274
275 #
276 # Get all the toggle-able settings combined
277 #
278 foreach my $key (keys(%{$settings})) {
279 next if ($key eq 'separator');
280 $settings->{$key} = 1
281 if (exists($config->{$key}) && $config->{$key} == 1);
282 $settings->{$key} = 1
283 if (exists($opts->{$key}));
284 }
285
286 #
287 # If verbose is on, we turn on indent
288 #
289 $settings->{'indent'} = 1 if ($settings->{'verbose'} == 1);
290
291 #
292 # Separator isn't togglable, needs to be done separately.
293 #
294 my $key = 'separator';
295 $settings->{$key} = $config->{$key} if (exists($config->{$key}));
296 $settings->{$key} = $opts->{$key} if (exists($opts->{$key}));
297
298 #
299 # If -f is passed in, we read from that file.
300 # Otherwise, we make sure STDIN isn't a terminal (i.e.
301 # it's a pipe or was redirected into is). Otherwise, we fail.
302 #
303 if (exists($opts->{'file'}) && $opts->{'file'} ne '') {
304 $settings->{'method'} = METHOD_FILE;
305 } elsif (! -t STDIN) {
306 $settings->{'method'} = METHOD_STDIN;
307 } else {
308 print STDERR "ERROR: No file was specified on the command line or sent"
309 . " in on STDIN.\n";
310 exit 1;
311 }
312
313 if ($settings->{'method'} == METHOD_FILE && ! -r $opts->{'file'} ) {
314 print STDERR "ERROR: $opts->{'file'} not readable\n";
315 exit 1;
316 }
317
318 return $settings;
319 }
Something went wrong with that request. Please try again.