ftp.cxx

Go to the documentation of this file.
00001 
00002 
00003 //
00004 // Copyright 2002, Lowell Boggs Jr.
00005 //
00006 // This file or directory, containing source code for a computer program,
00007 // is Copyrighted by Lowell Boggs, Jr.  987 Regency Drive, Lewisville
00008 // TX (USA), 75067.  You may use, copy, modify, and distribute this
00009 // source file without charge or obligation so long as you agree to
00010 // the following:
00011 //
00012 //  1.  You must indemnify Lowell Boggs against any and all financial
00013 //      obligations caused by its use, misuse, function, or malfunction.
00014 //      Further, you acknowledge that there is no warranty of any kind,
00015 //      whatsoever.
00016 //
00017 //  2.  You agree not to attempt to patent any portion of this original
00018 //      work -- though you may attempt to patent your own extensions to
00019 //      it if you so choose.
00020 //
00021 //  3.  You keep this copyright notice with the file and all copies
00022 //      of the file and do not change it anyway except language translation.
00023 //
00024 // You are responsible for enforcing your own compliance with these
00025 // conditions and may not use this source file if you cannot agree to the
00026 // above terms and conditions.
00027 
00031 
00032 
00033 #include <cxxtls/ftp.h>
00034 #include <cxxtls/strtool.h>
00035 #include <cxxtls/simple_regex.h>
00036 #include <stdlib.h>
00037 #include <portable_io.h>
00038 #include <string.h>
00039 #include <signal.h>
00040 
00041 namespace cxxtls
00042 {
00043 
00044 using namespace std;
00045 
00046 static string error_text;
00047 
00048 static string open   ("open ");
00049 static string user   ("user ");
00050 static string quit   ("quit ");
00051 static string dir    ("dir ");
00052 static string getf   ("get ");
00053 static string putf   ("put ");
00054 static string delf   ("del ");
00055 static string cd     ("cd ");
00056 static string eol    ("\n");
00057 static string space  (" ");
00058 static string echo   ("echo ");
00059 static string oparen (" ( ");
00060 static string cparen (" ) ");
00061 
00062 #ifdef _MSC_VER
00063 static string ftp    (" ftp -n <ftp.cmd ");
00064 static string sh     ("cmd /c ");
00065 #else
00066 static string ftp    (" | ftp -n 2>/dev/null ");
00067 static string sh     ("sh -c '");
00068 #endif
00069 
00070 static string semic  (" ; ");
00071 static string star   ("*");
00072 static string binary ("binary ");
00073 
00074 static bool is_ftp_error(char const *line)
00079 {
00080   static char const *loginfailure="Login Failed.";
00081   static char const *loginIncorrect="Login incorrect.";
00082   static char const *notconnected="Not connected.";
00083   static char const *user="User ";
00084 
00085   if(  0 == strncmp(line, loginfailure, strlen(loginfailure))
00086      ||
00087        0 == strncmp(line, loginfailure, strlen(loginIncorrect))
00088     )
00089   {
00090     error_text = "error:  incorrect username or password";
00091     return true;
00092   }
00093 
00094   if(0 == strncmp(line, notconnected, strlen(notconnected)))
00095   {
00096     error_text = "error:  invalid ftp site";
00097     return true;
00098   }
00099 
00100  if(0 == strncmp(line, user, strlen(user)))
00101  {
00102    error_text = "error: user cannot log in";
00103    return true;
00104  }
00105 
00106   return false;
00107 
00108 }
00109 
00110 static bool is_ftp_dir_line(char const *buffer)
00111   //
00112   // Returns true if a character string is the output of an
00113   // ftp dir command
00114   //
00115 {
00116   return buffer[0] == 'd' ||
00117          buffer[0] == 'l' ||
00118          buffer[0] == '-';
00119 }
00120 
00121 bool
00122 FTP::
00123 parse_ftp_dir_text(char const *buffer, FileStatus *statbuf, FileName *filename)
00124   //
00125   //  Parse the text provided by ftp describing the file status.
00126   //
00127   //  Return false if this is not a valid file status line
00128   //
00129 {
00130     //
00131     //    d---------   1 owner    group               0 Feb 10 10:30 incoming  size
00132     //
00133 
00134     StrTool::stringlist_t line;
00135 
00136 
00137     int patterns = StrTool::parse_words(buffer, &line, 9);
00138 
00139     if(patterns != 9)
00140       return false;
00141 
00142     StrTool::stringlist_t::const_iterator scan = line.begin();
00143 
00144     string const& permissions  = *scan++;
00145     string const& links        = *scan++;
00146     string const& owner        = *scan++;
00147     string const& group        = *scan++;
00148     string const& size         = *scan++;
00149     string const& month        = *scan++;
00150     string const& day          = *scan++;
00151     string const& year         = *scan++;
00152 
00153     string        name         = *scan;
00154 
00155     {
00156 
00157       if(!is_ftp_dir_line(permissions.c_str()))
00158         return false;
00159         
00160       if(permissions.size() < 10)
00161         return false;
00162         
00163       // size_t offset = name.find_first_of(" -> ");
00164 
00165        size_t offset = name.size();
00166 
00167       {
00168          static string arrow(" -> ");
00169         
00170          string::iterator where = std::search(name.begin(), name.end(),
00171                                               arrow.begin(), arrow.end()
00172                                              );
00173                                         
00174          if(where != name.end())
00175            offset = where - name.begin();
00176       }
00177 
00178 
00179 
00180       if(offset > 0 && offset < name.size() )
00181         name.erase(name.begin() + offset,
00182                    name.end()
00183                   );
00184         
00185       if(name[name.size() - 1] == '\n')
00186         name.erase(name.end() -1);
00187         
00188       *filename = name;
00189 
00190       int tmp;
00191 
00192       sscanf(size.c_str(), "%d", &tmp);
00193 
00194       statbuf->size = tmp;
00195 
00196       statbuf->owner_id = -2;
00197       statbuf->group_id = -2;
00198 
00199       statbuf->mode.construct_from_string(permissions);
00200 
00201 
00202       string time_buf(month);
00203 
00204       time_buf += " ";
00205       time_buf += day;
00206       time_buf += " ";
00207       time_buf += year;
00208 
00209       statbuf->time.construct_from_string(time_buf);
00210 
00211       string const* t = &group;  // silence stupid unused variable warnings
00212                     t = &owner;
00213                     t = &links;
00214 
00215       return true;
00216     }
00217 
00218 
00219   return false;
00220 }
00221 
00222 
00223 string
00224 FTP::
00225 error()
00226 {
00227   return error_text;
00228 }
00229 
00230 static int child_alarm_occurred=0;
00231 
00232 extern "C" void child_alarm_handler(int signum)
00233 {
00234 #ifndef _MSC_VER
00235    child_alarm_occurred=1;
00236    #ifdef __IBMCXX_
00237    signal(SIGCHLD, child_alarm_handler);
00238    #endif
00239 #endif
00240 }
00241 
00242 
00243 static FILE* ftplog = 0;
00244 
00245 
00246 int
00247 FTP::
00248 stat_matching(char const          *hostname,
00249               char const          *username,
00250               char const          *password,
00251               char const          *pattern,
00252               std::list<FileInfo> *result
00253              )
00254   //
00255   // Find a list of FileInfo's matching a specified file name pattern
00256   // on a remote host using ftp.  Returns 0 if no matches found
00257   // returns -1 if an error occurred.  Otherwise, returns the count
00258   // of matches.
00259   //
00260 {
00261   int matches=0;
00262   int dirents_found=0;
00263 
00264   if(!password || password[0] == 0)
00265   {
00266     error_text="Sorry, empty passwords not supported";
00267     return -1;
00268   }
00269 
00270   FileName ftp_program = FileName::find_executable("ftp");
00271 
00272   if(ftp_program.size() == 0)
00273   {
00274     error_text="Sorry, couldn't find 'ftp' executable in path";
00275     return -1;
00276   }
00277 
00278   error_text="";   // clean out the error string
00279 
00280   // make a command to feed to ftp's standard in:
00281   //
00282   //    open host
00283   //    user username password
00284   //    dir  pattern
00285   //    quit
00286   //
00287 
00288   FileName dirname(pattern);    // directory/file*.c
00289 
00290   FileName basename = dirname.basename();   // file*.c
00291 
00292   dirname = dirname.dirname();  // directory
00293 
00294   if(pattern[0] == 0 || strcmp(pattern,"*") == 0)
00295   {
00296     dirname="";
00297     pattern="*";
00298     basename=pattern;
00299   }
00300 
00301 #ifdef _MSC_VER
00302 
00303   string command(sh + space + ftp_program + " -n <ftp.cmd" );
00304 
00305   FileName cmdfile("ftp.cmd");
00306 
00307   bool err =
00308   cmdfile.write(
00309                   open + space + hostname + "\r\n" +
00310                   user + space + username + space + password + "\r\n" +
00311                   dir  + space + dirname  + "\r\n" +
00312                   quit + "\r\n"
00313                );
00314         
00315   if(err)
00316   {
00317     error_text="couldn't write to file ftp.cmd";
00318     return -1;
00319   }
00320 
00321 
00322 #else
00323   string command(sh +
00324                  oparen + echo + open + space + hostname + semic +
00325                           echo + user + space + username + space + password + semic +
00326                           echo + dir  + space + dirname  + semic +
00327                           echo + quit + semic +
00328                  cparen + ftp
00329                  + "'"
00330                 );
00331 #endif
00332 
00333 
00334   //
00335   // launch ftp with the command
00336   //
00337 
00338 
00339   child_alarm_handler(0);  // set the alarm
00340   child_alarm_occurred = 0; // clear the flag
00341 
00342   // ftplog = fopen("ftp.log", "w");  // Uncomment out this line to turn on log
00343 
00344   if(ftplog) fputs(command.c_str(), ftplog); // prints pasword into ftp.log!
00345 
00346 
00347   FILE *f = popen(command.c_str(), "r");
00348 
00349   if(!f)
00350   {
00351     error_text = "error:  command wouldn't start\n  " + command;
00352     return -1;
00353   }
00354 
00355   char buffer[max_ftp_line_length];
00356 
00357   //
00358   // read ftp's output and interpret what it says
00359   //
00360 
00361   int records_read = 0;
00362 
00363   while(!child_alarm_occurred && fgets(buffer, max_ftp_line_length, f))
00364   {
00365     ++records_read;
00366 
00367     int l = strlen(buffer);
00368 
00369     if(buffer[l-1] == '\n') --l;   // remote end of line crap
00370     if(buffer[l-1] == '\r') --l;
00371     buffer[l] = 0;
00372 
00373     if(ftplog) { fputs(buffer, ftplog); fflush(ftplog); }
00374 
00375     if( !is_ftp_dir_line(buffer) )
00376     {
00377 
00378       if(is_ftp_error(buffer))
00379       {
00380         if(records_read == 0)
00381         {
00382           error_text = "Login failure";
00383         }
00384         pclose(f);
00385         return -1;
00386       }
00387 
00388 
00389       continue;  // this isn't a directory line
00390     }
00391 
00392     FileInfo thisfile;
00393 
00394     if( !parse_ftp_dir_text(buffer, &thisfile.status_, &thisfile.name_) )
00395       continue;
00396 
00397     ++dirents_found;
00398 
00399     if(thisfile.name_.matches(basename))
00400     {
00401        ++matches;
00402        result->push_back(thisfile);
00403     }
00404 
00405   }
00406 
00407 #ifndef _MSC_VER
00408   signal(SIGCHLD, SIG_IGN);
00409 #endif
00410 
00411   pclose(f);
00412 
00413   return matches;
00414 
00415 }
00416 
00417 
00418 bool
00419 FTP::
00420 put(string      hostname,
00421     string      username,
00422     string      password,
00423     FileName    filename,
00424     string      localfile
00425    )
00426 {
00427   error_text="";   // clean out the error string
00428 
00429   FileName ftp_program = FileName::find_executable("ftp");
00430 
00431   if(ftp_program.size() == 0)
00432   {
00433     error_text="Sorry, couldn't find 'ftp' executable in path";
00434     return -1;
00435   }
00436 
00437   // make a command to feed to ftp's standard in:
00438   //
00439   //    open host
00440   //    user username password
00441   //    cd filename.dirname()
00442   //    binary
00443   //    put filename.basename()
00444   //    quit
00445   //
00446 
00447 #ifdef _MSC_VER
00448 
00449   string command(sh + space + ftp_program + " -n <ftp.cmd");
00450 
00451   FileName cmdfile("ftp.cmd");
00452 
00453   cmdfile.write(
00454                  open + space + hostname + "\r\n" +
00455                  user + space + username + space + password + "\r\n" +
00456                  cd   + space + filename.dirname() + "\r\n" +
00457                  binary + "\r\n" +
00458                  putf + space + localfile + space + filename.basename() + "\r\n" +
00459                  quit + "\r\n"
00460                );
00461 
00462 #else
00463   string command(sh +
00464                    oparen + echo + open + space + hostname + semic +
00465                             echo + user + space + username + space + password + semic +
00466                             echo + cd  + space + filename.dirname() + semic +
00467                             echo + binary + semic +
00468                             echo + putf + space + localfile + space + filename.basename()  + space + semic +
00469                             echo + quit + semic +
00470                    cparen + ftp +
00471                  "'"
00472                 );
00473 #endif
00474 
00475   //
00476   // launch ftp with the command
00477   //
00478 
00479   // ftplog = fopen("ftp.log", "w");  // Uncomment out this line to turn on log
00480 
00481   if(ftplog) fputs(command.c_str(), ftplog); // prints pasword into ftp.log!
00482 
00483   FILE *f = popen(command.c_str(), "r");
00484 
00485   if(!f)
00486   {
00487     error_text = "error:  command wouldn't start\n  " + command;
00488     return -1;
00489   }
00490 
00491   char buffer[max_ftp_line_length];
00492 
00493   //
00494   // read ftp's output and interpret what it says
00495   //
00496 
00497   int records_read=0;
00498 
00499   while(fgets(buffer, max_ftp_line_length, f))
00500   {
00501     ++records_read;
00502 
00503     {
00504       if(is_ftp_error(buffer))
00505       {
00506         pclose(f);
00507         return -1;
00508       }
00509     }
00510 
00511   }
00512 
00513 #ifndef _MSC_VER
00514   if(records_read == 0)
00515   {
00516     error_text = "error:  ftp wouldn't start -- PATH problem maybe?";
00517     pclose(f);
00518     return -1;
00519   }
00520 #endif
00521 
00522   pclose(f);
00523   return false;
00524 
00525 }
00526 
00527 
00528 bool
00529 FTP::
00530 get(string      hostname,
00531     string      username,
00532     string      password,
00533     FileName    filename,
00534     string      localfile
00535    )
00536 {
00537   error_text="";   // clean out the error string
00538 
00539   FileName ftp_program = FileName::find_executable("ftp");
00540 
00541   if(ftp_program.size() == 0)
00542   {
00543     error_text="Sorry, couldn't find 'ftp' executable in path";
00544     return -1;
00545   }
00546 
00547   // make a command to feed to ftp's standard in:
00548   //
00549   //    open host
00550   //    user username password
00551   //    cd filename.dirname()
00552   //    binary
00553   //    get filename.basename()
00554   //    quit
00555   //
00556 
00557 #ifdef _MSC_VER
00558   string command(sh + space + ftp_program + " -n <ftp.cmd");
00559 
00560   FileName cmdfile("ftp.cmd");
00561 
00562   cmdfile.write(
00563                  open + space + hostname +                    "\r\n" +
00564                  user + space + username + space + password + "\r\n" +
00565                  binary                                     + "\r\n" +
00566                  getf + space + filename.basename() +
00567                                            space    + localfile +
00568                                                               "\r\n" +
00569                  quit +                                       "\r\n"
00570                );
00571 
00572 #else
00573   string command(sh +
00574                    oparen + echo + open + space + hostname + semic +
00575                             echo + user + space + username + space + password + semic +
00576                             echo + cd  + space + filename.dirname() + semic +
00577                             echo + binary + semic +
00578                             echo + getf + space + filename.basename() + space + localfile + space + semic +
00579                             echo + quit + semic +
00580                    cparen + ftp +
00581                  "'"
00582                 );
00583 #endif
00584 
00585   //
00586   // launch ftp with the command
00587   //
00588 
00589   FILE *f = popen(command.c_str(), "r");
00590 
00591   if(!f)
00592   {
00593     error_text = "error:  command wouldn't start\n  " + command;
00594     return -1;
00595   }
00596 
00597   char buffer[max_ftp_line_length];
00598 
00599   //
00600   // read ftp's output and interpret what it says
00601   //
00602 
00603   while(fgets(buffer, max_ftp_line_length, f))
00604   {
00605     {
00606       if(is_ftp_error(buffer))
00607       {
00608         pclose(f);
00609         return -1;
00610       }
00611     }
00612 
00613   }
00614 
00615   pclose(f);
00616 
00617   if(localfile.size())
00618     filename = localfile;
00619   else
00620     filename = filename.basename();
00621 
00622   if(!filename.exists())
00623   {
00624     error_text = "error:  requested file did not download";
00625     return true;
00626   }
00627 
00628   return false;
00629 
00630 }
00631 
00632 
00633 bool
00634 FTP::
00635 del(string      hostname,
00636     string      username,
00637     string      password,
00638     FileName    filename
00639    )
00640 {
00641   error_text="";   // clean out the error string
00642 
00643   FileName ftp_program = FileName::find_executable("ftp");
00644 
00645   if(ftp_program.size() == 0)
00646   {
00647     error_text="Sorry, couldn't find 'ftp' executable in path";
00648     return -1;
00649   }
00650 
00651   // make a command to feed to ftp's standard in:
00652   //
00653   //    open host
00654   //    user username password
00655   //    cd filename.dirname()
00656   //    del filename.basename()
00657   //    quit
00658   //
00659 
00660 #ifdef _MSC_VER
00661 
00662   string command(sh + space + ftp_program + " -n <ftp.cmd");
00663 
00664   FileName cmdfile("ftp.cmd");
00665 
00666   cmdfile.write(
00667                  open + space + hostname +                    "\r\n" +
00668                  user + space + username + space + password + "\r\n" +
00669                  cd   + space + filename.dirname()          + "\r\n" +
00670                  delf + space + filename.basename() +         "\r\n" +
00671                  quit +                                       "\r\n"
00672                );
00673 
00674 
00675 #else
00676   string command(sh +
00677                    oparen + echo + open + space + hostname + semic +
00678                             echo + user + space + username + space + password + semic +
00679                             echo + cd  + space + filename.dirname() + semic +
00680                             echo + delf + space + filename.basename() + space + semic +
00681                             echo + quit + semic +
00682                    cparen + ftp +
00683                  "'"
00684                 );
00685 #endif
00686 
00687   //
00688   // launch ftp with the command
00689   //
00690 
00691   FILE *f = popen(command.c_str(), "r");
00692 
00693   if(!f)
00694   {
00695     error_text = "error:  command wouldn't start\n  " + command;
00696     return -1;
00697   }
00698 
00699   char buffer[max_ftp_line_length];
00700 
00701   //
00702   // read ftp's output and interpret what it says
00703   // When removing a file, it may be so fast that the ftp
00704   // process dies before we get to read its output....
00705   // This means we occaisionally miss some error messages...
00706 
00707   while(fgets(buffer, max_ftp_line_length, f))
00708   {
00709 
00710     {
00711       if(is_ftp_error(buffer))
00712       {
00713         pclose(f);
00714         return -1;
00715       }
00716     }
00717 
00718   }
00719 
00720   pclose(f);
00721   return false;
00722 
00723 }
00724 
00725 } // namespace cxxtls
Generated on Wed Feb 29 22:50:05 2012 for CXXUtilities by  doxygen 1.6.3