Solved

How to modify Perl subroutine to add extra parameter

Posted on 2010-09-22
8
457 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
  • 4
  • 3
8 Comments
 
LVL 26

Accepted Solution

by:
wilcoxon earned 250 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 26

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
 
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
What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

 
LVL 26

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

3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

Question has a verified solution.

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

Checking the Alert Log in AWS RDS Oracle can be a pain through their user interface.  I made a script to download the Alert Log, look for errors, and email me the trace files.  In this article I'll describe what I did and share my script.
This article will show, step by step, how to integrate R code into a R Sweave document
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
Connecting to an Amazon Linux EC2 Instance from Windows Using PuTTY.

929 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now