Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Improving an FTP process

by wardk (Deacon)
on Nov 09, 2000 at 23:48 UTC ( #40794=CUFP: print w/replies, xml ) Need Help??

In my daily life as a paid perl programmer (can there BE a better job?), I get to maintain perl of many different styles and techniques. I recently was creating a new process that is modeled on existing processes. So in approaching this project I was sure to try to leverage existing methods and hopefully, existing code.

Recently it was asked how to FTP through a firewall CPAN through a Firewall. This presents a code solution to that issue (actually, two solutions), beyond the simple response I gave at the time Re: CPAN through a Firewall.

Note that all the perl in this system pre-dates Perl 5, and my peers in this group on average have less than 6 months using Perl. So in addition to solving the problems at hand, my goal is also to help along these new perl programmers by showing them techniques that they will not find in their existing code (which currently makes use of no modules whatsoever). Also note that this CGI system used no warn, no strict and definately no taint, as well as a customized version of (but that is for another node).

What follows is a short description showing how this was being done currently, followed by the way I solved the same problem using newer perl tools (in this case, a CPAN module).

Note that I do not consider myself to be anywhere near a perl guru, and find it somewhat difficult to post meaningful replies that stand up to some of the amazing code I see posted by so many here on perlmonks. This is my attempt to humbly present my modest experiences in hope that at least some newbies might gain.

In this particular case, I need to FTP a datafile to a business partner through a firewall/proxy. In this application, the existing method for FTPing data outside was to invoke a system call to ftp, piping in the required user/pass/transfer commands, and piping the session messages to a flat file for later investigation, to confirm success.

Here is the code as it was being done today. Note I have trimmed the variable creation to conserve space, and hide real system names. Needless to say, the ftp is a bit non-intuitive, and if you're new to FTP/Unix/Perl (i.e. many of my peers) it's that much tougher to grok what's taking place.

system("ftp -n $gsRemoteMachine <<EOF> $gsStatusFile 2>&1\n\nuser $gsF +tpId$gsGateConnect $gsFtpPwd\nverbose\n$gsFtpMode\nput $gsFeedsDir$gs +FeedZipFile $gsRemoteDir$gsFeedZipFile\nclose\nbye\nEOF"); print "verifying FTP status..."; # Get FTP session record if(!open(STATUSFILE, $gsStatusFile)) { print "\n".$gsSysErr."Cannot open file $gsStatusFile. FTP failed.\n" +; email_error($gsEmailAdmin, $gsErrSys, $gsErrSys."Cannot open file $g +sStatusFile. FTP failed."); } @gaStatusLines = <STATUSFILE>; close(STATUSFILE); # check if "226 Transfer complete." occurs in gaStatusLines array for( $ii=0; $ii<=$#gaStatusLines; $ii++) { if(@gaStatusLines[$ii] eq "226 Transfer complete.\n") { $gnFtpFailed = 0; break; } } if( $gnFtpFailed ) { # "226 Transfer Complete" not found in gaStatusLines, so FTP failed. # print FTP fail message print "\n".$gsSysErr."FTP failed.\n"; print "\nFTP session:\n"; print @gaStatusLines; # send admin email email_error($gsEmailAdmin, $gsErrSys, $gsErrSys."FTP failed.\nFTP se +ssion:\n@gaStatusLines"); }

As you can see, alot of work has gone into determining what happened with the ftp session. It also relies on having some knowledge of ftp so that the commands are coded properly.

Enter Net::FTP

So I determined that Net::FTP was installed on our production server, but not our dev server, a simple message to the administrator rectified this problem.

Here is the code that replaced the above method of FTPing via Perl. Removed the system call (goodbye security issue), and the file IO. And solved the problem in less than half the lines of code. (Note I also introduced warn, strict and taint but that is for another node)

use Net::FTP; my $ftp; if (!($ftp = Net::FTP->new($gsGateMachine, -Passive => '1', -Firewall => $gsGateMachine)) ) { email_error($gsEmailAdmin, $gsErrSys, $gsErrSys."Connect failed. $@ +"); } $ftp->login("$gsFtpId\@$gsRemoteMachine","$gsFtpPwd"); if ( $ftp->put("$gsFeedsDir$gsFeedFile")) { push @ReportArray, "\nAJB FILE FTP SUCCESSFUL!\n"; else { push @ReportArray, "\n\tAJB FILE FTP FAILED $@\n"; email_error($gsEmailAdmin, $gsErrSys, $gsErrSys."FTP failed. $@"); }

Not only is it less typing, it's a bit easier to understand. And not having to open a status file to see what happened in the FTP session is a great improvement over the system call method.

Now I just hope I post this in the appropriate place...

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://40794]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (5)
As of 2022-10-05 11:14 GMT
Find Nodes?
    Voting Booth?
    My preferred way to holiday/vacation is:

    Results (23 votes). Check out past polls.