Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

How to modify Perl subroutine to add extra parameter

Posted on 2010-09-22
8
Medium Priority
?
466 Views
Last Modified: 2013-12-25
Hi,

I've been trying to modify a simple SMTP mail send script that I found online, but am struggling, being a Perl newbie and all. The script is as follows:
 
sub send_mail # send SMTP mail
	{
        my ($from, $to_addr, $subject, $body, $msg);

        $from       = shift;
        $to_addr    = shift;
        $subject	= shift;
        $body       = shift;
		$date 		= strftime "%a, %d %b %y %H:%M:%S %z",localtime;
        
        $msg = "MIME-Version: 1.0\n"
             . "From: $from\n"
             . "To: " . ( ref($to_addr) ? join(';', @$to_addr) : $to_addr ) . "\n"
             . "Date: $date\n"
             . "Subject: $subject\n\n"      # Double \n
             . $body;
		
        #
        # Open an SMTP session
        #
        $smtp = Net::SMTP->new( $SMTP_HOST,
                                'Debug' => 0,       # Change to a 1 to turn on debug messages
                            );

        if(!defined($smtp) || !($smtp))
        {
            print "SMTP ERROR: Unable to open smtp session.\n";

            return 0;
        }

        #
        # Pass the 'from' email address, exit if error
        #
        if (! ($smtp->mail( $from ) ) )
        {
            return 0;
        }

        #
        # Pass the recipient address(es)
        #
        if (! ($smtp->recipient( ( ref($to_addr) ? @$to_addr : $to_addr ) ) ) )
        {
            return 0;
        }

        #
        # Send the message
        #
        $smtp->data( $msg );

        $smtp->quit;
    }

Open in new window


I would like to pass the subroutine an extra parameter "bcc" which it would then use, if passed, to bcc the email. At the moment I can't work out how to do it.

Also I don't understand how comes the script can handle multiple "to" addresses, which it apparently can - how does it know which parameter is an email address and which is one of the parameters passed after the email addys? I get the impression its this line : ( ref($to_addr) ? join(';', @$to_addr) : $to_addr )  -- but no idea how it works!

Thanks
0
Comment
Question by:georgemason
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
8 Comments
 
LVL 27

Accepted Solution

by:
wilcoxon earned 1000 total points
ID: 33735704
Here's a slightly reformatted and modified version of your sub that will accept bcc addresses as the third argument (after to addresses).  Just change the order of arguments on the "my (...) = @_" line if you want it to be in a different order.

For to (and bcc), if you want to pass multiple addresses, they need to be in an array ref.  You are correct that it is the line with the ref($to_addr) that handles multiple vs single.  Here's a few examples to help illustrate it.

send_mail('me@here.net', 'you@there.net', undef, 'single recipient, no bcc', $body, $msg);
send_mail('me@here.net', ['you@there.net', 'him@elsewhere.net'], 'me@here.net', 'multiple to and single bcc', $body, $msg);
# send SMTP mail
sub send_mail {
    my ($from, $to_addr, $bcc, $subject, $body, $msg) = @_;

    $date = strftime "%a, %d %b %y %H:%M:%S %z",localtime;
    
    $msg = "MIME-Version: 1.0\n"
         . "From: $from\n"
         . "To: " . ( ref($to_addr) ? join(';', @$to_addr) : $to_addr ) . "\n"
         . "Date: $date\n"
         . "Subject: $subject\n\n"      # Double \n
         . $body;
            
    #
    # Open an SMTP session
    #
    $smtp = Net::SMTP->new( $SMTP_HOST,
                            Debug => 0, # Change to 1 to turn on debug messages
                        );

    unless ($smtp) {
        print "SMTP ERROR: Unable to open smtp session.\n";
        return 0;
    }

    #
    # Pass the 'from' email address, exit if error
    #
    $smtp->mail($from) or return 0;

    #
    # Pass the recipient address(es)
    #
    $smtp->recipient( ( ref($to_addr) ? @$to_addr : $to_addr ) ) or return 0;
    $smtp->bcc( ( ref($bcc) ? @$bcc : $bcc ) ) or return 0;

    #
    # Send the message
    #
    $smtp->data( $msg );

    $smtp->quit;
}

Open in new window

0
 
LVL 7

Expert Comment

by:ziceva
ID: 33735767
Try this:

[code]
sub send_mail # send SMTP mail
      {
        my ($from, $to_addr,$bcc, $subject, $body, $msg);

        $from       = shift;
        $to_addr    = shift;
        $bcc        = shift;
        $subject      = shift;
        $body       = shift;
      $date             = strftime "%a, %d %b %y %H:%M:%S %z",localtime;
       
        $msg = "MIME-Version: 1.0\n"
             . "From: $from\n"
             . "To: " . ( ref($to_addr) ? join(';', @$to_addr) : $to_addr ) . "\n"
             . "Bcc: $bcc\n"
             . "Date: $date\n"
             . "Subject: $subject\n\n"      # Double \n
             . $body;
            
        #
        # Open an SMTP session
        #
        $smtp = Net::SMTP->new( $SMTP_HOST,
                                'Debug' => 0,       # Change to a 1 to turn on debug messages
                            );

        if(!defined($smtp) || !($smtp))
        {
            print "SMTP ERROR: Unable to open smtp session.\n";

            return 0;
        }

        #
        # Pass the 'from' email address, exit if error
        #
        if (! ($smtp->mail( $from ) ) )
        {
            return 0;
        }

        #
        # Pass the recipient address(es)
        #
        if (! ($smtp->recipient( ( ref($to_addr) ? @$to_addr : $to_addr ) ) ) )
        {
            return 0;
        }

        #
        # Send the message
        #
        $smtp->data( $msg );

        $smtp->quit;
    }

[/code]

The "To" param can be an array like this:
@mail_list=("user1@domain1.com","user2@domain2.com");
0
 
LVL 27

Expert Comment

by:wilcoxon
ID: 33735840
ziceva's solution has three problems:
1) It will probably (not 100% sure) print the bcc recipient in the message (the $msg is the data/body)
2) It will not actually send the email to the bcc recipient ($smtp->recipient or $smtp->bcc must be called)
3) His description of the "To" param is inaccurate - it has to be an arrey reference (not an array)
0
Docker-Compose to Simplify Multi-Container Builds

Our veteran DevOps Author takes you through how to build a multi-container environment, managed with a single utility in order to simplify your deployments.

 
LVL 1

Author Comment

by:georgemason
ID: 33736183
@wilcoxon - excellent post in the first place, and thanks for the critique of the other poster's code. I've implemented your suggestions and the script works flawlessly.

One more question I have, since you're so good at explanations!

I notice you're using "unless (condition) command" to only run commands in the event that the condition evaluates to something other than zero - I assume this would occur if there was an error. Is there any way that I can use the same idea, to only run commands if something completes without error?

What I'm thinking is that I'm making some system calls to reset passwords etc. as below:

system("echo $password | passwd --stdin $reseller");
            print LOGFILE &currentTime . " Created user $reseller with password $password\n";

I'd like the log entry to only occur if the passwd command exits correctly - i'm guessing this can be done with unless?
0
 
LVL 27

Expert Comment

by:wilcoxon
ID: 33736401
Sure.  In the case of system, it would need to be something like these...

# simplest that works because system returns 0 on success
system("echo $password | passwd --stdin $reseller") or
print LOGFILE &currentTime . " Created user $reseller with password $password\n";

# alternate that is a little more readable - only does print on successful system call
my $stat = system("echo $password | passwd --stdin $reseller");
print LOGFILE &currentTime . " Created user $reseller with password $password\n" unless $stat;

On a system or exec call failure, you can examine $? to figure out exactly what the problem is (see "perldoc -f system" for more details).
0
 
LVL 1

Author Comment

by:georgemason
ID: 33736889
Great explanation, for your extra help I'll double the points. Thanks for taking the time to post - really appreciated.
0
 
LVL 1

Author Comment

by:georgemason
ID: 33736920
Apologies, awarded points to wrong person, have requested it be changed.

DOH!
0
 
LVL 1

Author Closing Comment

by:georgemason
ID: 33760774
Excellent response, worked perfectly and I learnt a bit in the process. Thanks very much!
0

Featured Post

NFR key for Veeam Agent for Linux

Veeam is happy to provide a free NFR license for one year.  It allows for the non‑production use and valid for five workstations and two servers. Veeam Agent for Linux is a simple backup tool for your Linux installations, both on‑premises and in the public cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article will show, step by step, how to integrate R code into a R Sweave document
In the first part of this tutorial we will cover the prerequisites for installing SQL Server vNext on Linux.
The viewer will learn how to count occurrences of each item in an array.
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

660 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question