File Coverage

blib/lib/Mail/Pegasus.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             #
3              
4             package Mail::Pegasus;
5              
6             require 5.005;
7              
8 1     1   7612 use strict;
  1         4  
  1         40  
9              
10 1     1   6 use vars qw($VERSION);
  1         2  
  1         46  
11              
12             #use IO::Scalar;
13 1     1   1808 use Mail::Internet;
  0            
  0            
14              
15             $VERSION=sprintf("%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/);
16              
17             my $debug = 0;
18             my $directory = undef;
19             my $heirarch = "HIERARCH.PM";
20              
21             sub new
22             {
23             my $self = shift;
24             my $this = {};
25             my %arg = @_;
26              
27             $debug = (exists $arg{Debug} ? $arg{Debug} : 0 );
28             $this->{heirarch} = (exists $arg{Heirarch} ? $arg{Heirarch} : "HIERARCH.PM" );
29             $this->{directory} = (exists $arg{Directory} ? $arg{Directory} : undef );
30              
31             bless $this;
32              
33             &init($this);
34             return $this;
35             }
36              
37             sub init
38             {
39             my $self = shift;
40              
41             my %folders=();
42             my %parents=();
43             my $parentrootid=1;
44             my @parentroots=();
45             my %ifolders=();
46              
47             $self->{'folders'} = \%ifolders;
48              
49             warn("Directory: $self->{directory}\n");
50             if ((!defined($self->{directory})) || (! -d $self->{directory}))
51             {
52             warn("You must specify a valid directory!\n");
53             return;
54             }
55              
56             my $h = $self->{directory} . "/" . $self->{heirarch};
57             if ((! -f $h) || (! -r $h))
58             {
59             warn("cannot read hierachy file!\n");
60             return;
61             }
62              
63             open(HFD,"<$h");
64             while(!eof(HFD))
65             {
66             my @fields;
67             my ($line, $n1, $n2, $folderid, $parent, $name);
68              
69             chomp($line=);
70             $line =~ s/\r$//;
71             printf("<<$line\n") if ($debug);
72             @fields = split(/,/,$line);
73             if (scalar @fields != 5)
74             {
75             warn("Can't parse line [$line]\n");
76             }
77             map($_ =~ s/^"//,@fields);
78             map($_ =~ s/"$//,@fields);
79             $n1 = $fields[0];
80             $n2 = $fields[1];
81             $folderid = $fields[2];
82             $parent = $fields[3];
83             $name = $fields[4];
84             if ($folderid eq "")
85             {
86             warn("Found BLANK folder ID in line [$line]. Skipping\n");
87             next;
88             }
89             if ($parent eq "")
90             {
91             if ($folderid =~ /:My mailbox$/)
92             {
93             $parent = "TOP00";
94             } else {
95             $parent = sprintf("TOP%.2d",$parentrootid++);
96             }
97             push(@parentroots,$parent);
98             }
99             printf(">>%s,%s,%s,%s,%s\n",$n1,$n2,$folderid,$parent,$name) if ($debug);
100              
101             if (exists($folders{$folderid}))
102             {
103             warn("Found duplicate folder ID $folderid\n");
104             } else {
105             $folders{$folderid} = {
106             'n1' => $n1,
107             'n2' => $n2,
108             'parent' => $parent,
109             'name' => $name,
110             'used' => 0,
111             };
112             printf("++%s->%s\n",$folderid, $parent) if ($debug);
113             push(@{$parents{$parent}},$folderid);
114             }
115             }
116             foreach my $parentroot (sort @parentroots)
117             {
118             &recursefolders(\%ifolders, \%parents, \%folders, $parentroot,$parentroot);
119             }
120             foreach my $ifolder (sort {length($a) <=> length($b)} keys %ifolders) {
121             my $file = sprintf("%s/%s.PMM",$self->{directory},$ifolders{$ifolder});
122             if (-f $file)
123             {
124             $ifolders{$ifolder} = $file;
125             } else {
126             $ifolders{$ifolder} = "";
127             }
128             printf("%s -> %s\n",$ifolder,$ifolders{$ifolder}) if ($debug);
129             }
130             close(FD);
131              
132             # INBOX is a special case and partly a hack.
133             $ifolders{'INBOX'} = "INBOX";
134             $self->{Folders} = \%ifolders;
135             }
136              
137             sub recursefolders
138             {
139             my $ifolders = shift;
140             my $parents = shift;
141             my $folders = shift;
142             my($parent,$path) = @_;
143             my($fpath,$folder);
144             foreach my $folderid (@{$parents->{$parent}}) {
145             $fpath = sprintf("%s|%s",$path,$folders->{$folderid}->{name});
146             # printf(">>>%s\n",$fpath);
147             if (exists($parents->{$folderid})) {
148             &recursefolders($ifolders,$parents,$folders,$folderid,$fpath);
149             } else {
150             printf(">>>%s\n",$fpath) if ($debug);
151             $fpath =~ s/^TOP\d{2}\|//;
152             $fpath =~ s/^My mailbox\|//;
153             $fpath =~ s/\|/\//g;
154             $folderid =~ s/^[0-9A-F]+://;
155             $folderid =~ s/^[0-9A-F]{4}://;
156             $ifolders->{$fpath} = $folderid;
157             }
158             }
159             }
160              
161             sub select_by_id
162             {
163             my $self = shift;
164             my $f = shift;
165             my $result = undef;
166              
167             $self->{'Selected'} = undef;
168              
169             if ($f !~ /^\d+$/)
170             {
171             warn("You must specify a number!\n");
172             } else {
173             if (defined($self->{Folders}))
174             {
175             my $x = $self->{Folders};
176             my $count = 1;
177             my $fname;
178             foreach my $fi (sort keys %$x)
179             {
180             $fname = $fi;
181             last if ($count eq $f);
182             $count++;
183             }
184             $f = $fname;
185             warn("select(): selected->$f\n") if ($debug);
186             &select($self, $f);
187             $result = $self;
188             }
189             }
190             return $result;
191             }
192              
193             sub select
194             {
195             my $self = shift;
196             my $f = shift;
197             my $result = undef;
198              
199             $self->{'Selected'} = undef;
200              
201             if ($f eq "")
202             {
203             warn("You must specify a folder name or number!\n");
204             } else {
205             if (defined($self->{Folders}))
206             {
207             my $x = $self->{Folders};
208              
209             if (defined($x->{$f}) && $x->{$f} ne "")
210             {
211             $self->{Selected} = $x->{$f};
212             if ($f eq "INBOX")
213             {
214             build_inbox_hash($self);
215             } else {
216             build_folder_hash($self);
217             }
218             $result = $self;
219             } else {
220             warn("$f does not appear to exist!\n");
221             }
222             }
223             }
224             return $result;
225             }
226              
227             sub list_folders
228             {
229             my $self = shift;
230             my $folder_array = [ ];
231              
232             foreach my $folder (sort keys %{$self->{Folders}})
233             {
234             push(@$folder_array, $folder);
235             }
236             warn("\@folder_array contains: \"" . join("\", \"", @$folder_array) . "\"\n") if ($debug);
237             return $folder_array;
238             }
239              
240             sub print_folders
241             {
242             my $self = shift;
243             my $count = 1;
244              
245             foreach my $folder (sort keys %{$self->{Folders}})
246             {
247             printf("%d. %s\n", $count, $folder);
248             $count++;
249             }
250             }
251              
252             sub find_first_message
253             {
254             my $self = shift;
255             my $file;
256             my $data;
257             my $count = 0;
258             my $ffile;
259              
260             if (!defined($self->{Selected}))
261             {
262             warn("No folder selected!\n");
263             return undef;
264             }
265             $file = $self->{Selected};
266             warn("find_first_message(): selected->$file\n") if ($debug);
267              
268             if (!-r $file)
269             {
270             die("Cannot Open File: $@");
271             }
272              
273             $file =~ /^[\S+\/]*\/(\S+)\.PMM$/ && ($ffile = $1);
274              
275             open FOLDER, "$file";
276             until ($data =~ /$ffile/ || eof FOLDER)
277             {
278             seek FOLDER, $count++, 0; # 0 = SEEK_SET
279             read FOLDER, $data, length($ffile); # read until we find the folder name
280             }
281             warn("find_first_message(): Found filename [$ffile] at $count byte\n") if ($debug);
282             $count += length($ffile);
283             $count++;
284              
285             # this is a really bad hack, but it seems to work..
286             until ($data =~ /R/ || eof FOLDER)
287             {
288             read FOLDER, $data, 1, $count++;
289             }
290             # $count now has the starting byte in the file of the first message
291             close FOLDER;
292             return $count;
293             }
294              
295             sub get_message
296             {
297             my $self = shift;
298             my $id = shift;
299              
300             my $data = "";
301             my $file = "";
302              
303             my $selected = $self->{Selected};
304             if ($selected eq "INBOX")
305             {
306             $file = $self->{$selected}->{$id}->{File};
307             } else {
308             $file = $selected;
309             }
310             my $msg_start = $self->{$selected}->{$id}->{Start};
311             my $msg_length = $self->{$selected}->{$id}->{Length};
312              
313             open FOLDER, "<$file";
314             seek FOLDER, $msg_start, 0; # 0 = SEEK_SET
315             read FOLDER, $data, $msg_length;
316             close FOLDER;
317              
318             return $data;
319             }
320              
321             sub message
322             {
323             my $self = shift;
324             my $id = shift;
325             my $result = undef;
326              
327             my $message = get_message($self, $id);
328             $result = new Mail::Internet [ $message =~ /(.*?\n)/g ];
329             return $result;
330             }
331              
332             sub head
333             {
334             my $self = shift;
335             my $id = shift;
336             my $result = undef;
337              
338             $result = {message($self, $id)}->head();
339             return $result;
340             }
341              
342             sub body
343             {
344             my $self = shift;
345             my $id = shift;
346             my $result = undef;
347              
348             $result = {message($self, $id)}->body();
349             return $result;
350             }
351              
352             sub get_message_status
353             {
354             my $self = shift;
355             my $id = shift;
356             my $result = undef;
357              
358             my $message = get_message($self, $id);
359             my $mail = new Mail::Internet [ $message =~ /(.*?\n)/g ];
360             my $mail_headers = $mail->head();
361             my $headers_ref = $mail_headers->header_hashref();
362             # print Dumper($headersRef);
363             if (defined($headers_ref->{'X-Pmflags'}))
364             {
365             $result = 1;
366             }
367             if (defined($headers_ref->{'X-PM-Placeholder'}))
368             {
369             $result = 0;
370             }
371             return $result;
372             }
373              
374             sub find_message
375             {
376             my $fh = shift;
377             my $start = shift;
378              
379             my $msg = "";
380             my $char = "";
381             my $count = 1;
382             my $eof = sprintf("%c", 26);
383              
384             seek $fh, $start, 0; # 0 = SEEK_SET
385             until(eof $fh)
386             {
387             read $fh, $char, 4096; # average message size is less than 4k
388             my $pos = index $char, $eof;
389             # warn ("$count -> $pos\n") if ($debug);
390             if ($pos > 0)
391             {
392             $count += $pos;
393             last;
394             } else {
395             $count += 4096;
396             }
397             }
398              
399             # reset and read message
400             seek $fh, $start, 0; # 0 = SEEK_SET
401             read $fh, $msg, $count;
402              
403             return ($msg,$count);
404             }
405              
406             sub build_inbox_hash
407             {
408             my $self = shift;
409             my $current_folder = "INBOX";
410             my $id = 0;
411             my $directory = $self->{directory};
412              
413             warn("Building hash for $current_folder ($directory)\n") if ($debug);
414              
415             opendir DIR, "$directory" or return undef;
416              
417             foreach my $file (readdir(DIR))
418             {
419             if ($file =~ /\.CNM/ && -f "$directory/$file")
420             {
421             my $folder_info_ref = {};
422             my $size = -s "$directory/$file";
423             warn("build_inbox_hash: Adding: $directory/$file ($size bytes)\n") if ($debug);
424             $self->{$current_folder}->{$id} = $folder_info_ref;
425             $self->{$current_folder}->{$id}->{'Start'} = 0;
426             $self->{$current_folder}->{$id}->{'Length'} = $size;
427             $self->{$current_folder}->{$id}->{'File'} = "$directory/$file";
428             $id++;
429             }
430             }
431             closedir DIR;
432             }
433              
434             sub build_folder_hash
435             {
436             my $self = shift;
437             my $current_folder = $self->{'Selected'};
438             my $id = 0;
439              
440             warn("Building hash for folder: $self->{'Selected'}\n") if ($debug);
441             $self->{FirstMsg} = find_first_message($self);
442             $self->{MessageStart} = $self->{FirstMsg};
443             warn("First Message Header found at: $self->{FirstMsg}\n") if ($debug);
444             open FOLDER, "<$current_folder";
445             seek FOLDER, $self->{FirstMsg}, 0; # 0 = SEEK_SET
446             until(eof FOLDER)
447             {
448             my $folder_info_ref = {};
449             my ($message, $record_length) = find_message(\*FOLDER, $self->{MessageStart});
450              
451             $self->{$current_folder}->{$id} = $folder_info_ref;
452             $self->{$current_folder}->{$id}->{'Start'} = $self->{MessageStart};
453             $self->{$current_folder}->{$id}->{'Length'} = ($record_length-1);
454              
455             $self->{MessageStart} += $record_length;
456             $id++;
457             }
458             close FOLDER;
459             }
460              
461             sub messages
462             {
463             my $self = shift;
464             my $id = 0;
465              
466             if (!defined($self->{Selected}))
467             {
468             warn("No folder selected!\n");
469             } else {
470             my $file = $self->{Selected};
471             foreach my $f (sort keys %{$self->{Folders}})
472             {
473             if ($file eq $self->{Folders}->{$f})
474             {
475             print "Folder: $f\n" if ($debug);
476             last;
477             }
478             }
479              
480             until(!defined($self->{$self->{'Selected'}}->{$id}))
481             {
482             $id++;
483             }
484             }
485             return $id;
486             }
487              
488             sub list_messages
489             {
490             my $self = shift;
491             my $result = undef;
492             my $id = 0;
493             my $ret = 0;
494              
495             my $data;
496             my $date;
497             my $subject;
498              
499             if (!defined($self->{Selected}))
500             {
501             warn("No folder selected!\n");
502             } else {
503             $ret = 1;
504             my $file = $self->{Selected};
505             foreach my $f (sort keys %{$self->{Folders}})
506             {
507             #print "Folder: $self->{Folders}->{$f}\n";
508             if ($file eq $self->{Folders}->{$f})
509             {
510             print "Folder: $f\n";
511             last;
512             }
513             }
514              
515             until(!defined($self->{$self->{'Selected'}}->{$id}))
516             {
517             my $message = get_message($self, $id);
518             # print("Message:\n" . $message . "\n");
519             my $mail = new Mail::Internet [ $message =~ /(.*?\n)/g ];
520             my $mail_headers = $mail->head();
521             my $headers_ref = $mail_headers->header_hashref();
522             if (defined($headers_ref->{'Date'}))
523             {
524             $date = join(", ", @{$headers_ref->{'Date'}});
525             chomp($date);
526             $date =~ s/\(\S+\)//g;
527             } else {
528             $date = "No Date Header!";
529             }
530             if (defined($headers_ref->{'Subject'}))
531             {
532             $subject = join("", @{$headers_ref->{'Subject'}});
533             $subject =~ s/\n//g;
534             chomp($subject);
535             } else {
536             $subject = "[No Subject]";
537             }
538             printf("ID: $id \tDate: $date \tSubject: $subject\n");
539             $id++;
540             }
541             }
542             return $ret;
543             }
544              
545             1;
546              
547             __END__