Replace /usr/bin/mail with Mail.app

Aug 22, '03 10:32:00AM

Contributed by: njnystrom

I have some programs ported over from Solaris that generate emails and send them using /usr/bin/mail. However, to get /usr/bin/mail to work, you have to activate sendmail or a sendmail replacement like postfix (found in Fink). Sendmail configuration is a real pain, so I wrote this Perl script.

The script is a drop-in replacement for /usr/bin/mail that uses AppleScript to tell Mail.app to send the mail. Save it somewhere in your path (I put it in ~/bin/mail) and invoke in the terminal with mail -s.

The body of the message is read from standard input. The script takes all the options /usr/bin/mail does, ignoring those that aren't applicable. I've added a new option -F to allow the user to set the From: address. There's also an option to set the Reply-To: address, but Mail.app doesn't like the AppleScript I generate for it (anyone know why?). I think the script can be easily adapted to behave like sendmail rather than mail, but I haven't attempted this.

[robg adds: I haven't tested this one ... I did, however, copy and paste the script from the above link into the remainder of this hint, just in case the parent site ever vanishes.]

The source:


#!/usr/bin/perl
# Drop-in replacement for /usr/bin/mail that uses Mail.app (via an
# applescript) rather than sendmail to send mail.  Unlike /usr/bin/mail,
# you can't use it for reading mail.
#
# usage: mail [<options>] <recipients>
#   options:
#     -v       be verbose
#     -g       activate Mail.app to approve the message
#     -F <from>  specify the From: address
#     -R <replyto> specify the Reply-To: address
#     -b <bcc>   specify Bcc: recipients in a comma-separated list
#     -c <cc>    specify Cc: recipients in a comma-separated list
#     -s <subject> specify the message subject
#
# The body of the message is read from standard input.
#
# Author: Nathaniel Nystrom <nystrom@cs.cornell.edu>
# This software is in the public domain.

use strict;
$|++;

my ($verbose, $gui);
my ($from, $replyto, @to, @cc, @bcc, $subject, $body);
my $prog;
($prog = $0) =~ s|.*/||;

while (@ARGV) {
  my $arg = shift @ARGV;

  if ($arg eq '-v') {
    $verbose++;
  }
  elsif ($arg eq '-g') {
    $gui++;
  }
  elsif ($arg eq '-F') {
    $from = shift @ARGV || &usage("missing sender");
  }
  elsif ($arg eq '-R') {
    $replyto = shift @ARGV || &usage("missing reply-to address");
  }
  elsif ($arg eq '-i' || $arg eq '-l' || $arg eq '-n') {
    # ignore; for /usr/bin/mail compatibility
  }
  elsif ($arg eq '-N' || $arg eq '-f' || $arg eq '-u') {
    &usage("invalid option $arg; $prog cannot be used for reading mail");
  }
  elsif ($arg eq '-s') {
    $subject = shift @ARGV || &usage("missing subject");
  }
  elsif ($arg eq '-c') {
    my $list = shift @ARGV || &usage("missing Cc list");
    @cc = split /\s*,\s*/, $list;
  }
  elsif ($arg eq '-b') {
    my $list = shift @ARGV || &usage("missing Bcc list");
    @bcc = split /\s*,\s*/, $list;
  }
  elsif ($arg =~ /^-/) {
    &usage("invalid option $arg");
  }
  else {
    @to = ($arg, @ARGV);
    last;
  }
}

&usage("missing recipients") unless @to;

unless (defined $subject) {
  print "Subject: ";
  $subject = <STDIN> || '';
  chomp $subject;
}

$body = '';

while (<STDIN>) {
  $body .= $_;
}

$replyto = $replyto || $from;

my $script = <<"EOS";
tell application "Mail"
  set newMessage to make new outgoing message
  tell newMessage
    set subject to "$subject"
    set content to "$body"
EOS

for (@to)  { $script .= &recipient('to', $_); }
for (@cc)  { $script .= &recipient('cc', $_); }
for (@bcc) { $script .= &recipient('bcc', $_); }

my $visible = $gui ? "true" : "false";
my $activate = $gui ? "activate" : "send newMessage";
my $fromln = $from ? "set sender to \"$from\"" : "";
my $replytoln = "";

# Doesn't work!  Anyone know why?
#my $replytoln = $replyto ? "set reply to to \"$replyto\"" : "";

$script .= <<"EOS";
    $fromln
    $replytoln
    set visible to $visible
  end tell
  $activate
end tell
EOS

if ($verbose >= 1) {
  print "From: $from\n" if $from;
  print "Reply-To: $replyto\n" if $replyto;
  print "To: ", join(',', @to), "\n" if @to;
  print "Cc: ", join(',', @cc), "\n" if @cc;
  print "Bcc: ", join(',', @bcc), "\n" if @bcc;

  if ($verbose >= 2) {
    print "Script >>>\n";
    print $script;
    print "<<<\n";
    print "\n";
    print $body;
  }
}

exec("osascript -e '$script' > /dev/null");
exit 0;


sub recipient {
  my ($type,$addr) = @_;
  return <<"EOS"
    make new $type recipient at end of $type recipients with properties {address: "$addr"}
EOS
}

sub usage {
  my $error = shift;
  print STDERR "Error: $error\n" if $error;
  print STDERR <<"EOS";
usage: $prog [<options>] <recipients>
  options:
    -v       be verbose
    -g       activate Mail.app to approve the message
    -F <from>  specify the From: address
    -R <replyto> specify the Reply-To: address
    -b <bcc>   specify Bcc: recipients in a comma-separated list
    -c <cc>    specify Cc: recipients in a comma-separated list
    -s <subject> specify the message subject
EOS
  exit 1;
}

Comments (21)


Mac OS X Hints
http://hints.macworld.com/article.php?story=2003082119192825