/* WebDownloader for X-Window * Copyright (C) 1999-2002 Koshelev Maxim * This Program is free but not GPL!!! You can't modify it * without agreement with author. You can't distribute modified * program but you can distribute unmodified program. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "ftpd.h" #include "ftp.h" #include "client.h" #include "liststr.h" #include "var.h" #include "locstr.h" #include "ntlocale.h" #include #include #include #include #include #include #include "signal.h" using namespace d4x; //****************************************/ void ftp_extract_link(char *src,char *dst) { char *delim=" -> "; if (src) { char *tmp=strstr(src,delim); if (tmp) { strcpy(dst,tmp+strlen(delim)); } else dst[0]=0; }; }; int ftp_type_from_str(char *data) { if (*data=='d') return T_DIR; if (*data=='l') return T_LINK; if (*data=='c' || *data=='b') return T_DEVICE; return T_FILE; }; int ftp_permisions_from_str(char *a) { if (a==NULL || strlen(a)<10) return(S_IRUSR|S_IWUSR); int perm=0; if (a[1]=='r') perm|=S_IRUSR; if (a[2]=='w') perm|=S_IWUSR; if (a[3]=='x' || a[3]=='s' || a[3]=='S') perm|=S_IXUSR ; if (a[4]=='r') perm|=S_IRGRP; if (a[5]=='w') perm|=S_IWGRP; if (a[6]=='x' || a[6]=='s' || a[6]=='S') perm|=S_IXGRP; if (a[7]=='r') perm|=S_IROTH; if (a[8]=='w') perm|=S_IWOTH; if (a[9]=='x') perm|=S_IXOTH; return perm; }; time_t ftp_date_from_dos(char *src){ struct tm *date=new tm; time_t NOW=time(NULL); localtime_r(&NOW,date); date->tm_sec=0; date->tm_isdst=-1; char *tmp=src; /* day-month-year min:hour[aa]*/ sscanf_int(tmp,&(date->tm_mday)); tmp+=3; sscanf_int(tmp,&(date->tm_mon)); tmp+=3; int year=date->tm_year; sscanf_int(tmp,&(year)); if (year+100<=date->tm_year) date->tm_year=year+100; else date->tm_year=year; if (date->tm_mon>12){ year=date->tm_mon; date->tm_mon=date->tm_mday; date->tm_mday=year; }; date->tm_mon-=1; /* parsing minutes and hours */ tmp=index(src,' '); if (tmp){ while (*tmp && *tmp==' ') tmp+=1; sscanf_int(tmp,&(date->tm_hour)); tmp+=3; sscanf_int(tmp,&(date->tm_min)); tmp+=2; if (*tmp=='p' || *tmp=='P') date->tm_hour+=12; }; NOW=mktime(date); delete(date); return(NOW); }; time_t ftp_date_from_str(char *src) { char *data=new char[strlen(src)+1]; char *tmp; int month,day; tmp=extract_string(src,data,5); if (is_string(data)){ tmp=extract_string(tmp,data); if (!is_string(data)){ tmp=extract_string(src,data,4); }; }; time_t NOW=time(NULL); struct tm *date=new tm; localtime_r(&NOW,date); date->tm_sec=0; date->tm_min=0; date->tm_hour=0; date->tm_isdst=-1; month=date->tm_mon; day=date->tm_mday; if (tmp && *tmp) { tmp=extract_string(tmp,data); date->tm_mon=convert_month(data); }; if (tmp && *tmp) { tmp=extract_string(tmp,data); sscanf(data,"%i",&(date->tm_mday)); }; if (tmp && *tmp) { extract_string(tmp,data); if (index(data,':')) { /* very ugly way to skip first zero */ char *tmpdata=data; sscanf_int(tmpdata,&(date->tm_hour)); tmpdata+=3; sscanf_int(tmpdata,&(date->tm_min)); if (monthtm_mon || (month==date->tm_mon && daytm_mday)) date->tm_year-=1; } else { sscanf(data,"%i",&(date->tm_year)); date->tm_year-=1900; }; }; NOW=mktime(date); delete date; delete[] data; return NOW; }; /* parsing string of LIST -la command char *src - string for parsing tFileInfo *dst - where put result int flag - need put strings (name and name of link) to result or not */ void ftp_cut_string_list(char *src,tFileInfo *dst,int flag) { if (src==NULL || dst==NULL) return; del_crlf(src); int srclen=strlen(src)+1; char *str1=new char[srclen]; char *name=new char[srclen]; *str1=0; *name=0; int par1; extract_string(src,str1,5); if (strlen(str1)){ // unix style listing extract_string(src,name,1); char *rsrc; if (is_string(name)) rsrc=src; else{ rsrc=skip_strings(src,2); extract_string(rsrc,str1,5); }; char *tmp; if (!is_string(str1)){ tmp=skip_strings(rsrc,8); sscanf(rsrc,"%s %u %s %s %lli %s %u %s %s", str1,&par1,str1,str1,&dst->size,str1,&par1,str1,name); }else{ tmp=skip_strings(rsrc,7); sscanf(rsrc,"%s %u %s %lli %s %u %s %s", str1,&par1,str1,&dst->size,str1,&par1,str1,name); }; dst->type=ftp_type_from_str(rsrc); if (dst->type!=T_DEVICE) { dst->perm=ftp_permisions_from_str(rsrc); dst->date=ftp_date_from_str(rsrc); }; if (flag) { if (tmp) dst->name.set(tmp); else dst->name.set(name); }; if (dst->type==T_LINK) { ftp_extract_link(rsrc,name); dst->body.set(name); if (flag) { tmp=strstr(dst->name.get()," -> "); if (tmp) *tmp=0; }; }else dst->body.set(NULL); }else{ // dos style listing char *new_src=extract_string(src,str1); new_src=extract_string(new_src,str1); new_src=extract_string(new_src,str1); if (new_src && *new_src){ dst->date=ftp_date_from_dos(src); if (strstr(str1,"DIR")){ dst->type=T_DIR; dst->perm=0775; }else{ sscanf(str1,"%lli",&(dst->size)); dst->type=T_FILE; dst->perm=0664; }; if (flag && new_src && *new_src) { while (*new_src==' ') new_src+=1; dst->name.set(new_src); dst->body.set(NULL); }; }; }; delete[] str1; delete[] name; }; //****************************************/ void tFtpDownload::print_error(int error_code){ switch(error_code){ case ERROR_DATA_CONNECT: LOG->log(LOG_ERROR,_("Can't establish data connection")); break; case ERROR_CWD: LOG->log(LOG_ERROR,_("Can't change directory")); break; case ERROR_TOO_MANY_USERS: LOG->log(LOG_WARNING,_("Server refused login, perhaps there are too many users of your class")); break; default: tDownloader::print_error(error_code); break; }; }; int tFtpDownload::change_dir() { int rvalue=0; if (DONT_CWD || CWDFlag) return 0; /* if (!equal(ADDR.username.get(),DEFAULT_USER)){ if ((rvalue=FTP->change_dir("/"))){ print_error(ERROR_CWD); return(rvalue); }; }; if ((rvalue=FTP->change_dir(ADDR.path.get()))){ print_error(ERROR_CWD); return(rvalue); }; */ if ((rvalue=FTP->change_dir(ADDR.path.c_str()))){ print_error(ERROR_CWD); return(rvalue); }; CWDFlag=1; return RVALUE_OK; }; //****************************************/ tFtpDownload::tFtpDownload():tDownloader(){ FTP=NULL; DIR=list=NULL; RetrNum=0; DONT_CWD=0; }; tFtpDownload::tFtpDownload(tWriterLoger *log):tDownloader(log){ FTP=NULL; DIR=list=NULL; RetrNum=0; DONT_CWD=0; }; void tFtpDownload::dont_cwd(){ DONT_CWD=1; }; int tFtpDownload::reconnect() { int success=1; Status=D_QUERYING; while (success) { if (FTP->get_status()!=STATUS_TIMEOUT && FTP->get_status()!=0) { print_error(ERROR_TOO_MANY_USERS); }; FTP->done(); if (config.number_of_attempts && RetrNum>=config.number_of_attempts) { print_error(ERROR_ATTEMPT_LIMIT); return -1; }; RetrNum++; print_error(ERROR_ATTEMPT); tDownloader::reconnect(); if (RetrNum>1) { LOG->log(LOG_OK,_("Sleeping")); d4x::USR1Off2On sig_unlocker_; sleep(config.time_for_sleep+1); }; if (FTP->reinit()==0){ success=0; if (config.proxy.ftp_user.get() && config.proxy.ftp_pass.get()){ FTP->registr(config.proxy.ftp_user.get(), config.proxy.ftp_pass.get()); success=FTP->connect(); }; if (success==0){ FTP->registr(ADDR.user,ADDR.pass); success=FTP->connect(); }; }; }; CWDFlag=0; return 0; }; void tFtpDownload::init_download(const std::string &path,const std::string &file) { ADDR.file=file; if (path[0]!='~' && path[0]!='/'){ ADDR.path="/"; ADDR.path/=path; }else ADDR.path=path; }; int tFtpDownload::init(const d4x::URL &hostinfo,tCfg *cfg,SocketPtr s) { FTP=new tFtpClient(cfg); RetrNum=0; ADDR=hostinfo; if (ADDR.user.empty()) ADDR.user=DEFAULT_USER; if (ADDR.pass.empty()) ADDR.pass=CFG.ANONYMOUS_PASS; DIR=NULL; list=NULL; config.copy_ints(cfg); if (cfg->split){ config.retry=0; config.rollback=0; }; config.copy_proxy(cfg); if (config.proxy.type==0 && config.proxy.ftp_host.get() && config.proxy.ftp_host.get()[0] && config.proxy.ftp_port) { FTP->init(config.proxy.ftp_host.get(),LOG,config.proxy.ftp_port,config.timeout); char port[MAX_LEN]; port[0]=0; if (ADDR.port!=get_port_by_proto(D_PROTO_FTP)) sprintf(port,":%i",ADDR.port); ADDR.user=ADDR.user+"@"+ADDR.host+port; } else FTP->init(ADDR.host,LOG,ADDR.port,config.timeout); FTP->set_passive(config.passive); FTP->set_retry(config.retry); FTP->set_dont_set_quit(config.dont_send_quit); if (!s){ while(reconnect()==0){ if (cfg->split==0 || FTP->force_reget()==0) return(0); }; return(-1); }; FTP->import_ctrl_socket(s); CWDFlag=0; RetrNum=1; tDownloader::reconnect(); return(0); }; tStringList *tFtpDownload::dir() { return DIR; }; int tFtpDownload::ftp_get_size_no_sdc(tStringList *l){ if (ADDR.mask){ return(FTP->get_size(NULL,l)); }; if (DONT_CWD){ TMP_FILEPATH=std::string("/")+ADDR.path; TMP_FILEPATH/=ADDR.file; return(FTP->get_size(TMP_FILEPATH.c_str(),l)); }; return(FTP->get_size(ADDR.file.c_str(),l)); }; int tFtpDownload::ftp_get_size(tStringList *l){ if (FTP->stand_data_connection()){ print_error(ERROR_DATA_CONNECT); return(-1); }; return(ftp_get_size_no_sdc(l)); }; int tFtpDownload::is_dir(){ if (FTP->change_dir(ADDR.file.c_str())==0) return(1); return(0); }; fsize_t tFtpDownload::ls_answer_short(){ tString *last=list->last(); if (!last) { LOG->log(LOG_WARNING,_("Empty answer. Trying to change directory to determine file type.")); D_FILE.perm=S_IRUSR|S_IWUSR; /* try to CWD */ if (is_dir()){ D_FILE.type=T_DIR; ADDR.path/=ADDR.file; ADDR.file.clear(); }else D_FILE.type=T_FILE; return 0; }; D_FILE.type=T_FILE; D_FILE.size=0; if (equal_first(last->body,"total")) { D_FILE.type=T_DIR; D_FILE.size=1; if (DIR) delete (DIR); DIR=list; list=NULL; D_FILE.perm=S_IRUSR|S_IWUSR|S_IXUSR; return 0; }; ftp_cut_string_list(last->body,&D_FILE,1); LOG->log_printf(LOG_OK,_("Length is %ll"),D_FILE.size); return D_FILE.size; }; fsize_t tFtpDownload::ls_answer_long(){ tString *last=list->last(); while(last){ if (strstr(last->body,ADDR.file.c_str())) break; last=(tString*)(last->next); }; if (is_dir() || ADDR.mask){ LOG->log(LOG_WARNING,_("This is a directory!")); D_FILE.size=1; if (DIR) delete DIR; DIR=list; list=NULL; D_FILE.type=T_DIR; D_FILE.perm=S_IRUSR|S_IWUSR; return 1; }; if (last==NULL){ LOG->log(LOG_WARNING,_("No such file or directory!")); return(-1); }; ftp_cut_string_list(last->body,&D_FILE,0); LOG->log_printf(LOG_OK,_("Length is %ll"),D_FILE.size); D_FILE.type=T_FILE; return(D_FILE.size); }; fsize_t tFtpDownload::get_size_only() { return(get_size()); }; fsize_t tFtpDownload::get_size() { if (!list) { list=new tStringList; list->init(0); } else list->done(); while (1) { if (!change_dir()) { int a=ftp_get_size(list); if (a==0 && list->count()<=2){ fsize_t sz=ls_answer_short(); if ((D_FILE.name.get() && ADDR.file==D_FILE.name.get()) || FTP->METHOD_TO_LIST==1) return(sz); FTP->METHOD_TO_LIST=1; list->done(); a=ftp_get_size(list); if (a==0 && list->count()<=2) return(ls_answer_short()); }; if (a==0 && list->count()>2) { return(ls_answer_long()); }; LOG->log(LOG_WARNING,_("Couldn't get size :((")); }; if (FTP->get_status()==STATUS_CMD_ERR || FTP->get_status()==STATUS_UNSPEC_ERR){ D_FILE.type=T_FILE; break; //an error occured }; if (reconnect()) break; }; return -1; }; int tFtpDownload::download_dir() { int ind=0; LOG->log(LOG_OK,_("Loading directory...")); if (!DIR) { DIR=new tStringList; DIR->init(0); while(1) { if (!change_dir()) { if (!FTP->stand_data_connection()) { if (!FTP->test_reget()) { DIR->done(); }; Status=D_DOWNLOAD; ind=ftp_get_size_no_sdc(DIR); if (ind==0) { LOG->log(LOG_OK,_("Listing was loaded")); return 0; }; LOG->log(LOG_WARNING,_("Listing not loaded completelly")); } else { print_error(ERROR_DATA_CONNECT); }; /* } else { int s=FTP->get_status(); if (s!=STATUS_TIMEOUT && (s!=STATUS_CMD_ERR || s!=STATUS_UNSPEC_ERR)) return -1; */ }; if (reconnect()) { return -1; }; }; }; return 0; }; int tFtpDownload::download(fsize_t len) { int rvalue=0; #ifdef DEBUG_ALL LOG->log_printf(LOG_OK,"tFtpDownload::download(%ll)",len); #endif //DEBUG ALL switch (D_FILE.type){ case T_DIR: { rvalue=download_dir(); break; }; case T_LINK: { LOG->log(LOG_OK,_("Link was loaded :))")); rvalue=0; break; }; default:{ if (LOADED && remote_file_changed()){ print_error(ERROR_FILE_UPDATED); if (config.retry==0) return(-1); LOADED=0; LOG->shift(0); LOG->truncate(); }; fsize_t length_to_load=len>0?LOADED+len:0; fsize_t ind=0; while(1) { if (!change_dir()) { if (!FTP->stand_data_connection()) { StartSize=rollback(); Status=D_DOWNLOAD; fsize_t to_load=len>0?length_to_load-LOADED:0; if (DONT_CWD){ TMP_FILEPATH=std::string("/")+ADDR.path; TMP_FILEPATH/=ADDR.file; ind=FTP->get_file_from(TMP_FILEPATH.c_str(),LOADED,to_load); }else ind=FTP->get_file_from(ADDR.file.c_str(),LOADED,to_load); #ifdef DEBUG_ALL LOG->log_printf(LOG_OK,"return to tFtpDownload::download with ind=%ll",ind); #endif //DEBUG ALL if (!FTP->test_reget()){ if (config.retry==0) break; StartSize=LOADED=0; }; if (ind>0) { LOADED+=ind; // LOG->log_printf(LOG_OK,_("%ll bytes loaded."),ind); LOG->log_printf(LOG_OK,"%ll bytes loaded.(LOADED=%ll,len=%ll,FTP->get_status()=%i)", ind,LOADED,len,FTP->get_status()); }; if (!FTP->get_status()) { rvalue=0; break; }; } else { print_error(ERROR_DATA_CONNECT); }; /* } else { int s=FTP->get_status(); if (s!=STATUS_TIMEOUT && (s!=STATUS_CMD_ERR || s!=STATUS_UNSPEC_ERR)){ rvalue=-1; break; }; */ }; if (reconnect()) { rvalue=-1; break; }; }; }; }; #ifdef DEBUG_ALL LOG->log_printf(LOG_OK,"exit from tFtpDownload::download with %i",rvalue); #endif //DEBUG ALL return rvalue; }; fsize_t tFtpDownload::get_readed() { if (D_FILE.type==T_FILE) return (FTP->get_readed()); if (DIR) return DIR->size(); if (list) return list->size(); return 0; }; fsize_t tFtpDownload::another_way_get_size() { return FTP->another_way_get_size(); }; int tFtpDownload::get_child_status() { return(FTP->get_status()); }; fsize_t tFtpDownload::get_start_size() { if (FTP && !FTP->test_reget()) return 0; return(StartSize); }; char *tFtpDownload::get_new_url() { return(copy_string(D_FILE.body.get())); }; int tFtpDownload::reget() { if (FTP) return FTP->test_reget(); return(1); }; void tFtpDownload::done() { if (FTP) FTP->done(); }; SocketPtr tFtpDownload::export_ctrl_socket(){ SocketPtr rval; if (FTP) return(FTP->export_ctrl_socket()); return rval; }; tFtpDownload::~tFtpDownload() { if (FTP) delete(FTP); if (DIR) delete(DIR); if (list) delete(list); };