1

I am currently trying to write an email in Perl to a user who has an apostrophe in it and can not figure out how to get it to send. Here is what the line looks like:

$to = "o'connell\@website.com";

Here is the line I use to send the email:

system("echo $message | mailx -s $subject $to ");

This is an example email of o'connell@website.com. It will not send it because of the apostrophe. I have tried the following and it hasn't worked. I am using mailx to send the email

$to = "o'connell\@website.com";
$to = "o''connell\@website.com";
$to = "o"'"connell\@website.com";

None of these worked. I can send an email to somebody who doesn't have it in their email though. Any suggestions?

AnFi
  • 10,493
  • 3
  • 23
  • 47
user081608
  • 1,093
  • 3
  • 22
  • 48
  • How do you send the email? What library do you use? – choroba Jul 21 '15 at 14:04
  • 1
    I use mailx to send them – user081608 Jul 21 '15 at 14:06
  • Well, you can 'specify' the email via `my $to = q{o'connell@website.com};` - there's no guarantee the mailserver will accept it though. – Sobrique Jul 21 '15 at 14:09
  • Interesting. What does the q mean? – user081608 Jul 21 '15 at 14:10
  • 1
    @user081608 Could you post the error message you receive? It is important which part of email delivery pipeline rejects the address. – AnFi Jul 21 '15 at 14:10
  • 1
    `mailx`? You mean you call `system` or backticks? – choroba Jul 21 '15 at 14:11
  • I am actually not getting an error message from the error checking i have set up. Thats what is confusing me. I can send an email to everyone else besides someone with an apostrophe in it. I will post the mailx line in the top that i send the email with. – user081608 Jul 21 '15 at 14:12
  • 2
    The problem's not the Perl string - it's getting it escaped properly for your shell, since you're piping into it via `system`. Take a look at: http://stackoverflow.com/questions/6306386/how-can-i-escape-an-arbitrary-string-for-use-as-a-command-line-argument-in-bash – Paul Roub Jul 21 '15 at 14:15
  • @Sobrique shouldn't that be `qw`? – jkeuhlen Jul 21 '15 at 14:20
  • You've shown the value you put in a variable. Please show what you do with that variable (e.g., **how** you pass it to mailx) to send the email. "I am actually not getting an error message from the error checking i have set up" doesn't mean a lot if we don't see the error checking you have set up. – Max Lybbert Jul 21 '15 at 14:23
  • No. I meant what I said. `q{}` is a quote operator _like_ `qw` but it's not the same. It's comparable to single quotes `'`. – Sobrique Jul 21 '15 at 14:23
  • @MaxLybbert I posted above the system call I use to send the mail. – user081608 Jul 21 '15 at 14:29

4 Answers4

5

That's not a Perl problem, it's a shell problem. You have to escape the values properly:

system("echo $message | mailx -s $subject \"$to\" ");

or, more readable:

system qq(echo $message | mailx -s $subject "$to");

I'd escape the subject, too, and you should be very careful about $message, as well - what if it contained

; rm -rf /

See Task::Kensho::Email on how to send email from Perl directly.

choroba
  • 231,213
  • 25
  • 204
  • 289
  • Interesting I did not think about that. So would I just set $to = "o'connell@website.com" with that format in system? – user081608 Jul 21 '15 at 14:23
  • The first system call you used fixed the issue but the second one did not work correctly. Should that be a single q? – user081608 Jul 21 '15 at 14:28
  • @user081608 If you do `$to = "o'connell@website.com";` with double quotes and you're not using strict, `@website` will be interpolated as an (empty) array, so you're actually setting the value of `$to` to `o'connell.com`. Use `$to = q{o'connell@website.com};` instead (and always `use strict; use warnings;`). – ThisSuitIsBlackNot Jul 21 '15 at 14:31
  • 2
    @user081608 No, it should be `qq`, and both of those snippets should do exactly the same thing. `qq` interpolates variables while `q` doesn't. See the section on [quote-like operators](http://perldoc.perl.org/perlop.html#Quote-Like-Operators) in `perldoc perlop`. – ThisSuitIsBlackNot Jul 21 '15 at 14:53
  • 1
    That's still no good. An email address can legitimately contain `"`. – ikegami Jul 21 '15 at 21:00
3

You may make perl execute mailx directly without shell "in between".
[ Take a look at simpler implementation in wdebeaum answer ]

$message="message\n";
$to='$to = 'o\'connell\@website.com';
$subject= "x's " . time(); # string with apostrophe and time mark for testing

# separator to make stackoverflow syntax coloring work after confusion above

if( my $child_pid = open(my $TO_KID,   "|-")) {
   # I am the parent:
   print $TO_KID $message;
   close($TO_KID);
   waitpid $child_pid, 0;
} elsif(defined($child_pid)) {
   # I am the child; use STDIN/STDOUT normally
   exec 'mailx','-s',$subject,$to;
   die "couldn't exec mailx: $!";
}else{
   # $child_pid is undefined - fork (in open) failed
   die "can't fork: $!";
}
AnFi
  • 10,493
  • 3
  • 23
  • 47
3

Like Andrzej, I'd say avoid using the shell. But there's a simpler way to accomplish that:

open(FH, "|-", "mailx", "-s", $subject, $to) or die "Can't mailx: $!";
print FH $message;
close(FH);

If you give the command and its arguments to open with the "|-" mode, open and close will handle the execing and waitpiding for you.

From perldoc -f open:

         The following blocks are more or less equivalent:

            open(FOO, "|tr '[a-z]' '[A-Z]'");
            open(FOO, "|-", "tr '[a-z]' '[A-Z]'");
            open(FOO, "|-") || exec 'tr', '[a-z]', '[A-Z]';
            open(FOO, "|-", "tr", '[a-z]', '[A-Z]');

            open(FOO, "cat -n '$file'|");
            open(FOO, "-|", "cat -n '$file'");
            open(FOO, "-|") || exec "cat", "-n", $file;
            open(FOO, "-|", "cat", "-n", $file);

        The last two examples in each block show the pipe as "list form",
        which is not yet supported on all platforms. A good rule of thumb
        is that if your platform has a real "fork()" (in other words, if
        your platform is Unix, including Linux and MacOS X), you can use
        the list form. You would want to use the list form of the pipe so
        you can pass literal arguments to the command without risk of the
        shell interpreting any shell metacharacters in them. However, this
        also bars you from opening pipes to commands that intentionally
        contain shell metacharacters, such as:

            open(FOO, "|cat -n | expand -4 | lpr")
                // die "Can't open pipeline to lpr: $!";

        See "Safe Pipe Opens" in perlipc for more examples of this.

...

        Closing any piped filehandle causes the parent process to wait for
        the child to finish, then returns the status value in $? and
        "${^CHILD_ERROR_NATIVE}".
wdebeaum
  • 4,101
  • 1
  • 22
  • 12
2

You are improperly building the shell command. Use the following:

use String::ShellQuote qw( shell_quote );

my $cmd1 = shell_quote('echo', $message);
my $cmd2 = shell_quote('mailx', '-s', $subject, $to);
system("$cmd1 | $cmd2");

But you can avoid using echo and involving a shell by using the following:

open(my $pipe, "|-", "mailx", "-s", $subject, $to)
   or die("Can't execute mailx: $!\n");
print($pipe $message);
close($pipe);

Even better would be to use a module designed to help you send emails.

ikegami
  • 367,544
  • 15
  • 269
  • 518