inlib  1.2.0
/Users/barrand/private/dev/softinex/old/inexlib-1.2/inlib/inlib/net/ftp
Go to the documentation of this file.
00001 // Copyright (C) 2010, Guy Barrand. All rights reserved.
00002 // See the file inlib.license for terms.
00003 
00004 #ifndef inlib_ftp
00005 #define inlib_ftp
00006 
00007 //inheritance :
00008 #include "inet_socket"
00009 
00010 #include "../typedefs"
00011 #include "../file"
00012 #include "../cstr"
00013 
00014 #include "../words"
00015 #include "../sto"
00016 #include "../sout"
00017 
00018 namespace inlib {
00019 namespace net {
00020 
00021 class ftp : public inet_socket {
00022 public:
00023   static bool parse(const std::string& a_url,std::string& a_host,std::string& a_path) {
00024     if(a_url.substr(0,6)!="ftp://") return false;
00025     //012345
00026     //   ^
00027     std::string s = a_url.substr(6,a_url.size()-6);
00028     std::string::size_type pos = s.find('/');
00029     if(pos==std::string::npos) return false;
00030     a_host = s.substr(0,pos);
00031     a_path = s.substr(pos,s.size()-pos);
00032     return true;
00033   }
00034 public:
00035   ftp(std::ostream& a_out,bool a_verbose)
00036   : inet_socket(a_out,a_verbose)
00037   {}
00038   virtual ~ftp(){}
00039 protected:
00040   ftp(const ftp& a_from): inet_socket(a_from){}
00041   ftp& operator=(const ftp&){return *this;}
00042 public:
00043   static const std::string& s_anonymous() {
00044     static const std::string s_v("anonymous");
00045     return s_v;
00046   }
00047 public:
00048   bool start(const std::string& a_host,
00049                     const std::string& a_user,  //anonymous
00050                     const std::string& a_pwd) { //empty string if anonymous
00051     int port = 21;
00052     if(!connect(a_host,port,10,1)) return false;
00053 
00054     //wait greeting. 220 (vsFTPd 2.0.1)
00055     std::string answer;
00056     if(!get_answer(answer)) return false;
00057 
00058    {std::string cmd = "USER "+a_user;
00059     if(!send_command(cmd,answer)) return false;}
00060 
00061    {std::string cmd = "PASS "+a_pwd;
00062     if(!send_command(cmd,answer)) return false;}
00063 
00064     return true;
00065   }
00066   
00067 protected:
00068   class xxx : public inlib::net::inet_socket {
00069   public:
00070     virtual bool fetch_cbk(inlib::uint64 a_length) {
00071       return m_ftp.fetch_cbk(a_length);
00072     }
00073   public:
00074     xxx(std::ostream& a_out,bool a_verbose,ftp& a_ftp)
00075     : inlib::net::inet_socket(a_out,a_verbose)
00076     ,m_ftp(a_ftp)
00077     {}
00078     virtual ~xxx(){}
00079   protected:
00080     xxx(const xxx& a_from)
00081     : inlib::net::inet_socket(a_from)
00082     ,m_ftp(a_from.m_ftp)
00083     {}
00084     xxx& operator=(const xxx& a_from){
00085       inlib::net::inet_socket::operator=(a_from);
00086       return *this;
00087     }
00088   protected:
00089     ftp& m_ftp;
00090   };
00091 public:
00092   
00093   bool fetch_file(const std::string& a_file,
00094                          const std::string& a_local,
00095                          unsigned int a_BLOCK = 65536,
00096                          bool a_active = true) {
00097 
00098     FILE* file = ::fopen(a_local.c_str(),"wb");
00099     if(!file) return false;
00100 
00101     inet_socket bind(m_out,m_verbose);
00102     xxx data(m_out,m_verbose,*this);
00103 
00104     std::string answer;
00105 
00106     if(a_active) {
00107 
00108       std::string this_host;
00109       if(!inet_host(m_out,this_host)){
00110         m_out << "inlib::ftp::fetch_file :" 
00111               << " cannot get computer name."
00112               << std::endl;
00113         ::fclose(file);
00114         return false;
00115       }
00116 
00117       bind_infos tag;
00118       bind.set_after_bind_func(data_after_bind,&tag);
00119       if(!bind.bind(this_host,0,10,1,true)) {::fclose(file);return false;}
00120 
00121     //m_out << "debug : inlib::ftp::fetch_file : sin_port " << tag.m_sin_port 
00122     //      << std::endl; 
00123 
00124      {char cmd[256];
00125 #ifdef WIN32
00126       _snprintf
00127 #else
00128       ::snprintf
00129 #endif
00130           (cmd,sizeof(cmd),"PORT %d,%d,%d,%d,%d,%d",
00131               (int)*((unsigned char *)(&tag.m_sin_addr)+0),
00132               (int)*((unsigned char *)(&tag.m_sin_addr)+1),
00133               (int)*((unsigned char *)(&tag.m_sin_addr)+2),
00134               (int)*((unsigned char *)(&tag.m_sin_addr)+3),
00135               (int)*((unsigned char *)(&tag.m_sin_port)+0),
00136               (int)*((unsigned char *)(&tag.m_sin_port)+1));
00137       if(!send_command(cmd,answer)) {::fclose(file);return false;}
00138       if(answer.substr(0,3)=="500"){
00139         m_out << "inlib::ftp::fetch_file :" 
00140               << " for cmd " << sout(std::string(cmd))
00141               << ", received a " << sout(answer)
00142               << std::endl;
00143         ::fclose(file);
00144         return false;
00145       }}
00146   
00147     } else {
00148 
00149       std::string cmd = "PASV";
00150       if(!send_command(cmd,answer)) return false;
00151       if(answer.substr(0,3)!="227"){
00152         m_out << "inlib::ftp::fetch_file :" 
00153               << " for cmd " << sout(std::string(cmd))
00154               << ", received a " << sout(answer)
00155               << std::endl;
00156         return false;
00157       }
00158       //We receive something as :
00159       //227 Entering Passive Mode (134,158,91,15,80,72)
00160 
00161       //Get the port from the two last number :
00162      
00163       std::string host;
00164       unsigned short svr_port;
00165       if(!host_port_from_answer(answer,host,svr_port)) {
00166         m_out << "inlib::ftp::fetch_file :" 
00167               << " can't get port from " << sout(answer) 
00168               << std::endl;
00169         return false;
00170       }
00171 
00172       if(m_verbose) {
00173         m_out << "inlib::ftp::fetch_file :" 
00174               << " server host " << host
00175               << " server port " << sout(svr_port) 
00176               << std::endl;
00177       }
00178 
00179       if(!data.connect(host,svr_port,10,1)) return false;
00180 
00181       if(m_verbose) {
00182         m_out << "inlib::ftp::fetch_file :" 
00183               << " connected to server."
00184               << std::endl;
00185       }
00186 
00187     } 
00188 
00189     if(!send_command("TYPE I",answer)) {::fclose(file);return false;}
00190   
00191     if(!send_command(std::string("RETR ")+a_file,answer)) {
00192       ::fclose(file);
00193       return false;
00194     }
00195 
00196     if(answer.substr(0,3)=="550"){
00197       m_out << "inlib::ftp::fetch_file :" 
00198             << " failed to open file " << sout(a_file) << "."
00199             << std::endl;
00200       ::fclose(file);
00201       return false;
00202     }
00203   
00204     // sometime we receive here the answer of TYPE or RETR !
00205     //if(answer.substr(0,3)!="150"){
00206     //  m_out << "inlib::ftp::fetch_file :" 
00207     //        << " answer is not 150. " << sout(answer) << "."
00208     //        << std::endl;
00209     //  ::fclose(file);
00210     //  return false;
00211     //}
00212   
00213     if(a_active) {
00214       int data_sock_id = bind.socket();
00215       if(!data.accept(data_sock_id,true)) {
00216         m_out << "inlib::ftp::fetch_dir :" 
00217               << " data.accept() failed." 
00218               << std::endl;
00219         return false;
00220       }
00221       bind.disconnect();
00222     }
00223 
00224     if(m_verbose) {
00225       m_out << "start transfer..." << std::endl;
00226     }
00227   
00228     inlib::uint64 length;
00229    {if(!data.fetch_upto_end(file,length,a_BLOCK)) {
00230       m_out << "inlib::ftp::fetch_file :" 
00231             << " fetch_upto_end failed." 
00232             << std::endl;
00233       ::fclose(file);
00234       return false;
00235     }
00236     if(m_verbose) {
00237       m_out << "end transfer."
00238             << " Got " << length << " bytes." 
00239             << std::endl;
00240     }}
00241   
00242     if(length) if(!get_answer(answer)) {::fclose(file);return false;}
00243 
00244     ::fclose(file);
00245 
00246     return true;
00247   }
00248 
00249   bool fetch_dir(const std::string& a_dir,
00250                         const std::string& a_local,
00251                         bool a_active = true) {
00252   
00253     inet_socket bind(m_out,m_verbose);
00254     inet_socket data(m_out,m_verbose);
00255 
00256     std::string answer;
00257   
00258     if(a_active) {
00259 
00260       std::string this_host;
00261       if(!inet_host(m_out,this_host)){
00262         m_out << "inlib::ftp::fetch_dir :" 
00263               << " cannot get computer name."
00264               << std::endl;
00265         return false;
00266       }
00267 
00268       bind_infos tag;
00269       bind.set_after_bind_func(data_after_bind,&tag);
00270       if(!bind.bind(this_host,0,10,1,true)) return false;
00271     
00272      {char cmd[256];
00273 #ifdef WIN32
00274       _snprintf
00275 #else
00276       ::snprintf
00277 #endif
00278           (cmd,sizeof(cmd),"PORT %d,%d,%d,%d,%d,%d",
00279               (int)*((unsigned char *)(&tag.m_sin_addr)+0),
00280               (int)*((unsigned char *)(&tag.m_sin_addr)+1),
00281               (int)*((unsigned char *)(&tag.m_sin_addr)+2),
00282               (int)*((unsigned char *)(&tag.m_sin_addr)+3),
00283               (int)*((unsigned char *)(&tag.m_sin_port)+0),
00284               (int)*((unsigned char *)(&tag.m_sin_port)+1));
00285       if(!send_command(cmd,answer)) return false;
00286       if(answer.substr(0,3)=="500"){
00287         m_out << "inlib::ftp::fetch_dir :" 
00288               << " for cmd " << sout(std::string(cmd))
00289               << ", received a " << sout(answer)
00290               << std::endl;
00291         return false;
00292       }}
00293 
00294     } else {
00295     
00296       std::string cmd = "PASV";
00297       if(!send_command(cmd,answer)) return false;
00298       if(answer.substr(0,3)!="227"){
00299         m_out << "inlib::ftp::fetch_dir :" 
00300               << " for cmd " << sout(std::string(cmd))
00301               << ", received a " << sout(answer)
00302               << std::endl;
00303         return false;
00304       }
00305       //We receive something as :
00306       //227 Entering Passive Mode (134,158,91,15,80,72)
00307 
00308       //Get the port from the two last number :
00309      
00310       std::string host;
00311       unsigned short svr_port;
00312       if(!host_port_from_answer(answer,host,svr_port)) {
00313         m_out << "inlib::ftp::fetch_dir :" 
00314               << " can't get port from " << sout(answer) 
00315               << std::endl;
00316         return false;
00317       }
00318 
00319       if(m_verbose) {
00320         m_out << "inlib::ftp::fetch_dir :" 
00321               << " server host " << host
00322               << " server port " << sout(svr_port) 
00323               << std::endl;
00324       }
00325 
00326       if(!data.connect(host,svr_port,10,1)) return false;
00327 
00328       if(m_verbose) {
00329         m_out << "inlib::ftp::fetch_dir :" 
00330               << " connected to server."
00331               << std::endl;
00332       }
00333     }
00334     
00335     if(!send_command("TYPE I",answer)) return false;
00336   
00337     if(!send_command(std::string("CWD ")+a_dir,answer)) return false;
00338 
00339     if(answer.substr(0,3)=="550"){
00340       m_out << "inlib::ftp::fetch_dir :" 
00341             << " failed to change directory " << sout(a_dir) << "."
00342             << std::endl;
00343       return false;
00344     }
00345   
00346     if(!send_command("LIST",answer)) return false;
00347   
00348     if(answer.substr(0,3)=="425"){
00349       m_out << "inlib::ftp::fetch_dir :" 
00350             << " failed with " << sout(answer) << "."
00351             << std::endl;
00352       return false;
00353     }
00354 
00355     // sometime we receive here the answer of TYPE or RETR !
00356     //if(answer.substr(0,3)!="150"){
00357     //  m_out << "inlib::ftp::fetch_dir :" 
00358     //        << " answer is not 150. " << sout(answer) << "."
00359     //        << std::endl;
00360     //  return false;
00361     //}
00362   
00363     if(a_active) {
00364       int data_sock_id = bind.socket();
00365       if(!data.accept(data_sock_id,true)) {
00366         m_out << "inlib::ftp::fetch_dir :" 
00367               << " data.accept() failed." 
00368               << std::endl;
00369         return false;
00370       }
00371       bind.disconnect();
00372     }
00373 
00374     if(m_verbose) {
00375       m_out << "start transfer..." << std::endl;
00376     }
00377   
00378     inlib::uint64 length;
00379    {char* buffer;
00380     if(!data.fetch_upto_end(buffer,length)) {
00381       m_out << "inlib::ftp::fetch_dir :" 
00382             << " fetch_upto_end failed."
00383             << std::endl;
00384       return false;
00385     }
00386     if(m_verbose) {
00387       m_out << "end transfer."
00388             << " Got " << length << " bytes." 
00389             << std::endl;
00390     }
00391     ::remove(a_local.c_str());
00392     if(!inlib::file::write_bytes(a_local,buffer,(size_t)length)) {
00393       m_out << "inlib::ftp::fetch_dir :" 
00394             << " can't write local file " << inlib::sout(a_local) 
00395             << std::endl;
00396       delete [] buffer;
00397       return false;
00398     }
00399     delete [] buffer;}
00400   
00401     if(length) if(!get_answer(answer)) return false;
00402 
00403     return true;
00404   }    
00405 protected:
00406   static char LF() {return 10;}
00407   static char CR() {return 13;}
00408 
00409   struct bind_infos {  
00410     unsigned int m_sin_addr;
00411     unsigned short m_sin_port;
00412   };
00413 
00414   static bool data_after_bind(const inet_socket::bind_info& a_info,void* a_tag) {
00415     bind_infos* tag = (bind_infos*)a_tag;
00416     tag->m_sin_addr = a_info.m_sin_addr;
00417     tag->m_sin_port = a_info.m_sin_port;
00418     return true;
00419   }
00420 
00421   bool get_answer(std::string& a_answer) {
00422     char* buffer;
00423     inlib::uint64 length;
00424     if(!fetch_upto_char(CR(),buffer,length)) {
00425       m_out << "fetch_upto_char failed." << std::endl; 
00426       a_answer.clear();
00427       return false;
00428     }
00429     //m_out << "length : " << length << std::endl;
00430     *(buffer+length-1) = 0;
00431     a_answer = buffer;
00432     delete [] buffer;
00433     if(m_verbose) {
00434       m_out << "get_answer : " << sout(a_answer) << std::endl;
00435     }
00436     return true;
00437   }
00438   
00439   bool send_command(const std::string& a_cmd,std::string& a_answer) {
00440     if(a_cmd.empty()) {
00441       a_answer.clear();
00442       return false;
00443     }
00444     if(m_verbose) {
00445       m_out << "send_command : " << sout(a_cmd) << std::endl;
00446     }
00447     //NOTE : use C str. std::string don't like CR,LF.
00448     char* cmd = inlib::str_new();    
00449     inlib::str_cat(cmd,a_cmd.c_str());
00450     inlib::str_cat(cmd,CR());
00451     inlib::str_cat(cmd,LF());
00452     if(!send_buffer(cmd,::strlen(cmd))) {
00453       m_out << "send_buffer failed." << std::endl;
00454       inlib::str_del(cmd);
00455       a_answer.clear();
00456       return false;
00457     }
00458     inlib::str_del(cmd);
00459     return get_answer(a_answer);
00460   }
00461 
00462   bool host_port_from_answer(std::string& a_answer,
00463                                std::string& a_host,unsigned short& a_port) {
00464     //for exa from :
00465     //  227 Entering Passive Mode (134,158,91,15,80,72)
00466     //get the port from last two ints.
00467 
00468     a_host.clear();
00469     a_port = 0;
00470     std::string::size_type lb = a_answer.find('(');
00471     std::string::size_type rb = a_answer.rfind(')');
00472     if((lb==std::string::npos) || (rb==std::string::npos) ) return false;
00473     std::string s = a_answer.substr(lb+1,rb-lb-1);
00474 
00475     std::vector<std::string> args;
00476     inlib::words(s,",",true,args);
00477     if(args.size()!=6) return false;
00478 
00479     unsigned short c5; //first byte
00480     if(!inlib::to<unsigned short>(args[4],c5)) return false;
00481     unsigned short c6; //second byte.
00482     if(!inlib::to<unsigned short>(args[5],c6)) return false;
00483 
00484     a_host = args[0];
00485     a_host += ".";
00486     a_host += args[1];
00487     a_host += ".";
00488     a_host += args[2];
00489     a_host += ".";
00490     a_host += args[3];
00491 
00492     a_port = c5*256+c6;
00493 
00494     return true;
00495   }
00496 
00497 };
00498 
00499 }}
00500 
00501 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines