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