1 /*
2 * ftp.c -- low(er) level FTP stuff
3 *
4 * Yet Another FTP Client
5 * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version. See COPYING for more details.
11 */
12
13 #include "syshdr.h"
14
15 #include "ftp.h"
16 #include "xmalloc.h"
17 #include "strq.h"
18 #include "gvars.h"
19 #ifdef HAVE_LIBSSH
20 #include "ssh_cmd.h"
21 #endif
22 #include "args.h"
23
24 /* in cmd.c */
25 void exit_yafc(void);
26
27 /* in tag.c */
28 void save_taglist(const char *alt_filename);
29
30 /* in bookmark.c */
31 void auto_create_bookmark(void);
32
33 Ftp *ftp = 0;
34
ftp_create(void)35 Ftp *ftp_create(void)
36 {
37 Ftp* ftp = xmalloc(sizeof(Ftp));
38 memset(ftp, 0, sizeof(Ftp));
39
40 ftp->verbosity = vbCommand;
41 ftp->tmp_verbosity = vbUnset;
42 ftp->last_mkpath = 0;
43 ftp->cache = list_new((listfunc)rdir_destroy);
44 ftp->dirs_to_flush = list_new((listfunc)free);
45 ftp->reply_timeout = 30;
46 ftp->open_timeout = 30;
47 ftp->taglist = list_new((listfunc)rfile_destroy);
48 #ifdef SECFTP
49 ftp->app_data = 0;
50 /* ftp->in_buffer = 0;*/
51 /* ftp->out_buffer = 0;*/
52 #endif
53 ftp->LIST_type = ltUnknown;
54 #ifdef HAVE_LIBSSH
55 ftp->session = NULL;
56 ftp->sftp_session = NULL;
57 #endif
58
59 return ftp;
60 }
61
ftp_set_trace(const char * filename)62 int ftp_set_trace(const char *filename)
63 {
64 if(gvLogfp)
65 /* already opened */
66 return 0;
67 gvLogfp = fopen(filename, "w");
68 if(gvLogfp) {
69 time_t now = time(0);
70 setbuf(gvLogfp, 0); /* change buffering */
71 fprintf(gvLogfp, "yafc " VERSION " trace file started %s\n",
72 ctime(&now));
73 return 0;
74 }
75 return -1;
76 }
77
ftp_vtrace(const char * fmt,va_list ap)78 void ftp_vtrace(const char *fmt, va_list ap)
79 {
80 if(gvLogfp)
81 vfprintf(gvLogfp, fmt, ap);
82 }
83
ftp_trace(const char * fmt,...)84 void ftp_trace(const char *fmt, ...)
85 {
86 va_list ap;
87
88 va_start(ap, fmt);
89 ftp_vtrace(fmt, ap);
90 va_end(ap);
91 }
92
ftp_err(const char * fmt,...)93 void ftp_err(const char *fmt, ...)
94 {
95 va_list ap;
96
97 va_start(ap, fmt);
98 vfprintf(stderr, fmt, ap);
99 va_end(ap);
100
101 va_start(ap, fmt);
102 ftp_vtrace(fmt, ap);
103 va_end(ap);
104 }
105
ftp_use(Ftp * useftp)106 void ftp_use(Ftp *useftp)
107 {
108 ftp = useftp;
109 }
110
ftp_destroy(Ftp * ftp)111 void ftp_destroy(Ftp *ftp)
112 {
113 if(!ftp)
114 return;
115
116 list_free(ftp->dirs_to_flush);
117 list_free(ftp->cache);
118 ftp->cache = ftp->dirs_to_flush = NULL;
119 host_destroy(ftp->host);
120 sock_destroy(ftp->data);
121 sock_destroy(ftp->ctrl);
122 ftp->host = NULL;
123 ftp->data = ftp->ctrl = NULL;
124 url_destroy(ftp->url);
125 ftp->url = NULL;
126 free(ftp->homedir);
127 free(ftp->curdir);
128 free(ftp->prevdir);
129 list_free(ftp->taglist);
130 free(ftp->ti.remote_name);
131 free(ftp->ti.local_name);
132
133 #ifdef SECFTP
134 sec_end();
135 #endif
136
137 free(ftp);
138 }
139
proxy_type(url_t * url)140 static int proxy_type(url_t *url)
141 {
142 listitem *li;
143 char *url_domain;
144
145 if(gvProxyType == 0 || url->noproxy == true || gvProxyUrl == 0)
146 return 0;
147
148 url_domain = strrchr(url->hostname, '.');
149
150 for(li=gvProxyExclude->first; li; li=li->next) {
151 char *xhost = (char *)li->data;
152 if(!xhost)
153 /* should not happen */
154 continue;
155
156 if(xhost[0] == '.' && url_domain) {
157 if(strcasecmp(url_domain, xhost) == 0)
158 /* exclude domain names */
159 return 0;
160 }
161 if(strcasecmp(xhost, "localnet") == 0 && url_domain == 0)
162 /* exclude unqualified hosts */
163 return 0;
164 if(strcasecmp(xhost, url->hostname) == 0)
165 /* exclude hostnames */
166 return 0;
167 }
168 return gvProxyType;
169 }
170
ftp_reset_vars(void)171 void ftp_reset_vars(void)
172 {
173 sock_destroy(ftp->data);
174 ftp->data = NULL;
175
176 sock_destroy(ftp->ctrl);
177 ftp->ctrl = NULL;
178
179 host_destroy(ftp->host);
180 ftp->host = NULL;
181
182 #ifdef HAVE_LIBSSH
183 if (ftp->session)
184 {
185 sftp_free(ftp->sftp_session);
186 ssh_disconnect(ftp->session);
187 ssh_free(ftp->session);
188 ftp->sftp_session = NULL;
189 ftp->session = NULL;
190 }
191 #endif
192
193 url_destroy(ftp->url);
194 ftp->url = NULL;
195
196 ftp->connected = false;
197 ftp->loggedin = false;
198
199 ftp->has_mdtm_command = true;
200 ftp->has_size_command = true;
201 ftp->has_pasv_command = true;
202 ftp->has_stou_command = true;
203 ftp->has_site_chmod_command = true;
204 ftp->has_site_idle_command = true;
205 ftp->has_mlsd_command = true;
206
207 list_clear(ftp->dirs_to_flush);
208 list_clear(ftp->cache);
209
210 /* don't assume server is in ascii mode initially even if RFC says so */
211 ftp->prev_type = '?';
212
213 ftp->code = ctNone;
214 ftp->fullcode = 0;
215
216 ftp->reply_timeout = gvCommandTimeout;
217
218 free(ftp->last_mkpath);
219 ftp->last_mkpath = 0;
220
221 #ifdef SECFTP
222 sec_end();
223 ftp->request_data_prot = 0;
224 ftp->buffer_size = 0;
225 #endif
226 ftp->LIST_type = ltUnknown;
227
228 list_free(ftp->taglist);
229 ftp->taglist = list_new((listfunc)rfile_destroy);
230 }
231
ftp_close(void)232 void ftp_close(void)
233 {
234 ftp_trace("Closing down connection...\n");
235 auto_create_bookmark();
236 if(gvLoadTaglist != 0) {
237 save_taglist(0);
238 }
239 ftp_reset_vars();
240 }
241
ftp_quit_all(void)242 void ftp_quit_all(void)
243 {
244 listitem *li;
245
246 /* nicely close all open connections */
247 for(li=gvFtpList->first; li; li=li->next) {
248 ftp_use((Ftp *)li->data);
249 ftp_quit();
250 }
251 }
252
253 #ifdef HAVE_POSIX_SIGSETJMP
254 static sigjmp_buf open_timeout_jmp;
255 #else
256 static jmp_buf open_timeout_jmp;
257 #endif
258
ftp_open_handler(int signum)259 static void ftp_open_handler(int signum)
260 {
261 ftp_longjmp(open_timeout_jmp, 1);
262 }
263
ftp_open_url(url_t * urlp,bool reset_vars)264 int ftp_open_url(url_t *urlp, bool reset_vars)
265 {
266 bool use_proxy;
267 int i;
268
269 if(reset_vars)
270 ftp_reset_vars();
271 /* don't assume server is in ascii mode initially even if RFC says so */
272 ftp->prev_type = '?';
273
274 #ifdef HAVE_POSIX_SIGSETJMP
275 if(sigsetjmp(open_timeout_jmp, 1))
276 #else
277 if(setjmp(open_timeout_jmp))
278 #endif
279 {
280 ftp_close();
281 ftp_err(_("Connection timeout after %u seconds\n"),
282 ftp->open_timeout);
283 return 1;
284 }
285 ftp_set_signal(SIGALRM, ftp_open_handler);
286 alarm(ftp->open_timeout);
287
288 use_proxy = (proxy_type(urlp) != 0);
289
290 ftp_err(_("Looking up %s... "),
291 use_proxy ? gvProxyUrl->hostname : urlp->hostname);
292
293 /* Set the default port (22) for SSH if no port is specified. We
294 * need to do this here, 'cause host_lookup() sets it to 21
295 * (default port for vanilla FTP)
296 */
297 if(urlp->protocol) {
298 if(strcmp(urlp->protocol, "sftp") == 0)
299 url_setprotocol(urlp, "ssh");
300 if(strcmp(urlp->protocol, "ssh") == 0 && urlp->port == -1)
301 urlp->port = 22; /* default SSH port */
302 }
303
304 ftp->host = host_create(use_proxy ? gvProxyUrl : urlp);
305
306 if (!urlp->protocol || strcmp(urlp->protocol, "ssh") != 0) {
307 // We only need to do a host lookup if we're not using sftp.
308 if(!host_lookup(ftp->host)) {
309 fprintf(stderr, "Host look up for '%s' failed: %s\n",
310 host_getname(ftp->host), host_geterror(ftp->host));
311 alarm(0);
312 ftp_set_signal(SIGALRM, SIG_IGN);
313 return -1;
314 }
315 }
316 /* keep the value in urlp->port
317 urlp->port = ntohs(ftp->host->port);
318 and set it to 21 if it is -1 */
319 if(urlp->port == -1) {
320 urlp->port = 21;
321 }
322
323
324 fprintf(stderr, "\r ");
325 i = strlen(use_proxy ? gvProxyUrl->hostname : urlp->hostname);
326 while(i--)
327 fprintf(stderr, " ");
328 fprintf(stderr, "\r");
329 ftp_trace("\n");
330
331 #ifdef HAVE_LIBSSH
332 if(urlp->protocol && strcmp(urlp->protocol, "ssh") == 0) {
333 int ret = ssh_open_url(urlp);
334 alarm(0);
335 return ret;
336 }
337 #endif
338
339 if(urlp->protocol && strcmp(urlp->protocol, "ftp") != 0) {
340 ftp_err(_("Sorry, don't know how to handle your '%s' protocol\n"
341 "trying 'ftp' instead...\n"),
342 urlp->protocol);
343 url_setprotocol(urlp, 0);
344 }
345
346 if(use_proxy) {
347 ftp_err(_("Connecting to proxy %s at port %d...\n"),
348 host_getoname(ftp->host), urlp->port);
349 } else {
350 ftp_err(_("Connecting to %s at port %d...\n"),
351 host_getoname(ftp->host), urlp->port);
352 }
353
354 ftp->ctrl = sock_create();
355 if (!ftp->ctrl) {
356 ftp_err(_("Unable to create socket.\n"));
357 alarm(0);
358 ftp_set_signal(SIGALRM, SIG_IGN);
359 return -1;
360 }
361
362 if(!sock_connect_host(ftp->ctrl, ftp->host)) {
363 alarm(0);
364 ftp_set_signal(SIGALRM, SIG_IGN);
365 return -1;
366 }
367 sock_lowdelay(ftp->ctrl);
368 char* ip = host_getip(ftp->host);
369 ftp_err(_("Connected to %s ([%s]:%d).\n"),
370 host_getoname(ftp->host), ip, urlp->port);
371 free(ip);
372
373 /* read startup message from server */
374 ftp_set_tmp_verbosity(vbCommand);
375 ftp_read_reply();
376 if(ftp->fullcode == 120) {
377 ftp_set_tmp_verbosity(vbCommand);
378 ftp_read_reply();
379 }
380 alarm(0);
381 ftp_set_signal(SIGALRM, SIG_IGN);
382 if(!sock_connected(ftp->ctrl)) {
383 ftp_close();
384 return 1;
385 }
386 ftp->connected = (ftp->fullcode == 220);
387
388 if(ftp->connected) {
389 void (*tracefunq)(const char *fmt, ...);
390
391 url_destroy(ftp->url);
392 ftp->url = url_clone(urlp);
393
394 tracefunq = (ftp->verbosity == vbDebug ? ftp_err : ftp_trace);
395
396 char* remote_addr = printable_address(sock_remote_addr(ftp->ctrl)),
397 *local_addr = printable_address(sock_local_addr(ftp->ctrl));
398 tracefunq("remote address: %s\n", remote_addr);
399 tracefunq("local address: %s\n", local_addr);
400 free(remote_addr);
401 free(local_addr);
402
403 return 0;
404 } else {
405 ftp_close();
406 return 1;
407 }
408 }
409
ftp_reopen(void)410 int ftp_reopen(void)
411 {
412 if(ftp && ftp->url) {
413 url_t *u = url_clone(ftp->url);
414 int r;
415
416 url_setdirectory(u, ftp->curdir);
417
418 r = ftp_open_url(u, false);
419 if(r == 0) {
420 r = ftp_login(u->username, gvAnonPasswd);
421 } else
422 ftp_close();
423 url_destroy(u);
424 return r;
425 }
426
427 return -1;
428 }
429
430 /* reads one line from server into ftp->reply
431 * returns 0 on success or -1 on failure
432 */
ftp_gets(void)433 static int ftp_gets(void)
434 {
435 ftp->reply[0] = 0;
436
437 if(!sock_connected(ftp->ctrl)) {
438 ftp_err(_("No control connection\n"));
439 return -1;
440 }
441
442 size_t i = 0;
443 bool next = true;
444 int nc = 0;
445 while(true)
446 {
447 int c = 0;
448 if (next)
449 c = sock_get(ftp->ctrl);
450 else
451 {
452 c = nc;
453 next = true;
454 }
455
456 if (c == EOF)
457 {
458 ftp_err(_("Server has closed control connection\n"));
459 ftp_close();
460 return -1;
461 }
462 else if(c == 255/*IAC*/)
463 {
464 /* handle telnet commands */
465 switch(c = sock_get(ftp->ctrl)) {
466 case 251/*WILL*/:
467 case 252/*WONT*/:
468 c = sock_get(ftp->ctrl);
469 sock_printf(ftp->ctrl, "%c%c%c", 255/*IAC*/, 254/*DONT*/, c);
470 sock_flush(ftp->ctrl);
471 break;
472 case 253/*DO*/:
473 case 254/*DONT*/:
474 c = sock_get(ftp->ctrl);
475 sock_printf(ftp->ctrl, "%c%c%c", 255/*IAC*/, 252/*WONT*/, c);
476 sock_flush(ftp->ctrl);
477 break;
478 default:
479 break;
480 }
481 continue;
482 }
483 else if (c == '\r')
484 {
485 nc = sock_get(ftp->ctrl);
486 if (nc == 0)
487 ; // do nothing
488 else if (nc == '\n') {
489 ftp->reply[i++] = (char)c;
490 break;
491 } else if(nc == EOF)
492 c = EOF ;
493 else
494 /* telnet protocol violation, hmpf... */
495 next = false;
496 }
497 else if (c == '\n')
498 break;
499 if(i < MAXREPLY)
500 ftp->reply[i++] = (char)c;
501 }
502
503 if(i >= MAXREPLY) {
504 ftp_err(_("Reply too long (truncated)\n"));
505 i = MAXREPLY;
506 }
507
508 ftp->reply[i] = '\0';
509 ftp->fullcode = atoi(ftp->reply);
510 #ifdef SECFTP
511 {
512 int r = 0;
513 switch(ftp->fullcode) { /* handle protected responses 6xx */
514 case 631:
515 r = sec_read_msg(ftp->reply, prot_safe);
516 break;
517 case 632:
518 r = sec_read_msg(ftp->reply, prot_private);
519 break;
520 case 633:
521 r = sec_read_msg(ftp->reply, prot_confidential);
522 break;
523 }
524 if(r == -1) {
525 ftp->fullcode = 0;
526 ftp->code = vbNone;
527 return 0;
528 } else
529 ftp->fullcode = atoi(ftp->reply);
530 }
531 #endif
532 strip_trailing_chars(ftp->reply, "\n\r");
533 ftp->code = ftp->fullcode / 100;
534 return ftp->fullcode;
535 }
536
ftp_getreply(bool withcode)537 const char *ftp_getreply(bool withcode)
538 {
539 char *r = ftp->reply;
540
541 #ifdef HAVE_LIBSSH
542 if (ftp->session)
543 return ssh_get_error(ftp->session);
544 #endif
545
546 if(withcode)
547 return r;
548
549 if(isdigit((int)r[0]))
550 r += 3;
551 if(r[0] == '-' || r[0] == ' ')
552 r++;
553 return r;
554 }
555
ftp_print_reply(void)556 static void ftp_print_reply(void)
557 {
558 verbose_t v = ftp_get_verbosity();
559
560 ftp_trace("<-- [%s] %s\n",
561 ftp->url ? ftp->url->hostname : host_getname(ftp->host),
562 ftp_getreply(true));
563
564 if(v >= vbCommand || (ftp->code >= ctTransient && v == vbError)) {
565 if(v == vbDebug)
566 fprintf(stderr, "<-- [%s] %s\n",
567 ftp->url ? ftp->url->hostname : host_getname(ftp->host),
568 ftp_getreply(true));
569 else
570 fprintf(stderr, "%s\n", ftp_getreply(false));
571 }
572 }
573
reply_ALRM_handler(int signum)574 static void reply_ALRM_handler(int signum)
575 {
576 ftp_err(_("Tired of waiting for reply, timeout after %u seconds\n"),
577 ftp->reply_timeout);
578 ftp_close();
579 if(gvJmpBufSet) {
580 ftp_trace("jumping to gvRestartJmp\n");
581 ftp_longjmp(gvRestartJmp, 1);
582 } else
583 exit_yafc();
584 }
585
586 /* reads reply
587 * returns 0 on success or -1 on error
588 */
ftp_read_reply(void)589 int ftp_read_reply(void)
590 {
591 char tmp[5]="xxx ";
592 int r;
593
594 ftp_set_signal(SIGALRM, reply_ALRM_handler);
595 if(ftp->reply_timeout)
596 alarm(ftp->reply_timeout);
597
598 sock_clearerr_in(ftp->ctrl);
599 r = ftp_gets();
600 if(!sock_connected(ftp->ctrl)) {
601 alarm(0);
602 ftp_set_signal(SIGALRM, SIG_DFL);
603 ftp_trace("sock is not connected\n");
604 return -1;
605 }
606
607 if(r == -1) {
608 alarm(0);
609 ftp_set_signal(SIGALRM, SIG_DFL);
610 ftp_trace("ftp_gets returned -1\n");
611 return -1;
612 }
613
614 ftp_print_reply();
615
616 if(ftp->reply[3] == '-') { /* multiline response */
617 strncpy(tmp, ftp->reply, 3);
618 do {
619 if(ftp_gets() == -1)
620 break;
621 ftp_print_reply();
622 } while(strncmp(tmp, ftp->reply, 4) != 0);
623 }
624 ftp->tmp_verbosity = vbUnset;
625 alarm(0);
626 ftp_set_signal(SIGALRM, SIG_DFL);
627 return r;
628 }
629
ftp_print_cmd(const char * cmd,va_list ap)630 static void ftp_print_cmd(const char *cmd, va_list ap)
631 {
632 if(ftp_get_verbosity() == vbDebug) {
633 ftp_err("--> [%s] ", ftp->url->hostname);
634 if(strncmp(cmd, "PASS", 4) == 0)
635 ftp_err("PASS ********");
636 else {
637 va_list aq;
638 va_copy(aq, ap);
639 vfprintf(stderr, cmd, ap);
640 ftp_vtrace(cmd, aq);
641 va_end(aq);
642 }
643 ftp_err("\n");
644 } else {
645 ftp_trace("--> [%s] ", ftp->url->hostname);
646 if(strncmp(cmd, "PASS", 4) == 0)
647 ftp_trace("PASS ********");
648 else
649 ftp_vtrace(cmd, ap);
650 ftp_trace("\n");
651 }
652 }
653
654 /* sends an FTP command on the control channel
655 * returns reply status code on success or -1 on error
656 */
ftp_cmd(const char * cmd,...)657 int ftp_cmd(const char *cmd, ...)
658 {
659 va_list ap;
660 int resp;
661 bool recon = false;
662
663 if(!sock_connected(ftp->ctrl)) {
664 ftp_err(_("No control connection\n"));
665 ftp->code = ctNone;
666 ftp->fullcode = -1;
667 return -1;
668 }
669
670 ftp_set_abort_handler();
671
672 ugly:
673
674 va_start(ap, cmd);
675 sock_krb_vprintf(ftp->ctrl, cmd, ap);
676 sock_printf(ftp->ctrl, "\r\n");
677 sock_flush(ftp->ctrl);
678 va_end(ap);
679
680 if (sock_error_out(ftp->ctrl)) {
681 ftp_err(_("error writing command"));
682 ftp_err(" (");
683 va_start(ap, cmd);
684 vfprintf(stderr, cmd, ap);
685 va_end(ap);
686 va_start(ap, cmd);
687 ftp_vtrace(cmd, ap);
688 va_end(ap);
689 ftp_err(")\n");
690 ftp->code = ctNone;
691 ftp->fullcode = -1;
692 return -1;
693 }
694
695 va_start(ap, cmd);
696 ftp_print_cmd(cmd, ap);
697 va_end(ap);
698
699 resp = ftp_read_reply();
700 ftp_set_close_handler();
701 if(resp == 421) { /* server is closing control connection! */
702 ftp_err(_("Server closed control connection\n"));
703 if(gvAutoReconnect && ftp_loggedin() && strcasecmp(cmd, "QUIT") != 0) {
704 if(recon) {
705 ftp_err(_("Reconnect failed\n"));
706 } else {
707 ftp_err(_("Automatic reconnect...\n"));
708 ftp_reopen();
709 recon = true;
710 goto ugly;
711 }
712 } else {
713 /* ftp_close();*/
714 ftp->fullcode = 421;
715 ftp->code = 4;
716 return -1;
717 }
718 }
719 return resp;
720 }
721
ftp_quit(void)722 void ftp_quit(void)
723 {
724 #ifdef HAVE_LIBSSH
725 if (ftp_connected() && !ftp->session)
726 #else
727 if (ftp_connected())
728 #endif
729 {
730 ftp_reply_timeout(10);
731 ftp_set_tmp_verbosity(vbCommand);
732 ftp_cmd("QUIT");
733 }
734 ftp_close();
735 }
736
ftp_get_verbosity(void)737 int ftp_get_verbosity(void)
738 {
739 if(ftp->tmp_verbosity != vbUnset)
740 return ftp->tmp_verbosity;
741 return ftp->verbosity;
742 }
743
ftp_set_verbosity(int verbosity)744 void ftp_set_verbosity(int verbosity)
745 {
746 ftp->verbosity = verbosity;
747 ftp->tmp_verbosity = vbUnset;
748 }
749
ftp_set_tmp_verbosity(int verbosity)750 void ftp_set_tmp_verbosity(int verbosity)
751 {
752 if(ftp->verbosity <= vbError)
753 ftp->tmp_verbosity = verbosity;
754 }
755
get_username(url_t * url,const char * guessed_username,bool isproxy)756 int get_username(url_t *url, const char *guessed_username, bool isproxy)
757 {
758 if(!url->username) {
759 char *prompt, *e;
760
761 if(!ftp->getuser_hook) {
762 ftp->loggedin = false;
763 return -1;
764 }
765
766 if (isproxy)
767 prompt = xstrdup(_("Proxy login: "));
768 else if (guessed_username)
769 {
770 if (asprintf(&prompt, _("login (%s): "), guessed_username) == -1)
771 {
772 fprintf(stderr, _("Failed to allocate memory.\n"));
773 ftp->loggedin = false;
774 return -1;
775 }
776 }
777 else
778 prompt = xstrdup(_("login (anonymous): "));
779 e = ftp->getuser_hook(prompt);
780 free(prompt);
781 if(e && *e == 0) {
782 free(e);
783 e = 0;
784 }
785
786 if(!e) {
787 if(guessed_username == 0) {
788 fprintf(stderr, _("You loose\n"));
789 ftp->loggedin = false;
790 return -1;
791 }
792 url_setusername(url, guessed_username);
793 } else {
794 url_setusername(url, e);
795 free(e);
796 }
797 }
798 return 0;
799 }
800
get_password(url_t * url,const char * anonpass,bool isproxy)801 int get_password(url_t *url, const char *anonpass, bool isproxy)
802 {
803 if(!url->password) {
804 char *e;
805
806 if(url_isanon(url) && isproxy == false) {
807 char *prompt;
808
809 e = 0;
810 if(ftp->getuser_hook) {
811 if(anonpass && isproxy == false)
812 {
813 if (asprintf(&prompt, _("password (%s): "), anonpass) == -1)
814 {
815 fprintf(stderr, _("Failed to allocate memory.\n"));
816 return -1;
817 }
818 }
819 else
820 prompt = xstrdup(_("password: "));
821 e = ftp->getuser_hook(prompt);
822 free(prompt);
823 }
824 if(!e || !*e) {
825 free(e);
826 e = xstrdup(anonpass);
827 }
828 } else {
829 if(!ftp->getpass_hook)
830 return -1;
831 e = ftp->getpass_hook(isproxy ? _("Proxy password: ")
832 : _("password: "));
833 }
834
835 if(!e) {
836 fprintf(stderr, _("You loose\n"));
837 return -1;
838 }
839 url_setpassword(url, e);
840 free(e);
841 }
842 return 0;
843 }
844
845 #ifdef SECFTP
846
secext_name(const char * mech)847 static const char *secext_name(const char *mech)
848 {
849 static struct {
850 const char *short_name;
851 const char *real_name;
852 } names[] = {
853 {"krb4", "KERBEROS_V4"},
854 {"krb5", "GSSAPI"},
855 {"gssapi", "GSSAPI"},
856 /* {"ssl", "SSL"},*/
857 {"none", "none"},
858 {0, 0}
859 };
860 int i;
861
862 for(i = 0; names[i].short_name; i++) {
863 if(strcasecmp(mech, names[i].short_name) == 0)
864 return names[i].real_name;
865 }
866
867 return 0;
868 }
869
mech_unsupported(const char * mech)870 static bool mech_unsupported(const char *mech)
871 {
872 if(strcasecmp(mech, "KERBEROS_V4") == 0)
873 return true;
874 #ifndef HAVE_KRB5
875 if(strcasecmp(mech, "GSSAPI") == 0)
876 return true;
877 #endif
878 #ifndef USE_SSL
879 if(strcasecmp(mech, "SSL") == 0)
880 return true;
881 #endif
882 return false;
883 }
884
885 #endif /* SECFTP */
886
ftp_login(const char * guessed_username,const char * anonpass)887 int ftp_login(const char *guessed_username, const char *anonpass)
888 {
889 int ptype, r;
890 static url_t *purl = 0;
891
892 if(!ftp_connected())
893 return 1;
894
895 if(!ftp->url)
896 return -1;
897
898 #ifdef HAVE_LIBSSH
899 if (ftp->session)
900 /* login authentication is performed by the ssh program */
901 return 0;
902 #endif
903
904 ptype = proxy_type(ftp->url);
905 if(purl) {
906 url_destroy(purl);
907 purl = 0;
908 }
909 if(ptype > 0)
910 purl = url_clone(gvProxyUrl);
911
912 r = get_username(ftp->url, guessed_username, false);
913 if(r != 0)
914 return r;
915 if(ptype > 1 && ptype < 7) {
916 r = get_username(purl, 0, true);
917 if(r != 0)
918 return r;
919 }
920
921 #ifdef SECFTP
922 ftp->sec_complete = false;
923 ftp->data_prot = prot_clear;
924
925 /* don't use secure stuff if anonymous
926 */
927 if(!url_isanon(ftp->url)) {
928 list *mechlist;
929 /* request a protection level
930 */
931 if(ftp->url->protlevel) {
932 if(sec_request_prot(ftp->url->protlevel) != 0)
933 ftp_err(_("Invalid protection level '%s'\n"),
934 ftp->url->protlevel);
935 }
936
937 /* get list of mechanisms to try
938 */
939 mechlist = ftp->url->mech ? ftp->url->mech : gvDefaultMechanism;
940 if(mechlist) {
941 listitem *li = mechlist->first;
942 int ret = 0;
943 for(; li; li=li->next) {
944 const char *mech_name;
945
946 mech_name = secext_name((char *)li->data);
947 if(mech_name == 0) {
948 ftp_err(_("unknown mechanism '%s'\n"), (char *)li->data);
949 continue;
950 }
951 if(mech_unsupported(mech_name)) {
952 ftp_err(_("Yafc was not compiled with support for %s\n"),
953 mech_name);
954 continue;
955 }
956 ret = sec_login(host_getname(ftp->host), mech_name);
957 if(ret == -1) {
958 if(ftp->code == ctError
959 && ftp->fullcode != 504 && ftp->fullcode != 534)
960 url_setmech(ftp->url, "none");
961 }
962 if(ret != 1)
963 break;
964 }
965 }
966 if(ftp->sec_complete)
967 ftp_err(_("Authentication successful.\n"));
968 else
969 ftp_err(_("*** Using plaintext username"
970 " and password ***\n"));
971 }
972 #endif
973
974 if(url_isanon(ftp->url))
975 fprintf(stderr, _("logging in anonymously...\n"));
976 ftp_set_tmp_verbosity(ftp->url->password ? vbError : vbCommand);
977
978 switch(ptype) {
979 case 0:
980 default:
981 ftp_cmd("USER %s", ftp->url->username);
982 break;
983 case 1:
984 ftp_cmd("USER %s@%s", ftp->url->username, ftp->url->hostname);
985 break;
986 case 2:
987 case 3:
988 case 4:
989 ftp_cmd("USER %s", purl->username);
990 if(ftp->code == ctContinue) {
991 r = get_password(purl, 0, true);
992 if(r != 0)
993 return 0;
994 ftp_cmd("PASS %s", purl->password);
995 /* FIXME: what reply code do we expect now? */
996 if(ftp->code < ctTransient) {
997 if(ptype == 2) {
998 ftp_cmd("USER %s@%s",
999 ftp->url->username, ftp->url->hostname);
1000 } else {
1001 if(ptype == 3)
1002 ftp_cmd("SITE %s", purl->hostname);
1003 else
1004 ftp_cmd("OPEN %s", purl->hostname);
1005 if(ftp->code < ctTransient)
1006 ftp_cmd("USER %s", ftp->url->username);
1007 }
1008 }
1009 }
1010 break;
1011 case 5:
1012 ftp_cmd("USER %s@%s@%s",
1013 ftp->url->username, purl->username, ftp->url->hostname);
1014 break;
1015 case 6:
1016 ftp_cmd("USER %s@%s", purl->username, ftp->url->hostname);
1017 if(ftp->code == ctContinue) {
1018 r = get_password(purl, 0, true);
1019 if(r != 0)
1020 return 0;
1021 ftp_cmd("PASS %s", purl->password);
1022 if(ftp->code < ctTransient)
1023 ftp_cmd("USER %s", ftp->url->username);
1024 }
1025 break;
1026 case 7:
1027 ftp_cmd("USER %s@%s:%i", ftp->url->username, ftp->url->hostname, ftp->url->port);
1028 break;
1029 }
1030
1031 if(ftp->code == ctContinue) {
1032 ftp->loggedin = false;
1033 r = get_password(ftp->url, anonpass, false);
1034 if(r != 0)
1035 return r;
1036 if(ptype == 5) {
1037 r = get_password(purl, 0, true);
1038 if(r != 0) {
1039 url_destroy(purl);
1040 purl = 0;
1041 return 0;
1042 }
1043 }
1044
1045 ftp_set_tmp_verbosity(vbCommand);
1046 switch(ptype) {
1047 default:
1048 case 0:
1049 case 1:
1050 case 2:
1051 case 3:
1052 case 4:
1053 case 6:
1054 ftp_cmd("PASS %s", ftp->url->password);
1055 break;
1056 case 5:
1057 ftp_cmd("PASS %s@%s", ftp->url->password, purl->password);
1058 break;
1059
1060 }
1061 }
1062
1063 url_destroy(purl);
1064 purl = 0;
1065
1066 if(ftp->code > ctContinue) {
1067 if(ftp->fullcode == 530 && ftp_loggedin()) {
1068 /* this probable means '530 Already logged in' */
1069 return 2;
1070 }
1071 ftp->loggedin = false;
1072 return 1;
1073 }
1074 if(ftp->code == ctComplete) {
1075 ftp->loggedin = true;
1076 #ifdef SECFTP
1077 /* we are logged in, now set the requested data protection level
1078 * requested from the autologin information in the config file,
1079 * if any, else uses default protection level 'clear', ie
1080 * no protection on the data channel
1081 */
1082 if(ftp->sec_complete) {
1083 sec_set_protection_level();
1084 fprintf(stderr, _("Data protection is %s\n"),
1085 level_to_name(ftp->data_prot));
1086 }
1087 #endif
1088
1089 ftp->homedir = ftp_getcurdir();
1090 ftp->curdir = xstrdup(ftp->homedir);
1091 ftp->prevdir = xstrdup(ftp->homedir);
1092 if(ftp->url->directory)
1093 ftp_chdir(ftp->url->directory);
1094 ftp_get_feat();
1095 return 0;
1096 }
1097 if(ftp->code == ctTransient)
1098 return 1;
1099 return -1;
1100 }
1101
ftp_loggedin(void)1102 bool ftp_loggedin(void)
1103 {
1104 return (ftp_connected() && ftp->loggedin);
1105 }
1106
ftp_connected(void)1107 bool ftp_connected(void)
1108 {
1109 #ifdef HAVE_LIBSSH
1110 return ftp->connected && (sock_connected(ftp->ctrl) || ftp->session);
1111 #else
1112 return ftp->connected && sock_connected(ftp->ctrl);
1113 #endif
1114 }
1115
ftp_getcurdir(void)1116 char *ftp_getcurdir(void)
1117 {
1118 #ifdef HAVE_LIBSSH
1119 if (ftp->session)
1120 return ssh_getcurdir();
1121 #endif
1122
1123 ftp_set_tmp_verbosity(vbNone);
1124 ftp_cmd("PWD");
1125 if(ftp->code == ctComplete) {
1126 char *beg, *end, *ret;
1127 beg = strchr(ftp->reply, '\"');
1128 if(!beg)
1129 return xstrdup("CWD?");
1130 beg++;
1131 end = strchr(beg, '\"');
1132 if(!end)
1133 return xstrdup("CWD?");
1134 ret = (char *)xmalloc(end-beg+1);
1135 strncpy(ret, beg, end-beg);
1136 stripslash(ret);
1137 /* path shouldn't include any quoted chars */
1138 path_dos2unix(ret);
1139 return ret;
1140 }
1141 return xstrdup("CWD?");
1142 }
1143
ftp_update_curdir_x(const char * p)1144 void ftp_update_curdir_x(const char *p)
1145 {
1146 free(ftp->prevdir);
1147 ftp->prevdir = ftp->curdir;
1148 ftp->curdir = xstrdup(p);
1149 path_dos2unix(ftp->curdir);
1150 }
1151
ftp_update_curdir(void)1152 static void ftp_update_curdir(void)
1153 {
1154 free(ftp->prevdir);
1155 ftp->prevdir = ftp->curdir;
1156 ftp->curdir = ftp_getcurdir();
1157 }
1158
ftp_chdir(const char * path)1159 int ftp_chdir(const char *path)
1160 {
1161 #ifdef HAVE_LIBSSH
1162 if (ftp->session)
1163 return ssh_chdir(path);
1164 #endif
1165
1166 ftp_set_tmp_verbosity(vbCommand);
1167 ftp_cmd("CWD %s", path);
1168 if(ftp->code == ctComplete) {
1169 /* Now, try to be smart ;-)
1170 * Many ftp servers include the current directory in the CWD reply
1171 * try to parse it, so we don't need to issue a PWD command
1172 */
1173
1174 if(strncasecmp(ftp->reply, "250 Changed to ", 15) == 0) {
1175 /* this is what Troll-ftpd replies: 250 Changed to /foo/bar */
1176 ftp_update_curdir_x(ftp->reply+15);
1177 ftp_trace("Parsed cwd '%s' from reply\n", ftp->curdir);
1178 } else if(strncasecmp(ftp->reply,
1179 "250 OK. Current directory is ", 29) == 0) {
1180 /* PureFTPd responds: "250 OK. Current directory is /foo/bar */
1181 ftp_update_curdir_x(ftp->reply+29);
1182 ftp_trace("Parsed cwd '%s' from reply\n", ftp->curdir);
1183 } else if(strstr(ftp->reply, " is current directory") != 0) {
1184 /* WarFTPD answers: 250 "/foo/bar/" is current directory */
1185 char *edq;
1186 char *sdq = strchr(ftp->reply, '\"');
1187 if(sdq) {
1188 edq = strchr(sdq+1, '\"');
1189 if(edq) {
1190 char *e = xstrndup(sdq+1, edq-sdq-1);
1191 stripslash(e);
1192 ftp_update_curdir_x(e);
1193 free(e);
1194 ftp_trace("Parsed cwd '%s' from reply\n", ftp->curdir);
1195 }
1196 }
1197 } else if(strncasecmp(ftp->reply,
1198 "250 Directory changed to ", 25) == 0) {
1199 /* Serv-U FTP Server for WinSock */
1200 ftp_update_curdir_x(ftp->reply + 25);
1201 ftp_trace("Parsed cwd '%s' from reply\n", ftp->curdir);
1202 } else
1203 ftp_update_curdir();
1204 return 0;
1205 }
1206 return -1;
1207 }
1208
ftp_cdup(void)1209 int ftp_cdup(void)
1210 {
1211 #ifdef HAVE_LIBSSH
1212 if(ftp->session)
1213 return ssh_cdup();
1214 #endif
1215
1216 ftp_set_tmp_verbosity(vbCommand);
1217 ftp_cmd("CDUP");
1218 if(ftp->code == ctComplete) {
1219 ftp_update_curdir();
1220 return 0;
1221 }
1222 return -1;
1223 }
1224
ftp_mkdir_verb(const char * path,verbose_t verb)1225 static int ftp_mkdir_verb(const char *path, verbose_t verb)
1226 {
1227 char *p;
1228
1229 #ifdef HAVE_LIBSSH
1230 if(ftp->session)
1231 return ssh_mkdir_verb(path, verb);
1232 #endif
1233
1234 p = xstrdup(path);
1235 stripslash(p);
1236
1237 ftp_set_tmp_verbosity(verb);
1238 ftp_cmd("MKD %s", p);
1239 if(ftp->code == ctComplete)
1240 ftp_cache_flush_mark_for(p);
1241 free(p);
1242 return ftp->code == ctComplete ? 0 : -1;
1243 }
1244
ftp_mkdir(const char * path)1245 int ftp_mkdir(const char *path)
1246 {
1247 return ftp_mkdir_verb(path, vbError);
1248 }
1249
ftp_rmdir(const char * path)1250 int ftp_rmdir(const char *path)
1251 {
1252 char *p;
1253
1254 #ifdef HAVE_LIBSSH
1255 if(ftp->session)
1256 return ssh_rmdir(path);
1257 #endif
1258
1259 p = xstrdup(path);
1260 stripslash(p);
1261 ftp_set_tmp_verbosity(vbError);
1262 ftp_cmd("RMD %s", p);
1263 if(ftp->code == ctComplete) {
1264 ftp_cache_flush_mark(p);
1265 ftp_cache_flush_mark_for(p);
1266 }
1267 free(p);
1268 return ftp->code == ctComplete ? 0 : -1;
1269 }
1270
ftp_unlink(const char * path)1271 int ftp_unlink(const char *path)
1272 {
1273 #ifdef HAVE_LIBSSH
1274 if(ftp->session)
1275 return ssh_unlink(path);
1276 #endif
1277
1278 ftp_cmd("DELE %s", path);
1279 if(ftp->code == ctComplete) {
1280 ftp_cache_flush_mark_for(path);
1281 return 0;
1282 }
1283 return -1;
1284 }
1285
ftp_chmod(const char * path,const char * mode)1286 int ftp_chmod(const char *path, const char *mode)
1287 {
1288 #ifdef HAVE_LIBSSH
1289 if(ftp->session)
1290 return ssh_chmod(path, mode);
1291 #endif
1292
1293 if(ftp->has_site_chmod_command) {
1294 ftp_set_tmp_verbosity(vbNone);
1295 ftp_cmd("SITE CHMOD %s %s", mode, path);
1296 if(ftp->fullcode == 502)
1297 ftp->has_site_chmod_command = false;
1298 if(ftp->code == ctComplete) {
1299 ftp_cache_flush_mark_for(path);
1300 return 0;
1301 }
1302 } else
1303 ftp_err(_("Server doesn't support SITE CHMOD\n"));
1304
1305 return -1;
1306 }
1307
ftp_reply_timeout(unsigned int secs)1308 void ftp_reply_timeout(unsigned int secs)
1309 {
1310 ftp->reply_timeout = secs;
1311 }
1312
ftp_idle(const char * idletime)1313 int ftp_idle(const char *idletime)
1314 {
1315 #ifdef HAVE_LIBSSH
1316 if(ftp->session)
1317 return ssh_idle(idletime);
1318 #endif
1319
1320 if(!ftp->has_site_idle_command) {
1321 ftp_err(_("Server doesn't support SITE IDLE\n"));
1322 return -1;
1323 }
1324
1325 ftp_set_tmp_verbosity(vbCommand);
1326 if(idletime)
1327 ftp_cmd("SITE IDLE %s", idletime);
1328 else
1329 ftp_cmd("SITE IDLE");
1330
1331 if(ftp->fullcode == 502)
1332 ftp->has_site_idle_command = false;
1333 return ftp->code == ctComplete ? 0 : -1;
1334 }
1335
ftp_noop(void)1336 int ftp_noop(void)
1337 {
1338 #ifdef HAVE_LIBSSH
1339 if(ftp->session)
1340 return ssh_noop();
1341 #endif
1342
1343 ftp_set_tmp_verbosity(vbCommand);
1344 ftp_cmd("NOOP");
1345 return ftp->code == ctComplete ? 0 : -1;
1346 }
1347
ftp_help(const char * arg)1348 int ftp_help(const char *arg)
1349 {
1350 #ifdef HAVE_LIBSSH
1351 if(ftp->session)
1352 return ssh_help(arg);
1353 #endif
1354
1355 ftp_set_tmp_verbosity(vbCommand);
1356 if(arg)
1357 ftp_cmd("HELP %s", arg);
1358 else
1359 ftp_cmd("HELP");
1360 return ftp->code == ctComplete ? 0 : -1;
1361 }
1362
ftp_filesize(const char * path)1363 unsigned long long ftp_filesize(const char *path)
1364 {
1365 #ifdef HAVE_LIBSSH
1366 if(ftp->session)
1367 return ssh_filesize(path);
1368 #endif
1369
1370 if(!ftp->has_size_command)
1371 return -1;
1372 if(ftp_type(tmBinary) != 0)
1373 return -1;
1374 ftp_set_tmp_verbosity(vbError);
1375 ftp_cmd("SIZE %s", path);
1376 if(ftp->fullcode == 502) {
1377 ftp->has_size_command = false;
1378 return -1;
1379 }
1380 if(ftp->code == ctComplete) {
1381 unsigned long long ret;
1382 sscanf(ftp->reply, "%*s %llu", &ret);
1383 return ret;
1384 }
1385 return -1;
1386 }
1387
ftp_read_directory(const char * path)1388 rdirectory *ftp_read_directory(const char *path)
1389 {
1390 FILE *fp = 0;
1391 rdirectory *rdir;
1392 bool is_curdir = false;
1393 bool _failed = false;
1394 char *dir;
1395 bool is_mlsd = false;
1396
1397 #ifdef HAVE_LIBSSH
1398 if (ftp->session)
1399 return ssh_read_directory(path);
1400 #endif
1401
1402 dir = ftp_path_absolute(path);
1403 stripslash(dir);
1404
1405 is_curdir = (strcmp(dir, ftp->curdir) == 0);
1406
1407 if((fp = tmpfile()) == NULL) { /* can't create a tmpfile */
1408 ftp_err("Unable to create temp file: %s\n", strerror(errno));
1409 free(dir);
1410 return 0;
1411 }
1412
1413 /* we do a "CWD" before the listing, because: we want a listing of
1414 * the directory contents, not the directory itself, and some
1415 * servers misunderstand this. If the target is a link to a
1416 * directory, we have to do this.
1417 */
1418 if(!is_curdir) {
1419 ftp_cmd("CWD %s", dir);
1420 if(ftp->code != ctComplete)
1421 goto failed;
1422 }
1423
1424 if(ftp->has_mlsd_command) {
1425 is_mlsd = true;
1426 #if 0
1427 /* PureFTPd (1.0.11) doesn't recognize directory arguments
1428 * with spaces, not even quoted, it just chops the argument
1429 * string after the first space, duh... so we have to CWD to
1430 * the directory...
1431 */
1432 char *asdf;
1433 asprintf(&asdf, "%s/", dir);
1434 /* Hack to get around issue in PureFTPd (up to version 0.98.2):
1435 * doing a 'MLSD link-to-dir' on PureFTPd closes the control
1436 * connection, however, 'MLSD link-to-dir/' works fine.
1437 */
1438 _failed = (ftp_list("MLSD", asdf, fp) != 0);
1439 free(asdf);
1440 #else
1441 _failed = (ftp_list("MLSD", 0, fp) != 0);
1442 #endif
1443 if(_failed && ftp->code == ctError)
1444 ftp->has_mlsd_command = false;
1445 }
1446 if(!ftp->has_mlsd_command) {
1447 _failed = (ftp_list("LIST", 0, fp) != 0);
1448 is_mlsd = false;
1449 }
1450
1451 if(!is_curdir)
1452 ftp_cmd("CWD %s", ftp->curdir);
1453
1454 if(_failed)
1455 goto failed;
1456
1457 rewind(fp);
1458
1459 rdir = rdir_create();
1460 if(rdir_parse(rdir, fp, dir, is_mlsd) != 0) {
1461 rdir_destroy(rdir);
1462 rdir = NULL;
1463 goto failed;
1464 }
1465
1466 fclose(fp);
1467 ftp_trace("added directory '%s' to cache\n", dir);
1468 list_additem(ftp->cache, rdir);
1469 free(dir);
1470
1471 rdir_sort(rdir);
1472 return rdir;
1473
1474 failed: /* forgive me father, for I have goto'ed */
1475 if (fp)
1476 fclose(fp);
1477 free(dir);
1478 return NULL;
1479 }
1480
ftp_get_directory(const char * path)1481 rdirectory *ftp_get_directory(const char *path)
1482 {
1483 rdirectory *rdir;
1484 char *ap;
1485
1486 ap = ftp_path_absolute(path);
1487 stripslash(ap);
1488
1489 rdir = ftp_cache_get_directory(ap);
1490 if(!rdir)
1491 rdir = ftp_read_directory(ap);
1492 free(ap);
1493 return rdir;
1494 }
1495
1496 /* returns the rfile at PATH
1497 * if it's not in the cache, reads the directory
1498 * returns 0 if not found
1499 */
ftp_get_file(const char * path)1500 rfile *ftp_get_file(const char *path)
1501 {
1502 rfile *f;
1503 char *ap;
1504
1505 if(!path)
1506 return 0;
1507
1508 ap = ftp_path_absolute(path);
1509 stripslash(ap);
1510
1511 f = ftp_cache_get_file(ap);
1512 if(!f) {
1513 char *p = base_dir_xptr(ap);
1514 rdirectory *rdir = ftp_get_directory(p);
1515 free(p);
1516 if(rdir)
1517 f = rdir_get_file(rdir, base_name_ptr(ap));
1518 }
1519 free(ap);
1520 return f;
1521 }
1522
1523 /* returns true if path A is part of path B
1524 */
ftp_path_part_of(const char * a,const char * b)1525 static bool ftp_path_part_of(const char *a, const char *b)
1526 {
1527 size_t alen;
1528
1529 if(!a || !b)
1530 return false;
1531
1532 alen = strlen(a);
1533
1534 /* see if a and b are equal to the length of a */
1535 if(strncmp(a, b, alen) == 0) {
1536 /* see if the last directory in b is complete */
1537 if(strlen(b) >= alen) {
1538 char c = b[alen];
1539 if(c == 0 || c == '/') {
1540 /* ftp_trace("directory %s already created\n", a);*/
1541 return true;
1542 }
1543 }
1544 }
1545
1546 return false;
1547 }
1548
1549 /* creates path (and all elements in path)
1550 * PATH should be an absolute path
1551 * returns -1 on error, 0 if no directories created, else 1
1552 */
ftp_mkpath(const char * path)1553 int ftp_mkpath(const char *path)
1554 {
1555 bool one_created = false;
1556 char *p, *orgp, *e = NULL;
1557
1558 if(!path)
1559 return 0;
1560
1561 /* check if we already has created this path */
1562 if(ftp_path_part_of(path, ftp->last_mkpath))
1563 return 0;
1564
1565 /* check if this path is a part of current directory */
1566 if(ftp_path_part_of(path, ftp->curdir))
1567 return 0;
1568
1569 orgp = p = xstrdup(path);
1570 path_collapse(p);
1571 unquote(p);
1572
1573 if(*p == '/') {
1574 e = xmalloc(1);
1575 *e = 0;
1576 }
1577
1578 while(true) {
1579 char* tmp = strqsep(&p, '/');
1580 if(!tmp)
1581 break;
1582
1583 char* foo = NULL;
1584 if (e)
1585 {
1586 if (asprintf(&foo, "%s/%s", e, tmp) == -1)
1587 {
1588 fprintf(stderr, _("Failed to allocate memory.\n"));
1589 free(e);
1590 free(orgp);
1591 return -1;
1592 }
1593 }
1594 else
1595 foo = xstrdup(tmp);
1596
1597 free(e);
1598 e = foo;
1599
1600 /* check if we already has created this path */
1601 if(ftp_path_part_of(e, ftp->last_mkpath))
1602 continue;
1603
1604 /* check if this path is a part of current directory */
1605 if(ftp_path_part_of(e, ftp->curdir))
1606 continue;
1607
1608 if(strncmp(e, ".", 2) != 0) {
1609 ftp_mkdir_verb(e, vbNone);
1610 one_created = (ftp->code == ctComplete);
1611 }
1612 }
1613
1614 free(ftp->last_mkpath);
1615 ftp->last_mkpath = path_absolute(path, ftp->curdir, ftp->homedir);
1616
1617 free(e);
1618 free(orgp);
1619 return one_created;
1620 }
1621
ftp_path_absolute(const char * path)1622 char *ftp_path_absolute(const char *path)
1623 {
1624 return path_absolute(path, ftp->curdir, ftp->homedir);
1625 }
1626
ftp_flush_reply(void)1627 void ftp_flush_reply(void)
1628 {
1629 if(!ftp_connected())
1630 return;
1631
1632 #ifdef HAVE_LIBSSH
1633 if (ftp->session)
1634 return;
1635 #endif
1636
1637 /* ftp_set_signal(SIGINT, SIG_IGN);*/
1638 fprintf(stderr, "flushing replies...\r");
1639
1640 /* ftp_reply_timeout(10);*/
1641
1642 while(ftp_connected()) {
1643 if (sock_check_pending(ftp->ctrl, false) == 1)
1644 ftp_read_reply();
1645 else
1646 break;
1647 }
1648 #if 0
1649 if(ftp_loggedin())
1650 ftp_chdir(ftp->curdir);
1651 #endif
1652 ftp_set_close_handler();
1653 }
1654
ftp_rename(const char * oldname,const char * newname)1655 int ftp_rename(const char *oldname, const char *newname)
1656 {
1657 char *on;
1658 char *nn;
1659
1660 #ifdef HAVE_LIBSSH
1661 if (ftp->session)
1662 return ssh_rename(oldname, newname);
1663 #endif
1664
1665 on = xstrdup(oldname);
1666 stripslash(on);
1667 ftp_cmd("RNFR %s", on);
1668 if(ftp->code != ctContinue) {
1669 free(on);
1670 return -1;
1671 }
1672
1673 nn = xstrdup(newname);
1674 stripslash(nn);
1675 ftp_cmd("RNTO %s", nn);
1676 if(ftp->code != ctComplete) {
1677 free(on);
1678 free(nn);
1679 return -1;
1680 }
1681
1682 ftp_cache_flush_mark_for(on);
1683 ftp_cache_flush_mark_for(nn);
1684 return 0;
1685 }
1686
1687 /*
1688 ** Function: bool isLeapYear(int y)
1689 **
1690 ** Description: Local routine used by gmt_mktime() below to determine
1691 ** which years are leap years.
1692 **
1693 ** Returns; true or false.
1694 */
isLeapYear(int y)1695 static bool isLeapYear(int y)
1696 {
1697 return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
1698 }
1699
1700 /*
1701 ** Function: time_t gmt_mktime(const struct tm *ts)
1702 **
1703 ** Description: Local routine used by ftp_filetime to convert a struct tm
1704 ** to a GMT time_t. mktime() is useless since it assumes
1705 ** struct tm is a local time.
1706 **
1707 ** Returns; Either -1 upon failure or the GMT time_t
1708 ** time upon success.
1709 */
1710 /* contributed by Charles Box */
gmt_mktime(const struct tm * ts)1711 time_t gmt_mktime(const struct tm *ts)
1712 {
1713 const int SECS_MIN = 60;
1714 const int SECS_HOUR = SECS_MIN * 60;
1715 const int SECS_DAY = SECS_HOUR * 24;
1716 const int SECS_YEAR = SECS_DAY * 365;
1717 const int daysPerMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30,
1718 31, 30, 31 };
1719 const int leapDaysPerMonth[] = { 31, 29, 31, 30, 31,
1720 30, 31, 31, 30, 31, 30, 31 };
1721 const int *maxDaysInMonth = daysPerMonth;
1722
1723 time_t gmt = -1;
1724
1725 int y, m;
1726
1727 /* watch out for bogus date!!! */
1728 if (!ts)
1729 return gmt;
1730 else if (ts->tm_year < 70 || ts->tm_year > 138)
1731 return gmt;
1732 else if (ts->tm_mon < 0 || ts->tm_mon > 11)
1733 return gmt;
1734
1735 if (isLeapYear(ts->tm_year + 1900))
1736 maxDaysInMonth = leapDaysPerMonth;
1737
1738 if (ts->tm_mday < 1 || ts->tm_mday > maxDaysInMonth[ts->tm_mon])
1739 return gmt;
1740 else if (ts->tm_hour < 0 || ts->tm_hour > 23)
1741 return gmt;
1742 else if (ts->tm_min < 0 || ts->tm_min > 59)
1743 return gmt;
1744 else if (ts->tm_sec < 0 || ts->tm_sec > 59)
1745 return gmt;
1746
1747 /* add in # secs for years past */
1748 gmt = (ts->tm_year - 70) * SECS_YEAR;
1749 /* add in # leap year days not including this year! */
1750 for (y = 1970; y < (ts->tm_year + 1900); ++y)
1751 {
1752 if (isLeapYear(y))
1753 gmt += SECS_DAY;
1754 }
1755 /* add in secs for all months this year excluding this month */
1756 for (m = 0; m < ts->tm_mon; ++m)
1757 gmt += maxDaysInMonth[m] * SECS_DAY;
1758
1759 /* add in secs for all days this month excluding today */
1760 gmt += (ts->tm_mday - 1) * SECS_DAY;
1761 /* add in hours today */
1762 gmt += ts->tm_hour * SECS_HOUR;
1763 /* add in minutes today */
1764 gmt += ts->tm_min * SECS_MIN;
1765 /* add in secs today */
1766 gmt += ts->tm_sec;
1767 return gmt;
1768 }
1769
1770 /*
1771 ** Function: time_t ftp_filetime(const char *filename)
1772 **
1773 ** Description: User routine for attempting to obtain a remote file's
1774 ** modification time in Universal Coordinated Time (GMT).
1775 **
1776 ** Returns; Either -1 upon failure or the time_t file modification
1777 ** time upon success.
1778 */
ftp_filetime(const char * filename,bool force)1779 time_t ftp_filetime(const char *filename, bool force)
1780 {
1781 if (!ftp_connected())
1782 return (time_t)-1;
1783
1784 if (!force)
1785 {
1786 rfile* f = ftp_cache_get_file(filename);
1787 if (f && f->mtime != (time_t)-1)
1788 return f->mtime;
1789 }
1790
1791 #ifdef HAVE_LIBSSH
1792 if (ftp->session)
1793 return ssh_filetime(filename);
1794 #endif
1795
1796 if (!ftp->has_mdtm_command)
1797 return (time_t)-1;
1798
1799 struct tm ts;
1800 memset(&ts, 0, sizeof(ts));
1801 ftp_set_tmp_verbosity(vbNone);
1802 ftp_cmd("MDTM %s", filename);
1803 if (ftp->fullcode == 202) {
1804 ftp->has_mdtm_command = false;
1805 return (time_t)-1;
1806 }
1807 if (ftp->fullcode != 213)
1808 return (time_t)-1;
1809 /* time is Universal Coordinated Time */
1810 sscanf(ftp->reply, "%*s %04d%02d%02d%02d%02d%02d", &ts.tm_year,
1811 &ts.tm_mon, &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
1812 ts.tm_year -= 1900;
1813 ts.tm_mon--;
1814 return gmt_mktime(&ts);
1815 }
1816
ftp_maybe_isdir(rfile * fp)1817 int ftp_maybe_isdir(rfile *fp)
1818 {
1819 if(risdir(fp))
1820 return 1;
1821
1822 if(rislink(fp)) {
1823 /* found a link; if the link is in the cache,
1824 * check if it's a directory, else we don't
1825 * read the directory just to check if it's a
1826 * directory
1827 */
1828 char *adir = base_dir_xptr(fp->path);
1829 char *ap = path_absolute(fp->link, adir, ftp->homedir);
1830 rfile *lnfp = ftp_cache_get_file(ap);
1831 free(adir);
1832 free(ap);
1833 if(lnfp)
1834 return risdir(lnfp) ? 1 : 0;
1835 else
1836 /* return maybe ;-) */
1837 return 2;
1838 }
1839 return 0;
1840 }
1841
ftp_pwd(void)1842 void ftp_pwd(void)
1843 {
1844 #ifdef HAVE_LIBSSH
1845 if (ftp->session)
1846 {
1847 ssh_pwd();
1848 return;
1849 }
1850 #endif
1851
1852 ftp_set_tmp_verbosity(vbCommand);
1853 ftp_cmd("PWD");
1854 }
1855
1856
perm2string(int perm)1857 char *perm2string(int perm)
1858 {
1859 char* attr = xmalloc(11);
1860 strncpy(attr, "----------", 11);
1861
1862 if (S_ISDIR(perm))
1863 attr[0] = 'd';
1864 else if (S_ISLNK(perm))
1865 attr[0] = 'l';
1866 else if (S_ISCHR(perm))
1867 attr[0] = 'c';
1868 else if (S_ISBLK(perm))
1869 attr[0] = 'b';
1870 else if (S_ISFIFO(perm))
1871 attr[0] = 'p';
1872 else if (S_ISSOCK(perm))
1873 attr[0] = 's';
1874
1875 if (test(perm, S_IRUSR))
1876 attr[1] = 'r';
1877 if (test(perm, S_IWUSR))
1878 attr[2] = 'w';
1879 if (test(perm, S_IXUSR))
1880 {
1881 if(test(perm, S_ISUID))
1882 attr[3] = 's';
1883 else
1884 attr[3] = 'x';
1885 }
1886 else if (test(perm, S_ISUID))
1887 attr[3] = 'S';
1888
1889 if (test(perm, S_IRGRP))
1890 attr[4] = 'r';
1891 if (test(perm, S_IWGRP))
1892 attr[5] = 'w';
1893 if (test(perm, S_IXGRP))
1894 {
1895 if (test(perm, S_ISGID))
1896 attr[6] = 's';
1897 else
1898 attr[6] = 'x';
1899 }
1900 else if (test(perm, S_ISGID))
1901 attr[6] = 'S';
1902
1903 if (test(perm, S_IROTH))
1904 attr[7] = 'r';
1905 if (test(perm, S_IWOTH))
1906 attr[8] = 'w';
1907 if (test(perm, S_IXOTH))
1908 {
1909 if(test(perm, S_ISVTX))
1910 attr[9] = 't';
1911 else
1912 attr[9] = 'x';
1913 }
1914 else if (test(perm, S_ISVTX))
1915 attr[9] = 'T';
1916
1917 return attr;
1918 }
1919
ftp_get_feat(void)1920 void ftp_get_feat(void)
1921 {
1922 /* ftp_cmd("FEAT");*/
1923 }
1924
ftp_connected_user()1925 char* ftp_connected_user()
1926 {
1927 if (!ftp_loggedin())
1928 return NULL;
1929
1930 #ifdef HAVE_LIBSSH
1931 if (ftp->session)
1932 return ssh_connected_user();
1933 #endif
1934
1935 return ftp->url->username ? xstrdup(ftp->url->username) : NULL;
1936 }
1937