Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!

Replace /usr/bin/mail with Mail.app UNIX

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;
}
    •    
  • Currently 2.50 / 5
  • 1
  • 2
  • 3
  • 4
  • 5
  (2 votes cast)
 
[12,870 views]  

Replace /usr/bin/mail with Mail.app | 21 comments | Create New Account
Click here to return to the 'Replace /usr/bin/mail with Mail.app' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
Replace /usr/bin/mail with Mail.app
Authored by: hmelton on Aug 22, '03 11:06:44AM

Great! I have used self-emailed status reports from all kinds of unix platforms and I have missed having this ability -- but not enough to solve the sendmail problems. I tested your script and it works fine on my ibook.



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: JohnnyMnemonic on Aug 22, '03 11:37:07AM

My understanding is that root and other system users will send alerts through email. I've wanted to see those alrerts, but not badly enough to enable sendmail, as you say. Will this method allow those alerts to be sent?



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: njnystrom on Aug 22, '03 01:45:30PM

I'm not sure if these alerts use /usr/bin/mail or invoke sendmail directly. I suppose you could install the script as /usr/bin/mail and see what happens, but I haven't tried this.

---
Nate



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: stetner on Aug 23, '03 07:57:54AM

I run sendmail myself, but I was curious what happens when you are logged out and Mail.app isn't running and can't be started due to no GUI running?



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: njnystrom on Aug 23, '03 01:49:12PM

I assume it wouldn't work if I were logged out, but I'm just using the script on my Powerbook, where I'm the only user and I'm always logged in. I think if you're running a server, you should just bite the bullet and configure sendmail or postfix.

---
Nate

[ Reply to This | # ]

reply to
Authored by: hayne on Aug 22, '03 02:00:21PM
# Doesn't work! Anyone know why?
#my $replytoln = $replyto ? "set reply to to \"$replyto\"" : "";
I guess the problem is with the variable name (reply to) using one of AppleScript's reserved words (to). You might try putting vertical bars (the 'pipe' symbol: | ) around the variable name:

my $replytoln = $replyto ? "set |reply to| to \"$replyto\"" : "";

[ Reply to This | # ]

reply to
Authored by: njnystrom on Aug 22, '03 03:25:48PM

Thanks. This does make the script parse now. But it seems Mail.app ignores it: the Reply-To: header is not set.

---
Nate



[ Reply to This | # ]
Patched version of the script
Authored by: njnystrom on Aug 22, '03 03:38:06PM

The version of the script inlined in the hint above fails when there are embedded double quotes in the message body. A patched version is at the original location on the web here.

This version no longer crashes with the -R option, but -R still doesn't work.

---
Nate

[ Reply to This | # ]

Patched version of the script
Authored by: truhe on Aug 22, '03 05:17:33PM

did anyone of you succeeded in making this work with php?

I can run the script with my account and as root in a terminal window. root can connect to my mail.app. however, other users are not able to do this and I get error messages:

kCGErrorIllegalArgument : initCGDisplayState: cannot map display interlocks.
kCGErrorIllegalArgument : CGSNewConnection cannot get connection port

Since php is executed as a different user I think, that it can't connect to my mail.app. Is there a way to connect to a special app of a special user on the local machine?

In terminal.app I'm also only able to run the app with "echo "testbody" | /usr/local/scripts/mail.pl -v -s testsubject truhe@metal.de" - everything else hangs after typing in the subject. Does any one of you know the correct syntax for the php.ini?



[ Reply to This | # ]
Re: initCGDisplayState error
Authored by: hayne on Aug 23, '03 06:17:46PM

The error you are getting are due to the fact that the script is trying to run a GUI application (Mail.app)
Only the one user who is logged in via the login window can use GUI apps.



[ Reply to This | # ]
Patched version of the script
Authored by: macubergeek on Aug 22, '03 07:33:42PM

kinda works
still kicks up this error:

## Component Manager: attempting to find symbols in a component alias of type (regR/carP/x!bt)



[ Reply to This | # ]
Re: component manager errors
Authored by: hayne on Aug 23, '03 06:10:01PM

See this hint:
http://www.macosxhints.com/article.php?story=20021211054158940



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: njnystrom on Aug 22, '03 08:15:57PM

Re: "everything else hangs after typing in the subject". It's supposed to hang. It's waiting for the message body on standard input. Type Ctrl-D to send end of file to the script.

As far as getting it to work with php. Have you tried running the script with sudo? You'll need to add NOPASSWD to /etc/sudoers. See this hint. Run sudo -u mail ...

---
Nate

[ Reply to This | # ]

Replace /usr/bin/mail with Mail.app
Authored by: truhe on Aug 22, '03 09:39:57PM

I don't like the sudoers thing... it opens my machine in a way I don't want...

In another hint someone added an applescript as a comment. within the first lines there was a call to another applescript, running on a remote machine with another username and password which also works on the same machine. this would solve the problem. unfortunately, I was not able to find this script :(



[ Reply to This | # ]
sudo is fine grained
Authored by: vajonez on Aug 25, '03 05:37:02PM
I see many people talk about 'risk' when using sudo with NOPASSWD: and I agree, when applied broadly it is a little silly. However, sudo has very fine grained control over who can do what and as whom (either with or without a password).

Bad idea:
user ALL=(ALL) NOPASSWD: ALL

Better idea:
user localhost=(user_to_run_as) NOPASSWD: /path/to/some/program_or_script

This allows user to run /path/to/some/program_or_script as user_to_run_as on localhost with no password. You can include muliple users in the parentheses and even exclude users by preceding the username with a !, i.e. (ALL, !root) means all users except root.

This, IMO, certainly beats having scripts with passwords hardcoded into them in cleartext.

See man sudoers for more information.

[ Reply to This | # ]

Replace /usr/bin/mail with Mail.app
Authored by: truhe on Aug 23, '03 04:59:39AM

it should work this way:

osascript -e 'tell application "Finder" of machine "epcc://USER:PASSWORD@127.0.0.1" sleep'

but this gives me the following error messages:

kCGErrorIllegalArgument : initCGDisplayState: cannot map display interlocks.
kCGErrorIllegalArgument : CGSNewConnection cannot get connection port
INIT_Processeses(), could not establish the default connection to the WindowServer.Abort

I enabled remote apple events and disabled my firewall for testing purposes, but it still fails...

any hints?



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: njnystrom on Aug 23, '03 01:38:03PM

You could also try something like this script:

% osascript -e 'do shell script "sudo -u USER whoami"
                password "PASSWORD" with administrator privileges'
USER

This works because root is allowed to sudo to any other user without a password.

---
Nate

[ Reply to This | # ]

Install CommuniGate Pro ;->
Authored by: JJ on Aug 26, '03 07:04:11PM

A wonderful overkill solution I use to solve this problem: install a trail version of CommuniGate Pro. (The trail version works forever, legally, just adding a "Sent by trail version of CGP" line to each message).

- CGPro is a relatively small (3.4MB) download <http://www.stalker.com/CommuniGatePro/#Current>
- very easy to install (standard package, no compiling or commandline configuration)
- uses almost no resources (even under heavy load!)
- will work fine for multiple logged in users
- but doesn't need the "GUI-login", Mail.app, or Applescripts

Installing it disables sendmail, archives the commandline "mail" and installs a drop-in replacement for it. (And you can also send mail by putting correctly formatted files in the Submitted directory.)

CGPro is configured through a webinterface; all you need to do in this case is disable external mail relay (as with any mailserver!). Like I said, it's overkill: SMTP/POP/IMAP/Webmail/Listserver and lots more... but it works fine, sending me status mails from multiple servers, doing nothing else...



[ Reply to This | # ]
Install CommuniGate Pro ;->
Authored by: DrShakagee on Aug 31, '03 02:07:43PM

Hey thanks for the CGPro hint. I just installed it on my server and after 1 minute of set up I have it up and running perfectly. It seems to have a great un-installer script which gave me the confidence to install it. 10x easier then sendmail, or even that mail.app script.



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: designr on Sep 01, '03 02:39:26PM

With the help of (http://www.macosxhints.com/article.php?story=20030205064206934&query=introduction+to+find) a previous hint, I piped a search for all files greater than 1 GB in size to this mail script:

find /Users -size +2097152 -ls | mailalt -s "Files Larger than 1 GB" meacct@medomain.com

(A unix guru friend recommended saving the file as /usr/bin/mailalt and leaving /usr/bin/mail intact. His argument is that if Apple ever updates /usr/bin/mail, it will overwrite the script.)

While this requires a "^D" to run in the Terminal, it runs fine as a cron job (root).



[ Reply to This | # ]
Replace /usr/bin/mail with Mail.app
Authored by: robleach on Dec 15, '07 10:25:55PM
Looks like the script is now available at http://www.nanocow.com/nystrom/macscripts/mail.txt

However, I get an error when I run it:

[code]2007-12-16 01:27:44.641 osascript[21867] CFLog (21): Error loading /System/Library/PrivateFrameworks/LiveType.framework/Versions/A/LiveType: error code 0, error number 2 (dyld: osascript can't open library: /System/Library/PrivateFrameworks/LiveType.framework/Versions/A/LiveType (No such file or directory, errno = 2)
)
[/code]

Rob

[ Reply to This | # ]