#!/usr/bin/perl
sub
DESTROY { }
1;
package
mailer;
sub
get_user {
$_
[0]->{
'user'
} }
sub
get_hostname {
$_
[0]->{
'hostname'
} }
sub
get_relayhost {
$_
[0]->{
'relayhost'
} }
sub
get_replyaddr {
$_
[0]->{
'replyaddr'
} }
sub
get_socket {
$_
[0]->{
'socket'
} }
sub
set_user {
$_
[0]->{
'user'
} =
$_
[1] }
sub
set_hostname {
$_
[0]->{
'hostname'
} =
$_
[1] }
sub
set_relayhost {
$_
[0]->{
'relayhost'
} =
$_
[1] }
sub
set_replyaddr {
$_
[0]->{
'replyaddr'
} =
$_
[1] }
sub
set_socket {
$_
[0]->{
'socket'
} =
$_
[1] }
sub
new {
my
$class
=
shift
;
my
$self
= {};
if
(
@_
) {
my
$a
;
while
(
defined
(
$a
=
shift
)) {
$self
->{
$a
}=
shift
;
}
}
bless
(
$self
,
$class
);
if
(!
exists
$ENV
{REPLYADDR}) {
if
(!
defined
$self
->get_user) {
if
(
exists
$ENV
{USER} or
exists
$ENV
{LOGNAME}) {
if
(
exists
$ENV
{USER}) {
$self
->set_user(
$ENV
{USER});
}
else
{
$self
->set_user(
$ENV
{LOGNAME});
}
}
else
{
die
"Your username is not defined. \$USER or \$LOGNAME must be set.\n"
;
}
}
if
(!
defined
$ENV
{HOSTNAME}) {
my
$hostname
= Sys::Hostname::hostname();
if
(!
$hostname
) {
die
"Unable to find a reasonable hostname. Use \$HOSTNAME.\n"
;
}
$self
->set_hostname(
$hostname
);
}
else
{
$self
->set_hostname(
$ENV
{HOSTNAME});
}
$self
->set_replyaddr(
$self
->get_user .
"@"
.
$self
->get_hostname);
}
else
{
$self
->set_replyaddr(
$ENV
{REPLYADDR});
}
my
$emsg
;
if
(!
exists
$ENV
{RELAYHOST}) {
$self
->set_relayhost(
'localhost'
);
$emsg
=
"You did not specify an \$RELAYHOST\nor this system is not listening to SMTP\n"
;
}
else
{
$self
->set_relayhost(
$ENV
{RELAYHOST});
$emsg
=
"Unable to connect to the specified relay host\n"
;
}
my
$socket
= IO::Socket::INET->new(
PeerAddr
=>
$self
->get_relayhost,
PeerPort
=> 25,
Proto
=>
"tcp"
,
Type
=> SOCK_STREAM,
Timeout
=> 15,
);
die
$emsg
.
$IO::Socket::errstr
.
"\n"
unless
$socket
;
my
(
$ofh
)=
select
(
$socket
); $|=1;
select
(
$ofh
);
$_
=<
$socket
>;
$self
->set_socket(
$socket
);
return
$self
;
}
sub
send
{
my
$self
=
shift
;
my
$message
=
shift
;
my
$sock
=
$self
->get_socket;
main::debug(
"Mailed from "
.
$self
->get_replyaddr);
print
$sock
"mail from: "
.
$self
->get_replyaddr .
"\n"
;
$_
=<
$sock
>;
{
if
(
defined
$message
->get_to) {
foreach
my
$recipient
(@{
$message
->get_to}) {
main::debug(
"Sending to recipient $recipient"
);
print
$sock
"rcpt to: $recipient\n"
;
$_
=<
$sock
>; main::debug(
"reply: $_"
);
}
}
else
{
die
"No recipient.\n"
;
}
if
(
defined
$message
->get_cc) {
foreach
my
$recipient
(@{
$message
->get_cc}) {
main::debug(
"Sending to recipient $recipient"
);
print
$sock
"rcpt to: $recipient\n"
;
$_
=<
$sock
>; main::debug(
"reply: $_"
);
}
}
if
(
defined
$message
->get_bcc) {
foreach
my
$recipient
(@{
$message
->get_bcc}) {
main::debug(
"Sending to recipient $recipient"
);
print
$sock
"rcpt to: $recipient\n"
;
$_
=<
$sock
>; main::debug(
"reply: $_"
);
}
}
my
(
$to
,
$cc
);
$to
=
join
(
', '
, @{
$message
->get_to});
print
$sock
"data\n"
;
$_
=<
$sock
>;
if
(
$to
) {
print
$sock
"To: $to\n"
;
}
if
(
defined
$message
->get_cc) {
$cc
=
join
(
', '
, @{
$message
->get_cc});
print
$sock
"Cc: $cc\n"
;
}
if
(
$message
->get_subject) {
print
$sock
"Subject: "
.
$message
->get_subject .
"\n"
;
}
print
$sock
"X-Mailer: Perl Power Tools mail v"
,
$main::VERSION
,
"\n\n"
;
print
$sock
$message
->body;
print
$sock
"\n.\n"
;
$_
=<
$sock
>;
main::debug(
"received after body: $_"
);
}
}
sub
DESTROY {
my
$self
=
shift
;
my
$sock
=
$self
->get_socket;
close
(
$sock
)
if
defined
$sock
;
}
1;
package
message;
sub
get_subject {
$_
[0]->{
'subject'
} }
sub
get_from {
$_
[0]->{
'from'
} }
sub
is_deleted {
$_
[0]->{
'deleted'
} }
sub
is_read {
$_
[0]->{
'read'
} }
sub
neverseen {
$_
[0]->{
'neverseen'
} }
sub
get_to {
$_
[0]->{
'to'
} }
sub
get_bcc {
$_
[0]->{
'bcc'
} }
sub
get_cc {
$_
[0]->{
'cc'
} }
sub
get_lines {
$_
[0]->{
'lines'
} }
sub
get_bytes {
$_
[0]->{
'bytes'
} }
sub
get_date {
$_
[0]->{
'date'
} }
sub
get_sequence {
$_
[0]->{
'sequence'
} }
sub
set_subject {
$_
[0]->{
'subject'
} =
$_
[1] }
sub
set_from {
$_
[0]->{
'from'
} =
$_
[1] }
sub
set_deleted {
$_
[0]->{
'deleted'
} =
$_
[1] }
sub
set_read {
$_
[0]->{
'read'
} =
$_
[1] }
sub
set_neverseen {
$_
[0]->{
'neverseen'
} =
$_
[1] }
sub
set_to {
$_
[0]->{
'to'
} =
$_
[1] }
sub
set_bcc {
$_
[0]->{
'bcc'
} =
$_
[1] }
sub
set_cc {
$_
[0]->{
'cc'
} =
$_
[1] }
sub
set_lines {
$_
[0]->{
'lines'
} =
$_
[1] }
sub
set_bytes {
$_
[0]->{
'bytes'
} =
$_
[1] }
sub
set_date {
$_
[0]->{
'date'
} =
$_
[1] }
sub
set_sequence {
$_
[0]->{
'sequence'
} =
$_
[1] }
sub
new {
my
$class
=
shift
;
my
$self
= {};
bless
(
$self
,
$class
);
return
$self
;
}
sub
load_from_array {
my
$self
=
shift
;
my
(
$l
,
@head
,
@body
);
$self
->set_lines(
scalar
@_
);
my
$bytes
;
foreach
(
@_
) {
$bytes
+=
length
(
$_
);
}
$bytes
+= (
$self
->get_lines) *
length
($/);
$self
->set_bytes(
$bytes
);
while
(
$l
=
shift
) {
push
(
@head
,
$l
)
}
@body
=
@_
;
push
(@{
$self
->{body}},
@body
);
push
(@{
$self
->{head}},
@head
);
$self
->_extract;
}
sub
_extract {
my
$self
=
shift
;
my
@hc
;
foreach
(@{
$self
->{head}}) {
unless
(/^\s+/) {
push
(
@hc
,
$_
);
}
else
{
$hc
[
$#hc
].=
$_
;
}
}
$self
->set_neverseen(1);
$self
->set_read(0);
my
$line
;
while
(
$line
=
shift
@hc
) {
my
(
$foo
,
$bar
);
if
((
$foo
)=
$line
=~/^Subject:\s+(.*)/i) {
$self
->set_subject(
$foo
);
}
if
((
$foo
)=
$line
=~/^To:\s+(.*)/i) {
my
@toaddrs
;
$foo
=~s/
"[^"
]+"//g;
foreach
my
$addtest
(
split
(/[,\s]+/,
$foo
)) {
$addtest
=~s/^\s+//g;
$addtest
=~s/\s$//g;
next
if
(!
$addtest
);
if
(
$addtest
=~/<(.*)>/) {
push
(
@toaddrs
, $1);
}
elsif
(
$addtest
=~/(\w+@[\w.]+)/){
push
(
@toaddrs
, $1);
}
else
{
push
(
@toaddrs
,
$addtest
);
}
}
$self
->set_to([
@toaddrs
]);
}
if
((
$foo
)=
$line
=~/^CC:\s+(.*)/i) {
my
@toaddrs
;
$foo
=~s/
"[^"
]+"//g;
foreach
my
$addtest
(
split
(/[,\s]+/,
$foo
)) {
$addtest
=~s/^\s+//g;
$addtest
=~s/\s$//g;
next
if
(!
$addtest
);
if
(
$addtest
=~/<(.*)>/) {
push
(
@toaddrs
, $1);
}
elsif
(
$addtest
=~/(\w+@[\w.]+)/){
push
(
@toaddrs
, $1);
}
else
{
push
(
@toaddrs
,
$addtest
);
}
}
$self
->set_cc([
@toaddrs
]);
}
if
((
$foo
,
$bar
)=
$line
=~/^From\s+(.*)\s+(\w{3}\s+\w{3}\s+\d+\s+\d+:\d+):\d+\s+\d+$/i) {
$self
->set_date(
$bar
);
$self
->set_from(
$foo
);
}
if
((
$foo
)=
$line
=~/^Status:\s+(.*)/i) {
$foo
=~/O/ and
$self
->set_neverseen(0);
$foo
=~/R/ and
$self
->set_read(1);
}
}
$self
->set_deleted(0);
}
sub
add_to_body {
my
$self
=
shift
;
push
(@{
$self
->{body}},
@_
);
}
sub
_printablestatus {
my
$self
=
shift
;
return
(
"X"
)
if
$self
->is_deleted;
return
(
" "
)
if
$self
->is_read;
return
(
"N"
)
if
$self
->neverseen;
return
(
"U"
);
}
sub
outputstatus {
my
$self
=
shift
;
my
$status
;
$status
.=
"R"
if
$self
->is_read;
$status
.=
"O"
;
return
(
$status
);
}
sub
summary {
my
$self
=
shift
;
my
$line
=
sprintf
"%2s %2d %16s %16s %3d/%4d "
,
$self
->_printablestatus,
$self
->get_sequence,
(
defined
$self
->get_from) ?
substr
(
$self
->get_from, 0, 16) :
" "
,
(
defined
$self
->get_date) ?
$self
->get_date :
" "
,
$self
->set_lines,
$self
->get_bytes;
if
(
defined
$self
->get_subject) {
$line
.=
substr
(
$self
->get_subject, 0,
$main::COLS
-
length
(
$line
)-1);
}
else
{
$line
.=
"(no subject)"
;
}
return
(
$line
);
}
sub
printhead {
my
$self
=
shift
;
$self
->set_read(1);
return
join
(
"\n"
,
grep
(!/^Status:/i, @{
$self
->{head}}));
}
sub
body {
my
$self
=
shift
;
$self
->set_read(1);
return
join
(
"\n"
, @{
$self
->{body}} );
}
sub
whole {
my
$self
=
shift
;
my
$lines
=
shift
;
$self
->set_read(1);
if
(!
defined
$lines
) {
return
join
(
"\n"
,
grep
(!/^Status:/i, @{
$self
->{head}}),
""
,@{
$self
->{
"body"
}});
}
else
{
return
join
(
"\n"
, (
grep
(!/^Status:/i, @{
$self
->{head}}),
""
,@{
$self
->{
"body"
}})[0..
$lines
] );
}
}
1;
package
mailbox;
sub
get_size {
$_
[0]->{
'size'
} }
sub
get_file {
$_
[0]->{
'file'
} }
sub
set_size {
$_
[0]->{
'size'
} =
$_
[1] }
sub
set_file {
$_
[0]->{
'file'
} =
$_
[1] }
sub
new {
my
$class
=
shift
;
my
$self
= {};
if
(
@_
) {
my
$a
;
while
(
defined
(
$a
=
shift
)) {
$self
->{
$a
}=
shift
;
}
}
@{
$self
->{messages}}=
"Invalid"
;
bless
(
$self
,
$class
);
return
$self
;
}
sub
load {
my
$self
=
shift
;
print
"Loading the mailfile "
,
$self
->get_file,
"\n"
;
my
$start
=1;
unless
(
defined
$self
->get_file) {
die
"No mailbox specified\n"
;
}
if
(-d
$self
->get_file) {
warn
$self
->get_file .
": is a directory\n"
;
return
;
}
unless
(
open
MBOX,
'<'
,
$self
->get_file) {
warn
$self
->get_file .
": cannot open: $!\n"
;
return
;
}
$self
->set_size(-s
$self
->get_file);
my
@MESS
=();
while
(<MBOX>) {
chomp
;
if
(
@MESS
and /^From /) {
my
$message
=message->new;
$message
->load_from_array(
@MESS
);
$message
->set_sequence(
$start
++);
push
(@{
$self
->{messages}},
$message
);
@MESS
=();
}
push
(
@MESS
,
$_
)
}
if
(
@MESS
) {
my
$message
=message->new;
$message
->load_from_array(
@MESS
);
$message
->set_sequence(
$start
++);
push
(@{
$self
->{messages}},
$message
);
}
close
(MBOX);
return
1;
}
sub
stuff {
my
$self
=
shift
;
push
(@{
$self
->{messages}},
$_
[0]);
$self
->set_size(0);
}
sub
write
{
my
$self
=
shift
;
my
$wa
=
shift
;
my
$mode
=
exists
$$wa
{append} ?
'>>'
:
'>'
;
my
$alt_msg
=
"\""
.
$self
->get_file .
"\" "
;
$alt_msg
.= (-e
$self
->get_file) ?
"[Appended]"
:
"[New File]"
;
if
(!
open
(MBOX,
$mode
,
$self
->get_file)) {
warn
"Failed to write to "
,
$self
->get_file,
": $!\n"
;
return
;
}
my
$bytes
=(-s
$self
->get_file);
my
$lines
;
foreach
(1..
$self
->lastmsg) {
my
$message
=
$self
->messagex(
$_
);
next
if
$message
->is_deleted;
if
(!
exists
$$wa
{bodyonly}) {
my
$foo
=
$message
->printhead;
print
MBOX
$foo
,
"\n"
;
$lines
+=
$foo
=~
tr
/\n//;
print
MBOX
"Status: "
,
$message
->outputstatus,
"\n"
;
$lines
+=1;
}
my
$foo
=
$message
->body;
print
MBOX
"\n"
,
$foo
,
"\n"
;
$lines
+=
$foo
=~
tr
/\n//;
$lines
+=2;
if
(
exists
$$wa
{unread}) {
$message
->set_read(0);
}
}
close
(MBOX);
$bytes
=(-s
$self
->get_file) -
$bytes
;
$alt_msg
.=
" $lines/$bytes"
;
return
(
$alt_msg
,
undef
);
}
sub
messagex {
my
$self
=
shift
;
my
$num
=
shift
;
return
if
$num
<= 0;
return
(${
$self
->{messages}}[
$num
]);
}
sub
replace {
my
$self
=
shift
;
${
$self
->{messages}}[
$_
[0]]=
$_
[1];
}
sub
lastmsg {
my
$self
=
shift
;
return
(
scalar
( @{
$self
->{messages}} )-1)
}
sub
nextmsg {
my
$self
=
shift
;
my
$current
=
shift
;
my
$msg
;
while
(
$msg
=
$self
->messagex(
$current
)) {
if
(!
defined
$msg
) {
return
undef
;
}
if
(
$msg
->is_deleted) {
$current
++;
next
;
}
last
;
}
return
(
$current
);
}
1;
package
editor;
sub
get_message {
$_
[0]->{
'message'
} }
sub
get_mesgno {
$_
[0]->{
'mesgno'
} }
sub
set_message {
$_
[0]->{
'message'
} =
$_
[1] }
sub
set_mesgno {
$_
[0]->{
'mesgno'
} =
$_
[1] }
sub
new {
my
$class
=
shift
;
my
$self
= {};
if
(
@_
) {
my
$a
;
while
(
defined
(
$a
=
shift
)) {
$self
->{
$a
}=
shift
;
}
}
bless
(
$self
,
$class
);
return
$self
;
}
sub
edit {
my
$self
=
shift
;
my
$args
=
shift
;
my
$msg
=
$self
->get_message;
if
(!
defined
$msg
) {
die
"Should Not Happen, edit without message"
;
}
if
(
exists
$$args
{subject}) {
print
"Subject: "
;
my
$subj
= <STDIN>;
chomp
$subj
;
$msg
->set_subject(
$subj
);
}
my
(
$line
,
$tilde
,
$cmd
,
$arg
);
my
@BODY
;
EDLOOP: {
$line
=<STDIN>;
if
( (not
defined
$line
) or (
$line
=~/^\.\s+$/)) {
last
EDLOOP; }
chomp
$line
;
unless
(
$line
=~/^~[^~]/) {
$line
=~s/^~+/~/g;
push
(
@BODY
,
$line
);
redo
EDLOOP;
}
(
$tilde
,
$cmd
,
$arg
)=
$line
=~/(~)(.)\s*(.*)?/;
warn
"Bad line"
if
(!
defined
$tilde
);
$_
=
$cmd
;
SWITCH: {
/s/ &&
do
{
$msg
->set_subject(
$arg
);
last
SWITCH; };
/
q/ && do { return; };
/
\!/ &&
do
{
system
(
$arg
);
last
SWITCH; };
/c/ &&
do
{
my
$ccs
=
$msg
->get_cc;
push
(@{
$ccs
},
split
(/[\s,;]+/,
$arg
));
$msg
->set_cc(
$ccs
);
last
SWITCH;
};
/b/ &&
do
{
my
$ccs
=
$msg
->get_bcc;
push
(@{
$ccs
},
split
(/[\s,;]+/,
$arg
));
$msg
->set_bcc(
$ccs
);
last
SWITCH;
};
/t/ &&
do
{
my
$tos
=
$msg
->get_to;
push
(@{
$tos
},
split
(/[\s,;]+/,
$arg
));
$msg
->set_to(
$tos
);
last
SWITCH;
};
/m/ &&
do
{
my
$msgs
= main::parse_msg_list(
$arg
,
$self
->get_mesgno);
foreach
(
@$msgs
) {
my
$rmsg
=
$main::box
->messagex(
$_
);
next
if
(!
defined
$rmsg
);
push
(
@BODY
,
grep
(s/^/> /g,
split
(/\n/,
$rmsg
->whole)));
}
last
SWITCH
};
/f/ &&
do
{
my
$msgs
= main::parse_msg_list(
$arg
,
$self
->get_mesgno);
foreach
(
@$msgs
) {
my
$rmsg
=
$main::box
->messagex(
$_
);
next
if
(!
defined
$rmsg
);
push
(
@BODY
,
split
(/\n/,
$rmsg
->whole));
}
last
SWITCH;
};
/r/ &&
do
{
open
(F,
'<'
,
$arg
) ||
do
{
warn
"Unable to open $arg: $!\n"
;
last
SWITCH; };
my
$bytes
=(-s
$arg
);
my
(
@FOO
)=<F>;
close
(F);
chomp
(
@FOO
);
push
(
@BODY
,
@FOO
);
print
"read $arg $bytes bytes\n"
;
};
/v/ &&
do
{
my
$vipath
= main::vipath() ||
last
SWITCH;
my
$tmp
= File::Temp->new;
my
$tmpfile
=
$tmp
->filename;
@BODY
=
grep
(s/$/\n/g,
@BODY
);
print
{
$tmp
}
@BODY
;
my
$rc
=
system
(
$vipath
,
$tmpfile
);
if
(
$rc
!= 0) {
warn
"Failed to execute '$vipath': $!\n"
;
last
SWITCH;
}
open
(F,
'<'
,
$tmpfile
) ||
die
"Unable to re-open $tmpfile: $!"
;
@BODY
=<F>;
chomp
(
@BODY
);
close
(F);
print
"(Continued)\n"
;
};
}
redo
EDLOOP;
}
$msg
->add_to_body(
@BODY
);
$self
->set_message(
$msg
);
return
(
$self
->get_message);
}
1;
use
vars
qw($opt_f $opt_s $opt_c $opt_b $opt_v)
;
our
$VERSION
=
'0.05'
;
our
$ROWS
= 23;
our
$COLS
= 80;
our
$BUFFERL
= 2;
my
$Program
= basename($0);
my
$box
;
my
%commands
=(
"chdir"
=> {
alias
=>
'c'
,
args
=>
'path'
, },
copy
=> {
alias
=>
'co'
,
args
=>
'msg,path'
,
func
=> \
&msg_copy
},
"delete"
=> {
alias
=>
'd'
,
args
=>
'msg'
,
func
=> \
&msg_delete
},
"exit"
=> {
alias
=>
'ex,x,xit'
,
func
=>
sub
{
exit
; } },
from
=> {
alias
=>
'f'
,
args
=>
'msg'
,
func
=> \
&from
},
headers
=> {
alias
=>
'h'
,
args
=>
'msg'
,
func
=> \
&listing
},
hold
=> {
alias
=>
'ho,preserve'
,
args
=>
'msg'
,
func
=> \
&unread
},
mail
=> {
alias
=>
'm'
,
args
=>
'addr'
,
func
=> \
&mail
},
"next"
=> {
alias
=>
'n'
,
func
=> \
&msg_next
},
"print"
=> {
alias
=>
'p'
,
args
=>
'msg'
,
func
=> \
&msg_print
},
quit
=> {
alias
=>
'q'
,
func
=> \
&quit
},
reply
=> {
alias
=>
'r'
,
args
=>
'msg'
,
func
=> \
&replyCC
},
Reply
=> {
alias
=>
'R'
,
args
=>
'msg'
,
func
=> \
&reply
},
save
=> {
alias
=>
's'
,
args
=>
'msg,path'
,
func
=> \
&msg_save
},
shell
=> {
alias
=>
'sh'
,
func
=> \
&shell
},
top
=> {
func
=> \
&top
},
undelete
=> {
alias
=>
'u'
,
args
=>
'msg'
,
func
=> \
&undelete
},
unread
=> {
alias
=>
'U'
,
args
=>
'msg'
,
func
=> \
&unread
},
visual
=> {
alias
=>
'v'
,
args
=>
'msg'
,
func
=> \
&visual
},
"write"
=> {
alias
=>
'w'
,
args
=>
'msg,path'
,
func
=> \
&msg_write
},
);
$SET::toplines
=5;
sub
listing {
my
(
$first
)=
@_
;
$first
=
$$first
[0];
foreach
(
$first
..
$first
+
$ROWS
-
$BUFFERL
) {
my
$message
=
$box
->messagex(
$_
);
if
(!
defined
$message
) {
warn
"Invalid message number: $first\n"
if
(
$_
==
$first
);
last
;
}
print
$message
->summary,
"\n"
;
}
return
$first
;
}
sub
vipath {
return
$ENV
{
'VISUAL'
}
if
(
defined
$ENV
{
'VISUAL'
});
return
'vi'
;
}
sub
shell {
system
(
"/bin/sh"
);
}
sub
mail {
my
(
$list
,
$mesgno
)=
@_
;
my
$msg
=message->new;
$msg
->set_to(
$list
);
my
$editor
=editor->new;
$editor
->set_mesgno(
$mesgno
);
$editor
->set_message(
$msg
);
$msg
=
$editor
->edit({
subject
=> 1});
if
(!
defined
$msg
) {
print
"Aborted\n"
;
return
;
}
my
$mailer
=mailer->new;
$mailer
->
send
(
$msg
);
}
sub
replyCC {
my
(
$list
)=
@_
;
my
(
@ccaddrs
,
@replies
,
$subj
,
$tc
);
foreach
(
@$list
) {
my
$original
=
$box
->messagex(
$_
);
next
if
(!
defined
$original
);
if
(!
$subj
) {
$subj
=
$original
->get_subject;
unless
(
$subj
=~/^re:/i) {
$subj
=
"re: $subj"
;
}
}
push
@replies
,
$original
->get_from;
$tc
=
$original
->get_cc;
push
(
@ccaddrs
,
@$tc
)
if
$tc
;
}
my
$msg
=message->new;
$msg
->set_to(\
@replies
);
$msg
->set_cc(\
@ccaddrs
);
$msg
->set_subject(
$subj
);
my
$editor
=editor->new;
$editor
->set_mesgno(@{
$list
}[0]);
$editor
->set_message(
$msg
);
print
"To: "
,
join
(
','
,
@replies
),
"\n"
;
if
(
@ccaddrs
) {
print
"Cc: "
,
join
(
','
,
@ccaddrs
),
"\n"
;
}
print
"Subject: $subj\n"
;
$msg
=
$editor
->edit;
if
(!
defined
$msg
) {
print
"Aborted\n"
;
return
;
}
my
$mailer
=mailer->new;
$mailer
->
send
(
$msg
);
}
sub
reply {
my
(
$list
)=
@_
;
my
(
@replies
,
$subj
);
foreach
(
@$list
) {
my
$original
=
$box
->messagex(
$_
);
next
if
(!
defined
$original
);
if
(!
$subj
) {
$subj
=
$original
->get_subject;
unless
(
$subj
=~/^re:/i) {
$subj
=
"re: $subj"
;
}
}
push
@replies
,
$original
->get_from;
}
my
$msg
=message->new;
$msg
->set_to(\
@replies
);
$msg
->set_subject(
$subj
);
my
$editor
=editor->new;
$editor
->set_mesgno(@{
$list
}[0]);
$editor
->set_message(
$msg
);
print
"To: "
,
join
(
','
,
@replies
),
"\n"
;
print
"Subject: $subj\n"
;
$msg
=
$editor
->edit;
if
(!
defined
$msg
) {
print
"Aborted\n"
;
return
;
}
my
$mailer
=mailer->new;
$mailer
->
send
(
$msg
);
}
sub
quit {
if
( (-s
$box
->get_file) !=
$box
->get_size ) {
warn
"Mail folder has changed size, not writing\n"
;
}
$box
->
write
;
exit
;
}
sub
visual {
my
(
$list
)=
@_
;
my
$cmd
= vipath() ||
return
;
foreach
my
$msgno
(
@$list
) {
my
$message
=
$box
->messagex(
$msgno
);
if
(!
defined
$message
) {
warn
"Invalid message number: $msgno\n"
;
return
;
}
my
$tmp
= File::Temp->new;
my
$path
=
$tmp
->filename;
my
$tmbox
= mailbox->new(
'file'
=>
$path
);
$tmbox
->stuff(
$message
);
$tmbox
->
write
;
my
$rc
=
system
(
$cmd
,
$path
);
if
(
$rc
!= 0) {
warn
"Failed to execute '$cmd': $!\n"
;
return
;
}
my
$tmbox2
= mailbox->new(
'file'
=>
$path
);
$tmbox2
->load;
$box
->replace(
$msgno
,
$tmbox2
->messagex(1));
}
}
sub
from {
my
(
$list
)=
@_
;
my
$lastgood
;
foreach
my
$msgno
(
@$list
) {
my
$message
=
$box
->messagex(
$msgno
);
if
(!
defined
$message
) {
warn
"Invalid message number: $msgno\n"
;
return
;
}
print
"Message: "
,
$message
->get_sequence,
"\n"
;
print
$message
->printhead,
"\n"
;
$lastgood
=
$msgno
;
}
return
$lastgood
;
}
sub
top {
msg_print(
@_
,
$SET::toplines
);
}
sub
msg_print {
my
(
$list
,
$dummy
,
$nlines
)=
@_
;
my
$lastgood
;
foreach
my
$msgno
(
@$list
) {
my
$message
=
$box
->messagex(
$msgno
);
if
(!
defined
$message
) {
warn
"Invalid message number: $msgno\n"
;
return
;
}
print
"Message: "
,
$message
->get_sequence,
"\n"
;
print
$message
->whole(
$nlines
),
"\n"
;
$lastgood
=
$msgno
;
}
return
$lastgood
;
}
sub
unread { toggle(
@_
,
'unread'
); }
sub
undelete { toggle(
@_
,
'undelete'
); }
sub
msg_delete { toggle(
@_
,
'delete'
); }
sub
toggle {
my
(
$msgs
,
$dumb
,
$option
)=
@_
;
foreach
my
$msgno
(
@$msgs
) {
my
$message
=
$box
->messagex(
$msgno
);
if
(!
defined
$message
) {
warn
"Invalid message number: $msgno\n"
;
return
;
}
if
(
$option
eq
'unread'
) {
$message
->set_read(0);
}
if
(
$option
eq
'undelete'
) {
$message
->set_deleted(0);
}
if
(
$option
eq
'delete'
) {
$message
->set_deleted(1);
}
}
return
;
}
sub
msg_next {
my
(
$first
)=
@_
;
$first
=
$$first
[0];
my
$nmsg
=
$box
->nextmsg(
$first
);
if
(!
defined
$nmsg
) {
warn
"At EOF\n"
;
return
;
}
my
$ret
=msg_print([
$nmsg
]);
return
++
$nmsg
if
(
defined
$ret
);
return
;
}
sub
msg_save { msg_store(
@_
, {
append
=> 1 }); }
sub
msg_copy { msg_store(
@_
, {
append
=> 1,
"unread"
=> 1,} ); }
sub
msg_write { msg_store(
@_
, {
append
=> 1,
bodyonly
=> 1,} ); }
sub
msg_store {
my
(
$msgs
,
$file
,
$options
)=
@_
;
print
"Saving message...$msgs to $file\n"
;
my
$tempbox
=mailbox->new(
file
=>
$file
);
foreach
my
$msg
(
@$msgs
) {
my
$message
=
$box
->messagex(
$msg
);
if
(!
defined
$message
) {
warn
"Invalid message number: $msg\n"
;
return
;
}
$tempbox
->stuff(
$message
);
}
my
(
$short
,
$long
)=
$tempbox
->
write
(
$options
);
print
"$short\n"
;
}
sub
parseargs {
my
(
$cmd
,
$mesgno
)=
@_
;
my
(
$cref
)=
undef
;
my
$word
=
$cmd
;
$word
=~s/^([a-zA-Z]+).*/$1/;
foreach
(
keys
%commands
) {
if
(
$word
eq
$_
) {
$cref
=
$commands
{
$_
};
}
elsif
(
exists
$commands
{
$_
}{alias}) {
if
(
grep
(
$word
eq
$_
,
split
(/,/,
$commands
{
$_
}{alias}))) {
$cref
=
$commands
{
$_
};
}
}
}
return
if
(!
defined
$cref
);
if
(!
exists
$$cref
{args}) {
return
(
$word
,
$$cref
{func}, [
$mesgno
]);
}
if
(
$$cref
{args}=~/msg,path/) {
my
$list
=
$cmd
;
$list
=~s/^[a-zA-Z]+//g;
my
(
$arg1
,
$arg2
)=
$list
=~/(.*)\s+([^\s+]+)$/;
$arg1
=parse_msg_list(
$arg1
,
$mesgno
);
return
(
$cmd
,
$$cref
{func},
$arg1
,
$arg2
);
}
if
(
$$cref
{args} eq
'addr'
) {
my
$list
=
$cmd
;
$list
=~s/^[a-zA-Z]+\s*//g;
my
@foo
=
split
(/[\s,;]+/,
$list
);
return
(
$cmd
,
$$cref
{func}, [
@foo
],
$mesgno
);
}
if
(
$$cref
{args} eq
'msg'
) {
my
$list
=
$cmd
;
$list
=~s/^[a-zA-Z]+//g;
my
$msglist
=parse_msg_list(
$list
,
$mesgno
);
return
(
$cmd
,
$$cref
{func},
$msglist
);
}
}
sub
parse_msg_list {
my
(
$list
,
$current
)=
@_
;
$list
=~ s/^\s+//g;
$list
=~ s/\s$//g;
return
[
$current
]
if
(
length
(
$list
) == 0);
my
@list
=();
foreach
my
$stuff
(
split
(/[, ]+/,
$list
)) {
$stuff
=
"1-\$"
if
(
$stuff
=~/^\*$/);
$stuff
=~s/\$/(
$box
->lastmsg)-1/e;
push
(
@list
,
$stuff
)
if
(
$stuff
=~/^\d+$/);
push
(
@list
, $1..$2)
if
(
$stuff
=~/^(\d+)-(\d+)/);
}
return
(\
@list
);
}
sub
Interactive {
my
(
$file
)=
@_
;
$box
=mailbox->new(
file
=>
$file
);
if
(!
$box
->load ) {
print
"You have no mail\n"
;
exit
;
}
my
$current
=1;
select
STDOUT; $|=1;
my
$cmd
=
"Init"
;
CMDS: {
if
(
$cmd
eq
"Init"
) {
$cmd
=
"h"
;
}
else
{
print
"> "
;
$cmd
=<STDIN>;
$cmd
=
'q'
unless
defined
$cmd
;
chomp
$cmd
;
$cmd
=~ s/\A\s+//;
}
GOTONE: {
if
(
$cmd
=~/^[a-zA-Z]+/) {
my
(
$fref
,
$arg1
,
$arg2
);
(
$cmd
,
$fref
,
$arg1
,
$arg2
)=parseargs(
$cmd
,
$current
);
if
(!
defined
$cmd
) {
warn
"Unknown command $cmd\n"
;
redo
CMDS;
}
if
(!
defined
$fref
) {
warn
"Unimplemented command $cmd\n"
;
redo
CMDS;
}
$current
=
&$fref
(
$arg1
,
$arg2
);
if
(!
defined
$current
) {
$current
=1;
}
}
elsif
(
$cmd
=~/^!(.*)/) {
system
(
"$1"
);
}
elsif
(
$cmd
=~/^[0-9]/) {
$cmd
=
"p $cmd"
;
redo
GOTONE;
}
elsif
(!
$cmd
) {
$cmd
=
"p"
;
redo
GOTONE;
}
else
{
warn
"Unknown command\n"
;
}
}
redo
CMDS;
}
}
sub
debug {
if
(
$opt_v
) {
print
STDERR
@_
, ((
$_
[
$#_
]=~/\n$/)?
""
:
"\n"
);
}
}
sub
Batch {
my
$message
=message->new;
$message
->set_to([
@_
]);
$message
->set_cc([
split
(/,/,
$opt_c
) ])
if
(
$opt_c
);
$message
->set_bcc([
split
(/,/,
$opt_b
) ])
if
(
$opt_b
);
$message
->set_subject(
$opt_s
)
if
(
$opt_s
);
my
$mailer
=mailer->new;
my
@BODY
=<STDIN>;
chomp
(
@BODY
);
$message
->add_to_body(
@BODY
);
$mailer
->
send
(
$message
);
}
sub
VERSION_MESSAGE {
print
"$Program version $VERSION\n"
;
exit
;
}
sub
usage {
warn
"usage: $Program [-s subject] [-c cc-addrs] [-b bcc-addrs] "
.
"to-addr ...\n $Program [-f mailbox]\n"
;
exit
1;
}
getopts(
'f:s:c:b:v'
) or usage();
if
(
@ARGV
) {
if
(
defined
$opt_f
) {
warn
"$Program: to-addr may not be specified with a mailbox\n"
;
usage();
}
Batch(
@ARGV
);
}
else
{
my
$mbox
=
'mbox'
;
if
(
defined
$opt_f
) {
$mbox
=
$opt_f
;
}
elsif
(
exists
$ENV
{
'MAIL'
}) {
$mbox
=
$ENV
{
'MAIL'
};
}
elsif
(
exists
$ENV
{
'HOME'
}) {
$mbox
= File::Spec->catfile(
$ENV
{
'HOME'
},
'mbox'
);
}
Interactive(
$mbox
);
}
1;