1 /*
2  * lftp - file transfer program
3  *
4  * Copyright (c) 1996-2020 by Alexander V. Lukyanov (lav@yars.free.net)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <ctype.h>
30 
31 #include "ftpclass.h"
32 #include "xstring.h"
33 #include "url.h"
34 #include "FtpListInfo.h"
35 #include "FileGlob.h"
36 #include "FtpDirList.h"
37 #include "log.h"
38 #include "FileCopyFtp.h"
39 #include "LsCache.h"
40 #include "buffer_ssl.h"
41 #include "buffer_zlib.h"
42 
43 #include "ascii_ctype.h"
44 #include "misc.h"
45 
46 #define TELNET_IAC	'\377'	 //255	/* interpret as command: */
47 #define TELNET_IP	'\364'	 //244	/* interrupt process--permanently */
48 #define TELNET_DM	'\362'	 //242	/* for telfunc calls */
49 #define TELNET_WILL	'\373'	 //251
50 #define TELNET_WONT	'\374'	 //252
51 #define TELNET_DO	'\375'	 //253
52 #define TELNET_DONT	'\376'	 //254
53 
54 #include <errno.h>
55 #include <time.h>
56 
57 #ifdef TM_IN_SYS_TIME
58 # include <sys/time.h>
59 #endif
60 
61 #ifdef HAVE_FCNTL_H
62 # include <fcntl.h>
63 #endif
64 
65 CDECL_BEGIN
66 #include "regex.h"
67 CDECL_END
68 
69 #if USE_SSL
70 # include "lftp_ssl.h"
71 #else
72 # define control_ssl 0
73 const bool Ftp::ftps=false;
74 #endif
75 
76 #define FTP_DEFAULT_PORT "21"
77 #define FTPS_DEFAULT_PORT "990"
78 #define FTP_DATA_PORT 20
79 #define FTPS_DATA_PORT 989
80 #define HTTP_DEFAULT_PROXY_PORT "3128"
81 
82 #define super NetAccess
83 
84 #define is5XX(code) ((code)>=500 && (code)<600)
85 #define is4XX(code) ((code)>=400 && (code)<500)
86 #define is3XX(code) ((code)>=300 && (code)<400)
87 #define is2XX(code) ((code)>=200 && (code)<300)
88 #define is1XX(code) ((code)>=100 && (code)<200)
89 #define cmd_unsupported(code) ((code)==500 || (code)==502)
90 #define site_cmd_unsupported(code) (cmd_unsupported((code)) || (code)==501)
91 
92 #ifndef EINPROGRESS
93 #define EINPROGRESS -1
94 #endif
95 
New()96 FileAccess *Ftp::New() { return new Ftp(); }
97 
ClassInit()98 void  Ftp::ClassInit()
99 {
100    // register the class
101    Register("ftp",Ftp::New);
102    FileCopy::fxp_create=FileCopyFtp::New;
103 
104 #if USE_SSL
105    Register("ftps",FtpS::New);
106 #endif
107 }
108 
109 
110 #if INET6
111 
112 struct eprt_proto_match
113 {
114    int proto;
115    int eprt_proto;
116 };
117 static const eprt_proto_match eprt_proto[]=
118 {
119    { AF_INET,  1 },
120    { AF_INET6, 2 },
121    { -1, -1 }
122 };
123 
encode_eprt(const sockaddr_u * a)124 const char *Ftp::encode_eprt(const sockaddr_u *a)
125 {
126    int proto;
127    if(a->sa.sa_family==AF_INET)
128       proto=1;
129    else if(a->sa.sa_family==AF_INET6)
130       proto=2;
131    else
132       return 0;
133    return xstring::format("|%d|%s|%d|",proto,a->address(),a->port());
134 }
135 #endif
136 
data_address_ok(const sockaddr_u * dp,bool verify_address,bool verify_port)137 bool Ftp::Connection::data_address_ok(const sockaddr_u *dp,bool verify_address,bool verify_port)
138 {
139    sockaddr_u d;
140    sockaddr_u c;
141    socklen_t len;
142    len=sizeof(d);
143    if(dp)
144       d=*dp;
145    else if(getpeername(data_sock,&d.sa,&len)==-1)
146    {
147       LogError(0,"getpeername(data_sock): %s\n",strerror(errno));
148       return !verify_address && !verify_port;
149    }
150    len=sizeof(c);
151    if(getpeername(control_sock,&c.sa,&len)==-1)
152    {
153       LogError(0,"getpeername(control_sock): %s\n",strerror(errno));
154       return !verify_address;
155    }
156 
157 #if INET6
158    if(d.sa.sa_family==AF_INET && c.sa.sa_family==AF_INET6
159       && IN6_IS_ADDR_V4MAPPED(&c.in6.sin6_addr))
160    {
161       if(memcmp(&d.in.sin_addr,&c.in6.sin6_addr.s6_addr[12],4))
162 	 goto address_mismatch;
163       if(d.in.sin_port!=htons(FTP_DATA_PORT)
164       && d.in.sin_port!=htons(FTPS_DATA_PORT))
165 	 goto wrong_port;
166    }
167 #endif
168 
169    if(d.sa.sa_family==AF_INET)
170    {
171       if(memcmp(&d.in.sin_addr,&c.in.sin_addr,sizeof(d.in.sin_addr)))
172 	 goto address_mismatch;
173       if(d.in.sin_port!=htons(FTP_DATA_PORT)
174       && d.in.sin_port!=htons(FTPS_DATA_PORT))
175 	 goto wrong_port;
176       return true;
177    }
178 #if INET6
179 # ifndef  IN6_ARE_ADDR_EQUAL
180 #  define IN6_ARE_ADDR_EQUAL(a,b) (!memcmp((a),(b),16))
181 # endif
182    if(d.sa.sa_family==AF_INET6)
183    {
184       if(!IN6_ARE_ADDR_EQUAL(&d.in6.sin6_addr,&c.in6.sin6_addr))
185 	 goto address_mismatch;
186       if(d.in6.sin6_port!=htons(FTP_DATA_PORT)
187       && d.in6.sin6_port!=htons(FTPS_DATA_PORT))
188          goto wrong_port;
189       return true;
190    }
191 #endif
192    return true;
193 
194 wrong_port:
195    if(!verify_port)
196       return true;
197    LogError(0,_("Data connection peer has wrong port number"));
198    return false;
199 
200 address_mismatch:
201    if(!verify_address)
202       return true;
203    LogError(0,_("Data connection peer has mismatching address"));
204    return false;
205 }
206 
207 /* Procedures for checking for a special answers */
208 /* General rule: check for valid replies first, errors second. */
209 
RestCheck(int act)210 void Ftp::RestCheck(int act)
211 {
212    if(is2XX(act) || is3XX(act))
213    {
214       real_pos=conn->rest_pos;  // REST successful
215       conn->last_rest=conn->rest_pos;
216       return;
217    }
218    real_pos=0;
219    if(pos==0)
220       return;
221    if(is5XX(act))
222    {
223       if(cmd_unsupported(act))
224 	 conn->rest_supported=false;
225       LogNote(2,_("Switching to NOREST mode"));
226       flags|=NOREST_MODE;
227       if(mode==STORE)
228 	 pos=0;
229       if(copy_mode!=COPY_NONE)
230 	 copy_failed=true;
231       return;
232    }
233    Disconnect(line);
234 }
235 
NoFileCheck(int act)236 void Ftp::NoFileCheck(int act)
237 {
238    if(is2XX(act))
239       return;
240    if(cmd_unsupported(act))
241    {
242       SetError(FATAL,all_lines);
243       return;
244    }
245    if(real_pos>0 && !GetFlag(IO_FLAG) && copy_mode==COPY_NONE
246    && ((is4XX(act) && strstr(line,"Append/Restart not permitted"))
247     || (is5XX(act) && !Transient5XX(act))))
248    {
249       DataClose();
250       LogNote(2,_("Switching to NOREST mode"));
251       flags|=NOREST_MODE;
252       real_pos=0;
253       if(mode==STORE)
254 	 pos=0;
255       state=EOF_STATE; // retry
256       return;
257    }
258    if(is5XX(act) && !Transient5XX(act))
259    {
260       SetError(NO_FILE,all_lines);
261       return;
262    }
263    if(copy_mode!=COPY_NONE)
264    {
265       copy_failed=true;
266       return;
267    }
268    DataClose();
269    state=EOF_STATE;
270    eof=false;
271    if(mode==STORE && GetFlag(IO_FLAG))
272       SetError(STORE_FAILED,0);
273    else if(NextTry())
274       retry_timer.Set(2); // retry after 2 seconds
275 }
276 
277 /* 5xx that aren't errors at all */
NonError5XX(int act) const278 bool Ftp::NonError5XX(int act) const
279 {
280    return (mode==LIST && act==550 && (!file || !file[0]))
281        // ...and proftpd workaround.
282        || (mode==LIST && act==450 && strstr(line,"No files found"));
283 }
284 
ServerSaid(const char * s) const285 bool Ftp::ServerSaid(const char *s) const
286 {
287    return strstr(line,s) && (!file || !strstr(file,s));
288 }
289 
290 /* 5xx that are really transient like 4xx */
Transient5XX(int act) const291 bool Ftp::Transient5XX(int act) const
292 {
293    if(!is5XX(act))
294       return false;
295 
296    if(act==530 && expect->FirstIs(Expect::PASS) && Retry530())
297       return true;
298 
299    // retry on these errors (ftp server ought to send 4xx code instead)
300    if(ServerSaid("Broken pipe") || ServerSaid("Too many")
301    || ServerSaid("timed out") || ServerSaid("closed by the remote host"))
302       return true;
303 
304    // if there were some data received, assume it is temporary error.
305    if(mode!=STORE && GetFlag(IO_FLAG))
306       return true;
307 
308    return false;
309 }
310 
311 #if USE_SSL
get_protect_res()312 const char *Ftp::get_protect_res()
313 {
314    if(mode==LIST || mode==MP_LIST || (mode==LONG_LIST && !use_stat_for_list))
315       return "ftp:ssl-protect-list";
316    else if(mode==RETRIEVE || mode==STORE)
317       return "ftp:ssl-protect-data";
318    return 0;
319 }
320 #endif
321 
322 // 226 Transfer complete.
TransferCheck(int act)323 void Ftp::TransferCheck(int act)
324 {
325    if(act==225 || act==226) // data connection is still open or ABOR worked.
326    {
327       copy_done=true;
328       conn->CloseAbortedDataConnection();
329 
330       if(!conn->received_150 && state!=DATA_OPEN_STATE)
331 	 goto simulate_eof;
332    }
333    if(act==211)
334    {
335       // permature STAT?
336       conn->stat_timer.ResetDelayed(3);
337       return;
338    }
339    if(act==213)	  // this must be a STAT reply.
340    {
341       conn->stat_timer.Reset();
342 
343       long long p;
344       // first try Serv-U format:
345       //    Status for user UUU from X.X.X.X
346       //    Stored 1 files, 0 Kbytes
347       //    Retrieved 0 files, 0 Kbytes
348       //    Receiving file XXX (YYY bytes)
349       const char *r=strstr(all_lines,"Receiving file");
350       if(r)
351       {
352 	 r=strrchr(r,'(');
353 	 char c=0;
354 	 if(r && sscanf(r,"(%lld bytes%c",&p,&c)==2 && c==')')
355 	    goto found_offset;
356       }
357       // wu-ftpd format:
358       //    Status: XXX of YYY bytes transferred
359       // or
360       //    Status: XXX bytes transferred
361       //
362       // find the first number.
363       for(const char *b=line+4; ; b++)
364       {
365 	 if(*b==0)
366 	    return;
367 	 if(!is_ascii_digit(*b))
368 	    continue;
369 	 if(sscanf(b,"%lld",&p)==1)
370 	    break;
371       }
372    found_offset:
373       if(copy_mode==COPY_DEST)
374 	 real_pos=pos=p;
375       return;
376    }
377    if(copy_mode!=COPY_NONE && is4XX(act))
378    {
379       copy_passive=!copy_passive;
380       copy_failed=true;
381       return;
382    }
383    if(NonError5XX(act))
384       goto simulate_eof;
385    if(act==426 && copy_mode==COPY_NONE)
386    {
387       if(conn->data_sock==-1 && strstr(line,"Broken pipe"))
388 	 return;
389    }
390    if(act==426 && mode==STORE)
391    {
392       DataClose();
393       state=EOF_STATE;
394       SetError(FATAL,all_lines);
395    }
396    if(is2XX(act) && conn->data_sock==-1)
397       eof=true;
398 #if USE_SSL
399    if(conn->auth_supported && act==522 && conn->prot=='C') {
400       const char *res=get_protect_res();
401       if(res) {
402 	 // try again with PROT P
403 	 DataClose();
404 	 ResMgr::Set(res,hostname,"yes");
405 	 state=EOF_STATE;
406 	 return;
407       }
408    }
409 #endif
410    NoFileCheck(act);
411    return;
412 
413 simulate_eof:
414    DataClose();
415    state=EOF_STATE;
416    eof=true;
417    return;
418 }
419 
Retry530() const420 bool Ftp::Retry530() const
421 {
422    const char *rexp=Query("retry-530",hostname);
423    if(re_match(all_lines,rexp,REG_ICASE))
424    {
425       LogNote(9,_("Server reply matched ftp:retry-530, retrying"));
426       return true;
427    }
428    if(!user)
429    {
430       rexp=Query("retry-530-anonymous",hostname);
431       if(re_match(all_lines,rexp,REG_ICASE))
432       {
433 	 LogNote(9,_("Server reply matched ftp:retry-530-anonymous, retrying"));
434 	 return true;
435       }
436    }
437    return false;
438 }
439 
LoginCheck(int act)440 void Ftp::LoginCheck(int act)
441 {
442    if(conn->ignore_pass)
443       return;
444    if(act==530 && Retry530()) // overloaded server?
445       goto retry;
446    if(is5XX(act))
447    {
448       SetError(LOGIN_FAILED,all_lines);
449       return;
450    }
451 
452    if(!is2XX(act) && !is3XX(act))
453    {
454    retry:
455       Disconnect(line);
456       NextPeer();
457       if(peer_curr==0)
458 	 reconnect_timer.Reset(); // count the reconnect-interval from this moment
459       last_connection_failed=true;
460    }
461    if(is3XX(act) && !expect->Has(Expect::ACCT_PROXY))
462    {
463       if(!QueryStringWithUserAtHost("acct"))
464       {
465 	 Disconnect(line);
466 	 SetError(LOGIN_FAILED,_("Account is required, set ftp:acct variable"));
467       }
468    }
469 }
470 
NoPassReqCheck(int act)471 void Ftp::NoPassReqCheck(int act) // for USER command
472 {
473    if(is2XX(act)) // in some cases, ftpd does not ask for pass.
474    {
475       conn->ignore_pass=true;
476       return;
477    }
478    if(act==331 && allow_skey && user && pass)
479    {
480       skey_pass.set(make_skey_reply());
481       if(force_skey && skey_pass==0)
482       {
483 	 SetError(LOGIN_FAILED,_("ftp:skey-force is set and server does not support OPIE nor S/KEY"));
484 	 return;
485       }
486    }
487    if(act==331 && allow_netkey && user && pass)
488    {
489       netkey_pass.set(make_netkey_reply());
490    }
491 
492    if(is3XX(act))
493       return;
494    if(act==530 && Retry530()) // overloaded server?
495       goto retry;
496    if(is5XX(act))
497    {
498       // proxy interprets USER as user@host, so we check for host name validity
499       if(proxy && (strstr(line,"host") || strstr(line,"resolve")))
500       {
501 	 LogNote(9,_("assuming failed host name lookup"));
502 	 SetError(LOOKUP_ERROR,all_lines);
503 	 return;
504       }
505       SetError(LOGIN_FAILED,all_lines);
506       return;
507    }
508 retry:
509    Disconnect(line);
510    reconnect_timer.Reset();	// count the reconnect-interval from this moment
511    last_connection_failed=true;
512 }
513 
514 // login to proxy.
proxy_LoginCheck(int act)515 void Ftp::proxy_LoginCheck(int act)
516 {
517    if(is2XX(act))
518       return;
519    if(is5XX(act))
520    {
521       SetError(LOGIN_FAILED,all_lines);
522       return;
523    }
524    Disconnect(line);
525    reconnect_timer.Reset();	// count the reconnect-interval from this moment
526 }
527 
proxy_NoPassReqCheck(int act)528 void Ftp::proxy_NoPassReqCheck(int act)
529 {
530    if(is3XX(act) || is2XX(act))
531       return;
532    if(is5XX(act))
533    {
534       SetError(LOGIN_FAILED,all_lines);
535       return;
536    }
537    Disconnect(line);
538    reconnect_timer.Reset();	// count the reconnect-interval from this moment
539 }
540 
normalize_path_vms(char * path)541 static void normalize_path_vms(char *path)
542 {
543    for(char *s=path; *s; s++)
544       *s=to_ascii_lower(*s);
545    char *colon=strchr(path,':');
546    if(colon)
547    {
548       memmove(path+1,path,strlen(path)+1);
549       path[0]='/';
550       path=colon+1;
551       if(path[1]=='[')
552 	 memmove(path,path+1,strlen(path));
553    }
554    else
555    {
556       path=strchr(path,'[');
557       if(!*path)
558 	 return;
559    }
560    *path++='/';
561    while(*path && *path!=']')
562    {
563       if(*path=='.')
564 	 *path='/';
565       path++;
566    }
567    if(!*path)
568       return;
569    if(path[1])
570       *path='/';
571    else
572       *path=0;
573 }
574 
ExtractPWD()575 char *Ftp::ExtractPWD()
576 {
577    char *pwd=string_alloca(line.length()+1);
578 
579    const char *scan=strchr(line,'"');
580    if(scan==0)
581       return 0;
582    scan++;
583    const char *right_quote=strrchr(scan,'"');
584    if(!right_quote)
585       return 0;
586 
587    char *store=pwd;
588    while(scan<right_quote)
589    {
590       // This is the method of quote encoding.
591       if(*scan=='"' && scan[1]=='"')
592 	 scan++;
593       *store++=*scan++;
594    }
595 
596    if(store==pwd)
597       return 0;	  // empty home not allowed.
598    *store=0;
599 
600    int dev_len=device_prefix_len(pwd);
601    if(pwd[dev_len]=='[')
602    {
603       conn->vms_path=true;
604       normalize_path_vms(pwd);
605    }
606    else if(dev_len==2 || dev_len==3)
607    {
608       conn->dos_path=true;
609    }
610 
611    if(!strchr(pwd,'/') || conn->dos_path)
612    {
613       // for safety -- against dosish ftpd
614       for(char *s=pwd; *s; s++)
615 	 if(*s=='\\')
616 	    *s='/';
617    }
618    return xstrdup(pwd);
619 }
620 
SendCWD(const char * path,const char * path_url,Expect::expect_t c)621 int Ftp::SendCWD(const char *path,const char *path_url,Expect::expect_t c)
622 {
623    int cwd_count=0;
624    if(QueryTriBool("ftp:use-tvfs",0,conn->tvfs_supported)) {
625       conn->SendCmd2("CWD",path);
626       expect->Push(new Expect(Expect::CWD_CURR,path));
627       cwd_count++;
628    } else if(path_url) {
629       path_url=url::path_ptr(path_url);
630       if(path_url[0]=='/')
631 	 path_url++;
632       if(path_url[0]=='~') {
633 	 if(path_url[1]==0)
634 	    path_url++;
635 	 else if(path_url[1]=='/')
636 	    path_url+=2;
637       }
638       LogNote(9,"using URL path `%s'",path_url);
639       char *path_url1=alloca_strdup(path_url); // to split it
640       xstring path2("~");
641       for(char *dir_url=strtok(path_url1,"/"); dir_url; dir_url=strtok(NULL,"/")) {
642 	 const char *dir=url::decode(dir_url);
643 	 if(dir[0]=='/')
644 	    path2.truncate();
645 	 if(path2.length()>0 && path2.last_char()!='/')
646 	    path2.append('/');
647 	 path2.append(dir);
648 	 conn->SendCmd2("CWD",dir);
649 	 expect->Push(new Expect(Expect::CWD_CURR,path2));
650 	 cwd_count++;
651       }
652    } else {
653       char *path1=alloca_strdup(path); // to split it
654       char *path2=alloca_strdup(path); // to re-assemble
655       if(AbsolutePath(path)) {
656 	 if(real_cwd && !strncmp(real_cwd,path,real_cwd.length())
657 	 && path[real_cwd.length()]=='/') {
658 	    path1+=real_cwd.length()+1;
659 	    path2[real_cwd.length()]=0;
660 	 } else {
661 	    int dev_len=device_prefix_len(path);
662 	    dev_len+=(path2[dev_len]=='/');
663 	    if(dev_len==1 && path[0]=='/' && real_cwd.ne("/")) {
664 	       // just a root directory, append first path component
665 	       const char *slash=strchr(path+1,'/');
666 	       if(slash)
667 		  dev_len=slash-path;
668 	       else
669 		  dev_len=strlen(path);
670 	    }
671 	    path2[dev_len]=0;
672 	    path1+=dev_len;
673 	    if(!path2[0]) {
674 	       if(real_cwd && strcmp(real_cwd,"~")
675 	       && (!home.path || strcmp(real_cwd,home.path))) {
676 		  conn->SendCmd("CWD");
677 		  expect->Push(new Expect(Expect::CWD_CURR,"~"));
678 		  cwd_count++;
679 	       }
680 	    } else if(!real_cwd || strcmp(real_cwd,path2)) {
681 	       conn->SendCmd2("CWD",path2);
682 	       expect->Push(new Expect(Expect::CWD_CURR,path2));
683 	       cwd_count++;
684 	    }
685 	 }
686       } else {
687 	 strcpy(path2,"~");
688 	 if(path1[0]=='~') {
689 	    if(path1[1]==0)
690 	       path1++;
691 	    else if(path1[1]=='/')
692 	       path1+=2;
693 	 }
694 	 if(real_cwd && strcmp(real_cwd,"~")
695 	 && (!home.path || strcmp(real_cwd,home.path))) {
696 	    conn->SendCmd("CWD");
697 	    expect->Push(new Expect(Expect::CWD_CURR,"~"));
698 	    cwd_count++;
699 	 }
700       }
701       int path2_len=strlen(path2);
702       for(char *dir=strtok(path1,"/"); dir; dir=strtok(NULL,"/")) {
703 	 if(path2_len>0 && path2[path2_len-1]!='/') {
704 	    strcpy(path2+path2_len,"/");
705 	    path2_len++;
706 	 }
707 	 strcpy(path2+path2_len,dir);
708 	 path2_len+=strlen(dir);
709 	 conn->SendCmd2("CWD",dir);
710 	 expect->Push(new Expect(Expect::CWD_CURR,path2));
711 	 cwd_count++;
712       }
713    }
714    Expect *last_cwd=expect->FindLastCWD();
715    if(last_cwd)
716    {
717       LogNote(9,"CWD path to be sent is `%s'",last_cwd->arg.get());
718       last_cwd->check_case=c;
719    }
720    return cwd_count;
721 }
722 
Handle_PASV()723 Ftp::pasv_state_t Ftp::Handle_PASV()
724 {
725    unsigned a0,a1,a2,a3,p0,p1;
726    /*
727     * Extract address. RFC1123 says:
728     * "...must scan the reply for the first digit..."
729     */
730    for(const char *b=line+4; ; b++)
731    {
732       if(*b==0)
733       {
734 	 Disconnect(line);
735 	 return PASV_NO_ADDRESS_YET;
736       }
737       if(!is_ascii_digit(*b))
738 	 continue;
739       if(sscanf(b,"%u,%u,%u,%u,%u,%u",&a0,&a1,&a2,&a3,&p0,&p1)==6)
740          break;
741    }
742    unsigned char *a,*p;
743    conn->data_sa.sa.sa_family=conn->peer_sa.sa.sa_family;
744    if(conn->data_sa.sa.sa_family==AF_INET)
745    {
746       a=(unsigned char*)&conn->data_sa.in.sin_addr;
747       p=(unsigned char*)&conn->data_sa.in.sin_port;
748    }
749 #if INET6
750    else if(conn->data_sa.sa.sa_family==AF_INET6)
751    {
752       a=((unsigned char*)&conn->data_sa.in6.sin6_addr)+12;
753       a[-1]=a[-2]=0xff; // V4MAPPED
754       p=(unsigned char*)&conn->data_sa.in6.sin6_port;
755    }
756 #endif
757    else
758    {
759       Disconnect("unsupported address family");
760       return PASV_NO_ADDRESS_YET;
761    }
762 
763    a[0]=a0; a[1]=a1; a[2]=a2; a[3]=a3;
764    p[0]=p0; p[1]=p1;
765 
766    bool ignore_pasv_address = QueryBool("ignore-pasv-address",hostname);
767    if(ignore_pasv_address)
768       LogNote(2,"Address returned by PASV is ignored according to ftp:ignore-pasv-address setting");
769    else if(conn->data_sa.is_reserved() || conn->data_sa.is_multicast()
770 	   || (QueryBool("fix-pasv-address",hostname) && !conn->proxy_is_http
771 	       && (conn->data_sa.is_private() != conn->peer_sa.is_private()
772 		   || conn->data_sa.is_loopback() != conn->peer_sa.is_loopback())))
773    {
774       // broken server, try to fix up
775       ignore_pasv_address=true;
776       conn->fixed_pasv=true;
777       LogNote(2,"Address returned by PASV seemed to be incorrect and has been fixed");
778    }
779 
780    if(ignore_pasv_address)
781    {
782       if(conn->data_sa.sa.sa_family==AF_INET)
783 	 memcpy(a,&conn->peer_sa.in.sin_addr,sizeof(conn->peer_sa.in.sin_addr));
784 #if INET6
785       else if(conn->data_sa.in.sin_family==AF_INET6) // peer_sa should be V4MAPPED
786 	 memcpy(a,&conn->peer_sa.in6.sin6_addr.s6_addr[12],4);
787 #endif
788    }
789 
790    return PASV_HAVE_ADDRESS;
791 }
792 
Handle_EPSV()793 Ftp::pasv_state_t Ftp::Handle_EPSV()
794 {
795    char delim;
796    char *format=alloca_strdup("|||%u|");
797    unsigned port;
798 
799    const char *c=strchr(line,'(');
800    c=c?c+1:line+4;
801    delim=*c;
802 
803    for(char *p=format; *p; p++)
804       if(*p=='|')
805 	 *p=delim;
806 
807    if(sscanf(c,format,&port)!=1)
808    {
809       LogError(0,_("cannot parse EPSV response"));
810       Disconnect(_("cannot parse EPSV response"));
811       return PASV_NO_ADDRESS_YET;
812    }
813 
814    conn->data_sa=conn->peer_sa;
815    if(conn->data_sa.sa.sa_family==AF_INET)
816       conn->data_sa.in.sin_port=htons(port);
817 #if INET6
818    else if(conn->data_sa.sa.sa_family==AF_INET6)
819       conn->data_sa.in6.sin6_port=htons(port);
820 #endif
821    else
822    {
823       Disconnect("unsupported address family");
824       return PASV_NO_ADDRESS_YET;
825    }
826    return PASV_HAVE_ADDRESS;
827 }
828 
Handle_EPSV_CEPR()829 Ftp::pasv_state_t Ftp::Handle_EPSV_CEPR()
830 {
831    unsigned port, proto;
832    char pasv_addr[40];
833 
834    const char *c_start=strchr(line,'(');
835    if(sscanf(c_start, "(|%u|%39[^'|']|%u|)", &proto, pasv_addr, &port)!=3)
836    {
837       LogError(0,_("cannot parse custom EPSV response"));
838       Disconnect(_("cannot parse custom EPSV response"));
839       return PASV_NO_ADDRESS_YET;
840    }
841 
842    conn->data_sa=conn->peer_sa;
843    // V4 / AF_INET
844    if (proto == 1)
845    {
846       inet_pton(AF_INET, pasv_addr, &conn->data_sa.in.sin_addr);
847       // conn->data_sa.
848       conn->data_sa.in.sin_port=htons(port);
849       conn->data_sa.sa.sa_family=AF_INET;
850    }
851 #if INET6
852    // V6 / AF_INET6
853    else if (proto == 2)
854    {
855       inet_pton(AF_INET6, pasv_addr, &conn->data_sa.in6.sin6_addr);
856       conn->data_sa.in6.sin6_port=htons(port);
857       conn->data_sa.sa.sa_family=AF_INET6;
858    }
859 #endif
860    else
861    {
862       Disconnect("unsupported address family");
863       return PASV_NO_ADDRESS_YET;
864    }
865 
866    return PASV_HAVE_ADDRESS;
867 }
868 
CatchDATE(int act)869 void Ftp::CatchDATE(int act)
870 {
871    if(!fileset_for_info)
872       return;
873 
874    FileInfo *fi=fileset_for_info->curr();
875    if(!fi)
876       return;
877 
878    if(is2XX(act))
879    {
880       if(line.length()>4 && is_ascii_digit(line[4]))
881 	 fi->SetDate(ConvertFtpDate(line+4),0);
882    }
883    else	if(is5XX(act))
884    {
885       if(cmd_unsupported(act))
886 	 conn->mdtm_supported=false;
887    }
888    else
889    {
890       Disconnect(line);
891       return;
892    }
893 
894    fi->NoNeed(fi->DATE);
895    if(!(fi->need&fi->SIZE))
896       fileset_for_info->next();
897 
898    TrySuccess();
899 }
CatchDATE_opt(int act)900 void Ftp::CatchDATE_opt(int act)
901 {
902    if(!opt_date)
903       return;
904 
905    if(is2XX(act) && line.length()>4 && is_ascii_digit(line[4]))
906    {
907       *opt_date=ConvertFtpDate(line+4);
908       opt_date=0;
909    }
910    else
911    {
912       if(cmd_unsupported(act))
913 	 conn->mdtm_supported=false;
914       *opt_date=NO_DATE;
915    }
916 }
917 
CatchSIZE(int act)918 void Ftp::CatchSIZE(int act)
919 {
920    if(!fileset_for_info)
921       return;
922 
923    FileInfo *fi=fileset_for_info->curr();
924    if(!fi)
925       return;
926 
927    long long size=NO_SIZE;
928 
929    if(is2XX(act))
930    {
931       if(line.length()>4) {
932 	 if(sscanf(line+4,"%lld",&size)!=1)
933 	    size=NO_SIZE;
934       }
935    }
936    else	if(is5XX(act))
937    {
938       if(cmd_unsupported(act))
939 	 conn->size_supported=false;
940    }
941    else
942    {
943       Disconnect(line);
944       return;
945    }
946 
947    if(size>=1)
948       fi->SetSize(size);
949    fi->NoNeed(fi->SIZE);
950    if(!(fi->need&fi->DATE))
951       fileset_for_info->next();
952 
953    TrySuccess();
954 }
CatchSIZE_opt(int act)955 void Ftp::CatchSIZE_opt(int act)
956 {
957    long long size=NO_SIZE;
958 
959    if(is2XX(act))
960    {
961       if(line.length()>4) {
962 	 if(sscanf(line+4,"%lld",&size)!=1)
963 	    size=NO_SIZE;
964       }
965    }
966    else
967    {
968       if(cmd_unsupported(act))
969 	 conn->size_supported=false;
970    }
971 
972    // SIZE 0 is ignored (for some buggy servers).
973    if(size<1)
974       return;
975 
976    if(mode==RETRIEVE)
977       entity_size=size;
978 
979    if(opt_size)
980    {
981       *opt_size=size;
982       opt_size=0;
983    }
984 }
985 
Connection(const char * c)986 Ftp::Connection::Connection(const char *c)
987    : closure(c), send_cmd_buffer(DirectedBuffer::PUT)
988 {
989    control_sock=-1;
990    telnet_layer_send=0;
991    data_sock=-1;
992    aborted_data_sock=-1;
993 #if USE_SSL
994    prot='C';  // current protection scheme 'C'lear or 'P'rivate
995    auth_sent=false;
996    auth_supported=true;
997    cpsv_supported=false;
998    sscn_supported=true;
999    sscn_on=false;
1000 #endif
1001    type='A';
1002    t_mode='S';
1003    last_rest=0;
1004    rest_pos=0;
1005 
1006    quit_sent=false;
1007    fixed_pasv=false;
1008    translation_activated=false;
1009    sync_wait=1;	// expect server greetings
1010    multiline_code=0;
1011    ignore_pass=false;
1012    try_feat_after_login=false;
1013    tune_after_login=false;
1014    utf8_activated=false;
1015 
1016    dos_path=false;
1017    vms_path=false;
1018    have_feat_info=false;
1019    mdtm_supported=true;
1020    size_supported=true;
1021    rest_supported=true;
1022    site_chmod_supported=true;
1023    site_utime_supported=true;
1024    site_utime2_supported=true;
1025    site_symlink_supported=true;
1026    site_mkdir_supported=false;
1027    pret_supported=false;
1028    utf8_supported=false;
1029    lang_supported=false;
1030    mlst_supported=false;
1031    clnt_supported=false;
1032    host_supported=false;
1033    mfmt_supported=false;
1034    mff_supported=false;
1035    epsv_supported=false;
1036    tvfs_supported=false;
1037    mode_z_supported=false;
1038    cepr_supported=false;
1039 
1040    proxy_is_http=false;
1041    may_show_password=false;
1042    can_do_pasv=true;
1043 
1044    ssl_after_proxy=false; // Are we in the SSL stage, after using a proxy?
1045 
1046    nop_time=0;
1047    nop_count=0;
1048    nop_offset=0;
1049 
1050    abor_close_timer.SetResource("ftp:abor-max-wait",closure);
1051    stat_timer.SetResource("ftp:stat-interval",closure);
1052    waiting_150_timer.SetResource("ftp:waiting-150-timeout",closure);
1053 #if USE_SSL
1054    waiting_ssl_shutdown.SetResource("ftp:ssl-shutdown-timeout",closure);
1055 #endif
1056 }
1057 
InitFtp()1058 void Ftp::InitFtp()
1059 {
1060 #if USE_SSL
1061    ftps=false;	  // ssl and prot='P' by default (port 990)
1062 #endif
1063 
1064    eof=false;
1065    state=INITIAL_STATE;
1066    flags=SYNC_MODE;
1067    allow_skey=true;
1068    allow_netkey=true;
1069    force_skey=false;
1070    verify_data_address=true;
1071    use_stat=true;
1072    use_stat_for_list=true;
1073    use_mdtm=true;
1074    use_size=true;
1075    use_telnet_iac=true;
1076    use_mlsd=false;
1077 
1078    max_buf=0x10000;
1079 
1080    copy_mode=COPY_NONE;
1081    copy_addr_valid=false;
1082    copy_passive=false;
1083    copy_protect=false;
1084    copy_ssl_connect=false;
1085    copy_done=false;
1086    copy_connection_open=false;
1087    copy_allow_store=false;
1088    copy_failed=false;
1089 
1090    disconnect_on_close=false;
1091    last_connection_failed=false;
1092 
1093    Reconfig();
1094 }
Ftp()1095 Ftp::Ftp() : super()
1096 {
1097    InitFtp();
1098 }
Ftp(const Ftp * f)1099 Ftp::Ftp(const Ftp *f) : super(f)
1100 {
1101    InitFtp();
1102 
1103    state=INITIAL_STATE;
1104    flags=f->flags&MODES_MASK;
1105 
1106    Reconfig();
1107 }
1108 
~Connection()1109 Ftp::Connection::~Connection()
1110 {
1111    CloseAbortedDataConnection();
1112    CloseDataConnection();
1113 
1114    control_send=0;
1115    control_recv=0;
1116 #if USE_SSL
1117    control_ssl=0; // ssl should be freed after send/recv
1118 #endif
1119 
1120    if(control_sock!=-1)
1121    {
1122       LogNote(7,_("Closing control socket"));
1123       close(control_sock);
1124    }
1125 }
1126 
PrepareToDie()1127 void Ftp::PrepareToDie()
1128 {
1129    Enter();
1130    Disconnect();
1131    if(conn)
1132    {
1133       FlushSendQueue();
1134       ReceiveResp();
1135    }
1136    Disconnect();
1137    Leave();
1138 }
1139 
AbsolutePath(const char * s) const1140 bool Ftp::AbsolutePath(const char *s) const
1141 {
1142    if(!s || !*s)
1143       return false;
1144    int dev_len=device_prefix_len(s);
1145    return(s[0]=='/'
1146       || (s[0]=='~' && s[1]!=0 && s[1]!='/')
1147       || (conn && ((conn->dos_path && dev_len==3) || (conn->vms_path && dev_len>2))
1148 	  && s[dev_len-1]=='/'));
1149 }
1150 
1151 // returns true if we need to sleep instead of moving to higher level.
GetBetterConnection(int level,bool limit_reached)1152 bool Ftp::GetBetterConnection(int level,bool limit_reached)
1153 {
1154    bool need_sleep=false;
1155 
1156 //    if(level==0 && cwd==0)
1157 //       return need_sleep;
1158 
1159    for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
1160    {
1161       Ftp *o=(Ftp*)fo; // we are sure it is Ftp.
1162 
1163       if(o->GetConnectLevel()!=CL_LOGGED_IN)
1164 	 continue;
1165       if(!SameConnection(o))
1166 	 continue;
1167 
1168       if(level==0 && xstrcmp(real_cwd,o->real_cwd))
1169 	 continue;
1170 
1171       if(o->conn->data_sock!=-1 || o->state!=EOF_STATE || o->mode!=CLOSED)
1172       {
1173 	 /* session is in use; last resort is to takeover an active connection */
1174 	 if(level<2)
1175 	    continue;
1176 	 /* only take over lower priority or suspended jobs */
1177 	 if(!connection_takeover || (o->priority>=priority && !o->IsSuspended()))
1178 	    continue;
1179 	 if(o->conn->data_sock!=-1 && o->expect->Count()<=1)
1180 	 {
1181 	    /* don't take over active connections if they won't be able to resume */
1182 	    if((o->flags&NOREST_MODE) && o->real_pos>0x1000)
1183 	       continue;
1184 	    if(o->QueryBool("web-mode",o->hostname))
1185 	       continue;
1186 	    o->DataAbort();
1187 	    o->DataClose();
1188 	    if(!o->conn)
1189 	       return need_sleep; // oops...
1190 	 }
1191 	 else
1192 	 {
1193 	    if(!o->expect->IsEmpty() || o->disconnect_on_close)
1194 	       continue;
1195 	 }
1196       }
1197       else
1198       {
1199 	 if(limit_reached)
1200 	 {
1201 	    /* wait until job is diff seconds idle before taking it over */
1202 	    int diff=o->last_priority-priority;
1203 	    if(diff>0)
1204 	    {
1205 	       /* number of seconds the task has been idle */
1206 	       if(o->idle_timer.TimePassed()<diff)
1207 	       {
1208 		  TimeoutS(1);
1209 		  need_sleep=true;
1210 		  continue;
1211 	       }
1212 	    }
1213 	 }
1214       }
1215 
1216       // so borrow the connection
1217       MoveConnectionHere(o);
1218       return false;
1219    }
1220    return need_sleep;
1221 }
1222 
HandleTimeout()1223 void  Ftp::HandleTimeout()
1224 {
1225    if(conn)
1226       conn->quit_sent=true;
1227    super::HandleTimeout();
1228    DisconnectNow();
1229 }
1230 
1231 // Create buffers after control socket had been connected.
MakeBuffers()1232 void Ftp::Connection::MakeBuffers()
1233 {
1234 #if USE_SSL
1235    control_ssl=0;
1236 #endif
1237    control_send=new IOBufferFDStream(
1238       new FDStream(control_sock,"control-socket"),IOBuffer::PUT);
1239    control_recv=new IOBufferFDStream(
1240       new FDStream(control_sock,"control-socket"),IOBuffer::GET);
1241 }
InitTelnetLayer()1242 void Ftp::Connection::InitTelnetLayer()
1243 {
1244    if(telnet_layer_send)
1245       return;
1246    control_send=telnet_layer_send=new IOBufferTelnet(control_send.borrow());
1247    control_recv=new IOBufferTelnet(control_recv.borrow());
1248 }
1249 
ProxyIsHttp()1250 bool Ftp::ProxyIsHttp()
1251 {
1252    if(!proxy_proto)
1253       return false;
1254    return !strcmp(proxy_proto,"http")
1255        || !strcmp(proxy_proto,"https");
1256 }
1257 
path_to_send()1258 const char *Ftp::path_to_send()
1259 {
1260    if(mode==QUOTE_CMD || mode==LIST || mode==LONG_LIST)
1261       return file;
1262 
1263    xstring prefix(cwd.path.copy());
1264    /* two cases:
1265     *    root cwd	/ vs /file		-> file
1266     *    non-root cwd	/cwd vs /cwd/file	-> file
1267     */
1268    if(prefix.last_char()!='/')
1269       prefix.append('/');
1270    /* cwd//file or cwd/ are not converted */
1271    if(file.begins_with(prefix) && file.length()>prefix.length() && file[prefix.length()]!='/')
1272       return file+prefix.length();
1273 
1274    return file;
1275 }
1276 
Do()1277 int   Ftp::Do()
1278 {
1279    const char *command=0;
1280    bool	 append_file=false;
1281    int	 res;
1282    socklen_t addr_len;
1283    const unsigned char *a;
1284    const unsigned char *p;
1285    automate_state oldstate;
1286    int	 m=STALL;
1287    const char *error;
1288 
1289    // check if idle time exceeded
1290    if(mode==CLOSED && conn && idle_timer.Stopped())
1291    {
1292       LogNote(1,_("Closing idle connection"));
1293       Disconnect();
1294       if(conn)
1295 	 idle_timer.Reset();
1296       return m;
1297    }
1298 
1299    if(conn && conn->quit_sent)
1300    {
1301       m|=FlushSendQueue();
1302       m|=ReceiveResp();
1303       if(expect && expect->IsEmpty())
1304       {
1305 	 DisconnectNow();
1306 	 return MOVED;
1307       }
1308       goto usual_return;
1309    }
1310 
1311    /* Some servers cannot detect ABOR, help them by reading remaining data
1312       and closing data connection in few seconds */
1313    if(conn && conn->aborted_data_sock!=-1)
1314    {
1315       char discard[0x2000];
1316       int res=read(conn->aborted_data_sock,discard,sizeof(discard));
1317       if(res==0 || conn->abor_close_timer.Stopped())
1318 	 conn->CloseAbortedDataConnection();
1319       else
1320 	 Block(conn->aborted_data_sock,POLLIN);
1321    }
1322 
1323    if(Error() || eof || mode==CLOSED)
1324    {
1325       // inactive behavior
1326       if(conn)
1327       {
1328 	 m|=FlushSendQueue();
1329 	 m|=ReceiveResp();
1330       }
1331       if(eof || mode==CLOSED)
1332 	 goto notimeout_return;
1333       goto usual_return;
1334    }
1335 
1336    if(!hostname)
1337       return m;
1338 
1339    if(mode==MP_LIST && !use_mlsd)
1340    {
1341       SetError(NOT_SUPP,_("MLSD is disabled by ftp:use-mlsd"));
1342       return MOVED;
1343    }
1344 
1345    switch(state)
1346    {
1347    case(INITIAL_STATE):
1348    {
1349       // walk through ftp classes and try to find identical idle ftp session
1350       // first try "easy" cases of session take-over.
1351       int connection_limit=GetConnectionLimit();
1352       for(int i=0; i<3; i++)
1353       {
1354 	 bool limit_reached=last_connection_failed
1355 	       || (connection_limit>0 && connection_limit<=CountConnections());
1356 	 if(i>=2 && !limit_reached)
1357 	    break;
1358 	 bool need_sleep=GetBetterConnection(i,limit_reached);
1359 	 if(state!=INITIAL_STATE)
1360 	    return MOVED;
1361 	 if(need_sleep)
1362 	    return m;
1363       }
1364 
1365       if(!resolver && mode!=CONNECT_VERIFY && !ReconnectAllowed())
1366 	 return m;
1367 
1368       if(ftps)
1369 	 m|=Resolve(FTPS_DEFAULT_PORT,"ftps","tcp");
1370       else
1371 	 m|=Resolve(FTP_DEFAULT_PORT,"ftp","tcp");
1372       if(!peer)
1373 	 return m;
1374 
1375       if(mode==CONNECT_VERIFY)
1376 	 return m;
1377 
1378       if(!ReconnectAllowed())
1379 	 return m;
1380 
1381       if(!NextTry())
1382 	 return MOVED;
1383 
1384       last_connection_failed=false;
1385       assert(!conn);
1386       assert(!expect);
1387       conn=new Connection(hostname);
1388       expect=new ExpectQueue();
1389 
1390       conn->proxy_is_http=ProxyIsHttp();
1391       if(conn->proxy_is_http)
1392 	 SetFlag(PASSIVE_MODE,1);
1393 
1394       conn->peer_sa=peer[peer_curr];
1395       conn->control_sock=SocketCreateTCP(conn->peer_sa.sa.sa_family);
1396       if(conn->control_sock==-1)
1397       {
1398 	 conn=0;
1399 	 expect=0;
1400 	 if(peer_curr+1<peer.count())
1401 	 {
1402 	    DontSleep();
1403 	    peer_curr++;
1404 	    retries--;
1405 	    return MOVED;
1406 	 }
1407 	 saved_errno=errno;
1408 	 LogError(9,"socket: %s",strerror(saved_errno));
1409 	 if(NonFatalError(saved_errno))
1410 	    return m;
1411 	 xstring& str=xstring::format(_("cannot create socket of address family %d"),
1412 		     conn->peer_sa.sa.sa_family);
1413 	 SetError(SEE_ERRNO,str);
1414 	 return MOVED;
1415       }
1416       if(QueryBool("use-ip-tos",hostname))
1417 	 MinimizeLatency(conn->control_sock);
1418 
1419       SayConnectingTo();
1420 
1421       res=SocketConnect(conn->control_sock,&conn->peer_sa);
1422       state=CONNECTING_STATE;
1423       if(res==-1 && errno!=EINPROGRESS)
1424       {
1425 	 saved_errno=errno;
1426 	 LogError(0,"connect(control_sock): %s",strerror(saved_errno));
1427 	 if(NotSerious(saved_errno))
1428 	 {
1429 	    Disconnect(strerror(saved_errno));
1430 	    return MOVED;
1431 	 }
1432 	 goto system_error;
1433       }
1434       m=MOVED;
1435       timeout_timer.Reset();
1436    }
1437    /* fallthrough */
1438    case(CONNECTING_STATE):
1439       assert(conn && conn->control_sock!=-1);
1440       res=Poll(conn->control_sock,POLLOUT,&error);
1441       if(res==-1) {
1442 	 LogError(0,_("Socket error (%s) - reconnecting"),error);
1443 	 Disconnect(error);
1444 	 return MOVED;
1445       }
1446       if(!(res&POLLOUT))
1447 	 goto usual_return;
1448 
1449 #if USE_SSL
1450       if(proxy && (!xstrcmp(proxy_proto,"ftps")
1451 	        || !xstrcmp(proxy_proto,"https")))
1452       {
1453 	 conn->MakeSSLBuffers(hostname);
1454       }
1455       else // note the following block
1456 #endif
1457       {
1458 	 conn->MakeBuffers();
1459       }
1460 
1461       if(!proxy || !conn->proxy_is_http)
1462 	 goto pre_CONNECTED_STATE;
1463 
1464       state=HTTP_PROXY_CONNECTED;
1465       m=MOVED;
1466       HttpProxySendConnect();
1467    /* fallthrough */
1468    case HTTP_PROXY_CONNECTED:
1469       if(!HttpProxyReplyCheck(conn->control_recv))
1470 	 goto usual_return;
1471 
1472    pre_CONNECTED_STATE:
1473 #if USE_SSL
1474       if(ftps && (!proxy || conn->proxy_is_http))
1475       {
1476 	 conn->MakeSSLBuffers(hostname);
1477 	 const char *initial_prot=ResMgr::Query("ftps:initial-prot",hostname);
1478 	 conn->prot=initial_prot[0];
1479       }
1480 #endif
1481       if(use_telnet_iac)
1482 	 conn->InitTelnetLayer();
1483 
1484       state=CONNECTED_STATE;
1485       m=MOVED;
1486       expect->Push(Expect::READY);
1487 
1488       if(use_feat)
1489       {
1490 	 if(!proxy || conn->proxy_is_http)
1491 	 {
1492 	    conn->SendCmd("FEAT");
1493 	    expect->Push(Expect::FEAT);
1494 	 }
1495 	 else
1496 	 {
1497 	    conn->try_feat_after_login=true;
1498 	 }
1499       }
1500    /* fallthrough */
1501    case CONNECTED_STATE:
1502    {
1503       m|=FlushSendQueue();
1504       m|=ReceiveResp();
1505       if(state!=CONNECTED_STATE || Error())
1506 	 return MOVED;
1507 
1508       if(expect->Has(Expect::FEAT) || conn->quit_sent)
1509 	 goto usual_return;
1510 
1511 #if USE_SSL
1512       if(QueryBool((user && pass)?"ssl-allow":"ssl-allow-anonymous",hostname)
1513       && !ftps && (!proxy || conn->proxy_is_http))
1514 	 SendAuth(Query("ssl-auth",hostname));
1515       if(state!=CONNECTED_STATE)
1516 	 return MOVED;
1517 
1518       if(conn->auth_sent && !expect->IsEmpty())
1519 	 goto usual_return;
1520 #endif
1521       // Do connection tuning after AUTH TLS.
1522       if(conn->have_feat_info)
1523 	 TuneConnectionAfterFEAT();
1524 
1525       const char *user_to_use=(user?user.get():anon_user.get());
1526       const char *proxy_auth_type=Query("proxy-auth-type",proxy);
1527 
1528       // Only enter if we are using a proxy, and not a http proxy.
1529       // If we are in the ssl-auth stage after using a proxy, skip this.
1530       bool auth_allowed=false;
1531       if(proxy && !conn->proxy_is_http && !conn->ssl_after_proxy)
1532       {
1533 	 if(!strcmp(proxy_auth_type,"joined") && proxy_user && proxy_pass)
1534 	 {
1535 	    user_to_use=xstring::cat(user_to_use,"@",proxy_user.get(),"@",
1536 		  hostname.get(),portname?":":NULL,portname.get(),NULL);
1537 	 }
1538 	 else if(!strcmp(proxy_auth_type,"joined-acct") && proxy_user && proxy_pass)
1539 	 {
1540 	    user_to_use=xstring::cat(user_to_use,"@",hostname.get(),
1541 		  portname?":":"",portname?portname.get():"",
1542 		  " ",proxy_user.get(),NULL);
1543 	    // proxy_pass is sent later with ACCT command
1544 	 }
1545 	 else if(!strcmp(proxy_auth_type,"proxy-user@host") && proxy_user && proxy_pass)
1546 	 {
1547 	    expect->Push(Expect::USER_PROXY);
1548 	    conn->SendCmd2("USER",xstring::cat(proxy_user.get(),"@",hostname.get(),
1549 		  portname?":":"",portname?portname.get():"",NULL));
1550 	    expect->Push(Expect::PASS_PROXY);
1551 	    conn->SendCmd2("PASS",proxy_pass);
1552 	    auth_allowed=true;
1553 	 }
1554 	 else // no proxy auth, or type is `open' or `user'.
1555 	 {
1556 	    if(proxy_user && proxy_pass)
1557 	    {
1558 	       expect->Push(Expect::USER_PROXY);
1559 	       conn->SendCmd2("USER",proxy_user);
1560 	       expect->Push(Expect::PASS_PROXY);
1561 	       conn->SendCmd2("PASS",proxy_pass);
1562 	    }
1563 	    if(!strcmp(proxy_auth_type,"open"))
1564 	    {
1565 	       expect->Push(Expect::OPEN_PROXY);
1566 	       conn->SendCmd2("OPEN",xstring::cat(hostname.get(),
1567 		     portname?":":NULL,portname.get(),NULL));
1568 	       auth_allowed=true;
1569 	    }
1570 	    else // "user" or no proxy auth
1571 	    {
1572 	       user_to_use=xstring::cat(user_to_use,"@",hostname.get(),
1573 		     portname?":":NULL,portname.get(),NULL);
1574 	    }
1575 	 }
1576 #if USE_SSL
1577 	 if(auth_allowed)
1578          {
1579 	    if(QueryBool((user && pass)?"ssl-allow":"ssl-allow-anonymous",hostname))
1580 	    {
1581 	       SendAuth(Query("ssl-auth",hostname));
1582 	       if(state!=CONNECTED_STATE)
1583 	          return MOVED;
1584 
1585                // We are now waiting for auth TLS.
1586 	       conn->ssl_after_proxy=true;
1587 
1588 	       if(conn->auth_sent && !expect->IsEmpty())
1589                   goto usual_return;
1590 	    }
1591          }
1592 #endif
1593       }
1594 
1595       skey_pass.set(0);
1596       netkey_pass.set(0);
1597 
1598       expect->Push(Expect::USER);
1599       conn->SendCmd2("USER",user_to_use);
1600 
1601       state=USER_RESP_WAITING_STATE;
1602       m=MOVED;
1603    }
1604    /* fallthrough */
1605    case(USER_RESP_WAITING_STATE):
1606    {
1607       if((GetFlag(SYNC_MODE) || (user && pass && allow_skey))
1608       && !expect->IsEmpty())
1609       {
1610 	 m|=FlushSendQueue();
1611 	 m|=ReceiveResp();
1612 	 if(state!=USER_RESP_WAITING_STATE || Error())
1613 	    return MOVED;
1614 	 if(!expect->IsEmpty())
1615 	    goto usual_return;
1616       }
1617 
1618       const char *proxy_auth_type=Query("proxy-auth-type",proxy);
1619       if(!conn->ignore_pass)
1620       {
1621 	conn->may_show_password = (skey_pass!=0) || (netkey_pass!=0) || (user==0) || pass_open;
1622 	 const char *pass_to_use=(pass?pass:anon_pass);
1623 	 if(allow_skey && skey_pass)
1624 	    pass_to_use=skey_pass;
1625 	 else if(allow_netkey && netkey_pass)
1626 	    pass_to_use=netkey_pass;
1627 	 else if(proxy && !conn->proxy_is_http && proxy_user && proxy_pass
1628 	 && !strcmp(proxy_auth_type,"joined"))
1629 	    pass_to_use=xstring::cat(pass_to_use,"@",proxy_pass.get(),NULL);
1630 	 expect->Push(Expect::PASS);
1631 	 conn->SendCmd2("PASS",pass_to_use);
1632 
1633       }
1634       if(proxy && !conn->proxy_is_http && proxy_user && proxy_pass
1635       && !strcmp(proxy_auth_type,"joined-acct"))
1636       {
1637 	 expect->Push(Expect::ACCT_PROXY);
1638 	 conn->SendCmd2("ACCT",proxy_pass);
1639       }
1640       SendAcct();
1641       if(conn->try_feat_after_login)
1642       {
1643 	 conn->SendCmd("FEAT");
1644 	 expect->Push(Expect::FEAT);
1645       }
1646       else
1647       {
1648 	 if(conn->tune_after_login)
1649 	    TuneConnectionAfterFEAT();
1650 	 if(conn->mlst_attr_supported)
1651 	    SendOPTS_MLST();
1652       }
1653       SendSiteGroup();
1654       SendSiteIdle();
1655       SendSiteCommands();
1656 
1657       if(!home_auto)
1658       {
1659 	 // if we don't yet know the home location, try to get it
1660 	 conn->SendCmd("PWD");
1661 	 expect->Push(Expect::PWD);
1662       }
1663 
1664 #if USE_SSL
1665       if(conn->ssl_is_activated())
1666       {
1667 	 conn->SendCmd("PBSZ 0");
1668 	 expect->Push(Expect::IGNORE);
1669 
1670 	 // select PROT mode before CCC if there is no need to change it later
1671 	 bool prot_data=QueryBool("ssl-protect-data");
1672 	 bool prot_list=QueryBool("ssl-protect-list");
1673 	 if(prot_data==prot_list)
1674 	    SendPROT(prot_data?'P':'C');
1675 
1676 	 if(QueryBool("ssl-use-ccc"))
1677 	 {
1678 	    conn->SendCmd("CCC");
1679 	    expect->Push(Expect::CCC);
1680 	 }
1681       }
1682 #endif // USE_SSL
1683 
1684       set_real_cwd(0);
1685    }
1686    /* fallthrough */
1687    pre_EOF_STATE:
1688       state=EOF_STATE;
1689       m=MOVED;
1690    case(EOF_STATE):
1691       m|=FlushSendQueue();
1692       m|=ReceiveResp();
1693       if(state!=EOF_STATE || Error())
1694 	 return MOVED;
1695 
1696       if(expect->Has(Expect::FEAT)
1697       || expect->Has(Expect::OPTS_UTF8)
1698       || expect->Has(Expect::LANG))
1699 	 goto usual_return;
1700 
1701 #if USE_SSL
1702       if(expect->Has(Expect::CCC)
1703       || expect->Has(Expect::PROT))
1704 	 goto usual_return;
1705 #endif // USE_SSL
1706 
1707       if(!conn->utf8_activated && charset && *charset)
1708 	 conn->SetControlConnectionTranslation(charset);
1709 
1710       if(mode==CONNECT_VERIFY)
1711 	 goto notimeout_return;
1712 
1713       if(mode==CHANGE_MODE && !conn->mff_supported && !conn->site_chmod_supported)
1714       {
1715 	 SetError(NOT_SUPP,_("MFF and SITE CHMOD are not supported by this site"));
1716 	 return MOVED;
1717       }
1718       if(mode==MP_LIST && !conn->mlst_supported)
1719       {
1720 	 SetError(NOT_SUPP,_("MLST and MLSD are not supported by this site"));
1721 	 return MOVED;
1722       }
1723 
1724       if(home.path==0 && !expect->IsEmpty())
1725 	 goto usual_return;
1726 
1727       if(!retry_timer.Stopped())
1728 	 goto usual_return;
1729 
1730       if(real_cwd==0)
1731 	 set_real_cwd(home_auto);
1732 
1733       ExpandTildeInCWD();
1734 
1735       if(!CheckRetries())
1736 	 return MOVED;
1737 
1738       if(mode!=CHANGE_DIR)
1739       {
1740 	 Expect *last_cwd=expect->FindLastCWD();
1741 	 // Send CWD if we have a CWD in flight and it differs from wanted cwd
1742 	 //          or we don't have a CWD and read_cwd differs from wanted cwd
1743 	 if((!last_cwd && xstrcmp(cwd,real_cwd) && !(real_cwd==0 && !xstrcmp(cwd,"~")))
1744 	    || (last_cwd && xstrcmp(last_cwd->arg,cwd)))
1745 	 {
1746 	    SendCWD(cwd,cwd.url,Expect::CWD_CURR);
1747 	 }
1748 	 else if(last_cwd && !xstrcmp(last_cwd->arg,cwd))
1749 	 {
1750 	    // no need for extra CWD, one's already sent.
1751 	    last_cwd->check_case=Expect::CWD_CURR;
1752 	 }
1753       }
1754 #if USE_SSL
1755       if(conn->ssl_is_activated() || (conn->auth_supported && conn->auth_sent))
1756       {
1757 	 char want_prot=conn->prot;
1758 	 const char *protect_res=get_protect_res();
1759 	 if(protect_res)
1760 	    want_prot=QueryBool(protect_res,hostname)?'P':'C';
1761 	 if(copy_mode!=COPY_NONE)
1762 	    want_prot=copy_protect?'P':'C';
1763 
1764 	 bool want_sscn=conn->sscn_on;
1765 	 if(copy_mode!=COPY_NONE)
1766 	    want_sscn=copy_protect && copy_ssl_connect
1767 		      && !(copy_passive && conn->cpsv_supported);
1768 	 else if(mode==RETRIEVE || mode==STORE || mode==LIST || mode==MP_LIST
1769 		  || (mode==LONG_LIST && !use_stat_for_list))
1770 	    want_sscn=false;
1771 
1772 	 if(conn->sscn_supported && want_sscn!=conn->sscn_on)
1773 	 {
1774 	    conn->SendCmd2("SSCN",want_sscn?"ON":"OFF");
1775 	    expect->Push(new Expect(Expect::SSCN,want_sscn?'Y':'N'));
1776 	 }
1777 	 SendPROT(want_prot);
1778       }
1779 #endif
1780       state=CWD_CWD_WAITING_STATE;
1781       m=MOVED;
1782    /* fallthrough */
1783    case CWD_CWD_WAITING_STATE:
1784    {
1785       m|=FlushSendQueue();
1786       m|=ReceiveResp();
1787       if(state!=CWD_CWD_WAITING_STATE || Error())
1788 	 return MOVED;
1789 
1790       // wait for all CWD to finish
1791       if(mode!=CHANGE_DIR && expect->FindLastCWD())
1792 	 goto usual_return;
1793 #if USE_SSL
1794       // PROT and SSCN are critical for data transfers
1795       if(expect->Has(Expect::PROT)
1796       || expect->Has(Expect::SSCN))
1797 	 goto usual_return;
1798 #endif
1799 
1800       // address of peer is not known yet
1801       if(copy_mode!=COPY_NONE && !copy_passive && !copy_addr_valid)
1802 	 goto usual_return;
1803 
1804       if(entity_size>=0 && entity_size<=pos
1805       && (mode==RETRIEVE || (mode==STORE && entity_size>0)))
1806       {
1807 	 if(mode==STORE)
1808 	    SendUTimeRequest();
1809 	 if(mode==RETRIEVE)
1810 	    LogNote(9,"received all data but no EOF\n");
1811 	 eof=true;
1812 	 goto pre_WAITING_STATE; // simulate eof.
1813       }
1814 
1815       if(!conn->rest_supported)
1816 	 flags|=NOREST_MODE;
1817 
1818       if(mode==STORE && GetFlag(NOREST_MODE) && pos>0)
1819 	 pos=0;
1820 
1821       if(copy_mode==COPY_NONE
1822       && (mode==RETRIEVE || mode==STORE || mode==LIST || mode==MP_LIST
1823           || (mode==LONG_LIST && !use_stat_for_list)))
1824       {
1825 	 assert(conn->data_sock==-1);
1826 	 conn->data_sock=SocketCreateUnboundTCP(conn->peer_sa.sa.sa_family,hostname);
1827 	 if(conn->data_sock==-1)
1828 	 {
1829 	    saved_errno=errno;
1830 	    LogError(0,"socket(data): %s",strerror(saved_errno));
1831 	    goto system_error;
1832 	 }
1833 	 if(QueryBool("use-ip-tos",hostname))
1834 	    MaximizeThroughput(conn->data_sock);
1835 
1836 	 addr_len=sizeof(conn->data_sa);
1837 	 getsockname(conn->control_sock,&conn->data_sa.sa,&addr_len);
1838 
1839 	 // Try to assign a port from given range
1840 	 Range range(Query("port-range"));
1841 	 for(int t=0; ; t++)
1842 	 {
1843 	    if(t>=10)
1844 	    {
1845 	       close(conn->data_sock);
1846 	       conn->data_sock=-1;
1847 	       TimeoutS(1);	 // retry later.
1848 	       return m;
1849 	    }
1850 	    if(t==9)
1851 	       ReuseAddress(conn->data_sock);   // try to reuse address.
1852 
1853 	    int port=0;
1854 	    if(!range.IsFull())
1855 	       port=range.Random();
1856 
1857 	    bool do_addr_bind=QueryBool("bind-data-socket")
1858 			   && !conn->peer_sa.is_loopback();
1859 
1860 	    if(!do_addr_bind && !port)
1861 		break;	// nothing to bind
1862 
1863 	    if(conn->data_sa.sa.sa_family==AF_INET)
1864 	    {
1865 	       conn->data_sa.in.sin_port=htons(port);
1866 	       if(!do_addr_bind)
1867 		  memset(&conn->data_sa.in.sin_addr,0,sizeof(conn->data_sa.in.sin_addr));
1868 	    }
1869 #if INET6
1870 	    else if(conn->data_sa.sa.sa_family==AF_INET6)
1871 	    {
1872 	       conn->data_sa.in6.sin6_port=htons(port);
1873 	       if(!do_addr_bind)
1874 		  memset(&conn->data_sa.in6.sin6_addr,0,sizeof(conn->data_sa.in6.sin6_addr));
1875 	    }
1876 #endif
1877 	    else
1878 	    {
1879 	       Fatal("unsupported network protocol");
1880 	       return MOVED;
1881 	    }
1882 
1883 	    if(bind(conn->data_sock,&conn->data_sa.sa,addr_len)==0)
1884 	       break;
1885 	    saved_errno=errno;
1886 
1887 	    // Fail unless socket was already taken
1888 	    if(saved_errno!=EINVAL && saved_errno!=EADDRINUSE)
1889 	    {
1890 	       LogError(0,"bind(data_sock,[%s]:%d): %s",
1891 		  SocketNumericAddress(&conn->data_sa),port,strerror(saved_errno));
1892 	       close(conn->data_sock);
1893 	       conn->data_sock=-1;
1894 	       if(NonFatalError(saved_errno))
1895 	       {
1896 		  TimeoutS(1);
1897 		  return m;
1898 	       }
1899 	       SetError(SEE_ERRNO,"Cannot bind data socket for ftp:port-range");
1900 	       return MOVED;
1901 	    }
1902 	    LogError(10,"bind(data_sock,[%s]:%d): %s",
1903 	       SocketNumericAddress(&conn->data_sa),port,strerror(saved_errno));
1904 	 }
1905 
1906 	 if(!GetFlag(PASSIVE_MODE))
1907 	    if (listen(conn->data_sock,1) < 0)
1908 		LogError(0,"listen failed: %s", strerror(errno));
1909 
1910 	 // get the allocated port
1911 	 addr_len=sizeof(conn->data_sa);
1912 	 getsockname(conn->data_sock,&conn->data_sa.sa,&addr_len);
1913       }
1914 
1915       char want_type=(ascii?'A':'I');
1916       char want_t_mode='S';
1917 
1918       if(conn->mode_z_supported && QueryBool("use-mode-z",hostname)
1919       && (mode==LIST || mode==LONG_LIST || mode==MP_LIST
1920 	  || ((mode==RETRIEVE || mode==STORE)
1921 	      && !re_match(file,Query("compressed-re"))))) {
1922 	 want_t_mode='Z';
1923       }
1924 
1925       if(GetFlag(NOREST_MODE) || pos==0)
1926 	 real_pos=0;
1927       else
1928 	 real_pos=-1;	// we don't yet know if REST will succeed
1929 
1930       flags&=~IO_FLAG;
1931       last_priority=priority;
1932       conn->received_150=false;
1933 
1934       switch((enum open_mode)mode)
1935       {
1936       case(RETRIEVE):
1937 	 if(file[0]==0)
1938 	    goto long_list;
1939 	 command="RETR";
1940 	 append_file=true;
1941          break;
1942       case(STORE):
1943 	 if(!QueryBool("rest-stor",hostname))
1944 	 {
1945 	    real_pos=0;	// some old servers don't handle REST/STOR properly.
1946 	    pos=0;
1947 	 }
1948          command="STOR";
1949 	 append_file=true;
1950          break;
1951       long_list:
1952       case(LONG_LIST):
1953 	 if(use_stat_for_list)
1954 	 {
1955 	    real_pos=0;
1956 	    command="STAT";
1957 	    conn->data_iobuf=new IOBuffer(IOBuffer::GET);
1958 	    rate_limit=new RateLimit(hostname);
1959 	    want_type=conn->type;
1960 	    want_t_mode=conn->t_mode;
1961 	 }
1962 	 else
1963 	 {
1964 	    want_type='A';
1965 	    if(!rest_list)
1966 	       real_pos=0;	// some ftp servers do not do REST/LIST.
1967 	    command="LIST";
1968 	 }
1969 	 if(list_options && list_options[0])
1970 	    command=xstring::cat(command," ",list_options.get(),NULL);
1971 	 if(file && file[0])
1972 	    append_file=true;
1973 	 if(use_stat_for_list && !append_file && !strchr(command,' '))
1974 	    command="STAT .";
1975          break;
1976       case(MP_LIST):
1977          want_type='A';
1978          real_pos=0; // REST doesn't work for MLSD
1979 	 command="MLSD";
1980 	 if(file && file[0])
1981 	    append_file=true;
1982          break;
1983       case(LIST):
1984          want_type='A';
1985          real_pos=0; // REST doesn't work for NLST
1986 	 command="NLST";
1987 	 if(file && file[0])
1988             append_file=true;
1989          break;
1990       case(CHANGE_DIR):
1991 	 if((real_cwd && !xstrcmp(real_cwd,file))
1992 	 || SendCWD(file,file_url,Expect::CWD)==0)
1993 	    cwd.Set(file,false,file_url,device_prefix_len(file));
1994 	 goto pre_WAITING_STATE;
1995       case(MAKE_DIR):
1996 	 command="MKD";
1997 	 if(mkdir_p && conn->site_mkdir_supported)
1998 	    command="SITE MKDIR";
1999 	 append_file=true;
2000 	 want_type=conn->type;
2001 	 break;
2002       case(REMOVE_DIR):
2003 	 command="RMD";
2004 	 append_file=true;
2005 	 want_type=conn->type;
2006 	 break;
2007       case(REMOVE):
2008 	 command="DELE";
2009 	 append_file=true;
2010 	 want_type=conn->type;
2011 	 break;
2012       case(QUOTE_CMD):
2013 	 real_pos=0;
2014 	 command="";
2015 	 append_file=true;
2016 	 conn->data_iobuf=new IOBuffer(IOBuffer::GET);
2017 	 rate_limit=new RateLimit(hostname);
2018 	 break;
2019       case(RENAME):
2020 	 command="RNFR";
2021 	 append_file=true;
2022 	 want_type=conn->type;
2023 	 break;
2024       case(LINK):
2025 	 command="SITE LINK";
2026 	 append_file=true;
2027 	 want_type=conn->type;
2028 	 break;
2029       case(SYMLINK):
2030 	 if(!conn->site_symlink_supported) {
2031 	    SetError(NOT_SUPP,_("SITE SYMLINK is not supported by the server"));
2032 	    return MOVED;
2033 	 }
2034 	 command="SITE SYMLINK";
2035 	 append_file=true;
2036 	 want_type=conn->type;
2037 	 break;
2038       case(ARRAY_INFO):
2039 	 break;
2040       case(CHANGE_MODE):
2041 	 {
2042 	    if(conn->mff_supported)
2043 	       command=xstring::format("MFF UNIX.mode=%03o;",chmod_mode);
2044 	    else
2045 	       command=xstring::format("SITE CHMOD %03o",chmod_mode);
2046 	    append_file=true;
2047 	    want_type=conn->type;
2048 	    break;
2049 	 }
2050       case(CONNECT_VERIFY):
2051       case(CLOSED):
2052 	 state=EOF_STATE;
2053       }
2054 
2055       if(want_type!=conn->type)
2056       {
2057 	 conn->SendCmdF("TYPE %c",want_type);
2058 	 expect->Push(new Expect(Expect::TYPE,want_type));
2059       }
2060       if(want_t_mode!=conn->t_mode) {
2061 	 conn->SendCmdF("MODE %c",want_t_mode);
2062 	 expect->Push(new Expect(Expect::MODE,want_t_mode));
2063       }
2064 
2065       const char *file=path_to_send();
2066       if(opt_size && conn->size_supported && file[0] && use_size)
2067       {
2068 	 conn->SendCmd2("SIZE",file,url::path_ptr(file_url),home);
2069 	 expect->Push(Expect::SIZE_OPT);
2070       }
2071       if(opt_date && conn->mdtm_supported && file[0] && use_mdtm)
2072       {
2073 	 conn->SendCmd2("MDTM",file,url::path_ptr(file_url),home);
2074 	 expect->Push(Expect::MDTM_OPT);
2075       }
2076 
2077       if(mode==ARRAY_INFO)
2078       {
2079 	 SendArrayInfoRequests();
2080 	 goto pre_WAITING_STATE;
2081       }
2082 
2083       const char *file_to_append=0;
2084       if(append_file)
2085 	 file_to_append=path_to_send();
2086 
2087       if(mode==QUOTE_CMD || mode==CHANGE_MODE || (mode==LONG_LIST && use_stat_for_list)
2088       || mode==REMOVE || mode==REMOVE_DIR || mode==MAKE_DIR || mode==RENAME)
2089       {
2090 	 if(mode==MAKE_DIR && mkdir_p && !conn->site_mkdir_supported)
2091 	 {
2092 	    Ref<StringSet> dirs(MkdirMakeSet());
2093 	    for(int i=0; i<dirs->Count(); i++)
2094 	    {
2095 	       conn->SendCmd2("MKD",dirs->String(i));
2096 	       expect->Push(Expect::IGNORE);
2097 	    }
2098 	 }
2099 
2100 	 if(append_file)
2101 	    conn->SendCmd2(command,file_to_append,url::path_ptr(file_url),home);
2102 	 else
2103 	    conn->SendCmd(command);
2104 
2105 	 Expect::expect_t e=Expect::FILE_ACCESS;
2106 	 if(mode==QUOTE_CMD)
2107 	 {
2108 	    e=Expect::QUOTED;
2109 	    if(!strncasecmp(file,"CWD",3)
2110 	    || !strncasecmp(file,"CDUP",4)
2111 	    || !strncasecmp(file,"XCWD",4)
2112 	    || !strncasecmp(file,"XCUP",4))
2113 	    {
2114 	       LogNote(9,"Resetting cwd");
2115 	       set_real_cwd(0);  // we do not know the path now.
2116 	    }
2117 	 }
2118 	 else if(mode==LONG_LIST)
2119 	    e=Expect::QUOTED;
2120 	 else if(mode==RENAME)
2121 	    e=Expect::RNFR;
2122 	 expect->Push(new Expect(e,file,command));
2123 	 goto pre_WAITING_STATE;
2124       }
2125       if(mode==LINK || mode==SYMLINK) {
2126 	 conn->SendCmdF("%s %s %s",command,file_to_append,file1.get());
2127 	 expect->Push(new Expect(Expect::FILE_ACCESS,0,command));
2128 	 goto pre_WAITING_STATE;
2129       }
2130 
2131       if((copy_mode==COPY_NONE && GetFlag(PASSIVE_MODE))
2132       || (copy_mode!=COPY_NONE && copy_passive))
2133       {
2134 	 if(QueryTriBool("use-pret",0,conn->pret_supported))
2135 	 {
2136 	    conn->SendCmd(xstring::cat("PRET ",command," ",file_to_append,NULL));
2137 	    expect->Push(Expect::PRET);
2138 	 }
2139 	 conn->can_do_pasv=(conn->peer_sa.sa.sa_family==AF_INET);
2140 #if INET6
2141 	 conn->can_do_pasv|=(conn->peer_sa.sa.sa_family==AF_INET6
2142 			&& IN6_IS_ADDR_V4MAPPED(&conn->peer_sa.in6.sin6_addr));
2143 #endif
2144 #if USE_SSL
2145 	 if(conn->can_do_pasv && copy_mode!=COPY_NONE && conn->prot=='P' && !conn->sscn_on && copy_ssl_connect) {
2146 	    conn->SendCmd("CPSV"); // same as PASV, but server does SSL_connect
2147 	    expect->Push(Expect::PASV);
2148 	 } else
2149 #endif // note the following statement
2150 	 if(!conn->can_do_pasv || (conn->epsv_supported && QueryBool("prefer-epsv",hostname))) {
2151 	    conn->SendCmd("EPSV");
2152 	    expect->Push(Expect::EPSV);
2153 	 } else {
2154 	    conn->SendCmd("PASV");
2155 	    expect->Push(Expect::PASV);
2156 	 }
2157 	 pasv_state=PASV_NO_ADDRESS_YET;
2158       }
2159       else // !PASSIVE
2160       {
2161 	 sockaddr_u control_sa;
2162 	 if(copy_mode!=COPY_NONE)
2163 	    conn->data_sa=copy_addr;
2164 	 if(conn->data_sa.sa.sa_family==AF_INET)
2165 	 {
2166 	    a=(const unsigned char*)&conn->data_sa.in.sin_addr;
2167 	    p=(const unsigned char*)&conn->data_sa.in.sin_port;
2168 	    // check if data socket address is unbound
2169 	    if((a[0]|a[1]|a[2]|a[3])==0)
2170 	    {
2171 	       socklen_t addr_len=sizeof(control_sa);
2172 	       getsockname(conn->control_sock,&control_sa.sa,&addr_len);
2173 	       a=(const unsigned char*)&control_sa.in.sin_addr;
2174 	    }
2175 #if INET6
2176 	 ipv4_port:
2177 #endif
2178 	    if(copy_mode==COPY_NONE)
2179 	    {
2180 	       const char *port_ipv4=Query("port-ipv4",hostname);
2181 	       struct in_addr fake_ip;
2182 	       if(port_ipv4 && port_ipv4[0])
2183 	       {
2184 		  if(inet_pton(AF_INET,port_ipv4,&fake_ip))
2185 		     a=(const unsigned char*)&fake_ip;
2186 	       }
2187 	    }
2188 	    conn->SendCmdF("PORT %d,%d,%d,%d,%d,%d",a[0],a[1],a[2],a[3],p[0],p[1]);
2189 	    expect->Push(Expect::PORT);
2190 	 }
2191 	 else
2192 	 {
2193 #if INET6
2194 	    if(conn->data_sa.sa.sa_family==AF_INET6
2195 	       && IN6_IS_ADDR_V4MAPPED(&conn->data_sa.in6.sin6_addr))
2196 	    {
2197 	       a=((unsigned char*)&conn->data_sa.in6.sin6_addr)+12;
2198 	       p=(unsigned char*)&conn->data_sa.in6.sin6_port;
2199 	       goto ipv4_port;
2200 	    }
2201 	    conn->SendCmd2("EPRT",encode_eprt(&conn->data_sa));
2202 	    expect->Push(Expect::PORT);
2203 #else
2204 	    Fatal(_("unsupported network protocol"));
2205 	    return MOVED;
2206 #endif
2207 	 }
2208       }
2209       if(mode==STORE && entity_size!=NO_SIZE && QueryBool("use-allo",hostname))
2210       {
2211 	 // ALLO is usually ignored by servers, but send it anyway.
2212 	 conn->SendCmdF("ALLO %lld",(long long)entity_size);
2213 	 expect->Push(Expect::ALLO);
2214       }
2215       // some broken servers don't reset REST after a transfer,
2216       // so check if last_rest was different.
2217       if(real_pos==-1 || conn->last_rest!=real_pos)
2218       {
2219          conn->rest_pos=(real_pos!=-1?real_pos:pos);
2220 	 conn->SendCmdF("REST %lld",(long long)conn->rest_pos);
2221 	 expect->Push(Expect::REST);
2222 	 real_pos=-1;
2223       }
2224       if(copy_mode!=COPY_DEST || copy_allow_store)
2225       {
2226 	 if(append_file)
2227 	    conn->SendCmd2(command,file_to_append,url::path_ptr(file_url),home);
2228 	 else
2229 	    conn->SendCmd(command);
2230 	 expect->Push(Expect::TRANSFER);
2231       }
2232       m=MOVED;
2233       if(copy_mode!=COPY_NONE && !copy_passive)
2234 	 goto pre_WAITING_STATE;
2235       if((copy_mode==COPY_NONE && GetFlag(PASSIVE_MODE))
2236       || (copy_mode!=COPY_NONE && copy_passive))
2237       {
2238 	 state=DATASOCKET_CONNECTING_STATE;
2239 	 goto datasocket_connecting_state;
2240       }
2241       state=ACCEPTING_STATE;
2242    }
2243    /* fallthrough */
2244    case(ACCEPTING_STATE):
2245       m|=FlushSendQueue();
2246       m|=ReceiveResp();
2247 
2248       if(state!=ACCEPTING_STATE || Error())
2249          return MOVED;
2250 
2251       res=Poll(conn->data_sock,POLLIN,&error);
2252       if(res==-1) {
2253 	 LogError(0,_("Data socket error (%s) - reconnecting"),error);
2254 	 Disconnect(error);
2255          return MOVED;
2256       }
2257 
2258       if(!(res&POLLIN))
2259 	 goto usual_return;
2260 
2261       res=SocketAccept(conn->data_sock,&conn->data_sa,hostname);
2262       if(res==-1)
2263       {
2264 	 saved_errno=errno;
2265 	 if(saved_errno==EWOULDBLOCK)
2266 	    goto usual_return;
2267 	 if(NotSerious(saved_errno))
2268 	 {
2269 	    LogError(0,"%s",strerror(saved_errno));
2270 	    Disconnect(strerror(saved_errno));
2271 	    return MOVED;
2272 	 }
2273 	 goto system_error;
2274       }
2275 
2276       close(conn->data_sock);
2277       conn->data_sock=res;
2278       if(QueryBool("use-ip-tos",hostname))
2279 	 MaximizeThroughput(conn->data_sock);
2280 
2281       LogNote(5,_("Accepted data connection from (%s) port %u"),
2282 	 SocketNumericAddress(&conn->data_sa),SocketPort(&conn->data_sa));
2283       if(!conn->data_address_ok(0,verify_data_address,verify_data_port))
2284       {
2285 	 Disconnect("invalid data connection address");
2286 	 return MOVED;
2287       }
2288 
2289       goto pre_waiting_150;
2290 
2291    case(DATASOCKET_CONNECTING_STATE):
2292    datasocket_connecting_state:
2293       if(pasv_state!=PASV_DATASOCKET_CONNECTING)
2294 	 m|=FlushSendQueue();
2295       m|=ReceiveResp();
2296 
2297       if(state!=DATASOCKET_CONNECTING_STATE || Error())
2298          return MOVED;
2299 
2300       switch(pasv_state)
2301       {
2302       case PASV_NO_ADDRESS_YET:
2303 	 goto usual_return;
2304 
2305       case PASV_HAVE_ADDRESS:
2306 	 if(copy_mode==COPY_NONE
2307 	 && !conn->data_address_ok(&conn->data_sa,verify_data_address,/*port_verify*/false))
2308 	 {
2309 	    Disconnect("invalid data connection address");
2310 	    return MOVED;
2311 	 }
2312 	 pasv_state=PASV_DATASOCKET_CONNECTING;
2313 	 if(copy_mode!=COPY_NONE)
2314 	 {
2315 	    memcpy(&copy_addr,&conn->data_sa,sizeof(conn->data_sa));
2316 	    copy_addr_valid=true;
2317 	    goto pre_WAITING_STATE;
2318 	 }
2319 
2320          // Clean up old data socket, as it might be from the wrong
2321          // address family.
2322          if(conn->data_sock!=-1)
2323              close(conn->data_sock);
2324 
2325          // Create new data socket with appropriate address family.
2326          conn->data_sock = SocketCreateTCP(conn->data_sa.sa.sa_family);
2327 
2328 	 if(!conn->proxy_is_http)
2329 	 {
2330 	    LogNote(5,_("Connecting data socket to (%s) port %u"),
2331 	       SocketNumericAddress(&conn->data_sa),SocketPort(&conn->data_sa));
2332 	    res=SocketConnect(conn->data_sock,&conn->data_sa);
2333 	 }
2334 	 else // proxy_is_http
2335 	 {
2336 	    LogNote(5,_("Connecting data socket to proxy %s (%s) port %u"),
2337 	       proxy.get(),SocketNumericAddress(&conn->peer_sa),SocketPort(&conn->peer_sa));
2338 	    res=SocketConnect(conn->data_sock,&conn->peer_sa);
2339 	 }
2340 	 if(res==-1 && errno!=EINPROGRESS)
2341 	 {
2342 	    saved_errno=errno;
2343         LogError(0,"connect: %s (%d)",strerror(saved_errno),saved_errno);
2344 	    Disconnect(strerror(saved_errno));
2345 	    if(NotSerious(saved_errno))
2346 	       return MOVED;
2347 	    goto system_error;
2348 	 }
2349 	 m=MOVED;
2350       /* fallthrough */
2351       case PASV_DATASOCKET_CONNECTING:
2352 	 res=Poll(conn->data_sock,POLLOUT,&error);
2353 	 if(res==-1)
2354 	 {
2355 	    LogError(0,_("Data socket error (%s) - reconnecting"),error);
2356 	    if(conn->fixed_pasv && QueryBool("auto-passive-mode",hostname))
2357 	    {
2358 	       LogNote(2,_("Switching passive mode off"));
2359 	       SetFlag(PASSIVE_MODE,0);
2360 	    }
2361 	    Disconnect(error);
2362 	    return MOVED;
2363 	 }
2364 	 if(!(res&POLLOUT))
2365 	    goto usual_return;
2366 	 LogNote(9,_("Data connection established"));
2367 	 if(!conn->proxy_is_http)
2368 	    goto pre_waiting_150;
2369 
2370 	 pasv_state=PASV_HTTP_PROXY_CONNECTED;
2371 	 m=MOVED;
2372 	 conn->data_iobuf=new IOBufferFDStream(new FDStream(conn->data_sock,"data-socket"),IOBuffer::PUT);
2373 	 HttpProxySendConnectData();
2374 	 conn->data_iobuf->Roll();
2375 	 // FIXME, data_iobuf could be not done yet
2376 	 conn->data_iobuf=new IOBufferFDStream(new FDStream(conn->data_sock,"data-socket"),IOBuffer::GET);
2377       /* fallthrough */
2378       case PASV_HTTP_PROXY_CONNECTED:
2379 	 if(HttpProxyReplyCheck(conn->data_iobuf))
2380 	    goto pre_waiting_150;
2381 	 goto usual_return;
2382       }
2383    /* fallthrough */
2384    pre_waiting_150:
2385       state=WAITING_150_STATE;
2386       conn->waiting_150_timer.Reset();
2387       rate_limit=new RateLimit(hostname);
2388       m=MOVED;
2389    case WAITING_150_STATE:
2390       m|=FlushSendQueue();
2391       m|=ReceiveResp();
2392       if(state!=WAITING_150_STATE || Error())
2393          return MOVED;
2394       if(!conn->received_150 && !expect->IsEmpty() && !conn->waiting_150_timer.Stopped())
2395 	 goto usual_return;
2396 
2397       // now init data connection properly and start data exchange
2398       state=DATA_OPEN_STATE;
2399       m=MOVED;
2400 
2401 #if USE_SSL
2402       if(conn->prot=='P')
2403       {
2404 	 Ref<lftp_ssl> ssl(new lftp_ssl(conn->data_sock,lftp_ssl::CLIENT,hostname));
2405 	 if(QueryBool("ssl-data-use-keys",hostname) || !conn->control_ssl)
2406 	    ssl->load_keys();
2407 	 // share session id between control and data connections.
2408 	 if(conn->control_ssl && QueryBool("ssl-copy-sid",hostname))
2409 	    ssl->copy_sid(conn->control_ssl);
2410 
2411 	 IOBuffer::dir_t dir=(mode==STORE?IOBuffer::PUT:IOBuffer::GET);
2412 	 IOBufferSSL *ssl_buf=new IOBufferSSL(ssl.borrow(),dir);
2413 	 conn->data_iobuf=ssl_buf;
2414       }
2415       else  // note the following block
2416 #endif
2417       {
2418 	 IOBuffer::dir_t dir=(mode==STORE?IOBuffer::PUT:IOBuffer::GET);
2419 	 if(!conn->data_iobuf || conn->data_iobuf->GetDirection()!=dir)
2420 	    conn->data_iobuf=new IOBufferFDStream(new FDStream(conn->data_sock,"data-socket"),dir);
2421       }
2422       if(conn->t_mode=='Z') {
2423 	 if(mode==STORE)
2424 	    conn->AddDataTranslator(new DataDeflator(Query("mode-z-level",hostname)));
2425 	 else
2426 	    conn->AddDataTranslator(new DataInflator());
2427       }
2428       if(mode==LIST || mode==LONG_LIST || mode==MP_LIST)
2429       {
2430 	 const char *cset=conn->utf8_activated?"UTF-8":charset.get();
2431 	 if(cset && *cset)
2432 	    conn->AddDataTranslation(cset,true);
2433       }
2434       rate_limit->SetBufferSize(conn->data_iobuf,max_buf);
2435    /* fallthrough */
2436    case(DATA_OPEN_STATE):
2437    {
2438       if(expect->IsEmpty() && conn->data_sock!=-1)
2439       {
2440 	 // When ftp server has sent "Transfer complete" it is idle,
2441 	 // but the data can be still unsent in server side kernel buffer.
2442 	 // So the ftp server can decide the connection is idle for too long
2443 	 // time and disconnect. This hack is to prevent the above.
2444 	 if(now.UnixTime() >= conn->nop_time+nop_interval)
2445 	 {
2446 	    // prevent infinite NOOP's
2447 	    if(conn->nop_offset==pos
2448 	    && timeout_timer.GetLastSetting()<conn->nop_count*nop_interval)
2449 	    {
2450 	       LogError(1,"NOOP timeout");
2451 	       HandleTimeout();
2452 	       return MOVED;
2453 	    }
2454 	    if(conn->nop_time!=0)
2455 	    {
2456 	       conn->nop_count++;
2457 	       conn->SendCmd("NOOP");
2458 	       expect->Push(Expect::IGNORE);
2459 	    }
2460 	    conn->nop_time=now;
2461 	    if(conn->nop_offset!=pos)
2462 	       conn->nop_count=0;
2463 	    conn->nop_offset=pos;
2464 	 }
2465 	 TimeoutS(nop_interval-(time_t(now)-conn->nop_time));
2466       }
2467 
2468       oldstate=state;
2469 
2470       m|=FlushSendQueue();
2471       m|=ReceiveResp();
2472 
2473       if(state!=oldstate || Error())
2474 	 return MOVED;
2475 
2476       timeout_timer.Reset(conn->data_iobuf->EventTime());
2477       if(conn->data_iobuf->Error() && conn->data_sock!=-1)
2478       {
2479 	 LogError(0,"%s",conn->data_iobuf->ErrorText());
2480 	 conn->CloseDataSocket();
2481 	 // workaround for proftpd bug - it resets data connection when no files found.
2482 	 if(mode==LIST && expect->IsEmpty() && !conn->received_150 && conn->data_iobuf->GetPos()==0)
2483 	 {
2484 	    DataClose();
2485 	    state=EOF_STATE;
2486 	    eof=true;
2487 	    return MOVED;
2488 	 }
2489       }
2490       // handle errors on data connection only when storing or got all replies
2491       // and read all data.
2492       if(conn->data_iobuf->Error()
2493       && (mode==STORE || (expect->IsEmpty() && conn->data_iobuf->Size()==0)))
2494       {
2495 	 if(conn->data_iobuf->ErrorFatal())
2496 	    SetError(FATAL,conn->data_iobuf->ErrorText());
2497 	 if(!expect->IsEmpty())
2498 	    DisconnectNow();
2499 	 else
2500 	 {
2501 	    DataClose();
2502 	    state=EOF_STATE;
2503 	    if(mode==STORE && GetFlag(IO_FLAG))
2504 	       SetError(STORE_FAILED,0);
2505 	    else if(NextTry())
2506 	       retry_timer.Set(2); // retry after 2 seconds
2507 	 }
2508 	 return MOVED;
2509       }
2510       if(mode!=STORE)
2511       {
2512 	 if(conn->data_iobuf->Size()>=rate_limit->BytesAllowedToGet())
2513 	 {
2514 	    conn->data_iobuf->Suspend();
2515 	    TimeoutS(1);
2516 	 }
2517 	 else if(conn->data_iobuf->Size()>=max_buf)
2518 	 {
2519 	    conn->data_iobuf->Suspend();
2520 	    m=MOVED;
2521 	 }
2522 	 else if(conn->data_iobuf->IsSuspended() && !IsSuspended())
2523 	 {
2524 	    conn->data_iobuf->Resume();
2525 	    if(conn->data_iobuf->Size()>0)
2526 	       m=MOVED;
2527 	 }
2528 	 if(conn->data_iobuf->Size()==0
2529 	 && (conn->data_iobuf->Eof() || conn->data_iobuf->TranslationEOF()))
2530 	 {
2531 	    if(conn->data_iobuf->Eof())
2532 	       LogNote(9,"Got EOF on data connection");
2533 	    else if(conn->data_iobuf->TranslationEOF())
2534 	       LogNote(9,"Whole entity has been received and decoded");
2535 	    conn->data_iobuf->PutEOF(); // for ssl shutdown
2536 	    DataClose();
2537 	    if(expect->IsEmpty())
2538 	    {
2539 	       eof=true;
2540 	       m=MOVED;
2541 	    }
2542 	    state=WAITING_STATE;
2543 	 }
2544       }
2545 
2546       if(state!=oldstate || Error())
2547          return MOVED;
2548 
2549       CheckTimeout();
2550 
2551       if(state!=oldstate)
2552          return MOVED;
2553 
2554       goto usual_return;
2555    }
2556 
2557    pre_WAITING_STATE:
2558       if(copy_mode!=COPY_NONE)
2559 	 TrySuccess();	// it is enough to get here in copying.
2560       state=WAITING_STATE;
2561       m=MOVED;
2562    case(WAITING_STATE):
2563    {
2564       oldstate=state;
2565 
2566       m|=FlushSendQueue();
2567       m|=ReceiveResp();
2568 
2569       if(state!=oldstate || Error())
2570          return MOVED;
2571 
2572       // more work to do?
2573       if(expect->IsEmpty() && mode==ARRAY_INFO && fileset_for_info->curr())
2574       {
2575 	 SendArrayInfoRequests();
2576 	 return MOVED;
2577       }
2578 
2579       if(conn->data_iobuf)
2580       {
2581 	 if(expect->IsEmpty() && conn->data_sock==-1 && !conn->data_iobuf->Eof())
2582 	 {
2583 	    conn->data_iobuf->PutEOF();
2584 	    m=MOVED;
2585 	 }
2586 	 timeout_timer.Reset(conn->data_iobuf->EventTime());
2587 	 if(conn->data_iobuf->Eof() && conn->data_iobuf->Size()==0)
2588 	 {
2589 	    state=EOF_STATE;
2590 	    DataAbort();
2591 	    DataClose();
2592 	    idle_timer.Reset();
2593 	    eof=true;
2594 	    return MOVED;
2595 	 }
2596       }
2597 
2598       if(copy_mode==COPY_DEST && !copy_allow_store)
2599 	 goto notimeout_return;
2600 
2601       if(copy_mode==COPY_DEST && !copy_done && copy_connection_open
2602       && expect->Count()==1 && use_stat
2603       && !conn->ssl_is_activated() && !conn->proxy_is_http)
2604       {
2605 	 if(conn->stat_timer.Stopped())
2606 	 {
2607 	    // send STAT to know current position.
2608 	    SendUrgentCmd("STAT");
2609 	    expect->Push(Expect::TRANSFER);
2610 	    FlushSendQueue(true);
2611 	    m=MOVED;
2612 	 }
2613       }
2614 
2615       // FXP is special - no data connection at all.
2616       if(copy_mode!=COPY_NONE)
2617 	 goto notimeout_return;
2618 
2619       if(expect->IsEmpty() && !eof && !conn->data_iobuf)
2620       {
2621 	 eof=true;
2622 	 m=MOVED;
2623       }
2624 
2625       goto usual_return;
2626    }
2627    case WAITING_CCC_SHUTDOWN:
2628       if(conn->control_recv->Error())
2629       {
2630 	 if(conn->control_recv->ErrorFatal())
2631 	    SetError(FATAL,conn->control_recv->ErrorText());
2632 	 Disconnect(conn->control_recv->ErrorText());
2633 	 return MOVED;
2634       }
2635       if(conn->control_recv->Eof()
2636       || conn->waiting_ssl_timer.Stopped())
2637       {
2638 	 conn->MakeBuffers();
2639 	 goto pre_EOF_STATE;
2640       }
2641       break;
2642    } /* end of switch */
2643 usual_return:
2644    if(m==MOVED)
2645       return MOVED;
2646    if(conn && CheckTimeout())
2647       return MOVED;
2648 notimeout_return:
2649    if(m==MOVED)
2650       return MOVED;
2651    if(conn && conn->data_sock!=-1)
2652    {
2653       if(state==ACCEPTING_STATE)
2654 	 Block(conn->data_sock,POLLIN);
2655       else if(state==DATASOCKET_CONNECTING_STATE)
2656       {
2657 	 if(pasv_state==PASV_DATASOCKET_CONNECTING)
2658 	    Block(conn->data_sock,POLLOUT);
2659       }
2660    }
2661    if(conn && conn->control_sock!=-1)
2662    {
2663       if(state==CONNECTING_STATE)
2664 	 Block(conn->control_sock,POLLOUT);
2665    }
2666    return m;
2667 
2668 system_error:
2669    assert(saved_errno!=0);
2670    if(NonFatalError(saved_errno))
2671    {
2672       TimeoutS(1);
2673       return m;
2674    }
2675    DisconnectNow();
2676    SetError(SEE_ERRNO,0);
2677    return MOVED;
2678 }
2679 
2680 #if USE_SSL
SendAuth(const char * auth)2681 void Ftp::SendAuth(const char *auth)
2682 {
2683    if(conn->auth_sent || conn->ssl_is_activated())
2684       return;
2685    if(!conn->auth_supported)
2686    {
2687       if(QueryBool("ssl-force",hostname))
2688 	 SetError(LOGIN_FAILED,_("ftp:ssl-force is set and server does not support or allow SSL"));
2689       return;
2690    }
2691 
2692    if(conn->auth_args_supported)
2693    {
2694       char *a=alloca_strdup(conn->auth_args_supported);
2695       bool saw_ssl=false;
2696       bool saw_tls=false;
2697       for(a=strtok(a,";"); a; a=strtok(0,";"))
2698       {
2699 	 if(!strcasecmp(a,auth))
2700 	    break;
2701 	 if(!strcasecmp(a,"SSL"))
2702 	    saw_ssl=true;
2703 	 else if(!strcasecmp(a,"TLS"))
2704 	    saw_tls=true;
2705       }
2706       if(!a)
2707       {
2708 	 const char *old_auth=auth;
2709 	 if(saw_tls)
2710 	    auth="TLS";
2711 	 else if(saw_ssl)
2712 	    auth="SSL";
2713 	 LogError(1,"AUTH %s is not supported, using AUTH %s instead",old_auth,auth);
2714       }
2715    }
2716    conn->SendCmd2("AUTH",auth);
2717    expect->Push(Expect::AUTH_TLS);
2718    conn->auth_sent=true;
2719    conn->prot='\0'; // send PROT command always, for non-conforming servers
2720 }
SendPROT(char want_prot)2721 void Ftp::SendPROT(char want_prot)
2722 {
2723    if(want_prot==conn->prot || !conn->auth_supported)
2724       return;
2725    conn->SendCmdF("PROT %c",want_prot);
2726    expect->Push(new Expect(Expect::PROT,want_prot));
2727 }
2728 #endif // USE_SSL
2729 
SendSiteIdle()2730 void Ftp::SendSiteIdle()
2731 {
2732    if(!QueryBool("use-site-idle"))
2733       return;
2734    conn->SendCmd2("SITE IDLE",idle_timer.GetLastSetting().Seconds());
2735    expect->Push(Expect::IGNORE);
2736 }
SendUTimeRequest()2737 void Ftp::SendUTimeRequest()
2738 {
2739    if(entity_date==NO_DATE || !file)
2740       return;
2741 
2742    char d[15];
2743    time_t n=entity_date;
2744    strftime(d,sizeof(d),"%Y%m%d%H%M%S",gmtime(&n));
2745    d[sizeof(d)-1]=0;
2746 
2747    const char *file_to_append=path_to_send();
2748    if(conn->mfmt_supported)
2749    {
2750       conn->SendCmd2(xstring::format("MFMT %s",d),file_to_append,url::path_ptr(file_url),home);
2751       expect->Push(Expect::IGNORE);
2752    }
2753    else if(conn->mff_supported)
2754    {
2755       conn->SendCmd2(xstring::format("MFF modify=%s;",d),file_to_append,url::path_ptr(file_url),home);
2756       expect->Push(Expect::IGNORE);
2757    }
2758    else if(QueryBool("use-site-utime2") && conn->site_utime2_supported)
2759    {
2760       conn->SendCmd2(xstring::format("SITE UTIME %s",d),file_to_append,url::path_ptr(file_url),home);
2761       expect->Push(Expect::SITE_UTIME2);
2762    }
2763    else if(QueryBool("use-site-utime") && conn->site_utime_supported)
2764    {
2765       conn->SendCmd(xstring::format("SITE UTIME %s %s %s %s UTC",file_to_append,d,d,d));
2766       expect->Push(Expect::SITE_UTIME);
2767    }
2768    else if(QueryBool("use-mdtm-overloaded"))
2769    {
2770       conn->SendCmd2(xstring::format("MDTM %s",d),file_to_append,url::path_ptr(file_url),home);
2771       expect->Push(Expect::IGNORE);
2772    }
2773 }
QueryStringWithUserAtHost(const char * var)2774 const char *Ftp::QueryStringWithUserAtHost(const char *var)
2775 {
2776    const char *u=user?user.get():"anonymous";
2777    const char *h=hostname?hostname.get():"";
2778    const char *closure=xstring::cat(u,"@",h,NULL);
2779    const char *val=Query(var,closure);
2780    if(!val || !val[0])
2781       val=Query(var,hostname);
2782    if(!val || !val[0])
2783       return 0;
2784    return val;
2785 }
SendAcct()2786 void Ftp::SendAcct()
2787 {
2788    const char *acct=QueryStringWithUserAtHost("acct");
2789    if(!acct)
2790       return;
2791    conn->SendCmd2("ACCT",acct);
2792    expect->Push(Expect::IGNORE);
2793 }
SendSiteGroup()2794 void Ftp::SendSiteGroup()
2795 {
2796    const char *group=QueryStringWithUserAtHost("site-group");
2797    if(!group)
2798       return;
2799    conn->SendCmd2("SITE GROUP",group);
2800    expect->Push(Expect::IGNORE);
2801 }
SendSiteCommands()2802 void Ftp::SendSiteCommands()
2803 {
2804    const char *site_commands=QueryStringWithUserAtHost("site");
2805    if(!site_commands)
2806       return;
2807    char *cmd=alloca_strdup(site_commands);
2808    for(;;) {
2809       char *sep=strstr(cmd,"  ");
2810       if(sep)
2811 	 *sep=0;
2812       conn->SendCmd2("SITE",cmd);
2813       expect->Push(Expect::IGNORE);
2814       if(!sep)
2815 	 break;
2816       cmd=sep+2;
2817    }
2818 }
2819 
SendArrayInfoRequests()2820 void Ftp::SendArrayInfoRequests()
2821 {
2822    for(int i=fileset_for_info->curr_index(); i<fileset_for_info->count(); i++)
2823    {
2824       FileInfo *fi=(*fileset_for_info)[i];
2825       bool sent=false;
2826       if((fi->need&fi->DATE) && conn->mdtm_supported && use_mdtm)
2827       {
2828 	 conn->SendCmd2("MDTM",ExpandTildeStatic(fi->name));
2829 	 expect->Push(Expect::MDTM);
2830 	 sent=true;
2831       }
2832       if((fi->need&fi->SIZE) && conn->size_supported && use_size)
2833       {
2834 	 conn->SendCmd2("SIZE",ExpandTildeStatic(fi->name));
2835 	 expect->Push(Expect::SIZE);
2836 	 sent=true;
2837       }
2838       if(!sent)
2839       {
2840 	 if(i==fileset_for_info->curr_index())
2841 	    fileset_for_info->next();   // if it is the first one, just skip it.
2842 	 else
2843 	    break;	   // otherwise, wait until it is the first.
2844       }
2845       else
2846       {
2847 	 if(GetFlag(SYNC_MODE))
2848 	    break;	   // don't flood the queues.
2849       }
2850    }
2851 }
2852 
ReplyLogPriority(int code) const2853 int Ftp::ReplyLogPriority(int code) const
2854 {
2855    // Greeting messages
2856    if(code==220 || code==230)
2857       return 3;
2858    if(code==250 && mode==CHANGE_DIR)
2859       return 3;
2860    if(code==451 && mode==CLOSED)
2861       return 4;
2862    /* Most 5XXs go to level 4, as it's the job's responsibility to
2863     * print fatal errors. Some 5XXs are treated as 4XX's; send those
2864     * to level 0. (Maybe they should go to 1; we're going to retry them,
2865     * after all. */
2866    if(is5XX(code))
2867       return Transient5XX(code)? 0:4;
2868 
2869    if(is4XX(code))
2870       return 0;
2871 
2872    // 221 is the reply to QUIT, but we don't expect it.
2873    if(code==221 && !conn->quit_sent)
2874       return 0;
2875 
2876    return 4;
2877 }
2878 
ReceiveOneLine()2879 int Ftp::ReceiveOneLine()
2880 {
2881    const char *resp;
2882    int resp_size;
2883    conn->control_recv->Get(&resp,&resp_size);
2884    if(resp==0) // eof
2885    {
2886       if(!conn->quit_sent)
2887 	 LogError(0,_("Peer closed connection"));
2888       DisconnectNow();
2889       return -1;
2890    }
2891    if(resp_size==0)
2892       return 0;
2893    int line_len=0;
2894    int skip_len=0;
2895    // find <CR><NL> pair
2896    const char *nl=find_char(resp,resp_size,'\n');
2897    for(;;)
2898    {
2899       if(!nl)
2900       {
2901 	 if(conn->control_recv->Eof())
2902 	 {
2903 	    skip_len=line_len=resp_size;
2904 	    break;
2905 	 }
2906 	 return 0;
2907       }
2908       if(nl>resp && nl[-1]=='\r')
2909       {
2910 	 line_len=nl-resp-1;
2911 	 skip_len=nl-resp+1;
2912 	 break;
2913       }
2914       if(nl==resp+resp_size-1 && now-conn->control_recv->EventTime()>5)
2915       {
2916 	 LogError(1,"server bug: single <NL>");
2917 	 nl=find_char(resp,resp_size,'\n');
2918 	 line_len=nl-resp;
2919 	 skip_len=nl-resp+1;
2920 	 break;
2921       }
2922       nl=find_char(nl+1,resp_size-(nl+1-resp),'\n');
2923    }
2924 
2925    line.nset(resp,line_len);
2926    conn->control_recv->Skip(skip_len);
2927 
2928    // Change <CR><NUL> to <CR> according to RFC2640.
2929    // Other occurencies of <NUL> are changed to '!'.
2930    char *w=line.get_non_const();
2931    const char *r=w;
2932    for(int i=line.length(); i>0; i--,r++)
2933    {
2934       if(*r)
2935 	 *w++=*r;
2936       else if(r==line || r[-1]!='\r')
2937 	 *w++='!';
2938    }
2939    line.truncate(line.length()-(r-w));
2940    return line.length();
2941 }
2942 
ReceiveResp()2943 int  Ftp::ReceiveResp()
2944 {
2945    int m=STALL;
2946 
2947    if(!conn || !conn->control_recv)
2948       return m;
2949 
2950    timeout_timer.Reset(conn->control_recv->EventTime());
2951    if(conn->control_recv->Error())
2952    {
2953       LogError(0,"%s",conn->control_recv->ErrorText());
2954       if(conn->control_recv->ErrorFatal())
2955 	 SetError(FATAL,conn->control_recv->ErrorText());
2956       DisconnectNow();
2957       return MOVED;
2958    }
2959 
2960    for(;;)  // handle all lines in buffer, one line per loop
2961    {
2962       if(!conn || !conn->control_recv)
2963 	 return m;
2964 
2965       int res=ReceiveOneLine();
2966       if(res==-1)
2967 	 return MOVED;
2968       if(res==0)
2969 	 return m;
2970 
2971       int code=0;
2972       if(line.length()>=3 && is_ascii_digit(line[0])
2973       && is_ascii_digit(line[1]) && is_ascii_digit(line[2]))
2974 	 sscanf(line,"%3d",&code);
2975 
2976       if(conn->multiline_code && conn->multiline_code!=code
2977       && QueryBool("ftp:strict-multiline",closure))
2978 	 code=0;  // reply can only terminate with the same code
2979 
2980       int log_prio=ReplyLogPriority(conn->multiline_code?conn->multiline_code:code);
2981 
2982       bool is_first_line=(line[3]=='-' && conn->multiline_code==0);
2983       bool is_last_line=(line[3]!='-' && code!=0);
2984 
2985       bool is_data=(!expect->IsEmpty() && expect->FirstIs(Expect::QUOTED) && conn->data_iobuf);
2986       int data_offset=0;
2987       if(is_data && mode==LONG_LIST)
2988       {
2989 	 if(code && !is2XX(code))
2990 	    is_data=false;
2991 	 if(code && line.length()>4)
2992 	 {
2993 	    data_offset=4;
2994 	    if(is_first_line && strstr(line+data_offset,"FTP server status"))
2995 	    {
2996 	       TurnOffStatForList();
2997 	       is_data=false;
2998 	    }
2999 	    if((is_first_line && !strncasecmp(line+data_offset,"Stat",4))
3000 	    || (is_last_line  && !strncasecmp(line+data_offset,"End",3)))
3001 	       is_data=false;
3002 	 }
3003       }
3004       if(is_data && conn->data_iobuf)
3005       {
3006 	 if(line[data_offset]==' ')
3007 	    data_offset++;
3008 	 conn->data_iobuf->Put(line+data_offset,line.length()-data_offset);
3009 	 conn->data_iobuf->Put("\n");
3010 	 log_prio=10;
3011       }
3012       LogRecv(log_prio,line);
3013 
3014       if(conn->multiline_code==0 || all_lines.length()==0)
3015 	 all_lines.set(line); // not continuation
3016       else if(all_lines.length()<0x4000)
3017 	 all_lines.vappend("\n",line.get(),NULL);
3018 
3019       if(code==0)
3020 	 continue;
3021 
3022       if(line[3]=='-')
3023       {
3024 	 if(conn->multiline_code==0)
3025 	    conn->multiline_code=code;
3026 	 continue;
3027       }
3028       if(conn->multiline_code && line[3]!=' ')
3029 	 continue; // The space is required to terminate multiline reply
3030       conn->multiline_code=0;
3031 
3032       if(!is1XX(code)) {
3033 	 if(conn->sync_wait>0)
3034 	    conn->sync_wait--; // clear the flag to send next command
3035 	 else {
3036 	    if(code!=421) {
3037 	       LogError(3,_("extra server response"));
3038 	       return m;
3039 	    }
3040 	 }
3041       }
3042 
3043       CheckResp(code);
3044       m=MOVED;
3045       if(error_code==NO_FILE || error_code==LOGIN_FAILED)
3046       {
3047 	 if(error_code==LOGIN_FAILED)
3048 	    reconnect_timer.Reset();	// count the reconnect-interval from this moment
3049 	 if(persist_retries++<max_persist_retries)
3050 	 {
3051 	    error_code=OK;
3052 	    Disconnect();
3053 	    LogNote(4,_("Persist and retry"));
3054 	    return m;
3055 	 }
3056       }
3057    }
3058    return m;
3059 }
3060 
HttpProxySendAuth(const SMTaskRef<IOBuffer> & buf)3061 void Ftp::HttpProxySendAuth(const SMTaskRef<IOBuffer>& buf)
3062 {
3063    if(!proxy_user || !proxy_pass)
3064       return;
3065    xstring& auth=xstring::cat(proxy_user.get(),":",proxy_pass.get(),NULL);
3066    int auth_len=auth.length();
3067    char *buf64=string_alloca(base64_length(auth_len)+1);
3068    base64_encode(auth,buf64,auth_len);
3069    buf->Format("Proxy-Authorization: Basic %s\r\n",buf64);
3070    Log::global->Format(4,"+--> Proxy-Authorization: Basic %s\r\n",buf64);
3071 }
HttpProxySendConnect()3072 void Ftp::HttpProxySendConnect()
3073 {
3074    const char *the_port=portname?portname.get():ftps?FTPS_DEFAULT_PORT:FTP_DEFAULT_PORT;
3075    conn->control_send->Format("CONNECT %s:%s HTTP/1.0\r\n",hostname.get(),the_port);
3076    Log::global->Format(4,"+--> CONNECT %s:%s HTTP/1.0\n",hostname.get(),the_port);
3077    HttpProxySendAuth(conn->control_send);
3078    conn->control_send->Put("\r\n");
3079    http_proxy_status_code=0;
3080 }
HttpProxySendConnectData()3081 void Ftp::HttpProxySendConnectData()
3082 {
3083    const char *the_host=SocketNumericAddress(&conn->data_sa);
3084    int the_port=SocketPort(&conn->data_sa);
3085    conn->data_iobuf->Format("CONNECT %s:%d HTTP/1.0\r\n",the_host,the_port);
3086    Log::global->Format(4,"+--> CONNECT %s:%d HTTP/1.0\n",the_host,the_port);
3087    HttpProxySendAuth(conn->data_iobuf);
3088    conn->data_iobuf->Put("\r\n");
3089    http_proxy_status_code=0;
3090 }
3091 // Check reply and return true when the reply is received and is ok.
HttpProxyReplyCheck(const SMTaskRef<IOBuffer> & buf)3092 bool Ftp::HttpProxyReplyCheck(const SMTaskRef<IOBuffer>& buf)
3093 {
3094    const char *b;
3095    int s;
3096    buf->Get(&b,&s);
3097    const char *nl=b?(const char*)memchr(b,'\n',s):0;
3098    if(!nl)
3099    {
3100       if(buf->Error())
3101       {
3102 	 LogError(0,"%s",buf->ErrorText());
3103 	 if(buf->ErrorFatal())
3104 	    SetError(FATAL,buf->ErrorText());
3105       }
3106       else if(buf->Eof())
3107 	 LogError(0,_("Peer closed connection"));
3108       if(conn && (buf->Eof() || buf->Error()))
3109 	 DisconnectNow();
3110       return false;
3111    }
3112 
3113    char *line=string_alloca(nl-b);
3114    memcpy(line,b,nl-b-1);	 // don't copy \r
3115    line[nl-b-1]=0;
3116    buf->Skip(nl-b+1);	 // skip \r\n too.
3117 
3118    Log::global->Format(4,"<--+ %s\n",line);
3119 
3120    if(!http_proxy_status_code)
3121    {
3122       if(1!=sscanf(line,"HTTP/%*d.%*d %d",&http_proxy_status_code)
3123       || !is2XX(http_proxy_status_code))
3124       {
3125 	 // check for retriable codes
3126 	 if(http_proxy_status_code==408 // Request Timeout
3127 	 || http_proxy_status_code==502 // Bad Gateway
3128 	 || http_proxy_status_code==503 // Service Unavailable
3129 	 || http_proxy_status_code==504)// Gateway Timeout
3130 	 {
3131 	    DisconnectNow();
3132 	    return false;
3133 	 }
3134 	 SetError(FATAL,line);
3135 	 return false;
3136       }
3137    }
3138    if(!*line)
3139       return true;
3140    return false;
3141 }
3142 
SendUrgentCmd(const char * cmd)3143 void Ftp::SendUrgentCmd(const char *cmd)
3144 {
3145    if(!use_telnet_iac || !conn->telnet_layer_send)
3146    {
3147       conn->SendCmd(cmd);
3148       return;
3149    }
3150 
3151    static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_DM};
3152 
3153 #if USE_SSL
3154    if(conn->ssl_is_activated())
3155    {
3156       // no way to send urgent data over ssl, send normally.
3157       conn->telnet_layer_send->Buffer::Put(pre_cmd,4);
3158    }
3159    else // note the following block
3160 #endif
3161    {
3162       int fl=fcntl(conn->control_sock,F_GETFL);
3163       fcntl(conn->control_sock,F_SETFL,fl&~O_NONBLOCK);
3164       FlushSendQueue(/*all=*/true);
3165       if(!conn || !conn->control_send)
3166 	 return;
3167       if(conn->control_send->Size()>0)
3168 	 conn->control_send->Roll();
3169       // only DM byte is to be sent in urgent mode
3170       send(conn->control_sock,pre_cmd,3,0);
3171       send(conn->control_sock,pre_cmd+3,1,MSG_OOB);
3172       fcntl(conn->control_sock,F_SETFL,fl);
3173    }
3174    conn->SendCmd(cmd);
3175 }
3176 
DataAbort()3177 void  Ftp::DataAbort()
3178 {
3179    if(!conn || state==CONNECTING_STATE || conn->quit_sent)
3180       return;
3181 
3182    if(conn->data_sock==-1 && copy_mode==COPY_NONE)
3183       return; // nothing to abort
3184 
3185    if(copy_mode!=COPY_NONE)
3186    {
3187       if(expect->IsEmpty())
3188 	 return; // the transfer seems to be finished
3189       if(!copy_addr_valid)
3190 	 return; // data connection cannot be established at this time
3191       if(!copy_connection_open && expect->FirstIs(Expect::TRANSFER))
3192       {
3193 	 // wu-ftpd-2.6.0 cannot interrupt accept() or connect().
3194 	 DisconnectNow();
3195 	 return;
3196       }
3197    }
3198    copy_connection_open=false;
3199 
3200    // if transfer has been completed then ABOR is not needed
3201    if(conn->data_sock!=-1 && expect->IsEmpty())
3202       return;
3203 
3204    expect->Close();
3205 
3206    if(!QueryBool("use-abor",hostname)
3207    || expect->Count()>1 || conn->proxy_is_http)
3208    {
3209       // check that we have a data socket to close, and the server is not
3210       // in uninterruptible accept() state.
3211       if(copy_mode==COPY_NONE
3212       && !(GetFlag(PASSIVE_MODE) && state==DATASOCKET_CONNECTING_STATE
3213            && (pasv_state==PASV_NO_ADDRESS_YET || pasv_state==PASV_HAVE_ADDRESS)))
3214 	 DataClose();	// just close data connection
3215       else
3216       {
3217 	 // otherwise, just close control connection.
3218 	 DisconnectNow();
3219       }
3220       return;
3221    }
3222 
3223    if(conn->aborted_data_sock!=-1)  // don't allow double ABOR.
3224    {
3225       DisconnectNow();
3226       return;
3227    }
3228 
3229    SendUrgentCmd("ABOR");
3230    expect->Push(Expect::ABOR);
3231    FlushSendQueue(true);
3232    conn->abor_close_timer.Reset();
3233 
3234    // don't close it now, wait for ABOR result
3235    conn->AbortDataConnection();
3236 
3237    // ABOR over SSL connection does not always work,
3238    // closing data socket should help it.
3239    if(conn->ssl_is_activated())
3240       conn->CloseAbortedDataConnection();
3241 
3242    if(QueryBool("web-mode"))
3243       Disconnect();
3244 }
3245 
ControlClose()3246 void Ftp::ControlClose()
3247 {
3248    if(conn && conn->control_send)
3249       conn->control_send->PutEOF();
3250    conn=0;
3251    expect=0;
3252 }
3253 
DisconnectNow()3254 void  Ftp::DisconnectNow()
3255 {
3256    DataClose();
3257    ControlClose();
3258    state=INITIAL_STATE;
3259    http_proxy_status_code=0;
3260 
3261    if(copy_mode!=COPY_NONE)
3262    {
3263       if(copy_addr_valid)
3264 	 copy_failed=true;
3265    }
3266    else
3267    {
3268       if(mode==STORE && GetFlag(IO_FLAG))
3269 	 SetError(STORE_FAILED,0);
3270       else if(fragile && GetFlag(IO_FLAG))
3271 	 SetError(FRAGILE_FAILED,0);
3272    }
3273    copy_addr_valid=false;
3274 }
3275 
DisconnectLL()3276 void  Ftp::DisconnectLL()
3277 {
3278    if(!conn)
3279       return;
3280 
3281    if(conn->quit_sent)
3282       return;
3283 
3284    /* protect against re-entering from FlushSendQueue */
3285    static bool disconnect_in_progress=false;
3286    if(disconnect_in_progress)
3287       return;
3288    disconnect_in_progress=true;
3289 
3290    bool no_greeting=(!expect->IsEmpty() && expect->FirstIs(Expect::READY));
3291 
3292    expect->Close();
3293    DataAbort();
3294    DataClose();
3295    if(conn && state!=CONNECTING_STATE && state!=HTTP_PROXY_CONNECTED
3296    && expect->Count()<2 && QueryBool("use-quit",hostname))
3297    {
3298       conn->SendCmd("QUIT");
3299       expect->Push(Expect::IGNORE);
3300       conn->quit_sent=true;
3301       goto out;
3302    }
3303    ControlClose();
3304 
3305    if(state==CONNECTING_STATE || no_greeting)
3306       NextPeer();
3307 
3308    DisconnectNow();
3309 
3310 out:
3311    disconnect_on_close=false;
3312    Timeout(0);
3313 
3314    disconnect_in_progress=false;
3315 }
3316 
CloseDataSocket()3317 void Ftp::Connection::CloseDataSocket()
3318 {
3319    if(data_sock==-1)
3320       return;
3321    LogNote(7,_("Closing data socket"));
3322    close(data_sock);
3323    data_sock=-1;
3324 }
3325 
CloseDataConnection()3326 void Ftp::Connection::CloseDataConnection()
3327 {
3328    data_iobuf=0;
3329    fixed_pasv=false;
3330    CloseDataSocket();
3331 }
AbortDataConnection()3332 void Ftp::Connection::AbortDataConnection()
3333 {
3334    CloseAbortedDataConnection();
3335    aborted_data_sock=data_sock;
3336    data_sock=-1;
3337    CloseDataConnection(); // clean up all other members.
3338 }
CloseAbortedDataConnection()3339 void Ftp::Connection::CloseAbortedDataConnection()
3340 {
3341    if(aborted_data_sock!=-1)
3342    {
3343       LogNote(9,_("Closing aborted data socket"));
3344       close(aborted_data_sock);
3345       aborted_data_sock=-1;
3346    }
3347 }
3348 
DataClose()3349 void  Ftp::DataClose()
3350 {
3351    rate_limit=0;
3352    if(!conn)
3353       return;
3354    conn->nop_time=0;
3355    conn->nop_offset=0;
3356    conn->nop_count=0;
3357    if(conn->data_sock!=-1 && QueryBool("web-mode"))
3358       disconnect_on_close=true;
3359    conn->CloseDataConnection();
3360    if(state==DATA_OPEN_STATE || state==DATASOCKET_CONNECTING_STATE)
3361       state=WAITING_STATE;
3362 }
3363 
FlushSendQueueOneCmd()3364 int Ftp::Connection::FlushSendQueueOneCmd()
3365 {
3366    const char *send_cmd_ptr;
3367    int send_cmd_count;
3368    send_cmd_buffer.Get(&send_cmd_ptr,&send_cmd_count);
3369 
3370    if(send_cmd_count==0)
3371       return 0;
3372 
3373    const char *cmd_begin=send_cmd_ptr;
3374    const char *line_end=(const char*)memchr(send_cmd_ptr,'\n',send_cmd_count);
3375    if(!line_end)
3376       return 0;
3377 
3378    int to_write=line_end+1-send_cmd_ptr;
3379    control_send->Put(send_cmd_ptr,to_write);
3380    send_cmd_buffer.Skip(to_write);
3381    sync_wait++;
3382 
3383    int log_level=5;
3384 
3385    if(!may_show_password && !strncasecmp(cmd_begin,"PASS ",5))
3386       LogSend(log_level,"PASS XXXX");
3387    else
3388    {
3389       xstring log;
3390       for(const char *s=cmd_begin; s<=line_end; s++)
3391       {
3392 	 if(*s==0)
3393 	    log.append("<NUL>");
3394 	 else if(*s==TELNET_IAC && telnet_layer_send)
3395 	 {
3396 	    s++;
3397 	    if(*s==TELNET_IAC)
3398 	       log.append('\377');
3399 	    else if(*s==TELNET_IP)
3400 	       log.append("<IP>");
3401 	    else if(*s==TELNET_DM)
3402 	       log.append("<DM>");
3403 	 }
3404 	 else
3405 	   log.append(*s?*s:'!');
3406       }
3407       LogSend(log_level,log);
3408    }
3409    return 1;
3410 }
3411 
FlushSendQueue(bool all)3412 int  Ftp::FlushSendQueue(bool all)
3413 {
3414    int m=STALL;
3415 
3416    if(!conn || !conn->control_send)
3417       return m;
3418 
3419    if(conn->control_send->Error())
3420    {
3421       LogError(0,"%s",conn->control_send->ErrorText());
3422       if(conn->control_send->ErrorFatal())
3423       {
3424 #if USE_SSL
3425 	 if(conn->ssl_is_activated() && !ftps && !QueryBool("ssl-force",hostname)
3426 	 && !conn->control_ssl->cert_error)
3427 	 {
3428 	    // retry without ssl
3429 	    ResMgr::Set("ftp:ssl-allow",hostname,"no");
3430 	    DontSleep();
3431 	 }
3432 	 else
3433 #endif
3434 	    SetError(FATAL,conn->control_send->ErrorText());
3435       }
3436       DisconnectNow();
3437       return MOVED;
3438    }
3439 
3440    if(conn->send_cmd_buffer.Size()==0)
3441       return m;
3442 
3443    while(conn->sync_wait<=0 || all || !GetFlag(SYNC_MODE))
3444    {
3445       int res=conn->FlushSendQueueOneCmd();
3446       if(!res)
3447 	 break;
3448       m|=MOVED;
3449    }
3450 
3451    if(m==MOVED)
3452       conn->control_send->Roll();
3453    timeout_timer.Reset(conn->control_send->EventTime());
3454 
3455    return m;
3456 }
3457 
Send(const char * buf)3458 void  Ftp::Connection::Send(const char *buf)
3459 {
3460    while(*buf)
3461    {
3462       char ch=*buf++;
3463       send_cmd_buffer.Put(&ch,1);
3464       if(ch=='\r')
3465 	 send_cmd_buffer.PutRaw("",1); // RFC2640
3466    }
3467 }
SendEncoded(const char * buf)3468 void  Ftp::Connection::SendEncoded(const char *buf)
3469 {
3470    while(*buf)
3471    {
3472       char ch=*buf++;
3473       if(ch=='%' && isxdigit((unsigned char)buf[0]) && isxdigit((unsigned char)buf[1]))
3474       {
3475 	 int n=0;
3476 	 if(sscanf(buf,"%2x",&n)==1)
3477 	 {
3478 	    buf+=2;
3479 	    ch=n;
3480 	    // don't translate encoded bytes
3481 	    send_cmd_buffer.PutRaw(&ch,1);
3482 	    send_cmd_buffer.ResetTranslation();
3483 	    goto next;
3484 	 }
3485       }
3486       send_cmd_buffer.Put(&ch,1);
3487 next: if(ch=='\r')
3488 	 send_cmd_buffer.PutRaw("",1); // RFC2640
3489    }
3490 }
3491 
SendCRNL()3492 void Ftp::Connection::SendCRNL()
3493 {
3494    send_cmd_buffer.PutRaw("\r\n",2);
3495    send_cmd_buffer.ResetTranslation();
3496 }
3497 
SendCmd(const char * cmd)3498 void Ftp::Connection::SendCmd(const char *cmd)
3499 {
3500    Send(cmd);
3501    SendCRNL();
3502 }
3503 
SendURI(const char * u,const char * home)3504 void Ftp::Connection::SendURI(const char *u,const char *home)
3505 {
3506    if(u[0]=='/' && u[1]=='~')
3507       u++;
3508    else if(!strncasecmp(u,"/%2F",4))
3509    {
3510       Send("/");
3511       u+=4;
3512    }
3513    else if(home && strcmp(home,"/"))
3514       Send(home);
3515    SendEncoded(u);
3516 }
3517 
SendCmd2(const char * cmd,const char * f,const char * u,const char * home)3518 void Ftp::Connection::SendCmd2(const char *cmd,const char *f,const char *u,const char *home)
3519 {
3520    if(cmd && cmd[0])
3521    {
3522       Send(cmd);
3523       send_cmd_buffer.Put(" ",1);
3524    }
3525    if(u)
3526       SendURI(u,home);
3527    else
3528       Send(f);
3529    SendCRNL();
3530 }
3531 
SendCmd2(const char * cmd,int v)3532 void Ftp::Connection::SendCmd2(const char *cmd,int v)
3533 {
3534    char buf[32];
3535    snprintf(buf,sizeof(buf),"%d",v);
3536    SendCmd2(cmd,buf);
3537 }
3538 
SendCmdF(const char * f,...)3539 void Ftp::Connection::SendCmdF(const char *f,...)
3540 {
3541    va_list v;
3542    va_start(v,f);
3543    xstring& s=xstring::vformat(f,v);
3544    va_end(v);
3545    SendCmd(s);
3546 }
3547 
AddDataTranslator(DataTranslator * t)3548 void Ftp::Connection::AddDataTranslator(DataTranslator *t)
3549 {
3550    if(data_iobuf->GetTranslator())
3551       data_iobuf=new IOBufferStacked(data_iobuf.borrow());
3552    data_iobuf->SetTranslator(t);
3553 }
AddDataTranslation(const char * charset,bool translit)3554 void Ftp::Connection::AddDataTranslation(const char *charset,bool translit)
3555 {
3556 #ifdef HAVE_ICONV
3557    if(data_iobuf->GetTranslator())
3558       data_iobuf=new IOBufferStacked(data_iobuf.borrow());
3559    data_iobuf->SetTranslation(charset,translit);
3560 #endif
3561 }
3562 
SendEOT()3563 int   Ftp::SendEOT()
3564 {
3565    if(mode!=STORE)
3566       return(OK); /* nothing to do */
3567 
3568    if(state!=DATA_OPEN_STATE)
3569       return(DO_AGAIN);
3570 
3571    if(!conn->data_iobuf->Eof())
3572       conn->data_iobuf->PutEOF();
3573 
3574    if(!conn->data_iobuf->Done())
3575       return(DO_AGAIN);
3576 
3577    DataClose();
3578    state=WAITING_STATE;
3579    return(OK);
3580 }
3581 
Close()3582 void  Ftp::Close()
3583 {
3584    if(mode!=CLOSED)
3585       idle_timer.Reset();
3586 
3587    flags&=~NOREST_MODE;	// can depend on a particular file
3588    eof=false;
3589 
3590    Resume();
3591    ExpandTildeInCWD();
3592    DataAbort();
3593    DataClose();
3594    if(conn)
3595    {
3596       expect->Close();
3597       switch(state)
3598       {
3599       case(CONNECTING_STATE):
3600       case(HTTP_PROXY_CONNECTED):
3601       case(CONNECTED_STATE):
3602       case(USER_RESP_WAITING_STATE):
3603 	 Disconnect();
3604 	 break;
3605       case(ACCEPTING_STATE):
3606       case(DATASOCKET_CONNECTING_STATE):
3607       case(CWD_CWD_WAITING_STATE):
3608       case(WAITING_STATE):
3609       case(DATA_OPEN_STATE):
3610       case(WAITING_150_STATE):
3611 	 state=EOF_STATE;
3612 	 break;
3613       case(INITIAL_STATE):
3614       case(EOF_STATE):
3615       case(WAITING_CCC_SHUTDOWN):
3616 	 break;
3617       }
3618    }
3619    else
3620    {
3621       state=INITIAL_STATE;
3622    }
3623    copy_mode=COPY_NONE;
3624    copy_protect=false;
3625    copy_ssl_connect=false;
3626    copy_addr_valid=false;
3627    copy_done=false;
3628    copy_connection_open=false;
3629    copy_allow_store=false;
3630    copy_failed=false;
3631    super::Close();
3632    if(disconnect_on_close)
3633       Disconnect();
3634 }
3635 
ExpectQueue()3636 Ftp::ExpectQueue::ExpectQueue()
3637 {
3638    first=0;
3639    last=&first;
3640    count=0;
3641 }
~ExpectQueue()3642 Ftp::ExpectQueue::~ExpectQueue()
3643 {
3644    while(first)
3645       delete Pop();
3646 }
Push(Expect * e)3647 void Ftp::ExpectQueue::Push(Expect *e)
3648 {
3649    *last=e;
3650    last=&e->next;
3651    e->next=0;
3652    count++;
3653 }
Push(Expect::expect_t e)3654 void Ftp::ExpectQueue::Push(Expect::expect_t e)
3655 {
3656    Push(new Expect(e));
3657 }
Pop()3658 Ftp::Expect *Ftp::ExpectQueue::Pop()
3659 {
3660    if(!first)
3661       return 0;
3662    Expect *res=first;
3663    first=first->next;
3664    if(last==&res->next)
3665       last=&first;
3666    res->next=0;
3667    count--;
3668    return res;
3669 }
Has(Expect::expect_t cc) const3670 bool Ftp::ExpectQueue::Has(Expect::expect_t cc) const
3671 {
3672    for(const Expect *scan=first; scan; scan=scan->next)
3673       if(cc==scan->check_case)
3674 	 return true;
3675    return false;
3676 }
FirstIs(Expect::expect_t cc) const3677 bool Ftp::ExpectQueue::FirstIs(Expect::expect_t cc) const
3678 {
3679    if(first && first->check_case==cc)
3680       return true;
3681    return false;
3682 }
Close()3683 void Ftp::ExpectQueue::Close()
3684 {
3685    for(Expect *scan=first; scan; scan=scan->next)
3686    {
3687       switch(scan->check_case)
3688       {
3689       case(Expect::IGNORE):
3690       case(Expect::PWD):
3691       case(Expect::USER):
3692       case(Expect::USER_PROXY):
3693       case(Expect::PASS):
3694       case(Expect::PASS_PROXY):
3695       case(Expect::OPEN_PROXY):
3696       case(Expect::ACCT_PROXY):
3697       case(Expect::READY):
3698       case(Expect::ABOR):
3699       case(Expect::CWD_STALE):
3700       case(Expect::PRET):
3701       case(Expect::PASV):
3702       case(Expect::EPSV):
3703       case(Expect::TRANSFER_CLOSED):
3704       case(Expect::FEAT):
3705       case(Expect::SITE_UTIME):
3706       case(Expect::SITE_UTIME2):
3707       case(Expect::TYPE):
3708       case(Expect::MODE):
3709       case(Expect::LANG):
3710       case(Expect::OPTS_UTF8):
3711       case(Expect::ALLO):
3712 #if USE_SSL
3713       case(Expect::AUTH_TLS):
3714       case(Expect::PROT):
3715       case(Expect::SSCN):
3716       case(Expect::CCC):
3717 #endif
3718 	 break;
3719       case(Expect::CWD_CURR):
3720       case(Expect::CWD):
3721 	 scan->check_case=Expect::CWD_STALE;
3722 	 break;
3723       case(Expect::NONE):
3724       case(Expect::REST):
3725       case(Expect::SIZE):
3726       case(Expect::SIZE_OPT):
3727       case(Expect::MDTM):
3728       case(Expect::MDTM_OPT):
3729       case(Expect::PORT):
3730       case(Expect::FILE_ACCESS):
3731       case(Expect::RNFR):
3732       case(Expect::QUOTED):
3733 	 scan->check_case=Expect::IGNORE;
3734 	 break;
3735       case(Expect::TRANSFER):
3736 	 scan->check_case=Expect::TRANSFER_CLOSED;
3737 	 break;
3738       }
3739    }
3740 }
FindLastCWD() const3741 Ftp::Expect *Ftp::ExpectQueue::FindLastCWD() const
3742 {
3743    Expect *last_cwd=0;
3744    for(Expect *scan=first; scan; scan=scan->next)
3745    {
3746       switch(scan->check_case)
3747       {
3748       case(Expect::CWD_CURR):
3749       case(Expect::CWD_STALE):
3750       case(Expect::CWD):
3751 	 last_cwd=scan;
3752       default:
3753 	 ;
3754       }
3755    }
3756    return last_cwd;
3757 }
3758 
IOReady()3759 bool  Ftp::IOReady()
3760 {
3761    if(copy_mode!=COPY_NONE && !copy_passive && !copy_addr_valid)
3762       return true;   // simulate to be ready as other fxp peer has to go
3763    if(Error())
3764       return true;   // report ready to propagate the error.
3765    return (state==DATA_OPEN_STATE || state==WAITING_STATE)
3766       && real_pos!=-1 && IsOpen();
3767 }
3768 
SuspendInternal()3769 void Ftp::SuspendInternal()
3770 {
3771    super::SuspendInternal();
3772    if(conn)
3773       conn->SuspendInternal();
3774 }
ResumeInternal()3775 void Ftp::ResumeInternal()
3776 {
3777    if(conn)
3778       conn->ResumeInternal();
3779    super::ResumeInternal();
3780 }
3781 
CanRead()3782 int   Ftp::CanRead()
3783 {
3784    if(Error())
3785       return(error_code);
3786 
3787    if(mode==CLOSED || eof)
3788       return(0);
3789 
3790    if(!conn || !conn->data_iobuf)
3791       return DO_AGAIN;
3792 
3793    if(expect->Has(Expect::REST) && real_pos==-1)
3794       return DO_AGAIN;
3795 
3796    if(state==DATASOCKET_CONNECTING_STATE)
3797       return DO_AGAIN;
3798 
3799    int size=conn->data_iobuf->Size();
3800    if(state==DATA_OPEN_STATE)
3801    {
3802       assert(rate_limit!=0);
3803       int allowed=rate_limit->BytesAllowedToGet();
3804       if(allowed==0)
3805 	 return DO_AGAIN;
3806       if(size>allowed)
3807 	 size=allowed;
3808    }
3809    if(norest_manual && real_pos==0 && pos>0)
3810       return DO_AGAIN;
3811    if(size==0)
3812       return DO_AGAIN;
3813    return size;
3814 }
3815 
Read(Buffer * buf,int size)3816 int   Ftp::Read(Buffer *buf,int size)
3817 {
3818    int size1=CanRead();
3819    if(size1<=0)
3820       return size1;
3821    if(size>size1)
3822       size=size1;
3823 
3824    int skip=0;
3825    if(real_pos+size<pos)
3826       skip=size;
3827    else if(real_pos<pos)
3828       skip=pos-real_pos;
3829 
3830    if(skip>0)
3831    {
3832       conn->data_iobuf->Skip(skip);
3833       rate_limit->BytesGot(skip);
3834       real_pos+=skip;
3835       size-=skip;
3836       if(size<=0)
3837 	 return DO_AGAIN;
3838    }
3839 
3840    assert(real_pos==pos);
3841 
3842    size=buf->MoveDataHere(conn->data_iobuf,size);
3843    if(size<=0)
3844       return DO_AGAIN;
3845    rate_limit->BytesGot(size);
3846    real_pos+=size;
3847    pos+=size;
3848 
3849    TrySuccess();
3850    flags|=IO_FLAG;
3851 
3852    return(size);
3853 }
3854 
3855 /*
3856    Write - send data to ftp server
3857 
3858    * Uploading is not reliable in this realization *
3859    Well, not less reliable than in any usual ftp client.
3860 
3861    The reason for this is uncheckable receiving of data on the remote end.
3862    Since that, we have to leave re-putting up to caller.
3863    Fortunately, class FileCopy does it.
3864 */
Write(const void * buf,int size)3865 int   Ftp::Write(const void *buf,int size)
3866 {
3867    if(mode!=STORE)
3868       return(0);
3869 
3870    if(Error())
3871       return(error_code);
3872 
3873    if(!conn || state!=DATA_OPEN_STATE || (expect->Has(Expect::REST) && real_pos==-1))
3874       return DO_AGAIN;
3875 
3876    if(!conn->data_iobuf)
3877       return DO_AGAIN;
3878 
3879    {
3880       assert(rate_limit!=0);
3881       int allowed=rate_limit->BytesAllowedToPut();
3882       if(allowed==0)
3883 	 return DO_AGAIN;
3884       if(size>allowed)
3885 	 size=allowed;
3886    }
3887    if(size+conn->data_iobuf->Size()>=max_buf)
3888       size=max_buf-conn->data_iobuf->Size();
3889    if(size<=0)
3890       return 0;
3891 
3892    conn->data_iobuf->Put((const char*)buf,size);
3893 
3894    if(retries+persist_retries>0
3895    && conn->data_iobuf->GetPos()>Buffered()+0x20000)
3896    {
3897       // reset retry count if some data were actually written to server.
3898       LogNote(10,"resetting retry count");
3899       TrySuccess();
3900    }
3901 
3902    assert(rate_limit!=0);
3903    rate_limit->BytesPut(size);
3904    pos+=size;
3905    real_pos+=size;
3906    flags|=IO_FLAG;
3907    return(size);
3908 }
3909 
StoreStatus()3910 int   Ftp::StoreStatus()
3911 {
3912    if(Error())
3913       return(error_code);
3914 
3915    if(mode!=STORE)
3916       return(OK);
3917 
3918    if(state==DATA_OPEN_STATE)
3919    {
3920       // have not send EOT by SendEOT, do it now
3921       SendEOT();
3922    }
3923 
3924    if(state==WAITING_STATE && expect->IsEmpty())
3925    {
3926       eof=true;
3927       return(OK);
3928    }
3929 
3930    return(IN_PROGRESS);
3931 }
3932 
MoveConnectionHere(Ftp * o)3933 void  Ftp::MoveConnectionHere(Ftp *o)
3934 {
3935    expect=o->expect.borrow();
3936    expect->Close(); // we need not handle other session's replies.
3937 
3938    assert(o->conn->data_iobuf==0);
3939 
3940    conn=o->conn.borrow();
3941    conn->ResumeInternal();
3942    o->state=INITIAL_STATE;
3943 
3944    line.move_here(o->line);
3945    all_lines.move_here(o->all_lines);
3946 
3947    if(peer_curr>=peer.count())
3948       peer_curr=0;
3949    timeout_timer.Reset(o->timeout_timer);
3950 
3951    if(!home)
3952       set_home(home_auto);
3953 
3954    set_real_cwd(o->real_cwd);
3955    o->Disconnect();
3956    state=EOF_STATE;
3957 }
3958 
SendOPTS_MLST()3959 void Ftp::SendOPTS_MLST()
3960 {
3961    char *facts=alloca_strdup(conn->mlst_attr_supported);
3962    char *store=facts;
3963    bool differs=false;
3964    for(char *tok=strtok(facts,";"); tok; tok=strtok(0,";"))
3965    {
3966       bool was_enabled=false;
3967       bool want_enable=false;
3968       int len=strlen(tok);
3969       if(len>0 && tok[len-1]=='*')
3970       {
3971 	 was_enabled=true;
3972 	 tok[--len]=0;
3973       }
3974       // "unique" not needed yet.
3975       static const char *const needed[]={
3976 	 "type","size","modify","perm",
3977 	 "UNIX.mode","UNIX.owner","UNIX.uid","UNIX.group","UNIX.gid",
3978 	 0};
3979       for(const char *const *scan=needed; *scan; scan++)
3980       {
3981 	 if(!strcasecmp(tok,*scan))
3982 	 {
3983 	    memmove(store,tok,len);
3984 	    store+=len;
3985 	    *store++=';';
3986 	    want_enable=true;
3987 	    break;
3988 	 }
3989       }
3990       differs|=(was_enabled^want_enable);
3991    }
3992    if(!differs || store==facts)
3993       return;
3994    *store=0;
3995    conn->SendCmd2("OPTS MLST",facts);
3996    expect->Push(Expect::IGNORE);
3997 }
3998 
TuneConnectionAfterFEAT()3999 void Ftp::TuneConnectionAfterFEAT()
4000 {
4001    if(conn->clnt_supported)
4002    {
4003       const char *client=Query("client",hostname);
4004       if(client && client[0])
4005       {
4006 	 conn->SendCmd2("CLNT",client);
4007 	 expect->Push(Expect::IGNORE);
4008       }
4009    }
4010    if(conn->lang_supported)
4011    {
4012       const char *lang_to_use=Query("lang",hostname);
4013       if(lang_to_use && lang_to_use[0])
4014 	 conn->SendCmd2("LANG",lang_to_use);
4015       else
4016 	 conn->SendCmd("LANG");
4017       expect->Push(Expect::LANG);
4018    }
4019    if(conn->utf8_supported && QueryBool("use-utf8",hostname))
4020    {
4021       // some non-RFC2640 servers require this command.
4022       conn->SendCmd("OPTS UTF8 ON");
4023       expect->Push(Expect::OPTS_UTF8);
4024    }
4025    if(conn->host_supported)
4026    {
4027       conn->SendCmd2("HOST",hostname);
4028       expect->Push(Expect::IGNORE);
4029    }
4030    if(conn->cepr_supported)
4031    {
4032        conn->SendCmd("CEPR on");
4033        expect->Push(Expect::IGNORE);
4034    }
4035 
4036    if(conn->try_feat_after_login && conn->mlst_attr_supported)
4037       SendOPTS_MLST();
4038    if(proxy && !conn->cepr_supported) // proxies with cepr can do epsv.
4039       conn->epsv_supported=false;
4040 }
4041 
CheckFEAT(char * reply,const char * line,bool trust)4042 void Ftp::Connection::CheckFEAT(char *reply,const char *line,bool trust)
4043 {
4044    if(trust) {
4045       // turn off these pre-FEAT extensions only when trusting FEAT reply,
4046       // as some servers forget to advertise them.
4047       mdtm_supported=false;
4048       size_supported=false;
4049       rest_supported=false;
4050       tvfs_supported=false;
4051    }
4052 #if USE_SSL
4053    auth_supported=false;
4054    auth_args_supported.set(0);
4055    cpsv_supported=false;
4056    sscn_supported=false;
4057 #endif
4058    pret_supported=false;
4059    epsv_supported=false;
4060    tvfs_supported=false;
4061    mode_z_supported=false;
4062    cepr_supported=false;
4063 
4064    char *scan=strchr(reply,'\n');
4065    if(scan)
4066       scan++;
4067    if(!scan || !*scan)
4068       return;
4069 
4070    for(char *f=strtok(scan,"\r\n"); f; f=strtok(0,"\r\n"))
4071    {
4072       if(!strncmp(f,line,3))
4073       {
4074 	 if(f[3]==' ')
4075 	    break;   // last line
4076 	 if(f[3]=='-')
4077 	    f+=4;    // workaround for broken servers, RFC2389 does not allow it.
4078       }
4079       while(*f==' ')
4080 	 f++;
4081 
4082       if(!strcasecmp(f,"UTF8"))
4083 	 utf8_supported=true;
4084       else if(!strncasecmp(f,"LANG ",5))
4085 	 lang_supported=true;
4086       else if(!strcasecmp(f,"PRET"))
4087 	 pret_supported=true;
4088       else if(!strcasecmp(f,"MDTM"))
4089 	 mdtm_supported=true;
4090       else if(!strcasecmp(f,"SIZE"))
4091 	 size_supported=true;
4092       else if(!strcasecmp(f,"CLNT") || !strncasecmp(f,"CLNT ",5))
4093 	 clnt_supported=true;
4094       else if(!strcasecmp(f,"HOST"))
4095 	 host_supported=true;
4096       else if(!strcasecmp(f,"MFMT"))
4097 	 mfmt_supported=true;
4098       else if(!strcasecmp(f,"MFF"))
4099 	 mff_supported=true;
4100       else if(!strncasecmp(f,"REST ",5)) // FIXME: actually REST STREAM
4101 	 rest_supported=true;
4102       else if(!strcasecmp(f,"REST"))
4103 	 rest_supported=true;
4104       else if(!strncasecmp(f,"MLST ",5))
4105       {
4106 	 mlst_supported=true;
4107 	 mlst_attr_supported.set(f+5);
4108       }
4109       else if(!strcasecmp(f,"EPSV"))
4110 	 epsv_supported=true;
4111       else if(!strcasecmp(f,"TVFS"))
4112 	 tvfs_supported=true;
4113       else if(!strncasecmp(f,"MODE Z",6)) {
4114 	 mode_z_supported=true;
4115 	 mode_z_opts_supported.set(f[6]==' '?f+7:NULL);
4116       }
4117       else if(!strcasecmp(f,"SITE SYMLINK"))
4118 	 site_symlink_supported=true;
4119       else if(!strcasecmp(f,"SITE MKDIR"))
4120 	 site_mkdir_supported=true;
4121 #if USE_SSL
4122       else if(!strncasecmp(f,"AUTH ",5))
4123       {
4124 	 auth_supported=true;
4125 	 if(auth_args_supported)
4126 	    auth_args_supported.vappend(";",f+5,NULL);
4127 	 else
4128 	    auth_args_supported.append(f+5);
4129       }
4130       else if(!strcasecmp(f,"AUTH"))
4131 	 auth_supported=true;
4132       else if(!strcasecmp(f,"CPSV"))
4133 	 cpsv_supported=true;
4134       else if(!strcasecmp(f,"SSCN"))
4135 	 sscn_supported=true;
4136       else if(!strcasecmp(f,"CEPR"))
4137 	 cepr_supported=true;
4138 #endif // USE_SSL
4139    }
4140    if(!trust) {
4141       // turn on EPSV support based on some other modern features
4142       epsv_supported|=mlst_supported|host_supported;
4143 #if USE_SSL
4144       // same for AUTH
4145       auth_supported|=epsv_supported;
4146 #endif
4147    }
4148    have_feat_info=true;
4149 }
4150 
TurnOffStatForList()4151 void Ftp::TurnOffStatForList()
4152 {
4153    DataClose();
4154    expect->Close();
4155    state=EOF_STATE;
4156    LogNote(2,"Setting ftp:use-stat-for-list to off");
4157    ResMgr::Set("ftp:use-stat-for-list",hostname,"off");
4158    use_stat_for_list=false;
4159 }
4160 
CheckResp(int act)4161 void Ftp::CheckResp(int act)
4162 {
4163    // close aborted accepting data socket when the connection is established
4164    if(is1XX(act) && GetFlag(PASSIVE_MODE) && conn->aborted_data_sock!=-1)
4165       conn->CloseAbortedDataConnection();
4166 
4167    if(is1XX(act) && expect->FirstIs(Expect::TRANSFER))
4168    {
4169       // allow data transfer
4170       conn->received_150=true;
4171 
4172       if(state==WAITING_STATE)
4173       {
4174 	 // set the FXP flag
4175 	 copy_connection_open=true;
4176 	 conn->stat_timer.ResetDelayed(2);
4177       }
4178 
4179       if(mode==RETRIEVE && entity_size<0 && QueryBool("catch-size",hostname))
4180       {
4181 	 // try to catch size
4182 	 const char *s=strrchr(line,'(');
4183 	 if(s && is_ascii_digit(s[1]))
4184 	 {
4185 	    long long size_ll;
4186 	    int n;
4187 	    if(1<=sscanf(s+1,"%lld%n",&size_ll,&n)
4188 	    && !strncmp(s+1+n," bytes",6))
4189 	    {
4190 	       entity_size=size_ll;
4191 	       if(opt_size)
4192 		  *opt_size=entity_size;
4193 	       LogNote(7,_("saw file size in response"));
4194 	    }
4195 	 }
4196       }
4197    }
4198 
4199    if(is1XX(act)) // intermediate responses are ignored
4200       return;
4201 
4202    if(act==421) { // server is going to disconnect:
4203       // don't try sending QUIT.
4204       conn->quit_sent=true;
4205       // adjust connection limit
4206       const char *rexp=Query("too-many-re",hostname);
4207       if(re_match(line,rexp,REG_ICASE)) {
4208 	 SiteData *data=GetSiteData();
4209 	 if(data->GetConnectionLimit()==0)
4210 	    data->SetConnectionLimit(CountConnections());
4211 	 data->DecreaseConnectionLimit();
4212       }
4213    }
4214 
4215    Expect *exp=expect->Pop();
4216    if(!exp)
4217    {
4218       if(act!=421)
4219 	 LogError(3,_("extra server response"));
4220       if(is2XX(act)) // some buggy servers send several 226 replies
4221 	 return;
4222       Disconnect(line);
4223       return;
4224    }
4225 
4226    Expect::expect_t cc=exp->check_case;
4227    const char *arg=exp->arg;
4228 
4229    // some servers mess all up
4230    if(act==331 && cc==Expect::READY && !GetFlag(SYNC_MODE) && expect->Count()>1)
4231    {
4232       delete expect->Pop();
4233       LogNote(2,_("Turning on sync-mode"));
4234       ResMgr::Set("ftp:sync-mode",hostname,"on");
4235       Disconnect();
4236       DontSleep(); // retry immediately
4237       goto leave;
4238    }
4239 
4240    switch(cc)
4241    {
4242    case Expect::NONE:
4243       if(is2XX(act)) // default rule.
4244 	 break;
4245       Disconnect(line);
4246       break;
4247 
4248    case Expect::QUOTED:
4249       if(mode==LONG_LIST && !is2XX(act) && use_stat_for_list)
4250 	 TurnOffStatForList();
4251       break;
4252    case Expect::IGNORE:
4253    ignore:
4254       break;
4255 
4256    case Expect::READY:
4257    case Expect::OPEN_PROXY:
4258       if(!GetFlag(SYNC_MODE) && re_match(all_lines,Query("auto-sync-mode",hostname)))
4259       {
4260 	 LogNote(2,_("Turning on sync-mode"));
4261 	 ResMgr::Set("ftp:sync-mode",hostname,"on");
4262 	 assert(GetFlag(SYNC_MODE));
4263 	 Disconnect();
4264 	 DontSleep(); // retry immediately
4265       }
4266       if(!is2XX(act))
4267       {
4268 	 Disconnect(line);
4269 	 if(cc==Expect::OPEN_PROXY && act==403)
4270 	 {
4271 	    SetError(LOGIN_FAILED,all_lines);
4272 	    break;
4273 	 }
4274 	 NextPeer();
4275 	 if(peer_curr==0)
4276 	    reconnect_timer.Reset();  // count the reconnect-interval from this moment
4277 	 last_connection_failed=true;
4278       }
4279       break;
4280 
4281    case Expect::REST:
4282       RestCheck(act);
4283       break;
4284 
4285    case Expect::CWD:
4286    case Expect::CWD_CURR:
4287       if(is2XX(act))
4288       {
4289 	 if(cc==Expect::CWD)
4290 	    cwd.Set(file,false,file_url,device_prefix_len(file));
4291 	 set_real_cwd(arg);
4292 	 cache->SetDirectory(this, arg, true);
4293 	 break;
4294       }
4295       if(is5XX(act))
4296       {
4297 	 if(!strcmp(arg,"~")) {
4298 	    // reconnect will change CWD to home directory
4299 	    Disconnect();
4300 	    DontSleep();
4301 	    break;
4302 	 }
4303 	 SetError(NO_FILE,all_lines);
4304 	 cache->SetDirectory(this, arg, false);
4305 	 break;
4306       }
4307       Disconnect(line);
4308       break;
4309 
4310    case Expect::CWD_STALE:
4311       if(is2XX(act))
4312 	 set_real_cwd(arg);
4313       goto ignore;
4314 
4315    case Expect::ABOR:
4316       conn->CloseAbortedDataConnection();
4317       goto ignore;
4318 
4319    case Expect::SIZE:
4320       CatchSIZE(act);
4321       break;
4322    case Expect::SIZE_OPT:
4323       CatchSIZE_opt(act);
4324       break;
4325    case Expect::MDTM:
4326       CatchDATE(act);
4327       break;
4328    case Expect::MDTM_OPT:
4329       CatchDATE_opt(act);
4330       break;
4331 
4332    case Expect::FILE_ACCESS:
4333    file_access:
4334       if(mode==CHANGE_MODE && site_cmd_unsupported(act))
4335       {
4336 	 if(exp->cmd.begins_with("SITE CHMOD"))
4337 	    conn->site_chmod_supported=false;
4338 	 else if(exp->cmd.begins_with("MFF"))
4339 	    conn->mff_supported=false;
4340 	 SetError(NO_FILE,all_lines);
4341 	 break;
4342       }
4343       if(mode==SYMLINK && site_cmd_unsupported(act))
4344       {
4345 	 if(exp->cmd.begins_with("SITE SYMLINK"))
4346 	    conn->site_symlink_supported=false;
4347 	 SetError(NO_FILE,all_lines);
4348 	 break;
4349       }
4350       NoFileCheck(act);
4351       break;
4352 
4353    case Expect::PRET:
4354       if(cmd_unsupported(act))
4355       {
4356 	 conn->pret_supported=false;
4357 	 break;
4358       }
4359       if(is5XX(act))
4360 	 SetError(NO_FILE,all_lines);
4361       break;
4362 
4363    case Expect::ALLO:
4364       if(cmd_unsupported(act) || act==202)
4365 	 ResMgr::Set("ftp:use-allo",hostname,"no");
4366       else if(is5XX(act))
4367 	 SetError(NO_FILE,all_lines);
4368       break;
4369 
4370    case Expect::PASV:
4371    case Expect::EPSV:
4372       if(is2XX(act))
4373       {
4374 	 if(line.length()<=4)
4375 	    goto passive_off;
4376 
4377 	 memset(&conn->data_sa,0,sizeof(conn->data_sa));
4378 
4379 	 if(cc==Expect::PASV)
4380 	    pasv_state=Handle_PASV();
4381 	 else // cc==Expect::EPSV
4382              if(conn->cepr_supported)
4383                 pasv_state=Handle_EPSV_CEPR();
4384              else
4385                 pasv_state=Handle_EPSV();
4386 
4387 	 if(pasv_state==PASV_NO_ADDRESS_YET)
4388 	    goto passive_off;
4389 
4390 	 if(conn->aborted_data_sock!=-1)
4391 	    SocketConnect(conn->aborted_data_sock,&conn->data_sa);
4392 
4393 	 break;
4394       }
4395       if(cmd_unsupported(act) && cc==Expect::EPSV
4396       && conn->can_do_pasv && QueryBool("prefer-epsv",hostname))
4397       {
4398 	 ResMgr::Set("ftp:prefer-epsv",hostname,"no");
4399 	 Disconnect("EPSV failed, will try PASV");
4400 	 DontSleep(); // retry immediately
4401 	 break;
4402       }
4403       if(copy_mode!=COPY_NONE)
4404       {
4405 	 copy_passive=!copy_passive;
4406 	 copy_failed=true;
4407 	 break;
4408       }
4409       if(is5XX(act))
4410       {
4411       passive_off:
4412 	 if(QueryBool("auto-passive-mode",hostname))
4413 	 {
4414 	    LogNote(2,_("Switching passive mode off"));
4415 	    SetFlag(PASSIVE_MODE,0);
4416 	 }
4417       }
4418       Disconnect(line);
4419       break;
4420 
4421    case Expect::PORT:
4422       if(is2XX(act))
4423 	 break;
4424       if(copy_mode!=COPY_NONE)
4425       {
4426 	 copy_passive=!copy_passive;
4427 	 copy_failed=true;
4428 	 break;
4429       }
4430       if(is5XX(act))
4431       {
4432 	 if(QueryBool("auto-passive-mode",hostname))
4433 	 {
4434 	    LogNote(2,_("Switching passive mode on"));
4435 	    SetFlag(PASSIVE_MODE,1);
4436 	 }
4437       }
4438       Disconnect(line);
4439       break;
4440 
4441    case Expect::PWD:
4442       if(is2XX(act))
4443       {
4444 	 if(!home_auto)
4445 	 {
4446 	    home_auto.set_allocated(ExtractPWD());
4447 	    PropagateHomeAuto();
4448 	 }
4449 	 if(!home)
4450 	    set_home(home_auto);
4451 	 cache->SetDirectory(this, home, true);
4452 	 break;
4453       }
4454       break;
4455 
4456    case Expect::RNFR:
4457       if(is3XX(act))
4458       {
4459 	 conn->SendCmd2("RNTO",file1);
4460 	 expect->Push(Expect::FILE_ACCESS);
4461 	 break;
4462       }
4463       goto file_access;
4464 
4465    case Expect::USER_PROXY:
4466       proxy_NoPassReqCheck(act);
4467       break;
4468    case Expect::USER:
4469       NoPassReqCheck(act);
4470       break;
4471    case Expect::PASS_PROXY:
4472    case Expect::ACCT_PROXY:
4473       proxy_LoginCheck(act);
4474       break;
4475    case Expect::PASS:
4476       LoginCheck(act);
4477       break;
4478 
4479    case Expect::TRANSFER:
4480       TransferCheck(act);
4481       if(mode==STORE && is2XX(act)
4482       && entity_size==real_pos)
4483 	 SendUTimeRequest();
4484       break;
4485 
4486    case Expect::TRANSFER_CLOSED:
4487       if(strstr(line,"ABOR")
4488       && expect->Count()>=2 && expect->FirstIs(Expect::ABOR))
4489       {
4490 	 LogError(1,"server bug: 426 reply missed");
4491 	 delete expect->Pop();
4492 	 conn->CloseAbortedDataConnection();
4493       }
4494       break;
4495    case Expect::FEAT:
4496       if(is2XX(act))
4497       {
4498 	 conn->CheckFEAT(all_lines.get_non_const(),line,QueryBool("trust-feat",hostname));
4499 	 if(conn->try_feat_after_login && conn->have_feat_info)
4500 	    TuneConnectionAfterFEAT();
4501       }
4502       else if(is5XX(act) && !cmd_unsupported(act))
4503 	 conn->try_feat_after_login=true;
4504       break;
4505 
4506    case Expect::SITE_UTIME:
4507       if(site_cmd_unsupported(act))
4508       {
4509 	 conn->site_utime_supported=false;
4510 	 SendUTimeRequest();  // try another method.
4511       }
4512       break;
4513    case Expect::SITE_UTIME2:
4514       if(site_cmd_unsupported(act))
4515       {
4516 	 conn->site_utime2_supported=false;
4517 	 SendUTimeRequest();  // try another method.
4518       }
4519       break;
4520    case Expect::TYPE:
4521       if(is2XX(act))
4522 	 conn->type=arg[0];
4523       break;
4524    case Expect::MODE:
4525       if(is2XX(act))
4526 	 conn->t_mode=arg[0];
4527       break;
4528    case Expect::OPTS_UTF8:
4529    case Expect::LANG:
4530       if(is2XX(act) && conn->utf8_supported)
4531       {
4532 	 conn->utf8_activated=true;
4533 	 conn->SetControlConnectionTranslation("UTF-8");
4534       }
4535       else if(is5XX(act) && !cmd_unsupported(act))
4536 	 conn->tune_after_login=true;
4537       break;
4538 
4539 #if USE_SSL
4540    case Expect::AUTH_TLS:
4541       if(is2XX(act) || is3XX(act))
4542       {
4543 	 conn->MakeSSLBuffers(hostname);
4544       }
4545       else
4546       {
4547 	 if(QueryBool("ssl-force",hostname))
4548 	    SetError(LOGIN_FAILED,_("ftp:ssl-force is set and server does not support or allow SSL"));
4549 	 conn->prot='C';
4550 	 conn->auth_supported=false;
4551       }
4552       break;
4553    case Expect::PROT:
4554       if(is2XX(act))
4555 	 conn->prot=arg[0];
4556       else if(!conn->prot)
4557 	 conn->prot=(ftps?'P':'C');
4558       break;
4559    case Expect::SSCN:
4560       if(is2XX(act))
4561 	 conn->sscn_on=(arg[0]=='Y');
4562       else if(cmd_unsupported(act))
4563 	 conn->sscn_supported=false;
4564       break;
4565    case Expect::CCC:
4566       if(is2XX(act))
4567       {
4568 	 conn->control_send->PutEOF();
4569 	 state=WAITING_CCC_SHUTDOWN;
4570 	 conn->waiting_ssl_timer.Reset();
4571       }
4572       break;
4573 #endif // USE_SSL
4574 
4575    } /* end switch */
4576 leave:
4577    delete exp;
4578 }
4579 
CurrentStatus()4580 const char *Ftp::CurrentStatus()
4581 {
4582    if(Error())
4583       return StrError(error_code);
4584    if(expect && expect->Has(Expect::FEAT))
4585       return _("FEAT negotiation...");
4586    switch(state)
4587    {
4588    case(EOF_STATE):
4589       if(conn && conn->control_sock!=-1)
4590       {
4591 	 if(conn->send_cmd_buffer.Size()>0)
4592 	    return(_("Sending commands..."));
4593 	 if(!expect->IsEmpty())
4594 	    return(_("Waiting for response..."));
4595 	 if(!retry_timer.Stopped())
4596 	    return _("Delaying before retry");
4597 	 return(_("Connection idle"));
4598       }
4599       return(_("Not connected"));
4600    case(INITIAL_STATE):
4601       if(hostname)
4602       {
4603 	 if(resolver)
4604 	    return(_("Resolving host address..."));
4605 	 if(!ReconnectAllowed())
4606 	    return DelayingMessage();
4607       }
4608       return(_("Not connected"));
4609    case(CONNECTING_STATE):
4610    case(HTTP_PROXY_CONNECTED):
4611       return(_("Connecting..."));
4612    case(CONNECTED_STATE):
4613 #if USE_SSL
4614       if(conn->auth_sent)
4615 	 return _("TLS negotiation...");
4616 #endif
4617       return _("Connected");
4618    case(USER_RESP_WAITING_STATE):
4619       return(_("Logging in..."));
4620    case(DATASOCKET_CONNECTING_STATE):
4621       if(pasv_state==PASV_NO_ADDRESS_YET)
4622 	 return(_("Waiting for response..."));
4623       return(_("Making data connection..."));
4624    case(CWD_CWD_WAITING_STATE):
4625       if(expect->FindLastCWD())
4626 	 return(_("Changing remote directory..."));
4627       /*fallthrough*/
4628    case(WAITING_STATE):
4629       if(copy_mode==COPY_SOURCE)
4630 	 return "";
4631       if(copy_mode==COPY_DEST && expect->IsEmpty())
4632 	 return _("Waiting for other copy peer...");
4633       if(mode==STORE)
4634 	 return(_("Waiting for transfer to complete"));
4635    case(WAITING_150_STATE):
4636       return(_("Waiting for response..."));
4637    case(WAITING_CCC_SHUTDOWN):
4638       return(_("Waiting for TLS shutdown..."));
4639    case(ACCEPTING_STATE):
4640       return(_("Waiting for data connection..."));
4641    case(DATA_OPEN_STATE):
4642 #if USE_SSL
4643       if(conn->prot=='P')
4644       {
4645 	 if(mode==STORE)
4646 	    return(_("Sending data/TLS"));
4647          else
4648 	    return(_("Receiving data/TLS"));
4649       }
4650 #endif
4651       if(conn->data_sock!=-1)
4652       {
4653 	 if(mode==STORE)
4654 	    return(_("Sending data"));
4655          else
4656 	    return(_("Receiving data"));
4657       }
4658       return(_("Waiting for transfer to complete"));
4659    }
4660    abort();
4661 }
4662 
ConvertFtpDate(const char * s)4663 time_t	 Ftp::ConvertFtpDate(const char *s)
4664 {
4665    struct tm tm;
4666    memset(&tm,0,sizeof(tm));
4667    int year,month,day,hour,minute,second;
4668 
4669    int skip=0;
4670    int n=sscanf(s,"%4d%n",&year,&skip);
4671 
4672    // try to workaround server's y2k bug
4673    // I hope in the next 300 years the y2k bug will be finally fixed :)
4674    if(n==1 && year>=1910 && year<=1930)
4675    {
4676       n=sscanf(s,"%5d%n",&year,&skip);
4677       year=year-19100+2000;
4678    }
4679 
4680    if(n!=1)
4681       return NO_DATE;
4682 
4683    n=sscanf(s+skip,"%2d%2d%2d%2d%2d",&month,&day,&hour,&minute,&second);
4684 
4685    if(n!=5)
4686       return NO_DATE;
4687 
4688    tm.tm_year=year-1900;
4689    tm.tm_mon=month-1;
4690    tm.tm_mday=day;
4691    tm.tm_hour=hour;
4692    tm.tm_min=minute;
4693    tm.tm_sec=second;
4694 
4695    return mktime_from_utc(&tm);
4696 }
4697 
SetFlag(int flag,bool val)4698 void  Ftp::SetFlag(int flag,bool val)
4699 {
4700    flag&=MODES_MASK;  // only certain flags can be changed
4701    if(val)
4702       flags|=flag;
4703    else
4704       flags&=~flag;
4705 }
4706 
SameSiteAs(const FileAccess * fa) const4707 bool  Ftp::SameSiteAs(const FileAccess *fa) const
4708 {
4709    if(!SameProtoAs(fa))
4710       return false;
4711    Ftp *o=(Ftp*)fa;
4712    return(!xstrcasecmp(hostname,o->hostname) && !xstrcmp(portname,o->portname)
4713    && !xstrcmp(user,o->user) && !xstrcmp(pass,o->pass)
4714    && ftps==o->ftps);
4715 }
4716 
SameConnection(const Ftp * o) const4717 bool  Ftp::SameConnection(const Ftp *o) const
4718 {
4719    if(!strcasecmp(hostname,o->hostname) && !xstrcmp(portname,o->portname)
4720    && !xstrcmp(user,o->user) && !xstrcmp(pass,o->pass)
4721    && (user || !xstrcmp(anon_user,o->anon_user))
4722    && (pass || !xstrcmp(anon_pass,o->anon_pass))
4723    && ftps==o->ftps)
4724       return true;
4725    return false;
4726 }
4727 
SameLocationAs(const FileAccess * fa) const4728 bool  Ftp::SameLocationAs(const FileAccess *fa) const
4729 {
4730    if(!SameProtoAs(fa))
4731       return false;
4732    Ftp *o=(Ftp*)fa;
4733    if(!hostname || !o->hostname)
4734       return false;
4735    if(SameConnection(o))
4736    {
4737       if(home && o->home && strcmp(home,o->home))
4738 	 return false;
4739       return !xstrcmp(cwd,o->cwd);
4740    }
4741    return false;
4742 }
4743 
Done()4744 int   Ftp::Done()
4745 {
4746    if(Error())
4747       return(error_code);
4748 
4749    if(mode==CLOSED)
4750       return OK;
4751 
4752    if(mode==ARRAY_INFO)
4753    {
4754       if(state==WAITING_STATE && expect->IsEmpty() && !fileset_for_info->curr())
4755 	 return(OK);
4756       return(IN_PROGRESS);
4757    }
4758 
4759    if(copy_mode==COPY_DEST && !copy_allow_store)
4760       return(IN_PROGRESS);
4761 
4762    if(mode==CHANGE_DIR || mode==RENAME
4763    || mode==MAKE_DIR || mode==REMOVE_DIR || mode==REMOVE || mode==CHANGE_MODE
4764    || mode==LINK || mode==SYMLINK
4765    || copy_mode!=COPY_NONE)
4766    {
4767       if(state==WAITING_STATE && expect->IsEmpty())
4768 	 return(OK);
4769       return(IN_PROGRESS);
4770    }
4771    if(mode==CONNECT_VERIFY)
4772    {
4773       if(state!=INITIAL_STATE)
4774 	 return OK;
4775       return(peer?OK:IN_PROGRESS);
4776    }
4777    abort();
4778 }
4779 
ResetLocationData()4780 void Ftp::ResetLocationData()
4781 {
4782    super::ResetLocationData();
4783    flags=0;
4784    home_auto.set(FindHomeAuto());
4785    Reconfig();
4786    state=INITIAL_STATE;
4787 }
4788 
AnonymousQuietMode()4789 bool Ftp::AnonymousQuietMode()
4790 {
4791    if(user && user.ne("anonymous") && user.ne("ftp"))
4792       return false;
4793    const char *pass_to_use=(pass?pass:anon_pass);
4794    return pass_to_use && *pass_to_use=='-';  // minus sign in password means quiet mode
4795 }
4796 
Reconfig(const char * name)4797 void Ftp::Reconfig(const char *name)
4798 {
4799    closure.set(hostname);
4800    super::Reconfig(name);
4801 
4802    if(!xstrcmp(name,"net:idle") || !xstrcmp(name,"ftp:use-site-idle"))
4803    {
4804       if(conn && conn->data_sock==-1 && state==EOF_STATE && !conn->quit_sent)
4805 	 SendSiteIdle();
4806       return;
4807    }
4808 
4809    SetFlag(SYNC_MODE,	QueryBool("sync-mode"));
4810    if(!conn || !conn->proxy_is_http)
4811       SetFlag(PASSIVE_MODE,QueryBool("passive-mode"));
4812    rest_list = QueryBool("rest-list");
4813 
4814    nop_interval = Query("nop-interval").to_number(1,30);
4815 
4816    allow_skey = QueryBool("skey-allow");
4817    force_skey = QueryBool("skey-force");
4818    allow_netkey = QueryBool("netkey-allow");
4819    verify_data_address = QueryBool("verify-address");
4820    verify_data_port = QueryBool("verify-port");
4821 
4822    use_stat = QueryBool("use-stat");
4823    use_stat_for_list=QueryBool("use-stat-for-list") && !AnonymousQuietMode();
4824    use_mdtm = QueryBool("use-mdtm");
4825    use_size = QueryBool("use-size");
4826    use_feat = QueryBool("use-feat");
4827    use_mlsd = QueryBool("use-mlsd");
4828 
4829    use_telnet_iac = QueryBool("use-telnet-iac");
4830 
4831    max_buf = Query("xfer:buffer-size");
4832 
4833    anon_user.set(Query("anon-user"));
4834    anon_pass.set(Query("anon-pass"));
4835 
4836    if(!name || !xstrcmp(name,"ftp:list-options"))
4837    {
4838       if(name && !IsSuspended())
4839 	 cache->TreeChanged(this,"/");
4840       list_options.set(Query("list-options"));
4841    }
4842 
4843    if(!name || !xstrcmp(name,"ftp:charset"))
4844    {
4845       if(name && !IsSuspended())
4846 	 cache->TreeChanged(this,"/");
4847       charset.set(Query("charset"));
4848       if(conn && conn->have_feat_info && !conn->utf8_activated
4849       && !(expect->Has(Expect::LANG) || expect->Has(Expect::OPTS_UTF8))
4850       && charset && *charset)
4851 	 conn->SetControlConnectionTranslation(charset);
4852    }
4853 
4854    const char *h=QueryStringWithUserAtHost("home");
4855    if(h && h[0] && AbsolutePath(h))
4856       set_home(h);
4857    else
4858       set_home(home_auto);
4859 
4860    if(NoProxy(hostname))
4861       SetProxy(0);
4862    else
4863       SetProxy(Query("proxy"));
4864 
4865    if(proxy && proxy_port==0)
4866    {
4867       if(ProxyIsHttp())
4868 	 proxy_port.set(HTTP_DEFAULT_PROXY_PORT);
4869       else
4870 	 proxy_port.set(FTP_DEFAULT_PORT);
4871    }
4872 
4873    if(conn && conn->control_sock!=-1)
4874       SetSocketBuffer(conn->control_sock);
4875    if(conn && conn->data_sock!=-1)
4876       SetSocketBuffer(conn->data_sock);
4877    if(conn && conn->data_iobuf && rate_limit)
4878       rate_limit->SetBufferSize(conn->data_iobuf,max_buf);
4879 }
4880 
SetError(int ec,const char * e)4881 void Ftp::SetError(int ec,const char *e)
4882 {
4883    // join multiline error message into single line, removing `550-' prefix.
4884    if(e && strchr(e,'\n'))
4885    {
4886       char *joined=string_alloca(strlen(e)+1);
4887       const char *prefix=e;
4888       char *store=joined;
4889       while(*e)
4890       {
4891 	 if(*e=='\n')
4892 	 {
4893 	    if(e[1])
4894 	       *store++=' ';
4895 	    e++;
4896 	    if(!strncmp(e,prefix,3) && (e[3]=='-' || e[3]==' '))
4897 	       e+=4;
4898 	 }
4899 	 else
4900 	 {
4901 	    *store++=*e++;
4902 	 }
4903       }
4904       *store=0;
4905       e=joined;
4906    }
4907    super::SetError(ec,e);
4908 
4909    switch((status)ec)
4910    {
4911    case(SEE_ERRNO):
4912    case(LOOKUP_ERROR):
4913    case(NO_HOST):
4914    case(FATAL):
4915    case(LOGIN_FAILED):
4916       Disconnect();
4917       break;
4918    case(IN_PROGRESS):
4919    case(OK):
4920    case(NOT_OPEN):
4921    case(NO_FILE):
4922    case(FILE_MOVED):
4923    case(STORE_FAILED):
4924    case(DO_AGAIN):
4925    case(NOT_SUPP):
4926    case(FRAGILE_FAILED):
4927       break;
4928    }
4929 }
4930 
GetConnectLevel() const4931 Ftp::ConnectLevel Ftp::GetConnectLevel() const
4932 {
4933    if(!conn)
4934       return CL_NOT_CONNECTED;
4935    if(state==CONNECTING_STATE || state==HTTP_PROXY_CONNECTED)
4936       return CL_CONNECTING;
4937    if(state==CONNECTED_STATE)
4938       return CL_JUST_CONNECTED;
4939    if(state==USER_RESP_WAITING_STATE)
4940       return CL_NOT_LOGGED_IN;
4941    if(conn->quit_sent)
4942       return CL_JUST_BEFORE_DISCONNECT;
4943    return CL_LOGGED_IN;
4944 }
4945 
MakeListInfo(const char * path)4946 ListInfo *Ftp::MakeListInfo(const char *path)
4947 {
4948    return new FtpListInfo(this,path);
4949 }
MakeGlob(const char * pattern)4950 Glob *Ftp::MakeGlob(const char *pattern)
4951 {
4952    return new GenericGlob(this,pattern);
4953 }
MakeDirList(ArgV * args)4954 DirList *Ftp::MakeDirList(ArgV *args)
4955 {
4956    return new FtpDirList(this,args);
4957 }
4958 
4959 
4960 extern "C"
4961    const char *calculate_skey_response (int, const char *, const char *);
4962 
make_skey_reply()4963 const char *Ftp::make_skey_reply()
4964 {
4965    static const char * const skey_head[] = {
4966       "S/Key MD5 ",
4967       "s/key ",
4968       "opiekey ",
4969       "otp-md5 ",
4970       0
4971    };
4972 
4973    const char *cp;
4974    for(int i=0; ; i++)
4975    {
4976       if(skey_head[i]==0)
4977 	 return 0;
4978       cp=strstr(all_lines,skey_head[i]);
4979       if(cp)
4980       {
4981 	 cp+=strlen(skey_head[i]);
4982 	 break;
4983       }
4984    }
4985 
4986    LogNote(9,"found s/key substring");
4987 
4988    int skey_sequence=0;
4989    char *buf=string_alloca(strlen(cp));
4990 
4991    if(sscanf(cp,"%d %s",&skey_sequence,buf)!=2 || skey_sequence<1)
4992       return 0;
4993 
4994    return calculate_skey_response(skey_sequence,buf,pass);
4995 }
4996 
4997 extern "C"
4998    const char *calculate_netkey_response (const char *, const char *);
4999 
make_netkey_reply()5000 const char *Ftp::make_netkey_reply()
5001 {
5002    static const char * const netkey_head[] = {
5003       "encrypt challenge, ",
5004       0
5005    };
5006 
5007    const char *cp;
5008    for(int i=0; ; i++)
5009    {
5010       if(netkey_head[i]==0)
5011 	 return 0;
5012       cp=strstr(all_lines,netkey_head[i]);
5013       if(cp)
5014       {
5015 	 cp+=strlen(netkey_head[i]);
5016 	 break;
5017       }
5018    }
5019 
5020    if(cp) {
5021       xstring &buf=xstring::get_tmp(cp);
5022       buf.truncate_at(' ');
5023       buf.truncate_at(',');
5024       LogNote(9,"found netkey challenge %s",buf.get());
5025       return calculate_netkey_response(pass,buf);
5026    }
5027    return 0;
5028 }
5029 
Buffered()5030 int Ftp::Buffered()
5031 {
5032    if(!conn || !conn->data_iobuf)
5033       return 0;
5034    if(state!=DATA_OPEN_STATE || conn->data_sock==-1 || mode!=STORE)
5035       return 0;
5036    return conn->data_iobuf->Size()+SocketBuffered(conn->data_sock);
5037 }
5038 
ProtocolSubstitution(const char * host)5039 const char *Ftp::ProtocolSubstitution(const char *host)
5040 {
5041    if(NoProxy(host))
5042       return 0;
5043    const char *proxy=ResMgr::Query("ftp:proxy",host);
5044    if(proxy && QueryBool("use-hftp",host)
5045    && (!strncmp(proxy,"http://",7) || !strncmp(proxy,"https://",8)))
5046       return "hftp";
5047    return 0;
5048 }
5049 
5050 
5051 #if USE_SSL
5052 #undef super
5053 #define super Ftp
FtpS()5054 FtpS::FtpS()
5055 {
5056    ftps=true;
5057    res_prefix="ftp";
5058 }
~FtpS()5059 FtpS::~FtpS()
5060 {
5061 }
FtpS(const FtpS * o)5062 FtpS::FtpS(const FtpS *o) : super(o)
5063 {
5064    ftps=true;
5065    res_prefix="ftp";
5066 
5067    Reconfig();
5068 }
New()5069 FileAccess *FtpS::New() { return new FtpS(); }
5070 
MakeSSLBuffers(const char * hostname)5071 void Ftp::Connection::MakeSSLBuffers(const char *hostname)
5072 {
5073    control_ssl=new lftp_ssl(control_sock,lftp_ssl::CLIENT,hostname);
5074    control_ssl->load_keys();
5075    IOBufferSSL *send_ssl=new IOBufferSSL(control_ssl,IOBufferSSL::PUT);
5076    IOBufferSSL *recv_ssl=new IOBufferSSL(control_ssl,IOBufferSSL::GET);
5077 
5078    control_send=send_ssl;
5079    control_recv=recv_ssl;
5080    telnet_layer_send=0;
5081 }
5082 #endif
5083 
PutTranslated(Buffer * target,const char * put_buf,int size)5084 void TelnetEncode::PutTranslated(Buffer *target,const char *put_buf,int size)
5085 {
5086    size_t put_size=size;
5087    const char *iac;
5088    while(put_size>0)
5089    {
5090       iac=(const char*)memchr(put_buf,(unsigned char)TELNET_IAC,put_size);
5091       if(!iac)
5092       {
5093 	 target->Put(put_buf,put_size);
5094 	 break;
5095       }
5096       target->Put(put_buf,iac+1-put_buf);
5097       put_size-=iac+1-put_buf;
5098       put_buf=iac+1;
5099       // double the IAC to send it literally.
5100       target->Put(iac,1);
5101    }
5102 }
PutTranslated(Buffer * target,const char * put_buf,int size)5103 void TelnetDecode::PutTranslated(Buffer *target,const char *put_buf,int size)
5104 {
5105    if(Size()>0)
5106    {
5107       Put(put_buf,size);
5108       Get(&put_buf,&size);
5109    }
5110    if(size<=0)
5111       return;
5112    size_t put_size=size;
5113    const char *iac;
5114    while(put_size>0)
5115    {
5116       iac=(const char*)memchr(put_buf,(unsigned char)TELNET_IAC,put_size);
5117       if(!iac)
5118 	 break;
5119       target->Put(put_buf,iac-put_buf);
5120       Skip(iac-put_buf);
5121       put_size-=iac-put_buf;
5122       put_buf=iac;
5123       if(put_size<2)
5124       {
5125 	 if(Size()==0)
5126 	    Put(put_buf,put_size); // remember incomplete sequence
5127 	 return;
5128       }
5129       switch(iac[1])
5130       {
5131       // 3-byte commands
5132       case TELNET_WILL:
5133       case TELNET_WONT:
5134       case TELNET_DO:
5135       case TELNET_DONT:
5136 	 if(put_size<3)
5137 	 {
5138 	    if(Size()==0)
5139 	       Put(put_buf,put_size); // remember incomplete sequence
5140 	    return;
5141 	 }
5142 	 Skip(3);
5143 	 put_buf+=3;
5144 	 put_size-=3;
5145 	 break;
5146       // 2-byte commands
5147       case TELNET_IAC:
5148 	 target->Put(iac,1);
5149 	 /*fallthrough*/
5150       default:
5151 	 Skip(2);
5152 	 put_buf+=2;
5153 	 put_size-=2;
5154       }
5155    }
5156    if(put_size>0)
5157    {
5158       target->Put(put_buf,put_size);
5159       Skip(put_size);
5160    }
5161 }
5162 
SetControlConnectionTranslation(const char * cs)5163 void Ftp::Connection::SetControlConnectionTranslation(const char *cs)
5164 {
5165    if(translation_activated)
5166       return;
5167    if(telnet_layer_send)
5168    {
5169       // cannot do two conversions in one DirectedBuffer, stack it.
5170       control_recv=new IOBufferStacked(control_recv.borrow());
5171    }
5172    send_cmd_buffer.SetTranslation(cs,false);
5173    control_recv->SetTranslation(cs,true);
5174    translation_activated=true;
5175 }
5176 
5177 #include "modconfig.h"
5178 #ifdef MODULE_PROTO_FTP
module_init()5179 void module_init()
5180 {
5181    Ftp::ClassInit();
5182 }
5183 #endif
5184