releasing/cbrtools/perl/RemoteSite/FTP.pm
changeset 607 378360dbbdba
parent 602 3145852acc89
equal deleted inserted replaced
591:22486c9c7b15 607:378360dbbdba
       
     1 # Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 # All rights reserved.
       
     3 # This component and the accompanying materials are made available
       
     4 # under the terms of the License "Eclipse Public License v1.0"
       
     5 # which accompanies this distribution, and is available
       
     6 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 # 
       
     8 # Initial Contributors:
       
     9 # Nokia Corporation - initial contribution.
       
    10 # 
       
    11 # Contributors:
       
    12 # 
       
    13 # Description:
       
    14 # 
       
    15 #
       
    16 # Description:
       
    17 # RemoteSite::FTP.pm
       
    18 #
       
    19 
       
    20 package RemoteSite::FTP;
       
    21 
       
    22 use strict;
       
    23 use Net::FTP;
       
    24 use File::Basename;
       
    25 use IO::File;
       
    26 
       
    27 use RemoteSite;
       
    28 use vars qw(@ISA);
       
    29 @ISA=("RemoteSite");
       
    30 
       
    31 #
       
    32 # Constants
       
    33 #
       
    34 
       
    35 use constant DEFAULTRECONNECTS => 5;
       
    36 use constant DEFAULTTIMEOUT => 30;
       
    37 use constant BLOCKSIZE => 32768;
       
    38 
       
    39 #
       
    40 # Initialization
       
    41 #
       
    42 
       
    43 sub Initialize {
       
    44   my $self = shift;
       
    45 
       
    46   my %args = @_;
       
    47   $self->{username} = $args{username};
       
    48   $self->{password} = $args{password};
       
    49   $self->{passiveMode} = $args{passive_mode};
       
    50   $self->{resumeMode} = $args{resume_mode};
       
    51   $self->{timeout} = $args{timeout};
       
    52   $self->{reconnects} = $args{reconnects};
       
    53 
       
    54   #call base class initialization
       
    55   $self->SUPER::Initialize(@_);
       
    56 
       
    57   #if username or password not defined ask for them interactively
       
    58   unless ($self->Username()) {
       
    59     $self->HandleError("No remote host defined.") unless $self->Host();
       
    60     print 'FTP username: ';
       
    61     my $userName = <STDIN>;
       
    62     if ($userName) {
       
    63       chomp ($userName);
       
    64       $self->Username($userName);
       
    65     }
       
    66   }
       
    67   unless ($self->Password()) {
       
    68     print 'FTP password: ';
       
    69     $self->Password(Utils::QueryPassword());
       
    70   }
       
    71 
       
    72   #set timeout to default value if not set or not a positive integer
       
    73   unless (defined $self->{timeout} and $self->{timeout} =~ /^\d+$/) {
       
    74     $self->{timeout} = DEFAULTTIMEOUT;
       
    75   }
       
    76 
       
    77   #set reconnects to default value if not set or not a positive integer
       
    78   unless (defined $self->{reconnects} and $self->{reconnects} =~ /^\d+$/) {
       
    79     $self->{reconnects} = DEFAULTRECONNECTS;
       
    80   }
       
    81 
       
    82   #connect to FTP site, login and set to binary mode
       
    83   $self->Connect();
       
    84 }
       
    85 
       
    86 #
       
    87 # Public getters/setters
       
    88 #
       
    89 
       
    90 sub Username {
       
    91   my $self = shift;
       
    92   if (defined $_[0]) {$self->{username} = shift;}
       
    93   return $self->{username};
       
    94 }
       
    95 
       
    96 sub Password {
       
    97   my $self = shift;
       
    98   if (defined $_[0]) {$self->{password} = shift;}
       
    99   return $self->{password};
       
   100 }
       
   101 
       
   102 sub PassiveMode {
       
   103   my $self = shift;
       
   104   if (defined $_[0]) {$self->{passiveMode} = shift;}
       
   105   return $self->{passiveMode};
       
   106 }
       
   107 
       
   108 sub ResumeMode {
       
   109   my $self = shift;
       
   110   if (defined $_[0]) {$self->{resumeMode} = shift;}
       
   111   return $self->{resumeMode};
       
   112 }
       
   113 
       
   114 sub Timeout {
       
   115   my $self = shift;
       
   116   return $self->{timeout};
       
   117 }
       
   118 
       
   119 sub Reconnects {
       
   120   my $self = shift;
       
   121   return $self->{reconnects};
       
   122 }
       
   123 
       
   124 #
       
   125 # Public (from RemoteSite)
       
   126 #
       
   127 
       
   128 sub SendFile {
       
   129   my $self = shift;
       
   130   my $localFile = shift;
       
   131   my $remoteFile = shift;
       
   132 
       
   133   unless (defined $localFile and defined $remoteFile) {
       
   134     $self->HandleError("Incorrect args passed to ".ref($self)."::SendFile");
       
   135   }
       
   136   $remoteFile =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   137 
       
   138   my $localFileSize = Utils::FileSize($localFile);
       
   139 
       
   140   if ($self->{verbose}) {
       
   141     print 'Uploading '.basename($localFile).' to FTP site '.$self->Host()." ...\n";
       
   142   }
       
   143   elsif ($localFileSize) {
       
   144     print 'Uploading '.basename($localFile).':    ';
       
   145   }
       
   146 
       
   147   #check the file to upload exists
       
   148   unless (-e $localFile) {
       
   149     $self->HandleError("Local file $localFile does not exist");
       
   150   }
       
   151 
       
   152   #check remote dir exists and create it if it doesn't
       
   153   my $remoteDir = dirname($remoteFile);
       
   154   unless ($self->DirExists($remoteDir)) {
       
   155     $self->MakeDir($remoteDir);
       
   156   }
       
   157 
       
   158   #if a file with same name as the remote file already exists delete it (even if it has different case)
       
   159   if (my $actualFileName = $self->FileExists($remoteFile)) {
       
   160     $self->DeleteFile($actualFileName);
       
   161   }
       
   162 
       
   163   #create a temporary file name in the remote directory for uploading to
       
   164   my $tmpFile = $self->CreateTemporaryFile($remoteDir);
       
   165 
       
   166   #send the file
       
   167   if ($self->ResumeMode()) {
       
   168     $self->SendFileWithResume($localFile, $tmpFile);
       
   169   }
       
   170   else {
       
   171     if ($self->{verbose} and $localFileSize) {
       
   172       print "Upload progress: ";
       
   173     }
       
   174     $self->DisplayProgress($localFileSize);
       
   175     $self->SendFileWithoutResume($localFile, $tmpFile);
       
   176   }
       
   177 
       
   178   #rename the temporary file to the final remote file name
       
   179   $self->MoveFile($tmpFile, $remoteFile);
       
   180 
       
   181   if ($self->{verbose} > 1) {
       
   182     print "Upload successful. Stored as $remoteFile on FTP site.\n";
       
   183   }
       
   184 }
       
   185 
       
   186 sub GetFile {
       
   187   my $self = shift;
       
   188   my $remoteFile = shift;
       
   189   my $localFile = shift;
       
   190 
       
   191   unless (defined $localFile and defined $remoteFile) {
       
   192     $self->HandleError("Incorrect args passed to ".ref($self)."::GetFile");
       
   193   }
       
   194 
       
   195   $remoteFile =~ s{\\}{\/}g;     #convert back slashes to forward slashes
       
   196 
       
   197   if ($self->{verbose}) {
       
   198     print "Downloading ".$remoteFile." from FTP site ".$self->Host()." ...\n";
       
   199   }
       
   200   else {
       
   201     print "Downloading ".basename($remoteFile).":    ";
       
   202   }
       
   203 
       
   204   #check that the file to download exists
       
   205   my $actualFileName;
       
   206   unless ($actualFileName = $self->FileExists($remoteFile)) {
       
   207     $self->HandleError("Remote file $remoteFile does not exist");
       
   208   }
       
   209 
       
   210   $remoteFile = $actualFileName;  #handles case sensitivity correctly
       
   211 
       
   212 
       
   213   #check local dir exists and create it if it doesn't
       
   214   my $localDir = dirname($localFile);
       
   215   unless (-e $localDir) {
       
   216     Utils::MakeDir($localDir);
       
   217     if ($self->{verbose}) {
       
   218       print "Created directory $localDir on local drive\n";
       
   219     }
       
   220   }
       
   221 
       
   222   my $remoteFileSize = $self->FileSize($remoteFile);
       
   223 
       
   224   if ($self->{verbose} and $remoteFileSize) {
       
   225     print "Download progress: ";
       
   226   }
       
   227 
       
   228   #get the file
       
   229   if ($self->ResumeMode()) {
       
   230     $self->DisplayProgress($remoteFileSize);
       
   231     $self->GetFileWithResume($remoteFile, $localFile);
       
   232   }
       
   233   else {
       
   234     $self->DisplayProgress($remoteFileSize);
       
   235     $self->GetFileWithoutResume($remoteFile, $localFile);
       
   236   }
       
   237 
       
   238   if ($self->{verbose} > 1) {
       
   239     print "Download successful. Stored as $localFile on local site.\n";
       
   240   }
       
   241 }
       
   242 
       
   243 sub FileExists {
       
   244   my $self = shift;
       
   245   my $remoteFile = shift;
       
   246 
       
   247   unless (defined $remoteFile) {
       
   248     return 0;
       
   249   }
       
   250 
       
   251   #use Carp qw/cluck/;
       
   252   #cluck "Called FileExists";
       
   253 
       
   254   # List the directory the file is in, and see if the file name is in it.
       
   255   $remoteFile =~ s{\/}{\\}g;     #convert forward slashes to back slashes
       
   256   (my $path, my $baseName, my $ext) = Utils::SplitFileName($remoteFile);
       
   257   my $fileName = $baseName . $ext;
       
   258   $path =~ s/\\$//;       #remove trailing slash
       
   259   $path =~ s/\\/\//g;     #convert back slashes to forward slashes
       
   260   my $ls = $self->DirList($path);
       
   261   print "Checking for existence of remote file \"$remoteFile\" by looking for \"$fileName\" in \"$path\".\n" if ($self->{verbose} && $ls);
       
   262   return 0 unless $ls; # definitely doesn't exist if nothing in the directory
       
   263 
       
   264   my @present = grep /(\/|\\|^\s*)\Q$fileName\E\s*$/i, @$ls;
       
   265   if (@present) {
       
   266     print "Have found file: YES\n" if ($self->{verbose});
       
   267     $present[0] = $path."/".$present[0] if ( $present[0] !~ /\// );
       
   268     return $present[0];
       
   269   }
       
   270   else {
       
   271     print "Have found file: NO\n" if ($self->{verbose});
       
   272     return 0;
       
   273   }
       
   274 }
       
   275 
       
   276 sub DirList {
       
   277   my $self = shift;
       
   278   my $remoteDir = shift;
       
   279 
       
   280   print "Listing FTP directory $remoteDir\n" if ($self->{verbose});
       
   281 
       
   282   my $dirlist_retries = 3;
       
   283 
       
   284   $remoteDir =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   285 
       
   286   my $retry;
       
   287   for ($retry = 0; $retry < $dirlist_retries; $retry++) {
       
   288 
       
   289     unless ($self->Connected()) {
       
   290       $self->Connect();
       
   291     }
       
   292 
       
   293     # The Net::FTP module that we're using here has two options for listing the contents
       
   294     # of a directory. They are the 'ls' and 'dir' calls.
       
   295     # The 'ls' call is great, and just returns a list of the items. But, irritatingly, it
       
   296     # misses out directories: the returned list just contains names of *files*.
       
   297     # dir is better, in some ways, as it lists directories too, but its output format
       
   298     # varies from one FTP site to the next. So we have to stick with ls.
       
   299     print "About to call dir(\"$remoteDir\")\n" if ($self->{verbose});
       
   300     my $ls = $self->{ftp}->ls($remoteDir);
       
   301     my $resp = $self->{ftp}->message;
       
   302     print "FTP response to list command was \"$resp\"\n" if ($self->{verbose});
       
   303     if (ref $ls) {
       
   304       print "FTP dir returned \"$ls\" which is a ".(ref $ls)." containing ".(scalar @$ls)." items\n" if ($self->{verbose});
       
   305       $ls = undef if ($resp eq ""); # if we didn't get "Opening BINARY mode connection..." or something similar, then we've
       
   306         # come across the problem where Net::FTP says Net::FTP: Unexpected EOF on command channel at d:/reltools/2.6x/personal/bin/Net
       
   307         # /FTP/dataconn.pm line 73. Unfortunately, it doesn't die, and it returns an empty array, so the only way to find out this has
       
   308         # happened is to check message.
       
   309       $ls = undef if ($resp =~ m/^connection closed/i);
       
   310     }
       
   311     # $ls might now be undef
       
   312     if (ref($ls)) {
       
   313       return $ls;
       
   314     }
       
   315     else {
       
   316       if ($self->Connected()) {
       
   317         return undef;
       
   318       }
       
   319       else {
       
   320         print "Warning: Listing of \"$remoteDir\" failed due to an FTP site problem: " . $self->{ftp}->message . ". ";
       
   321         if ($self->PassiveMode()) {
       
   322           print "PASV mode FTP is currently enabled. This can cause connectivity issues under certain circumstances. ",
       
   323             "To disable, remove the pasv_transfer_mode directive from your reltools.ini file.\n";
       
   324         }
       
   325         else {
       
   326           print "PASV mode FTP is currently disabled. Enabling it can prevent connectivity issues under certain circumstances. ",
       
   327             "To enable, add the pasv_transfer_mode directive to your reltools.ini file.\n";
       
   328         }
       
   329         # Fall through to next loop iteration
       
   330       }
       
   331     }
       
   332   }
       
   333   die "Error: have tried to list \"$remoteDir\" $retry times with no success - giving up\n";
       
   334 }
       
   335 
       
   336 sub MakeDir {
       
   337   my $self = shift;
       
   338   my $remoteDir = shift;
       
   339 
       
   340   $remoteDir =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   341 
       
   342   unless ($self->Connected()) {
       
   343     $self->Connect();
       
   344   }
       
   345 
       
   346   if ($self->{ftp}->mkdir($remoteDir, 1)) {
       
   347     if ($self->{verbose}) {
       
   348       print "Created directory $remoteDir on FTP site\n";
       
   349     }
       
   350   }
       
   351   else {
       
   352     if ($self->Connected()) {
       
   353       $self->HandleError("Cannot make directory $remoteDir on FTP site");
       
   354     }
       
   355     else {
       
   356       $self->MakeDir($remoteDir);
       
   357     }
       
   358   }
       
   359 }
       
   360 
       
   361 sub FileSize {
       
   362   my $self = shift;
       
   363   my $file = shift;
       
   364 
       
   365   $file =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   366 
       
   367   unless ($self->Connected()) {
       
   368     $self->Connect();
       
   369   }
       
   370 
       
   371   my $size;
       
   372   if (defined($size = $self->{ftp}->size($file))) {
       
   373     return $size;
       
   374   }
       
   375   else {
       
   376     if ($self->Connected()) {
       
   377       return 0;
       
   378     }
       
   379     else {
       
   380       $self->FileSize($file);  #try to get the size again after reconnecting
       
   381     }
       
   382   }
       
   383 }
       
   384 
       
   385 sub DeleteFile {
       
   386   my $self = shift;
       
   387   my $file = shift;
       
   388 
       
   389   $file =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   390 
       
   391   unless ($self->Connected()) {
       
   392     $self->Connect();
       
   393   }
       
   394 
       
   395   if ($self->{ftp}->delete($file)) {
       
   396     return;
       
   397   }
       
   398   elsif ($self->{ftp}->rmdir($file)) {
       
   399     return;
       
   400   }
       
   401   else {
       
   402     if ($self->Connected()) {
       
   403       $self->HandleError("Cannot delete $file on FTP site");
       
   404     }
       
   405     else {
       
   406       $self->DeleteFile($file);
       
   407     }
       
   408   }
       
   409 }
       
   410 
       
   411 sub MoveFile {
       
   412   my $self = shift;
       
   413   my $oldFile = shift;
       
   414   my $newFile = shift;
       
   415 
       
   416   $oldFile =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   417   $newFile =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   418 
       
   419   unless ($self->Connected()) {
       
   420     $self->Connect();
       
   421   }
       
   422 
       
   423   if ($self->{ftp}->rename($oldFile, $newFile)) {
       
   424     return;
       
   425   }
       
   426   else {
       
   427     if ($self->Connected()) {
       
   428       $self->HandleError("Cannot move $oldFile to $newFile on FTP site");
       
   429     }
       
   430     else {
       
   431       $self->MoveFile($oldFile, $newFile);
       
   432     }
       
   433   }
       
   434 }
       
   435 
       
   436 sub FileModifiedTime {
       
   437   my $self = shift;
       
   438   my $file = shift;
       
   439 
       
   440   $file =~ s{\\}{\/}g;   #convert back slashes to forward slashes
       
   441 
       
   442   unless ($self->Connected()) {
       
   443     $self->Connect();
       
   444   }
       
   445 
       
   446   my $modifiedTime;
       
   447   if (defined($modifiedTime = $self->{ftp}->mdtm($file))) {
       
   448     return $modifiedTime;
       
   449   }
       
   450   else {
       
   451     if ($self->Connected()) {
       
   452       print "Warning: failed to find modified time for file \"$file\"\n";
       
   453       return undef;
       
   454     }
       
   455     else {
       
   456       $self->FileModifiedTime($file);
       
   457     }
       
   458   }
       
   459 }
       
   460 
       
   461 #
       
   462 # Private
       
   463 #
       
   464 
       
   465 sub Connect {
       
   466   my $self = shift;
       
   467 
       
   468   unless ($self->Host()) {
       
   469     $self->HandleError("Cannot connect FTP host name not defined");
       
   470   }
       
   471   my $debug = (($self->{verbose} && $self->{verbose} > 1) ? 1 : 0);
       
   472 
       
   473   #Attempt to connect (or reconnect if connection fails)
       
   474   for (1..$self->Reconnects()) {
       
   475     $self->{ftp} = undef;
       
   476     if ($self->{verbose}) {
       
   477       print "Connecting to FTP site ".$self->Host()."...\n";
       
   478     }
       
   479     $self->{ftp} = Net::FTP->new($self->Host(),
       
   480 				 Passive => $self->PassiveMode(),
       
   481 				 Debug => $debug,
       
   482 				 Timeout => $self->Timeout());
       
   483     if (defined $self->{ftp}) {
       
   484       #login to FTP site
       
   485       $self->{ftp}->login($self->Username(), $self->Password())
       
   486 	or $self->HandleError("FTP login failed");
       
   487 
       
   488       #change transfer mode to binary
       
   489       $self->{ftp}->binary()
       
   490 	or $self->HandleError("Failed to set FTP server to binary transfer mode");
       
   491       return;
       
   492     }
       
   493   }
       
   494   $self->HandleError("Cannot connect to FTP site ".$self->Host());
       
   495 }
       
   496 
       
   497 sub Connected {
       
   498   my $self = shift;
       
   499   return (defined $self->{ftp} and defined $self->{ftp}->pwd);
       
   500 }
       
   501 
       
   502 sub SendFileWithResume {
       
   503   my $self = shift;
       
   504   my $localFile = shift;
       
   505   my $remoteFile = shift;
       
   506 
       
   507   #open the local file for reading
       
   508   $self->{localfh} = IO::File->new("< $localFile");
       
   509   binmode($self->{localfh});
       
   510 
       
   511   my $localFileSize = Utils::FileSize($localFile);
       
   512 
       
   513   my $buffer;
       
   514   my $bytesSent;
       
   515   my $totalBytesSent = 0;
       
   516 
       
   517  RESUME:
       
   518   #Open the temporary file on the FTP site for writing/appending
       
   519   $self->{dataconn} = $self->OpenRemoteFileForAppending($remoteFile);
       
   520 
       
   521   if ($self->{verbose} and $localFileSize) {
       
   522     print "Upload progress:    ";
       
   523   }
       
   524 
       
   525   #upload temporary file in blocks
       
   526   while ($self->{localfh}->read($buffer, BLOCKSIZE)) {
       
   527     eval {
       
   528       $bytesSent = $self->{dataconn}->write($buffer, length($buffer));
       
   529     };
       
   530     unless ($bytesSent) {
       
   531       if (my $ftpResponse = $self->{ftp}->getline()) {
       
   532         $self->{ftp}->ungetline($ftpResponse);
       
   533         next if ($ftpResponse !~ m/^(3|4|5)/);
       
   534         chomp $ftpResponse;
       
   535         print "\nError: The FTP server returned \'$ftpResponse\'\n";
       
   536       }
       
   537       
       
   538       if ($self->Connected()) {
       
   539 	$self->HandleError("Cannot append to remote file $remoteFile");
       
   540       }
       
   541       else {
       
   542 	#connection dropped. Reconnect and resume upload
       
   543 	if ($self->{verbose}) {print "\n"}
       
   544 	$self->Connect();
       
   545 	$totalBytesSent = $self->FileSize($remoteFile);
       
   546 	seek($self->{localfh}, $totalBytesSent, 0);
       
   547 	goto RESUME;
       
   548       }
       
   549     }
       
   550     else {
       
   551       $totalBytesSent += $bytesSent;
       
   552       $self->UpdateProgress($totalBytesSent, $localFileSize);
       
   553     }
       
   554   }
       
   555 
       
   556   #close the remote and local files now the transfer has finished
       
   557   $self->CloseAllOpenFiles();
       
   558 }
       
   559 
       
   560 sub SendFileWithoutResume {
       
   561   my $self = shift;
       
   562   my $localFile = shift;
       
   563   my $remoteFile = shift;
       
   564 
       
   565   my $putSuccess;
       
   566   eval {
       
   567     $putSuccess = $self->{ftp}->put($localFile, $remoteFile);
       
   568   };
       
   569   unless ($putSuccess) {
       
   570     $self->HandleError("Problem occurred during FTP upload of $localFile");
       
   571   }
       
   572 }
       
   573 
       
   574 sub GetFileWithResume {
       
   575   my $self = shift;
       
   576   my $remoteFile = shift;
       
   577   my $localFile = shift;
       
   578 
       
   579   my $totalBytesReceived = 0;
       
   580   my $getSuccess;
       
   581 
       
   582  RESUME:
       
   583   unless ($self->Connected()) {
       
   584     $self->Connect();
       
   585   }
       
   586 
       
   587   eval {
       
   588     $getSuccess = $self->{ftp}->get($remoteFile, $localFile, $totalBytesReceived);
       
   589   };
       
   590 
       
   591   unless ($getSuccess or !$@) {
       
   592     if ($self->Connected()) {
       
   593       $self->HandleError("Problem occurred during FTP download of $remoteFile");
       
   594     }
       
   595     else {
       
   596       $totalBytesReceived = Utils::FileSize($localFile);
       
   597       goto RESUME;
       
   598     }
       
   599   }
       
   600 }
       
   601 
       
   602 sub GetFileWithoutResume {
       
   603   my $self = shift;
       
   604   my $remoteFile = shift;
       
   605   my $localFile = shift;
       
   606 
       
   607   unless ($self->Connected()) {
       
   608     $self->Connect();
       
   609   }
       
   610 
       
   611   my $getSuccess;
       
   612   eval {
       
   613     $getSuccess = $self->{ftp}->get($remoteFile, $localFile);
       
   614   };
       
   615   unless ($getSuccess) {
       
   616     $self->HandleError("Problem occurred during FTP download of $remoteFile");
       
   617   }
       
   618 }
       
   619 
       
   620 sub DirExists {
       
   621   my $self = shift;
       
   622   my $remoteDir = shift;
       
   623 
       
   624   $remoteDir =~ s{\\}{\/}g;     #convert back slashes to forward slashes
       
   625 
       
   626   unless ($self->Connected()) {
       
   627     $self->Connect();
       
   628   }
       
   629 
       
   630   my $pwd = $self->{ftp}->pwd() or $self->HandleError("Problem reading current working directory on FTP site\n");
       
   631   my $exists = 0;
       
   632   if ($self->{ftp}->cwd($remoteDir)) {
       
   633     $exists = 1;
       
   634     $self->{ftp}->cwd($pwd) or $self->HandleError("Problem changing current working directory back to $pwd on FTP site\n");
       
   635   }
       
   636 
       
   637   return $exists;
       
   638 }
       
   639 
       
   640 
       
   641 sub OpenRemoteFileForAppending {
       
   642   my $self = shift;
       
   643   my $remoteFile = shift;
       
   644 
       
   645   unless ($self->Connected()) {
       
   646     $self->Connect();
       
   647   }
       
   648 
       
   649   my $dataconn;
       
   650   if (defined($dataconn = $self->{ftp}->appe($remoteFile))) {
       
   651     return $dataconn;
       
   652   }
       
   653   else {
       
   654     if ($self->Connected()) {
       
   655       $self->HandleError("Cannot open $remoteFile for appending on FTP site");
       
   656     }
       
   657     else {
       
   658       $self->OpenRemoteFileForAppending($remoteFile);
       
   659     }
       
   660   }
       
   661 }
       
   662 
       
   663 sub CloseAllOpenFiles {
       
   664    my $self = shift;
       
   665 
       
   666   if ($self->{localfh}) {
       
   667     $self->{localfh}->close;
       
   668     $self->{localfh} = undef;
       
   669   }
       
   670   if ($self->{dataconn}) {
       
   671     $self->{dataconn}->close();
       
   672     $self->{dataconn} = undef;
       
   673   }
       
   674 }
       
   675 
       
   676 sub DisplayProgress {
       
   677   my $self = shift;
       
   678   my $total = shift;
       
   679 
       
   680   my $numHashes = 50;
       
   681   my $bytesPerHash = int $total / $numHashes;
       
   682   if ($total) {
       
   683     $self->{ftp}->hash(\*STDERR, $bytesPerHash);
       
   684   }
       
   685 }
       
   686 
       
   687 sub UpdateProgress {
       
   688   my $self = shift;
       
   689   my $current = shift;
       
   690   my $total = shift;
       
   691 
       
   692   my $bytesPerPercent = int $total/100;
       
   693   if ($current == $total) {
       
   694     print "\b\b\b100%\n";
       
   695   }
       
   696   elsif ($bytesPerPercent == 0) {
       
   697     print "\b\b0%";
       
   698   }
       
   699   else {
       
   700     my $percentComplete = int $current/$bytesPerPercent;
       
   701     if ($percentComplete < 10) {
       
   702       print "\b\b$percentComplete%";
       
   703     }
       
   704     else {
       
   705       print "\b\b\b$percentComplete%";
       
   706     }
       
   707   }
       
   708 }
       
   709 
       
   710 sub HandleError {
       
   711   my $self = shift;
       
   712   my $errorString = shift;
       
   713 
       
   714   if (defined $self->{ftp}) {
       
   715     $self->{ftp}->quit();
       
   716     $self->{ftp} = undef;
       
   717   }
       
   718   $self->CloseAllOpenFiles();
       
   719 
       
   720   #call the super class error handler
       
   721   $self->SUPER::HandleError($errorString);
       
   722 }
       
   723 
       
   724 sub CreateTemporaryFile {
       
   725   my $self = shift;
       
   726   my $remoteDir = shift;
       
   727 
       
   728   my $fileNum = 10000;
       
   729   my $tmpFile = $remoteDir.'/lpdrt'.$fileNum.'.tmp';
       
   730   while ($self->FileExists($tmpFile)) {
       
   731     ++$fileNum;
       
   732     $tmpFile = $remoteDir.'/lpdrt'.$fileNum.'.tmp';
       
   733   }
       
   734   return $tmpFile;
       
   735 }
       
   736 
       
   737 
       
   738 #
       
   739 # Destructor
       
   740 #
       
   741 
       
   742 sub DESTROY {
       
   743   my $self = shift;
       
   744 
       
   745   $self->CloseAllOpenFiles();
       
   746 
       
   747   if (defined $self->{ftp}) {
       
   748     if ($self->{verbose}) {
       
   749       print "Dropping connection to FTP site ".$self->Host()."\n";
       
   750     }
       
   751     $self->{ftp}->quit();
       
   752     $self->{ftp} = undef;
       
   753   }
       
   754 }
       
   755 
       
   756 1;
       
   757 
       
   758 =head1 NAME
       
   759 
       
   760 RemoteSite::FTP.pm - Access a remote FTP site.
       
   761 
       
   762 =head1 SYNOPSIS
       
   763 
       
   764  use RemoteSite::FTP;
       
   765 
       
   766  $ftp = RemoteSite::FTP->New(host => 'ftp.somehost.com',
       
   767 	         	     username => 'myusername',
       
   768 			     password => 'mypassword',
       
   769 			     verbose => 1);
       
   770 
       
   771  if ($ftp->FileExists('/somedir/someremotefile')) {
       
   772    do something...
       
   773  }
       
   774  $ftp->SendFile('somelocalfile', 'someremotefile');
       
   775  $ftp->GetFile('someremotefile', 'somelocalfile');
       
   776 
       
   777 =head1 DESCRIPTION
       
   778 
       
   779 C<RemoteSite::FTP> is inherited from the abstract base class C<RemoteSite>, implementing the abstract methods required for transfer of files to and from a remote site when the remote site is an FTP server.
       
   780 
       
   781 =head1 INTERFACE
       
   782 
       
   783 =head2 New
       
   784 
       
   785 Passed an argument list in the form of hash key value pairs. The supported arguments are...
       
   786 
       
   787   host             => $host_address_string
       
   788   username         => $user_name_string
       
   789   password         => $pass_word_string
       
   790   passiveMode      => $passive_mode_bool
       
   791   resumeTransfers  => $resume_transfers_bool
       
   792   timeout          => $timeout_integer
       
   793   reconnects       => $reconnects_integer
       
   794   verbose          => $verbosity_integer
       
   795 
       
   796 Returns a reference to a C<RemoteSite::FTP> object
       
   797 
       
   798 =head2 Host
       
   799 
       
   800 Returns the current value of the C<host> attribute which contains the host FTP address. If passed an argument sets the attribute to this new value.
       
   801 
       
   802 =head2 Username
       
   803 
       
   804 Returns the current value of the C<username> attribute which stores the user name required to access the FTP site. If passed an argument sets the attribute to this new value.
       
   805 
       
   806 =head2 Password
       
   807 
       
   808 Returns the current value of the C<password> attribute which stores the password required to access the FTP site. If passed an argument sets the attribute to this new value.
       
   809 
       
   810 =head2 SendFile
       
   811 
       
   812 Passed a local and a remote file name. Uploads the local file to the FTP site. Dies if upload fails
       
   813 
       
   814 =head2 GetFile
       
   815 
       
   816 Passed a remote and local file name. Downloads the remote file from the FTP site and stores it on the local drive. Dies if download fails.
       
   817 
       
   818 =head2 FileExists
       
   819 
       
   820 Passed a filename (with full path) on the FTP site. Returns a non zero value if the file exists.
       
   821 
       
   822 =head2 DirList
       
   823 
       
   824 Passed a directory name. Returns a list of files contained in the directory or undef if fails to read directory
       
   825 
       
   826 =head2 MakeDir
       
   827 
       
   828 Passed a directory name. Creates the directory on the FTP site
       
   829 
       
   830 =head2 DeleteFile
       
   831 
       
   832 Passed a file name. Deletes the file on the FTP site. Dies if fails
       
   833 
       
   834 =head2 FileSize
       
   835 
       
   836 Passed a file name. Returns the size of the file. Returns 0 if fails.
       
   837 
       
   838 =head2 FileModifiedTime
       
   839 
       
   840 Passed a file name. Returns the last modified time stamp of the file. Returns undef if fails
       
   841 
       
   842 =head2 MoveFile
       
   843 
       
   844 Passed two file names. Renames the first file to the second file name. Dies if fails.
       
   845 
       
   846 =head1 KNOWN BUGS
       
   847 
       
   848 None
       
   849 
       
   850 =head1 COPYRIGHT
       
   851 
       
   852  Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
       
   853  All rights reserved.
       
   854  This component and the accompanying materials are made available
       
   855  under the terms of the License "Eclipse Public License v1.0"
       
   856  which accompanies this distribution, and is available
       
   857  at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
   858  
       
   859  Initial Contributors:
       
   860  Nokia Corporation - initial contribution.
       
   861  
       
   862  Contributors:
       
   863  
       
   864  Description:
       
   865  
       
   866 
       
   867 =cut