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(©_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