1 /* ncftpls.c
2  *
3  * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
4  * All rights reserved.
5  *
6  * A non-interactive utility to list directories on a remote FTP server.
7  * Very useful in shell scripts!
8  */
9 
10 #include "syshdrs.h"
11 #ifdef PRAGMA_HDRSTOP
12 #	pragma hdrstop
13 #endif
14 
15 #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
16 #	include "..\ncftp\util.h"
17 #	include "..\ncftp\bookmark.h"
18 #	include "..\ncftp\spool.h"
19 #	include "..\ncftp\pref.h"
20 #	include "..\ncftp\gl_getline.h"
21 #else
22 #	include "../ncftp/util.h"
23 #	include "../ncftp/bookmark.h"
24 #	include "../ncftp/spool.h"
25 #	include "../ncftp/pref.h"
26 #	include "../ncftp/gl_getline.h"
27 #endif
28 
29 #include "gpshare.h"
30 
31 FTPLibraryInfo gLib;
32 FTPConnectionInfo fi;
33 
34 extern int gFirewallType;
35 extern char gFirewallHost[64];
36 extern char gFirewallUser[32];
37 extern char gFirewallPass[32];
38 extern unsigned int gFirewallPort;
39 extern char gFirewallExceptionList[256];
40 extern int gFwDataPortMode;
41 extern const char gOS[], gVersion[];
42 extern Bookmark gBm;
43 
44 static int FTPRemoteRecursiveMList(FTPCIPtr cip, const char *const rdir, /* FTPFileInfoListPtr files, */ FTPLineListPtr lines);
45 
46 static void
FTPRemoteRecursiveMListSubdir(FTPCIPtr cip,char * const parentdir,const size_t pdsize,const size_t pdlen,const char * const subdir,FTPLineListPtr lines)47 FTPRemoteRecursiveMListSubdir(FTPCIPtr cip, char *const parentdir, const size_t pdsize, const size_t pdlen, const char *const subdir, FTPLineListPtr lines)
48 {
49 	size_t sdlen = strlen(subdir);
50 	size_t newlen;
51 	char *relpath = parentdir;
52 	int mls = 1;
53 	int unlsrc;
54 	FTPFileInfoPtr fip;
55 	FTPLineList ll;
56 	FTPLinePtr lp;
57 	FTPFileInfoList fil;
58 	MLstItem mli;
59 	char *cp;
60 	char *newl;
61 
62 	if (pdlen + sdlen + /* '/' */ 1 + /* '\0' */ 1 > pdsize) {
63 		return;
64 	}
65 
66 	if (pdlen == 0) {
67 		memcpy(relpath + 0, subdir, sdlen + /* '\0' */ 1);
68 		newlen = sdlen;
69 	} else {
70 		relpath[pdlen] = '/';
71 		memcpy(relpath + pdlen + 1, subdir, sdlen + /* '\0' */ 1);
72 		newlen = pdlen + sdlen + 1;
73 	}
74 
75 	(void) AddLine(lines, "");
76 	if (Dynscpy(&newl, relpath, ":", (char *) 0) != NULL) {
77 		(void) AddLine(lines, newl);
78 		free(newl);
79 	}
80 
81 	/* Paths collected must be relative. */
82 	if (((FTPListToMemory2(cip, relpath, &ll, "-a", 0, &mls)) < 0) || (ll.first == NULL)) {
83 		/* Not an error unless the first directory could not be opened. */
84 		DisposeLineListContents(&ll);
85 		goto done;
86 	}
87 
88 	/* "MLSD" succeeded */
89 	unlsrc = UnMlsD(cip, &fil, &ll);
90 	if (unlsrc < 0) {
91 		/* Return the lines as is -- even though it's invalid. */
92 		goto done;
93 	} else if (unlsrc == 0) {
94 		/* empty */
95 		goto done;
96 	}
97 	fip = fil.first;
98 
99 	/* Concat the raw MLST data. */
100 	for (lp = ll.first; lp != NULL ; lp=lp->next) {
101 		if (lp->line != NULL) {
102 			if ((UnMlsT(cip, lp->line, &mli) == 0) && ((mli.ftype == '-') || (mli.ftype == 'd')) && (strchr(mli.fname, '/') == NULL) && ((cp = strchr(lp->line, ' ')) != NULL)) {
103 				/* The server returned just a simple filename
104 				 * for this item.  Try to prepend the relative
105 				 * pathname to it.
106 				 */
107 				newl = NULL;
108 				*cp++ = '\0';
109 				if (Dynscpy(&newl, lp->line, " ", relpath, "/", cp, (char *) 0) != NULL) {
110 					free(lp->line);
111 					lp->line = newl;
112 				}
113 			}
114 			(void) AddLine(lines, lp->line);
115 		}
116 	}
117 	DisposeLineListContents(&ll);
118 
119 	/* Iterate through any subdirectories present. */
120 	for (fip = fil.first; fip != NULL; fip = fip->next) {
121 		if (fip->type != 'd')
122 			continue;
123 		FTPRemoteRecursiveMListSubdir(cip, parentdir, pdsize, newlen, fip->relname, lines);
124 	}
125 	DisposeFileInfoListContents(&fil);
126 
127 done:
128 	relpath[pdlen] = '\0';
129 }	/* FTPRemoteRecursiveMListSubdir */
130 
131 
132 
133 
134 int
FTPRemoteRecursiveMList(FTPCIPtr cip,const char * const rdir,FTPLineListPtr lines)135 FTPRemoteRecursiveMList(FTPCIPtr cip, const char *const rdir, FTPLineListPtr lines)
136 {
137 	FTPFileInfoList fil;
138 	FTPFileInfoPtr fip;
139 	int result, cwdresult;
140 	char rcwd[512];
141 	char startdir[512];
142 	size_t sdlen;
143 	int mls = 1;
144 	int unlsrc;
145 
146 	rcwd[0] = '\0';
147 	InitLineList(lines);
148 
149 	if (cip->hasMLSD != kCommandAvailable) {
150 		return (kErrMLSDNotAvailable);
151 	}
152 
153 	if ((result = FTPGetCWD(cip, rcwd, sizeof(rcwd))) < 0)
154 		return (result);
155 
156 	if (rdir == NULL)
157 		return (-1);
158 
159 	if (FTPChdir(cip, rdir) < 0) {
160 		/* Probably not a directory.  */
161 		return (cip->errNo = kErrNotADirectory);
162 	}
163 
164 	STRNCPY(startdir, rdir);
165 	sdlen = strlen(startdir);
166 
167 	/* Paths collected must be relative. */
168 	if (((result = FTPListToMemory2(cip, "", lines, "-a", 0, &mls)) < 0) || (lines->first == NULL)) {
169 		DisposeLineListContents(lines);
170 		goto done;
171 	}
172 
173 	/* "MLSD" succeeded */
174 	unlsrc = UnMlsD(cip, &fil, lines);
175 	if (unlsrc < 0) {
176 		/* Return the lines as is -- even though it's invalid. */
177 		result = kErrInvalidMLSTResponse;
178 		goto done;
179 	} else if (unlsrc == 0) {
180 		/* empty */
181 		result = kNoErr;
182 		goto done;
183 	}
184 	fip = fil.first;
185 
186 	/* Iterate through any subdirectories present. */
187 	for (fip = fil.first; fip != NULL; fip = fip->next) {
188 		if (fip->type != 'd')
189 			continue;
190 		FTPRemoteRecursiveMListSubdir(cip, startdir, sizeof(startdir), sdlen, fip->relname, lines);
191 	}
192 	DisposeFileInfoListContents(&fil);
193 
194 done:
195 	/* Ready to wrap things up -- revert to the state we had been in. */
196 	if (rcwd[0] != '\0') {
197 		if ((cwdresult = FTPChdir(cip, rcwd)) < 0) {
198 			if (result == kNoErr)
199 				result = cwdresult;
200 		}
201 	}
202 
203 	return (result);
204 }	/* FTPRemoteRecursiveMList */
205 
206 
207 
208 
209 static void
210 #if (defined(__GNUC__)) && (__GNUC__ >= 2)
211 	__attribute__ ((noreturn))
212 #endif
Usage(void)213 Usage(void)
214 {
215 	FILE *fp;
216 
217 	fp = OpenPager();
218 	(void) fprintf(fp, "NcFTPLs %.5s\n\n", gVersion + 11);
219 	(void) fprintf(fp, "Usages:\n");
220 	(void) fprintf(fp, "  ncftpls [FTP flags] [-x \"ls flags\"] ftp://url.style.host/path/name/\n");
221 	(void) fprintf(fp, "\nls Flags:\n\
222   -m     Use machine readable (MLSD) list format, if the server supports it.\n\
223   -1     Most basic format, one item per line.\n\
224   -l     Long list format.\n\
225   -C     Columnized list format (default).\n\
226   -R     Long list format, recurse subdirectories if server allows it.\n\
227   -g     Recursive and print one path per line; like \"/usr/bin/find . -print\"\n\
228   -gg    As above, but append a \"/\" character to directory pathnames.\n\
229   -a     Show all files, if server allows it (as in \"/bin/ls -a\").\n\
230   -i XX  Filter the listing (if server supports it) with the wildcard XX.\n\
231   -x XX  List command flags to try on the remote server (without leading dash).\n");
232 	(void) fprintf(fp, "\nFTP Flags:\n\
233   -u XX  Use username XX instead of anonymous.\n\
234   -p XX  Use password XX with the username.\n\
235   -P XX  Use port number XX instead of the default FTP service port (21).\n\
236   -j XX  Use account XX with the account (deprecated).\n\
237   -d XX  Use the file XX for debug logging.\n");
238 	(void) fprintf(fp, "\
239   -t XX  Timeout after XX seconds.\n\
240   -f XX  Read the file XX for user and password information.\n\
241          If file XX does not exist, check for bookmark XX in $HOME/.ncftp/bookmarks.\n\
242   -E     Use regular (PORT) data connections.\n\
243   -F     Use passive (PASV) data connections (default).\n\
244   -K     Show disk usage by attempting SITE DF.\n");
245 	(void) fprintf(fp, "\
246   -o XX  Specify miscellaneous options (see documentation).\n\
247   -W XX  Send raw FTP command XX after logging in.\n\
248   -X XX  Send raw FTP command XX after each listing.\n\
249   -Y XX  Send raw FTP command XX before logging out.\n\
250   -Z     Do not actually perform the directory listing.  Useful with -Y.\n\
251   -r XX  Redial XX times until connected.\n");
252 	(void) fprintf(fp, "\nExamples:\n\
253   ncftpls ftp://ftp.freebsd.org/pub/FreeBSD/\n\
254   ncftpls -1 ftp://ftp.freebsd.org/pub/FreeBSD/\n\
255   ncftpls -la -i '*.TXT' ftp://ftp.freebsd.org/pub/FreeBSD/\n\
256   ncftpls -m ftp://ftp.ncftp.com/ncftpd/\n\
257   ncftpls -x \"lrt\" ftp://ftp.freebsd.org/pub/FreeBSD/\n");
258 
259 	(void) fprintf(fp, "%s", "\nNote: The standard specifies that URL pathnames are are relative pathnames.\n  For FTP, this means that URLs specify relative pathnames from the start\n  directory, which for user logins, are typically the user's home directory.\n  If you want to use absolute pathnames, you need to include a literal slash,\n  using the \"%2F\" code for a \"/\" character.  Examples:\n\n");
260 
261 	(void) fprintf(fp, "%s", "\
262   ncftpls -u linus ftp://ftp.kernel.org/%2Fusr/src/\n\
263   ncftpls ftp://steve@ftp.apple.com/%2Fetc/\n");
264 
265 	(void) fprintf(fp, "\nLibrary version: %s.\n", gLibNcFTPVersion + 5);
266 	(void) fprintf(fp, "\nThis is a freeware program by Mike Gleason (http://www.ncftp.com).\n");
267 	(void) fprintf(fp, "This was built using LibNcFTP (http://www.ncftp.com/libncftp/).\n");
268 
269 	ClosePager(fp);
270 	DisposeWinsock();
271 	exit(kExitUsage);
272 }	/* Usage */
273 
274 
275 
276 
277 static void
Abort(int sigNum)278 Abort(int sigNum)
279 {
280 	signal(sigNum, Abort);
281 
282 	/* Hopefully the I/O operation in progress
283 	 * will complete, and we'll abort before
284 	 * it starts a new block.
285 	 */
286 	fi.cancelXfer++;
287 
288 	/* If the user appears to be getting impatient,
289 	 * restore the default signal handler so the
290 	 * next ^C abends the program.
291 	 */
292 	if (fi.cancelXfer >= 2)
293 		signal(sigNum, SIG_DFL);
294 }	/* Abort */
295 
296 
297 
298 
299 main_void_return_t
main(int argc,char ** argv)300 main(int argc, char **argv)
301 {
302 	int result, c;
303 	FTPConnectionInfo savedfi;
304 	FTPConnectionInfo startfi;
305 	ExitStatus es;
306 	char url[256];
307 	char urlfile[128];
308 	char rootcwd[256];
309 	char curcwd[256];
310 	int i;
311 	FTPLineList cdlist, dirlisting;
312 	FTPLinePtr lp, lp2;
313 	int rc;
314 	int ndirs;
315 	int dfmode = 0;
316 	int tryMLSD = 0;
317 	const char *pattern = "";
318 	const char *patterntouse = NULL;
319 	const char *userflags = NULL, *lsflagstouse;
320 	int lslong = 0, lsrecursive = 0, lsall = 0, lsone = 0, lscolumned = -1, lslikefind = 0, lsF = 0;
321 	int do_listings = 1;
322 	char lsflags[32];
323 	char *curdir, *coloncp, *slashcp, *lslinecp;
324 	const char *tailcp;
325 	MLstItem mli;
326 	ResponsePtr rp;
327 	FILE *ofp, *savedDebugLog = NULL;
328 	char precmd[320], postcmd[320], perfilecmd[320];
329 	int do_extracmds = 0;
330 	int configLoaded = kConfigNotLoaded;
331 	GetoptInfo opt;
332 
333 	InitWinsock();
334 #if (defined(SOCKS)) && (SOCKS >= 5)
335 	SOCKSinit(argv[0]);
336 #endif	/* SOCKS */
337 #ifdef SIGPOLL
338 	NcSignal(SIGPOLL, (FTPSigProc) SIG_IGN);
339 #endif
340 	result = FTPInitLibrary(&gLib);
341 	if (result < 0) {
342 		(void) fprintf(stderr, "ncftpls: init library error %d (%s).\n", result, FTPStrError(result));
343 		DisposeWinsock();
344 		exit(kExitInitLibraryFailed);
345 	}
346 	result = FTPInitConnectionInfo(&gLib, &fi, kDefaultFTPBufSize);
347 	if (result < 0) {
348 		(void) fprintf(stderr, "ncftpls: init connection info error %d (%s).\n", result, FTPStrError(result));
349 		DisposeWinsock();
350 		exit(kExitInitConnInfoFailed);
351 	}
352 
353 	InitUserInfo();
354 	fi.dataPortMode = kFallBackToSendPortMode;
355 	LoadFirewallPrefs(0);
356 	if (gFwDataPortMode >= 0)
357 		fi.dataPortMode = gFwDataPortMode;
358 	fi.debugLog = NULL;
359 	fi.errLog = stderr;
360 	fi.xferTimeout = 60 * 60;
361 	fi.connTimeout = 30;
362 	fi.ctrlTimeout = 135;
363 	(void) STRNCPY(fi.user, "anonymous");
364 	fi.host[0] = '\0';
365 	urlfile[0] = '\0';
366 	InitLineList(&cdlist);
367 	precmd[0] = '\0';
368 	postcmd[0] = '\0';
369 	perfilecmd[0] = '\0';
370 	es = kExitSuccess;
371 
372 	GetoptReset(&opt);
373 #define kGetoptOptions "1lRx:P:u:j:p:h:e:d:t:r:f:o:EFKW:X:Y:maCi:gI:Z"
374 	while ((c = Getopt(&opt, argc, argv, kGetoptOptions)) > 0) switch(c) {
375 		case 'P':
376 		case 'u':
377 		case 'j':
378 		case 'p':
379 		case 'h':
380 			/* Use these options later */
381 			break;
382 		case 'e':
383 			if (strcmp(opt.arg, "stdout") == 0)
384 				fi.errLog = stdout;
385 			else if (opt.arg[0] == '-')
386 				fi.errLog = stdout;
387 			else if (strcmp(opt.arg, "stderr") == 0)
388 				fi.errLog = stderr;
389 			else {
390 				fi.errLog = fopen(opt.arg, FOPEN_APPEND_TEXT);
391 				fi.debugTimestamping = 2;
392 			}
393 			break;
394 		case 'd':
395 			if (strcmp(opt.arg, "stdout") == 0)
396 				fi.debugLog = stdout;
397 			else if (opt.arg[0] == '-')
398 				fi.debugLog = stdout;
399 			else if (strcmp(opt.arg, "stderr") == 0)
400 				fi.debugLog = stderr;
401 			else {
402 				fi.debugLog = fopen(opt.arg, FOPEN_APPEND_TEXT);
403 				fi.debugTimestamping = 2;
404 			}
405 			break;
406 		case 't':
407 			SetTimeouts(&fi, opt.arg);
408 			break;
409 		case 'r':
410 			SetRedial(&fi, opt.arg);
411 			break;
412 		case 'f':
413 			if ((configLoaded = ReadConfigFile(opt.arg, &fi)) == kErrorLoadingConfig) {
414 				perror(opt.arg);
415 				exit(kExitBadConfigFile);
416 			}
417 			break;
418 		case 'o':
419 			fi.manualOverrideFeatures = opt.arg;
420 			break;
421 		case 'E':
422 			fi.dataPortMode = kSendPortMode;
423 			break;
424 		case 'F':
425 			fi.dataPortMode = kPassiveMode;
426 			break;
427 		case 'i':
428 			pattern = opt.arg;
429 			break;
430 		case 'a':
431 			lsall = 1;
432 			break;
433 		case 'l':
434 			lslong = 1;
435 			break;
436 		case '1':
437 			lsone = 1;
438 			break;
439 		case 'C':
440 			lscolumned = 1;
441 			break;
442 		case 'R':
443 			lsrecursive = 1;
444 			break;
445 		case 'g':
446 			lslikefind++;
447 			lsone = 1;
448 			lsrecursive = 1;
449 			lslong = lscolumned = 0;
450 			if (lslikefind > 1)
451 				lsF = 1;
452 			break;
453 		case 'x':
454 			userflags = opt.arg;
455 			break;
456 		case 'm':
457 			tryMLSD = 1;
458 			break;
459 		case 'K':
460 			dfmode++;
461 			break;
462 		case 'W':
463 			STRNCAT(precmd, opt.arg);
464 			STRNCAT(precmd, "\n");
465 			break;
466 		case 'X':
467 			STRNCAT(perfilecmd, opt.arg);
468 			STRNCAT(perfilecmd, "\n");
469 			break;
470 		case 'Y':
471 			STRNCAT(postcmd, opt.arg);
472 			STRNCAT(postcmd, "\n");
473 			break;
474 		case 'Z':
475 			/* This option can be used to use ncftpls as a
476 			 * quick and dirty FTP command program.  In other words,
477 			 * use this option to skip the actual listing and then
478 			 * do an FTP command with -Y.
479 			 */
480 			do_listings = 0;
481 			break;
482 		case 'I':
483 			if (AddrStrToAddr(opt.arg, &fi.preferredLocalAddr, 21) < 0) {
484 				fprintf(stderr, "Bad IP address (\"%s\") used with -I.\n", opt.arg);
485 				Usage();
486 			}
487 			break;
488 		default:
489 			Usage();
490 	}
491 	if (opt.ind > argc - 1)
492 		Usage();
493 
494 	/* In case we loaded a bookmark or config file, we allow the following
495 	 * options to override those settings.
496 	 */
497 	GetoptReset(&opt);
498 	while ((c = Getopt(&opt, argc, argv, kGetoptOptions)) > 0) switch(c) {
499 		case 'P':
500 			fi.port = (unsigned int) atoi(opt.arg);
501 			break;
502 		case 'u':
503 			(void) STRNCPY(fi.user, opt.arg);
504 			memset(opt.arg, 0, strlen(fi.user));
505 			opt.arg[0] = '?';
506 			break;
507 		case 'j':
508 			(void) STRNCPY(fi.acct, opt.arg);
509 			memset(opt.arg, 0, strlen(fi.acct));
510 			opt.arg[0] = '?';
511 			break;
512 		case 'p':
513 			(void) STRNCPY(fi.pass, opt.arg);	/* Don't recommend doing this! */
514 			fi.leavePass = 1;
515 			if (fi.pass[0] == '\0')
516 				fi.passIsEmpty = 1;
517 			memset(opt.arg, 0, strlen(fi.pass));
518 			opt.arg[0] = '?';
519 			break;
520 		case 'h':
521 			(void) STRNCPY(fi.host, opt.arg);
522 			memset(opt.arg, 0, strlen(fi.user));
523 			opt.arg[0] = '?';
524 			break;
525 	}
526 
527 	STRNCPY(lsflags, "-CF");
528 	if (lslong != 0) {
529 		STRNCPY(lsflags, "-l");
530 	} else if (lsone != 0) {
531 		STRNCPY(lsflags, "-1");
532 	}
533 	if (lsrecursive != 0) {
534 		if ((lsone == 0) && (lscolumned != 1)) {
535 			/* Maintain backwards compatibility */
536 			STRNCPY(lsflags, "-lR");
537 		} else {
538 			STRNCAT(lsflags, "R");
539 		}
540 	}
541 	if (lsF != 0) {
542 		STRNCAT(lsflags, "F");
543 	}
544 	if (lsall != 0) {
545 		STRNCAT(lsflags, "a");
546 	}
547 	lsflagstouse = lsflags;
548 	if (userflags != NULL) {
549 		lsflags[0] = '-';
550 		lsflags[1] = '\0';
551 		while (*userflags== '-')
552 			userflags++;
553 		STRNCAT(lsflags, userflags);
554 	}
555 
556 	InitOurDirectory();
557 
558 	if ((precmd[0] != '\0') || (postcmd[0] != '\0') || (perfilecmd[0] != '\0'))
559 		do_extracmds = 1;
560 	startfi = fi;
561 	memset(&savedfi, 0, sizeof(savedfi));
562 	ndirs = argc - opt.ind;
563 	for (i=opt.ind; i<argc; i++) {
564 		fi = startfi;
565 		(void) STRNCPY(url, argv[i]);
566 		patterntouse = pattern;
567 		rc = FTPDecodeURL(&fi, url, &cdlist, urlfile, sizeof(urlfile), (int *) 0, NULL);
568 		(void) STRNCPY(url, argv[i]);
569 		if (rc == kMalformedURL) {
570 			(void) fprintf(stderr, "Malformed URL: %s\n", url);
571 			DisposeWinsock();
572 			exit(kExitMalformedURL);
573 		} else if (rc == kNotURL) {
574 			(void) fprintf(stderr, "Not a URL: %s\n", url);
575 			DisposeWinsock();
576 			exit(kExitMalformedURL);
577 		} else if (urlfile[0] != '\0') {
578 			patterntouse = urlfile;
579 		}
580 
581 		if ((strcmp(fi.host, savedfi.host) == 0) && (strcmp(fi.user, savedfi.user) == 0)) {
582 			fi = savedfi;
583 
584 			/* This host is currently open, so keep using it. */
585 			if (FTPChdir(&fi, rootcwd) < 0) {
586 				FTPPerror(&fi, fi.errNo, kErrCWDFailed, "ncftpls: Could not chdir to", rootcwd);
587 				es = kExitChdirFailed;
588 				DisposeWinsock();
589 				exit((int) es);
590 			}
591 		} else {
592 			if (savedfi.connected != 0) {
593 				if (do_extracmds != 0)
594 					(void) FTPSetTransferType(&fi, kTypeBinary);
595 				savedDebugLog = fi.debugLog;
596 				if ((do_listings == 0) && (fi.debugLog == NULL))
597 					fi.debugLog = stdout;
598 				(void) AdditionalCmd(&fi, postcmd, NULL);
599 				fi.debugLog = savedDebugLog;
600 
601 				(void) FTPCloseHost(&savedfi);
602 			}
603 			memset(&savedfi, 0, sizeof(savedfi));
604 
605 			if (strcmp(fi.user, "anonymous") && strcmp(fi.user, "ftp")) {
606 				if ((fi.pass[0] == '\0') && (fi.passIsEmpty == 0)) {
607 					(void) gl_getpass("Password: ", fi.pass, sizeof(fi.pass));
608 				}
609 			}
610 
611 			if (MayUseFirewall(fi.host, gFirewallType, gFirewallExceptionList) != 0) {
612 				fi.firewallType = gFirewallType;
613 				(void) STRNCPY(fi.firewallHost, gFirewallHost);
614 				(void) STRNCPY(fi.firewallUser, gFirewallUser);
615 				(void) STRNCPY(fi.firewallPass, gFirewallPass);
616 				fi.firewallPort = gFirewallPort;
617 			}
618 
619 			es = kExitOpenTimedOut;
620 			if ((result = FTPOpenHost(&fi)) < 0) {
621 				(void) fprintf(stderr, "ncftpls: cannot open %s: %s.\n", fi.host, FTPStrError(result));
622 				es = kExitOpenFailed;
623 				DisposeWinsock();
624 				exit((int) es);
625 			}
626 
627 			if (fi.hasCLNT != kCommandNotAvailable)
628 				(void) FTPCmd(&fi, "CLNT NcFTPLs %.5s %s", gVersion + 11, gOS);
629 
630 			if (do_extracmds != 0)
631 				(void) FTPSetTransferType(&fi, kTypeBinary);
632 			savedDebugLog = fi.debugLog;
633 			if ((do_listings == 0) && (fi.debugLog == NULL))
634 				fi.debugLog = stdout;
635 			(void) AdditionalCmd(&fi, precmd, NULL);
636 			fi.debugLog = savedDebugLog;
637 
638 			if (FTPGetCWD(&fi, rootcwd, sizeof(rootcwd)) < 0) {
639 				FTPPerror(&fi, fi.errNo, kErrPWDFailed, "ncftpls", "could not get current remote working directory");
640 				es = kExitChdirFailed;
641 				DisposeWinsock();
642 				exit((int) es);
643 			}
644 		}
645 
646 		es = kExitChdirTimedOut;
647 		if ((FTPChdirList(&fi, &cdlist, NULL, 0, (kChdirFullPath|kChdirOneSubdirAtATime))) != 0) {
648 			FTPPerror(&fi, fi.errNo, kErrCWDFailed, "ncftpls: Could not change directory", NULL);
649 			es = kExitChdirFailed;
650 			DisposeWinsock();
651 			exit((int) es);
652 		}
653 
654 		if (ndirs > 1) {
655 			fprintf(stdout, "%s%s\n\n",
656 				(i > opt.ind) ? "\n\n\n" : "", url);
657 		}
658 		fflush(stdout);
659 
660 		if (dfmode != 0) {
661 			if (FTPGetCWD(&fi, curcwd, sizeof(curcwd)) < 0) {
662 				FTPPerror(&fi, fi.errNo, kErrPWDFailed, "ncftpls", "could not get current remote working directory from remote host");
663 				es = kExitChdirFailed;
664 				DisposeWinsock();
665 				exit((int) es);
666 			}
667 
668 			rp = InitResponse();
669 			if (rp != NULL) {
670 				result = RCmd(&fi, rp, "SITE DF %s", curcwd);
671 				ofp = fi.debugLog;
672 				fi.debugLog = stdout;
673 				PrintResponse(&fi, &rp->msg);
674 				fi.debugLog = ofp;
675 				DoneWithResponse(&fi, rp);
676 			}
677 			if (dfmode == 1)
678 				continue;	/* Don't bother with the listing unless -KK. */
679 		}
680 
681 		es = kExitXferTimedOut;
682 
683 		if (do_listings != 0) {
684 			(void) signal(SIGINT, Abort);
685 			PrintF(&fi, "ncftpls DIRLIST: directory, file, or wildcard = \"%s\";  lsflags = \"%s\";  tryMLS = %d.\n", patterntouse, lsflagstouse, tryMLSD);
686 			if (
687 				(lsrecursive && tryMLSD && FTPRemoteRecursiveMList(&fi, patterntouse, &dirlisting) >= 0) ||
688 				(FTPListToMemory2(&fi, patterntouse, &dirlisting, lsflagstouse, /* allow blank lines from server? yes */ 1, &tryMLSD) >= 0)
689 			) {
690 				es = kExitSuccess;
691 				if (do_extracmds != 0)
692 					(void) FTPSetTransferType(&fi, kTypeBinary);
693 				savedDebugLog = fi.debugLog;
694 				if ((do_listings == 0) && (fi.debugLog == NULL))
695 					fi.debugLog = stdout;
696 				(void) AdditionalCmd(&fi, perfilecmd, curcwd);
697 				fi.debugLog = savedDebugLog;
698 				savedfi = fi;
699 
700 				if (lslikefind > 0) {
701 					curdir = NULL;
702 					for (lp = dirlisting.first; lp != NULL; ) {
703 						lp2 = lp;
704 						lp = lp->next;
705 						tailcp = "";
706 						if (tryMLSD != 0) {
707 							if (UnMlsT(&fi, lp2->line, &mli) < 0)
708 								continue;
709 							if (mli.ftype == 'd') {
710 								tailcp = "/";
711 							} else {
712 								tailcp = "";
713 							}
714 							lslinecp = strchr(lp2->line, ' ');
715 							if ((lslinecp != NULL) && (lslinecp != lp2->line) && (lslinecp[-1] == ';')) {
716 								/* Valid MLSD format; skip ahead one byte to the pathname. */
717 								lslinecp++;
718 							} else {
719 								lslinecp = lp2->line;
720 							}
721 						} else {
722 							lslinecp = lp2->line;
723 						}
724 						if (lslinecp != NULL) {
725 							if ((lslinecp[0] == '.') && ((lslinecp[1] == '/') || (lslinecp[1] == '\\')))
726 								lslinecp += 2;
727 							coloncp = strrchr(lslinecp, ':');
728 							if ((coloncp != NULL) && (coloncp[1] == '\0')) {
729 								*coloncp = '\0';
730 								curdir = lslinecp;
731 								if (strcmp(curdir, ".") == 0)
732 									curdir = NULL;
733 							} else if ((tryMLSD == 0) && (coloncp != NULL) && (strlen(coloncp) > 4) && (strncmp(coloncp + 3, "ermission denied", strlen("ermission denied")) == 0)) {
734 								continue;
735 							} else if ((lslinecp[0] != '\0') && (lslinecp[0] != '\n') && (lslinecp[0] != '\r')) {
736 								slashcp = strchr(lslinecp, '/');
737 								if (slashcp == NULL)
738 									slashcp = strchr(lslinecp, '\\');
739 								if ((slashcp == NULL) || (slashcp[1] == '\0') || (slashcp[1] == '\n') || (slashcp[1] == '\r')) {
740 									if (curdir == NULL) {
741 										/* Could print "./" then file */
742 										(void) fprintf(stdout, "%s%s\n", lslinecp, tailcp);
743 									} else {
744 										(void) fprintf(stdout, "%s/%s%s\n", curdir, lslinecp, tailcp);
745 									}
746 								} else {
747 									(void) fprintf(stdout, "%s%s\n", lslinecp, tailcp);
748 								}
749 							}
750 						}
751 					}
752 				} else {
753 					/* Print the completed listing. */
754 					for (lp = dirlisting.first; lp != NULL; ) {
755 						lp2 = lp;
756 						lp = lp->next;
757 						if (lp2->line != NULL) {
758 							(void) fprintf(stdout, "%s\n", lp2->line);
759 						}
760 					}
761 				}
762 			} else {
763 				es = kExitXferFailed;
764 			}
765 			(void) signal(SIGINT, SIG_DFL);
766 		}
767 	}
768 
769 	if (do_extracmds != 0)
770 		(void) FTPSetTransferType(&fi, kTypeBinary);
771 
772 	savedDebugLog = fi.debugLog;
773 	if ((do_listings == 0) && (fi.debugLog == NULL))
774 		fi.debugLog = stdout;
775 	(void) AdditionalCmd(&fi, postcmd, NULL);
776 	fi.debugLog = savedDebugLog;
777 
778 	(void) FTPCloseHost(&fi);
779 
780 	DisposeWinsock();
781 	exit((int) es);
782 }	/* main */
783