Archive for the ‘Perl’ Category

FTP files with Perl and NET::FTP

net ftp perl

Needed to run some reports out of Oracle then FTP them. Could probably do this easier with normal shell command or something but I only have access to Perl at work. So thankfully NET::FTP is an installed module which made this quite painless.

The main thing to take away from this is the parameters you can use for NET::FTP, mainly using passive mode which I found a big help when the FTP server blocked active mode requests. Also to make this useful in any way, writing debug/ error tracking logic is very very useful since we are dealing with external servers and numerous issues can arise.

Whilst I have used an array to store my files, you can of course do a look up on your directory and pull all files from directory X or something similar instead. But in my instance the files are always the same and overwritten each time with new data so I don’t need to worry about archiving or changing filenames.

You can read more at: http://perldoc.perl.org/Net/FTP.html

The Code is below:

use Net::FTP;
use strict;
use warnings;

my $host="ftp.myserver.com";
my @ERRORS = "";
my $newerr = "";
my @files = ("file1", "file2", "file3");

my $ftp=Net::FTP->new($host,Timeout=>240, Port=>21,  Passive => 1) or $newerr=1;
  push @ERRORS, "Can't connect to $host: $!n" if $newerr;
myerr() if $newerr;
  print "Connected to servern";

$ftp->login("username","password") or $newerr=1;
  push @ERRORS, "Can't login at $host: $!n" if $newerr;
$ftp->quit if $newerr;

myerr() if $newerr;
  print "Logged inn";

foreach my $file(@files) {
  $ftp->put($file, $file) or $newerr=1;
  push @ERRORS, "Can't the copy files  $!n" if $newerr;
  $ftp->quit if $newerr;
  myerr() if $newerr;
}

  print "Copied the filesn";
$ftp->quit;

sub myerr {
  print "Error: n";
  print @ERRORS;
  exit 0;
}

First we declare our module then we say we’re going to use strict and warnings which are very hadny for trapping errors and typos and feeding back to you.

Next we declare a few variables – one to store our FTP server, an array to store our errors, a variable to store if there’s a new error to add to the array and finally an array to store our file list.

We then initialise our NET::FTP object and attempt a connection to the server – you can specify many more settings in your connection that I have. Here we’re just checking to see if the server exists and we can ping it.

Next we attempt a login with a username and password and alert us if the login attempt is unsuccessful.

And then we loop through our file array and for each file we use the NET::FTP ‘put’ function, which copies the file to server, if it fails then alert us that there’s been a problem. To do this error tracking we are using push and basically this for adding a value to the end of an array by treating it as a list.

There are many other functions we could call here with our NET::FTP object like cwd(), pwd(), delete() and rename() functions to name but a few.

Anyway at the end of this we then loop through our array that stores errors and then print them out should an error occur. So you can see this is actually pretty easy to get started with.

Getting a date in Perl using time and localtime

Time

3 lines of code to output the date in Perl. In this instance getting yesterdays date.

Firstly we get our time by calling the time function, which stores the number of seconds since the systems epoch time – normally 1st January 1970 UTC – If I want yestedays date I call the time function minus the number of seconds in the day, 24 * 60 * 60, multiply that by days in week, month, year etc.. to get other dates.

Next we call the localtime function which takes our time and converts it to a 9 element list to allow us to return day, month, year etc… so for my date I want the elements: 5 for year, 4 for month and 3 for the day.

So we have our date, the only thing we need to do now is format the string using the sprintf function, basically to give us a 2 digit figure for the month. Since perl starts its months at 0 we need to add +1 to the month number. Also we need to add +1900 to our year in order to make sure that we’re y2k compliant – the year element in our list normally returns the number of years since 1900, so 109 will be 2009 rather than taking the last 2 digits ’09’ which can be any year.

If you’ve not used sprintf basically its a function that formats any variable to a given pattern, also available in PHP and maybe a few other languages, anyway, the 3 lines of code are:

my $timeYesterday = time - 24 * 60 * 60;
my ($year, $month, $day) = (localtime($timeYesterday))[5,4,3];
my $date = sprintf ("%04d%02d%02d", $year+1900, $month+1, $day);

That returns 20090720, if I want hyphens in there then I can do:

my $date = sprintf ("%04d-%02d-%02d", $year+1900, $month+1, $day);

That returns 2009-07-20, and if I want a 2 digit year then I can do:

my $date = sprintf ("%02d-%02d-%02d", $year+1900, $month+1, $day);

Which returns 09-07-20

Sending mail and attachments from Perl using NET::SMTP

perlnetsmtp

Sending a mail attachment using NET::SMTP is pretty easy, the only really hard bit is making sure the module is installed on your server and that of course you can run Perl. I’ve written this primarily from doing this from a server but you could ust as easily use this on websites etc…

Basically to send an attachment we need to read the file into a string/ array in Perl which in turn MIME will then associate the content of this string to a reserved file to be attached. We also use MIME to send mutipart emails, so we’ll be using boundaries to separate each part, the headers, the message and the attachment. The code is below:

#!/usr/bin/perl
use Net::SMTP;
use strict;
use warnings;

my $from = 'you@yourserver.co.uk';
my $to = 'me@luckylarry.co.uk';
my $to2 = 'someoneelse@luckylarry.co.uk';
my $attachFile = 'myFile.csv';
my $boundary = 'frontier';
my $data_file="/path/to/file/myFile.csv";

open(DATA, $data_file) || die("Could not open the file");
  my @csv = DATA;
close(DATA);

my $smtp = Net::SMTP->new('smtp-out.yourserver.co.uk');

$smtp->mail($from);
$smtp->recipient($to,$to2, { SkipBad => 1 });
$smtp->data();
$smtp->datasend("Subject: Test mail from youn");
$smtp->datasend("MIME-Version: 1.0n");
$smtp->datasend("Content-type: multipart/mixed;ntboundary="$boundary"n");
$smtp->datasend("n");
$smtp->datasend("--$boundaryn");
$smtp->datasend("Content-type: text/plainn");
$smtp->datasend("Content-Disposition: quoted-printablen");
$smtp->datasend("nTest From Youn");
$smtp->datasend("--$boundaryn");
$smtp->datasend("Content-Type: application/text; name="$attachFile"n");
$smtp->datasend("Content-Disposition: attachment; filename="$attachFile"n");
$smtp->datasend("n");
$smtp->datasend("@csvn");
$smtp->datasend("--$boundary--n");
$smtp->dataend();
$smtp->quit;

exit;

So first up we tell our perl script the installation of where Perl should be with the shebang – #!/usr/bin/perl. Next we say we’re going to use strict and warnings which is just good sense when using Perl, it means the program will error if you haven’t referenced your variables properly or got a typo somewhere.

We then define our variables, who the mail is coming from the recipients – this can be an array instead of a variable per user. Next we the file that we want to attach and the MIME boundary, so every time the boundary variable is called this will be the separator between content types. After this we declare where the actual data file is which we’re going to then line read the csv file and write that to an array to store each line – probably not the most efficient way for larger files.

We finally declare the SMTP object which points to your mail server. We follow this with defining the data for the SMTP object.

$smtp->mail($from);
$smtp->recipient($to,$to2, { SkipBad => 1 });

Who is it coming from? Who is it going to? Skip any bad email addresses. You could use $smtp->to(); instead of recipients but I’m not sure that allows multiple mail addresses.

$smtp->data();
$smtp->datasend("Subject: Test mail from youn");
$smtp->datasend("MIME-Version: 1.0n");
$smtp->datasend("Content-type: multipart/mixed;ntboundary="$boundary"n");
$smtp->datasend("n");

Start the building of the data. First the mail subject, the MIME type and the content type – we’re stating that there will be multiple mixed content sections and each section will be split as defined by the boundary section.

$smtp->datasend("--$boundaryn");
$smtp->datasend("Content-type: text/plainn");
$smtp->datasend("Content-Disposition: quoted-printablen");
$smtp->datasend("nTest From Youn");
$smtp->datasend("--$boundaryn");

We separate that section with a boundary and then give the content type and disposition for our message in the email. Finishing with another boundary definition.

$smtp->datasend("Content-Type: application/text; name="$attachFile"n");
$smtp->datasend("Content-Disposition: attachment; filename="$attachFile"n");
$smtp->datasend("n");
$smtp->datasend("@csvn");
$smtp->datasend("--$boundary--n");

We then do the same but this time for the mail attachment giving a content type and disposition. In this section we then read back the contents of our csv file that we read into an array at the start, this content gets then associated to the reserved filename in our mail attachment content type. Finish this with another boundary.

$smtp->dataend();
$smtp->quit;

exit;

The final thing is to end the data stream/ definition for our NET::SMTP object , quit the SMTP object and then finally issue the exit command to exit our Perl script.

In this case I’m going to be calling my perl script from my bash script on the server, hence why I’m using exit as I really don’t want to keep this open!. And that’s it.

Perl documentation on NET::SMTP is here
More information for the MIME type for Email can be found here