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