inlib
1.2.0
|
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