b8b8d3009330f1da36ae99ca014a433169ccd904
[reactos.git] / rosapps / applications / net / ncftp / ncftp / cmds.c
1 /* cmds.c
2 *
3 * Copyright (c) 1992-2001 by Mike Gleason.
4 * All rights reserved.
5 *
6 */
7
8 #include "syshdrs.h"
9 #include "shell.h"
10 #include "util.h"
11 #include "ls.h"
12 #include "bookmark.h"
13 #include "cmds.h"
14 #include "main.h"
15 #include "trace.h"
16 #include "log.h"
17 #include "pref.h"
18 #include "spool.h"
19 #include "getline.h"
20 #include "readln.h"
21 #include "getopt.h"
22
23 /* This was the directory path when the user logged in. For anonymous users,
24 * this should be "/", but for real users, it should be their home directory.
25 * This variable is used later when we want to calculate a relative path
26 * from the starting directory.
27 */
28 char gStartDir[512];
29
30 /* The pathname to the current working directory. This always needs to be
31 * current, so whenever a directory change is done this should be changed.
32 */
33 char gRemoteCWD[512];
34
35 /* Same, but the previous directory the user was in, or empty string if
36 * there is none.
37 */
38 char gPrevRemoteCWD[512];
39
40 /* Another buffer we use just temporarily when switching directories. */
41 char gScratchCWD[512];
42
43 /* The only reason we do this is to get gcc/lint to shut up
44 * about unused parameters.
45 */
46 int gUnusedArg;
47 #define ARGSUSED(x) x = (argc != 0) || (argv != 0) || (cmdp != 0) || (aip != 0)
48
49 /* Used temporarily, but put it here because it's big. */
50 FTPConnectionInfo gTmpURLConn;
51
52 /* If the user doesn't want to be prompted for a batch of files,
53 * they can tell us to answer this answer for each item in the batch.
54 */
55 int gResumeAnswerAll;
56
57 extern FTPLibraryInfo gLib;
58 extern FTPConnectionInfo gConn;
59 extern char gOurDirectoryPath[];
60 extern Command gCommands[];
61 extern char gVersion[];
62 extern size_t gNumCommands;
63 extern int gDebug, gDoneApplication;
64 extern char *gOptArg;
65 extern int gOptInd, gGotSig;
66 extern int gFirstTimeUser;
67 extern unsigned int gFirewallPort;
68 extern int gFirewallType;
69 extern char gFirewallHost[64];
70 extern char gFirewallUser[32];
71 extern char gFirewallPass[32];
72 extern char gFirewallExceptionList[];
73 extern char gPager[], gHome[], gShell[];
74 extern char gOS[];
75 extern int gAutoResume, gRedialDelay;
76 extern int gAutoSaveChangesToExistingBookmarks;
77 extern Bookmark gBm;
78 extern int gLoadedBm, gConfirmClose, gSavePasswords, gScreenColumns;
79 extern char gLocalCWD[512], gPrevLocalCWD[512], gOurInstallationPath[];
80 extern int gMayCancelJmp;
81 #if defined(WIN32) || defined(_WINDOWS)
82 #elif defined(HAVE_SIGSETJMP)
83 extern sigjmp_buf gCancelJmp;
84 #else /* HAVE_SIGSETJMP */
85 extern jmp_buf gCancelJmp;
86 #endif /* HAVE_SIGSETJMP */
87
88
89
90
91 /* Open the users $PAGER, or just return stdout. Make sure to use
92 * ClosePager(), and not fclose/pclose directly.
93 */
94 static FILE *
95 OpenPager(void)
96 {
97 FILE *fp;
98 char *pprog;
99
100 (void) fflush(stdout);
101 pprog = gPager;
102 fp = popen((pprog[0] == '\0') ? "more" : pprog, "w");
103 if (fp == NULL)
104 return (stdout);
105 return (fp);
106 } /* OpenPager */
107
108
109
110
111 /* Close (maybe) a file previously created by OpenPager. */
112 static void
113 ClosePager(FILE *pagerfp)
114 {
115 #ifdef SIGPIPE
116 sigproc_t osigpipe;
117 #endif
118
119 if ((pagerfp != NULL) && (pagerfp != stdout)) {
120 #ifdef SIGPIPE
121 osigpipe = (sigproc_t) NcSignal(SIGPIPE, SIG_IGN);
122 #endif
123 (void) pclose(pagerfp);
124 #ifdef SIGPIPE
125 (void) NcSignal(SIGPIPE, osigpipe);
126 #endif
127 }
128 } /* ClosePager */
129
130
131
132
133 /* Fills in the bookmarkName field of the Bookmark. */
134 int
135 PromptForBookmarkName(BookmarkPtr bmp)
136 {
137 char dfltname[64];
138 char bmname[64];
139
140 DefaultBookmarkName(dfltname, sizeof(dfltname), gConn.host);
141 if (dfltname[0] == '\0') {
142 (void) printf("Enter a name for this bookmark: ");
143 } else {
144 (void) printf("Enter a name for this bookmark, or hit enter for \"%s\": ", dfltname);
145 }
146 fflush(stdin);
147 (void) FGets(bmname, sizeof(bmname), stdin);
148 if (bmname[0] != '\0') {
149 (void) STRNCPY(bmp->bookmarkName, bmname);
150 return (0);
151 } else if (dfltname[0] != '\0') {
152 (void) STRNCPY(bmp->bookmarkName, dfltname);
153 return (0);
154 }
155 return (-1);
156 } /* PromptForBookmarkName */
157
158
159
160 void
161 CurrentURL(char *dst, size_t dsize, int showpass)
162 {
163 Bookmark bm;
164 char dir[160];
165
166 memset(&bm, 0, sizeof(bm));
167 (void) STRNCPY(bm.name, gConn.host);
168 if ((gConn.user[0] != '\0') && (! STREQ(gConn.user, "anonymous")) && (! STREQ(gConn.user, "ftp"))) {
169 (void) STRNCPY(bm.user, gConn.user);
170 (void) STRNCPY(bm.pass, (showpass == 0) ? "PASSWORD" : gConn.pass);
171 (void) STRNCPY(bm.acct, gConn.acct);
172 }
173
174 bm.port = gConn.port;
175
176 /* We now save relative paths, because the pathname in URLs are
177 * relative by nature. This makes non-anonymous FTP URLs shorter
178 * because it doesn't have to include the pathname of their
179 * home directory.
180 */
181 (void) STRNCPY(dir, gRemoteCWD);
182 AbsoluteToRelative(bm.dir, sizeof(bm.dir), dir, gStartDir, strlen(gStartDir));
183
184 BookmarkToURL(&bm, dst, dsize);
185 } /* CurrentURL */
186
187
188
189
190 /* Fills in the fields of the Bookmark structure, based on the FTP current
191 * session.
192 */
193 void
194 FillBookmarkInfo(BookmarkPtr bmp)
195 {
196 char dir[160];
197
198 (void) STRNCPY(bmp->name, gConn.host);
199 if ((STREQ(gConn.user, "anonymous")) || (STREQ(gConn.user, "ftp"))) {
200 bmp->user[0] = '\0';
201 bmp->pass[0] = '\0';
202 bmp->acct[0] = '\0';
203 } else {
204 (void) STRNCPY(bmp->user, gConn.user);
205 (void) STRNCPY(bmp->pass, gConn.pass);
206 (void) STRNCPY(bmp->acct, gConn.acct);
207 }
208
209 /* We now save relative paths, because the pathname in URLs are
210 * relative by nature. This makes non-anonymous FTP URLs shorter
211 * because it doesn't have to include the pathname of their
212 * home directory.
213 */
214 (void) STRNCPY(dir, gRemoteCWD);
215 AbsoluteToRelative(bmp->dir, sizeof(bmp->dir), dir, gStartDir, strlen(gStartDir));
216 bmp->port = gConn.port;
217 (void) time(&bmp->lastCall);
218 bmp->hasSIZE = gConn.hasSIZE;
219 bmp->hasMDTM = gConn.hasMDTM;
220 bmp->hasPASV = gConn.hasPASV;
221 bmp->hasUTIME = gConn.hasUTIME;
222 if (gFirewallType == kFirewallNotInUse)
223 (void) STRNCPY(bmp->lastIP, gConn.ip);
224 } /* FillBookmarkInfo */
225
226
227
228
229 /* Saves the current FTP session settings as a bookmark. */
230 void
231 SaveCurrentAsBookmark(void)
232 {
233 int saveBm;
234 char ans[64];
235
236 /* gBm.bookmarkName must already be set. */
237 FillBookmarkInfo(&gBm);
238
239 saveBm = gSavePasswords;
240 if (gLoadedBm != 0)
241 saveBm = 1;
242 if ((saveBm < 0) && (gBm.pass[0] != '\0')) {
243 (void) printf("\n\nYou logged into this site using a password.\nWould you like to save the password with this bookmark?\n\n");
244 (void) printf("Save? [no] ");
245 (void) memset(ans, 0, sizeof(ans));
246 fflush(stdin);
247 (void) fgets(ans, sizeof(ans) - 1, stdin);
248 if ((saveBm = StrToBool(ans)) == 0) {
249 (void) printf("\nNot saving the password.\n");
250 }
251 }
252 if (PutBookmark(&gBm, saveBm) < 0) {
253 (void) fprintf(stderr, "Could not save bookmark.\n");
254 } else {
255 /* Also marks whether we saved it. */
256 gLoadedBm = 1;
257 (void) printf("Bookmark \"%s\" saved.\n", gBm.bookmarkName);
258
259 ReCacheBookmarks();
260 }
261 } /* SaveCurrentAsBookmark */
262
263
264
265
266 /* If the user did not explicitly bookmark this site already, ask
267 * the user if they want to save one.
268 */
269 void
270 SaveUnsavedBookmark(void)
271 {
272 char url[256];
273 char ans[64];
274 int c;
275
276 if ((gConfirmClose != 0) && (gLoadedBm == 0) && (gOurDirectoryPath[0] != '\0')) {
277 FillBookmarkInfo(&gBm);
278 BookmarkToURL(&gBm, url, sizeof(url));
279 (void) printf("\n\nYou have not saved a bookmark for this site.\n");
280 (void) sleep(1);
281 (void) printf("\nWould you like to save a bookmark to:\n\t%s\n\n", url);
282 for (;;) {
283 (void) printf("Save? (yes/no) ");
284 (void) memset(ans, 0, sizeof(ans));
285 fflush(stdin);
286 if (fgets(ans, sizeof(ans) - 1, stdin) == NULL) {
287 c = 'n';
288 break;
289 }
290 c = ans[0];
291 if ((c == 'n') || (c == 'y'))
292 break;
293 if (c == 'N') {
294 c = 'n';
295 break;
296 } else if (c == 'Y') {
297 c = 'y';
298 break;
299 }
300 }
301 if (c == 'n') {
302 (void) printf("Not saved. (If you don't want to be asked this, \"set confirm-close no\")\n\n\n");
303
304 } else if (PromptForBookmarkName(&gBm) < 0) {
305 (void) printf("Nevermind.\n");
306 } else {
307 SaveCurrentAsBookmark();
308 }
309 } else if ((gLoadedBm == 1) && (gOurDirectoryPath[0] != '\0') && (strcmp(gOurDirectoryPath, gBm.dir) != 0)) {
310 /* Bookmark has changed. */
311 if (gAutoSaveChangesToExistingBookmarks != 0) {
312 SaveCurrentAsBookmark();
313 }
314 }
315 } /* SaveUnsavedBookmark */
316
317
318
319 /* Save the current host session settings for later as a "bookmark", which
320 * will be referred to by a bookmark abbreviation name.
321 */
322 void
323 BookmarkCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
324 {
325 /* The only reason we do this is to get gcc/lint to shut up
326 * about unused parameters.
327 */
328 ARGSUSED(gUnusedArg);
329
330 if (gOurDirectoryPath[0] == '\0') {
331 (void) printf("Sorry, configuration information is not saved for this user.\n");
332 } else if ((argc <= 1) || (argv[1][0] == '\0')) {
333 /* No name specified on the command line. */
334 if (gBm.bookmarkName[0] == '\0') {
335 /* Not previously bookmarked. */
336 if (PromptForBookmarkName(&gBm) < 0) {
337 (void) printf("Nevermind.\n");
338 } else {
339 SaveCurrentAsBookmark();
340 }
341 } else {
342 /* User wants to update an existing bookmark. */
343 SaveCurrentAsBookmark();
344 }
345 } else {
346 (void) STRNCPY(gBm.bookmarkName, argv[1]);
347 SaveCurrentAsBookmark();
348 }
349 } /* BookmarkCmd */
350
351
352
353
354 /* Dump a remote file to the screen. */
355 void
356 CatCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
357 {
358 int result;
359 int i;
360
361 ARGSUSED(gUnusedArg);
362 for (i=1; i<argc; i++) {
363 result = FTPGetOneFile2(&gConn, argv[i], NULL, kTypeAscii, STDOUT_FILENO, kResumeNo, kAppendNo);
364 FTPPerror(&gConn, result, kErrCouldNotStartDataTransfer, "cat", argv[i]);
365 }
366 } /* CatCmd */
367
368
369
370 static void
371 NcFTPCdResponseProc(const FTPCIPtr cipUNUSED, ResponsePtr rp)
372 {
373 LinePtr lp;
374 LineListPtr llp;
375
376 gUnusedArg = (cipUNUSED != NULL);
377 if ((rp->printMode & kResponseNoPrint) != 0)
378 return;
379 llp = &rp->msg;
380 for (lp = llp->first; lp != NULL; lp = lp->next) {
381 if ((lp == llp->first) && (rp->codeType == 2)) {
382 if (ISTRNCMP(lp->line, "CWD command", 11) == 0)
383 continue;
384 if (lp->line[0] == '"')
385 continue; /* "/pub/foo" is... */
386 }
387 (void) printf("%s\n", lp->line);
388 }
389 } /* NcFTPCdResponseProc */
390
391
392
393
394 /* Manually print a response obtained from the remote FTP user. */
395 void
396 PrintResp(LineListPtr llp)
397 {
398 LinePtr lp;
399
400 if (llp != NULL) {
401 for (lp = llp->first; lp != NULL; lp = lp->next) {
402 if ((lp == llp->first) && (ISTRNCMP(lp->line, "CWD command", 11) == 0))
403 continue;
404 (void) printf("%s\n", lp->line);
405 }
406 }
407 } /* PrintResp */
408
409
410
411
412 /* Do a chdir, and update our notion of the current directory.
413 * Some servers return it back as part of the CWD response,
414 * otherwise do a CWD command followed by a PWD.
415 */
416 int
417 nFTPChdirAndGetCWD(const FTPCIPtr cip, const char *cdCwd, const int quietMode)
418 {
419 ResponsePtr rp;
420 size_t cdCwdLen;
421 int result;
422 #ifdef USE_WHAT_SERVER_SAYS_IS_CWD
423 int foundcwd;
424 char *l, *r;
425 #endif
426
427 if (cip == NULL)
428 return (kErrBadParameter);
429 if (strcmp(cip->magic, kLibraryMagic))
430 return (kErrBadMagic);
431
432 if ((cdCwd == NULL) || (cdCwd[0] == '\0')) {
433 result = kErrInvalidDirParam;
434 cip->errNo = kErrInvalidDirParam;
435 } else {
436 rp = InitResponse();
437 if (rp == NULL) {
438 result = kErrMallocFailed;
439 cip->errNo = kErrMallocFailed;
440 /* Error(cip, kDontPerror, "Malloc failed.\n"); */
441 } else {
442 cdCwdLen = strlen(cdCwd);
443 if (strcmp(cdCwd, "..") == 0) {
444 result = RCmd(cip, rp, "CDUP");
445 } else {
446 result = RCmd(cip, rp, "CWD %s", cdCwd);
447 }
448 if (result == 2) {
449 #ifdef USE_WHAT_SERVER_SAYS_IS_CWD
450 (void) STRNCPY(gScratchCWD, gRemoteCWD);
451 foundcwd = 0;
452 if ((r = strrchr(rp->msg.first->line, '"')) != NULL) {
453 /* "xxxx" is current directory.
454 * Strip out just the xxxx to copy into the remote cwd.
455 *
456 * This is nice because we didn't have to do a PWD.
457 */
458 l = strchr(rp->msg.first->line, '"');
459 if ((l != NULL) && (l != r) && (l == rp->msg.first->line)) {
460 *r = '\0';
461 ++l;
462 (void) Strncpy(gRemoteCWD, l, sizeof(gRemoteCWD));
463 *r = '"'; /* Restore, so response prints correctly. */
464 foundcwd = 1;
465 result = kNoErr;
466 }
467 }
468 if (quietMode)
469 rp->printMode |= kResponseNoPrint;
470 NcFTPCdResponseProc(cip, rp);
471 DoneWithResponse(cip, rp);
472 if (foundcwd == 0) {
473 result = FTPGetCWD(cip, gRemoteCWD, sizeof(gRemoteCWD));
474 if (result != kNoErr) {
475 PathCat(gRemoteCWD, sizeof(gRemoteCWD), gScratchCWD, cdCwd);
476 result = kNoErr;
477 }
478 }
479 #else /* USE_CLIENT_SIDE_CALCULATED_CWD */
480 if (quietMode)
481 rp->printMode |= kResponseNoPrint;
482 NcFTPCdResponseProc(cip, rp);
483 DoneWithResponse(cip, rp);
484 (void) STRNCPY(gScratchCWD, gRemoteCWD);
485 PathCat(gRemoteCWD, sizeof(gRemoteCWD), gScratchCWD, cdCwd);
486 result = kNoErr;
487 #endif
488 } else if (result > 0) {
489 result = kErrCWDFailed;
490 cip->errNo = kErrCWDFailed;
491 DoneWithResponse(cip, rp);
492 } else {
493 DoneWithResponse(cip, rp);
494 }
495 }
496 }
497 return (result);
498 } /* nFTPChdirAndGetCWD */
499
500
501
502
503 int
504 Chdirs(FTPCIPtr cip, const char *const cdCwd)
505 {
506 char *cp, *startcp;
507 int result;
508 int lastSubDir;
509
510 if (cip == NULL)
511 return (kErrBadParameter);
512 if (strcmp(cip->magic, kLibraryMagic))
513 return (kErrBadMagic);
514
515 if (cdCwd == NULL) {
516 result = kErrInvalidDirParam;
517 cip->errNo = kErrInvalidDirParam;
518 return result;
519 }
520
521 if ((cdCwd[0] == '\0') || (strcmp(cdCwd, ".") == 0)) {
522 result = 0;
523 return (result);
524 }
525
526 cp = cip->buf;
527 cp[cip->bufSize - 2] = '\0';
528 if ((cdCwd[0] == '.') && (cdCwd[1] == '.') && ((cdCwd[2] == '\0') || IsLocalPathDelim(cdCwd[2]))) {
529 PathCat(cip->buf, cip->bufSize, gRemoteCWD, cdCwd);
530 } else {
531 (void) Strncpy(cip->buf, cdCwd, cip->bufSize);
532 }
533 if (cp[cip->bufSize - 2] != '\0')
534 return (kErrBadParameter);
535
536 StrRemoveTrailingLocalPathDelim(cp);
537 do {
538 startcp = cp;
539 cp = StrFindLocalPathDelim(cp + 0);
540 if (cp != NULL) {
541 *cp++ = '\0';
542 }
543 lastSubDir = (cp == NULL);
544 result = nFTPChdirAndGetCWD(cip, (*startcp != '\0') ? startcp : "/", lastSubDir ? 0 : 1);
545 if (result < 0) {
546 cip->errNo = result;
547 }
548 } while ((!lastSubDir) && (result == 0));
549
550 return (result);
551 } /* Chdirs */
552
553
554
555
556 /* Remote change of working directory command. */
557 void
558 ChdirCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
559 {
560 int result;
561 LineList ll;
562 LinePtr lp;
563
564 ARGSUSED(gUnusedArg);
565
566 if (argc <= 1) {
567 if (gStartDir[0] != '\0') {
568 (void) STRNCPY(gPrevRemoteCWD, gRemoteCWD);
569 result = Chdirs(&gConn, gStartDir);
570 if (result != kNoErr) {
571 /* State is incoherent if this happens! */
572 FTPPerror(&gConn, result, kErrCWDFailed, "Could not chdir to", gStartDir);
573 }
574 } else {
575 PrintCmdUsage(cmdp);
576 }
577 } else {
578 InitLineList(&ll);
579 result = FTPRemoteGlob(&gConn, &ll, argv[1], (aip->noglobargv[1] != 0) ? kGlobNo: kGlobYes);
580 if (result < 0) {
581 FTPPerror(&gConn, result, kErrGlobFailed, argv[0], argv[1]);
582 } else {
583 lp = ll.first;
584 if ((lp != NULL) && (lp->line != NULL)) {
585 if ((strcmp(lp->line, "-") == 0) && (gPrevRemoteCWD[0] != '\0')) {
586 free(lp->line);
587 lp->line = StrDup(gPrevRemoteCWD);
588 if (lp->line == NULL) {
589 result = kErrMallocFailed;
590 gConn.errNo = kErrMallocFailed;
591 } else {
592 (void) STRNCPY(gPrevRemoteCWD, gRemoteCWD);
593 result = Chdirs(&gConn, lp->line);
594 }
595 } else {
596 (void) STRNCPY(gPrevRemoteCWD, gRemoteCWD);
597 result = Chdirs(&gConn, lp->line);
598 }
599 if (result != kNoErr)
600 FTPPerror(&gConn, result, kErrCWDFailed, "Could not chdir to", lp->line);
601 }
602 }
603 DisposeLineListContents(&ll);
604 }
605 } /* ChdirCmd */
606
607
608
609
610 /* Chmod files on the remote host, if it supports it. */
611 void
612 ChmodCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
613 {
614 int i, result;
615
616 ARGSUSED(gUnusedArg);
617 for (i=2; i<argc; i++) {
618 result = FTPChmod(
619 &gConn, argv[i], argv[1],
620 (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes
621 );
622 if (result < 0) {
623 FTPPerror(&gConn, result, kErrChmodFailed, "chmod", argv[i]);
624 /* but continue */
625 }
626 }
627
628 /* Really should just flush only the modified directories... */
629 FlushLsCache();
630 } /* ChmodCmd */
631
632
633
634
635 /* Close the current session to a remote FTP server. */
636 void
637 CloseCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
638 {
639 ARGSUSED(gUnusedArg);
640 if (gConn.connected == 0)
641 (void) printf("Already closed.\n");
642 else
643 CloseHost();
644 } /* CloseCmd */
645
646
647
648 /* User interface to the program's debug-mode setting. */
649 void
650 DebugCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
651 {
652 ARGSUSED(gUnusedArg);
653 if (argc > 1)
654 SetDebug(atoi(argv[1]));
655 else
656 SetDebug(!gDebug);
657 } /* DebugCmd */
658
659
660
661
662 /* Delete files on the remote host. */
663 void
664 DeleteCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
665 {
666 int result;
667 int i, c;
668 int recursive = kRecursiveNo;
669
670 ARGSUSED(gUnusedArg);
671 GetoptReset();
672 while ((c = Getopt(argc, argv, "rf")) > 0) switch(c) {
673 case 'r':
674 recursive = kRecursiveYes;
675 break;
676 case 'f':
677 /* ignore */
678 break;
679 default:
680 PrintCmdUsage(cmdp);
681 return;
682 }
683
684 for (i=gOptInd; i<argc; i++) {
685 result = FTPDelete(
686 &gConn, argv[i], recursive,
687 (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes
688 );
689 if (result < 0) {
690 FTPPerror(&gConn, result, kErrDELEFailed, "delete", argv[i]);
691 /* but continue */
692 }
693 }
694
695 /* Really should just flush only the modified directories... */
696 FlushLsCache();
697 } /* DeleteCmd */
698
699
700
701
702 /* Command shell echo command. This is mostly useful for testing the command
703 * shell, as a sample command which prints some output.
704 */
705 void
706 EchoCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
707 {
708 int i;
709 int result;
710 int np = 0;
711 LineList ll;
712 LinePtr lp;
713
714 ARGSUSED(gUnusedArg);
715 for (i=1; i<argc; i++) {
716 InitLineList(&ll);
717 result = FTPLocalGlob(&gConn, &ll, argv[i], (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
718 if (result < 0) {
719 FTPPerror(&gConn, result, kErrGlobFailed, "local glob", argv[i]);
720 } else {
721 for (lp = ll.first; lp != NULL; lp = lp->next) {
722 if (lp->line != NULL) {
723 if (np > 0)
724 (void) printf(" ");
725 (void) printf("%s", lp->line);
726 np++;
727 }
728 }
729 }
730 DisposeLineListContents(&ll);
731 }
732 (void) printf("\n");
733 } /* EchoCmd */
734
735
736
737
738 static int
739 NcFTPConfirmResumeDownloadProc(
740 const char *volatile *localpath,
741 volatile longest_int localsize,
742 volatile time_t localmtime,
743 const char *volatile remotepath,
744 volatile longest_int remotesize,
745 volatile time_t remotemtime,
746 volatile longest_int *volatile startPoint)
747 {
748 int zaction = kConfirmResumeProcSaidBestGuess;
749 char tstr[80], ans[32];
750 static char newname[128]; /* arrggh... static. */
751
752 if (gResumeAnswerAll != kConfirmResumeProcNotUsed)
753 return (gResumeAnswerAll);
754
755 if (gAutoResume != 0)
756 return (kConfirmResumeProcSaidBestGuess);
757
758 tstr[sizeof(tstr) - 1] = '\0';
759 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &localmtime));
760 (void) printf(
761 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
762 "\nThe local file \"%s\" already exists.\n\tLocal: %12lld bytes, dated %s.\n",
763 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
764 "\nThe local file \"%s\" already exists.\n\tLocal: %12qd bytes, dated %s.\n",
765 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
766 "\nThe local file \"%s\" already exists.\n\tLocal: %12I64d bytes, dated %s.\n",
767 #else
768 "\nThe local file \"%s\" already exists.\n\tLocal: %12ld bytes, dated %s.\n",
769 #endif
770 *localpath,
771 localsize,
772 tstr
773 );
774
775 if ((remotemtime != kModTimeUnknown) && (remotesize != kSizeUnknown)) {
776 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &remotemtime));
777 (void) printf(
778 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
779 "\tRemote: %12lld bytes, dated %s.\n",
780 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
781 "\tRemote: %12qd bytes, dated %s.\n",
782 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
783 "\tRemote: %12I64d bytes, dated %s.\n",
784 #else
785 "\tRemote: %12ld bytes, dated %s.\n",
786 #endif
787 remotesize,
788 tstr
789 );
790 if ((remotemtime == localmtime) && (remotesize == localsize)) {
791 (void) printf("\t(Files are identical, skipped)\n\n");
792 return (kConfirmResumeProcSaidSkip);
793 }
794 } else if (remotesize != kSizeUnknown) {
795 (void) printf(
796 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
797 "\tRemote: %12lld bytes, date unknown.\n",
798 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
799 "\tRemote: %12qd bytes, date unknown.\n",
800 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
801 "\tRemote: %12I64d bytes, date unknown.\n",
802 #else
803 "\tRemote: %12ld bytes, date unknown.\n",
804 #endif
805 remotesize
806 );
807 } else if (remotemtime != kModTimeUnknown) {
808 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &remotemtime));
809 (void) printf(
810 "\tRemote: size unknown, dated %s.\n",
811 tstr
812 );
813 }
814
815 printf("\n");
816 (void) memset(ans, 0, sizeof(ans));
817 for (;;) {
818 (void) printf("\t[O]verwrite?");
819 if ((gConn.hasREST == kCommandAvailable) && (remotesize != kSizeUnknown) && (remotesize > localsize))
820 printf(" [R]esume?");
821 printf(" [A]ppend to? [S]kip? [N]ew Name?\n");
822 (void) printf("\t[O!]verwrite all?");
823 if ((gConn.hasREST == kCommandAvailable) && (remotesize != kSizeUnknown) && (remotesize > localsize))
824 printf(" [R!]esume all?");
825 printf(" [S!]kip all? [C]ancel > ");
826 fflush(stdin);
827 (void) fgets(ans, sizeof(ans) - 1, stdin);
828 switch ((int) ans[0]) {
829 case 'c':
830 case 'C':
831 ans[0] = 'C';
832 ans[1] = '\0';
833 zaction = kConfirmResumeProcSaidCancel;
834 break;
835 case 'o':
836 case 'O':
837 ans[0] = 'O';
838 zaction = kConfirmResumeProcSaidOverwrite;
839 break;
840 case 'r':
841 case 'R':
842 if ((gConn.hasREST != kCommandAvailable) || (remotesize == kSizeUnknown)) {
843 printf("\tResume is not available on this server.\n\n");
844 ans[0] = '\0';
845 break;
846 } else if (remotesize < localsize) {
847 printf("\tCannot resume when local file is already larger than the remote file.\n\n");
848 ans[0] = '\0';
849 break;
850 } else if (remotesize <= localsize) {
851 printf("\tLocal file is already the same size as the remote file.\n\n");
852 ans[0] = '\0';
853 break;
854 }
855 ans[0] = 'R';
856 *startPoint = localsize;
857 zaction = kConfirmResumeProcSaidResume;
858 if (OneTimeMessage("auto-resume") != 0) {
859 printf("\n\tNOTE: If you want NcFTP to guess automatically, \"set auto-resume yes\"\n\n");
860 }
861 break;
862 case 's':
863 case 'S':
864 ans[0] = 'S';
865 zaction = kConfirmResumeProcSaidSkip;
866 break;
867 case 'n':
868 case 'N':
869 ans[0] = 'N';
870 ans[1] = '\0';
871 zaction = kConfirmResumeProcSaidOverwrite;
872 break;
873 case 'a':
874 case 'A':
875 ans[0] = 'A';
876 ans[1] = '\0';
877 zaction = kConfirmResumeProcSaidAppend;
878 break;
879 case 'g':
880 case 'G':
881 ans[0] = 'G';
882 zaction = kConfirmResumeProcSaidBestGuess;
883 break;
884 default:
885 ans[0] = '\0';
886 }
887 if (ans[0] != '\0')
888 break;
889 }
890
891 if (ans[0] == 'N') {
892 (void) memset(newname, 0, sizeof(newname));
893 printf("\tSave as: ");
894 fflush(stdin);
895 (void) fgets(newname, sizeof(newname) - 1, stdin);
896 newname[strlen(newname) - 1] = '\0';
897 if (newname[0] == '\0') {
898 /* Nevermind. */
899 printf("Skipped %s.\n", remotepath);
900 zaction = kConfirmResumeProcSaidSkip;
901 } else {
902 *localpath = newname;
903 }
904 }
905
906 if (ans[1] == '!')
907 gResumeAnswerAll = zaction;
908 return (zaction);
909 } /* NcFTPConfirmResumeDownloadProc */
910
911
912
913
914 /* Download files from the remote system. */
915 void
916 GetCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
917 {
918 int opt;
919 int renameMode = 0;
920 int recurseFlag = kRecursiveNo;
921 int appendFlag = kAppendNo;
922 int resumeFlag = kResumeYes;
923 int tarflag = kTarYes;
924 const char *dstdir = NULL;
925 int rc;
926 int i;
927 int doGlob;
928 int xtype = gBm.xferType;
929 int nD = 0;
930 int deleteFlag = kDeleteNo;
931 char pattern[256];
932 vsigproc_t osigint;
933 ConfirmResumeDownloadProc confirmProc;
934
935 confirmProc = NcFTPConfirmResumeDownloadProc;
936 gResumeAnswerAll = kConfirmResumeProcNotUsed; /* Ask at least once each time */
937 ARGSUSED(gUnusedArg);
938 GetoptReset();
939 while ((opt = Getopt(argc, argv, "aAzfrRTD")) >= 0) switch (opt) {
940 case 'a':
941 xtype = kTypeAscii;
942 break;
943 case 'A':
944 /* Append to local files, instead of truncating
945 * them first.
946 */
947 appendFlag = kAppendYes;
948 break;
949 case 'f':
950 case 'Z':
951 /* Do not try to resume a download, even if it
952 * appeared that some of the file was transferred
953 * already.
954 */
955 resumeFlag = kResumeNo;
956 confirmProc = NoConfirmResumeDownloadProc;
957 break;
958 case 'z':
959 /* Special flag that lets you specify the
960 * destination file. Normally a "get" will
961 * write the file by the same name as the
962 * remote file's basename.
963 */
964 renameMode = 1;
965 break;
966 case 'r':
967 case 'R':
968 /* If the item is a directory, get the
969 * directory and all its contents.
970 */
971 recurseFlag = kRecursiveYes;
972 break;
973 case 'T':
974 /* If they said "-R", they may want to
975 * turn off TAR mode if they are trying
976 * to resume the whole directory.
977 * The disadvantage to TAR mode is that
978 * it always downloads the whole thing,
979 * which is why there is a flag to
980 * disable this.
981 */
982 tarflag = kTarNo;
983 break;
984 case 'D':
985 /* You can delete the remote file after
986 * you downloaded it successfully by using
987 * the -DD option. It requires two -D's
988 * to minimize the odds of accidentally
989 * using a single -D.
990 */
991 nD++;
992 break;
993 default:
994 PrintCmdUsage(cmdp);
995 return;
996 }
997
998 if (nD >= 2)
999 deleteFlag = kDeleteYes;
1000
1001 if (renameMode != 0) {
1002 if (gOptInd > argc - 2) {
1003 PrintCmdUsage(cmdp);
1004 (void) fprintf(stderr, "\nFor get with rename, try \"get -z remote-path-name local-path-name\".\n");
1005 return;
1006 }
1007 osigint = NcSignal(SIGINT, XferCanceller);
1008 rc = FTPGetOneFile3(&gConn, argv[gOptInd], argv[gOptInd + 1], xtype, (-1), resumeFlag, appendFlag, deleteFlag, NoConfirmResumeDownloadProc, 0);
1009 if (rc < 0)
1010 FTPPerror(&gConn, rc, kErrCouldNotStartDataTransfer, "get", argv[gOptInd]);
1011 } else {
1012 osigint = NcSignal(SIGINT, XferCanceller);
1013 for (i=gOptInd; i<argc; i++) {
1014 doGlob = (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes;
1015 STRNCPY(pattern, argv[i]);
1016 StrRemoveTrailingSlashes(pattern);
1017 rc = FTPGetFiles3(&gConn, pattern, dstdir, recurseFlag, doGlob, xtype, resumeFlag, appendFlag, deleteFlag, tarflag, confirmProc, 0);
1018 if (rc < 0)
1019 FTPPerror(&gConn, rc, kErrCouldNotStartDataTransfer, "get", argv[i]);
1020 }
1021 }
1022 (void) NcSignal(SIGINT, osigint);
1023 (void) fflush(stdin);
1024
1025 if (deleteFlag == kDeleteYes) {
1026 /* Directory is now out of date */
1027 FlushLsCache();
1028 }
1029 } /* GetCmd */
1030
1031
1032
1033
1034 /* Display some brief help for specified commands, or a list of commands. */
1035 void
1036 HelpCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1037 {
1038 CommandPtr c;
1039 int showall = 0, helpall = 0;
1040 int i, j, k, n;
1041 int nRows, nCols;
1042 int nCmds2Print;
1043 int screenColumns;
1044 int len, widestName;
1045 char *cp, spec[16];
1046 const char *cmdnames[64];
1047
1048 ARGSUSED(gUnusedArg);
1049 assert(gNumCommands < (sizeof(cmdnames) / sizeof(char *)));
1050 if (argc == 2) {
1051 showall = (strcmp(argv[1], "showall") == 0);
1052 helpall = (strcmp(argv[1], "helpall") == 0);
1053 }
1054 if (argc == 1 || showall) {
1055 (void) printf("\
1056 Commands may be abbreviated. 'help showall' shows hidden and unsupported \n\
1057 commands. 'help <command>' gives a brief description of <command>.\n\n");
1058
1059 /* First, see how many commands we will be printing to the screen.
1060 * Unless 'showall' was given, we won't be printing the hidden
1061 * (i.e. not very useful to the end-user) commands.
1062 */
1063 c = gCommands;
1064 nCmds2Print = 0;
1065 for (n = 0; n < (int) gNumCommands; c++, n++)
1066 if ((!iscntrl((int) c->name[0])) && (!(c->flags & kCmdHidden) || showall))
1067 nCmds2Print++;
1068
1069 (void) memset((char *) cmdnames, 0, sizeof(cmdnames));
1070
1071 /* Now form the list we'll be printing, and noting what the maximum
1072 * length of a command name was, so we can use that when determining
1073 * how to print in columns.
1074 */
1075 c = gCommands;
1076 i = 0;
1077 widestName = 0;
1078 for (n = 0; n < (int) gNumCommands; c++, n++) {
1079 if ((!iscntrl((int) c->name[0])) && (!(c->flags & kCmdHidden) || showall)) {
1080 cmdnames[i++] = c->name;
1081 len = (int) strlen(c->name);
1082 if (len > widestName)
1083 widestName = len;
1084 }
1085 }
1086
1087 if ((cp = (char *) getenv("COLUMNS")) == NULL)
1088 screenColumns = 80;
1089 else
1090 screenColumns = atoi(cp);
1091
1092 /* Leave an extra bit of whitespace for the margins between columns. */
1093 widestName += 2;
1094
1095 nCols = (screenColumns + 0) / widestName;
1096 nRows = nCmds2Print / nCols;
1097 if ((nCmds2Print % nCols) > 0)
1098 nRows++;
1099
1100 for (i = 0; i < nRows; i++) {
1101 for (j = 0; j < nCols; j++) {
1102 k = nRows * j + i;
1103 if (k < nCmds2Print) {
1104 (void) sprintf(spec, "%%-%ds",
1105 (j < nCols - 1) ? widestName : widestName - 2
1106 );
1107 (void) printf(spec, cmdnames[k]);
1108 }
1109 }
1110 (void) printf("\n");
1111 }
1112 } else if (helpall) {
1113 /* Really intended for me, so I can debug the help strings. */
1114 for (c = gCommands, n = 0; n < (int) gNumCommands; c++, n++) {
1115 PrintCmdHelp(c);
1116 PrintCmdUsage(c);
1117 }
1118 } else {
1119 /* For each command name specified, print its help stuff. */
1120 for (i=1; i<argc; i++) {
1121 c = GetCommandByName(argv[i], 0);
1122 if (c == kAmbiguousCommand) {
1123 (void) printf("%s: ambiguous command name.\n", argv[i]);
1124 } else if (c == kNoCommand) {
1125 (void) printf("%s: no such command.\n", argv[i]);
1126 } else {
1127 if (i > 1)
1128 (void) printf("\n");
1129 PrintCmdHelp(c);
1130 PrintCmdUsage(c);
1131 }
1132 }
1133 }
1134 } /* HelpCmd */
1135
1136
1137
1138
1139 /* Displays the list of saved bookmarks, so that the user can then choose
1140 * one by name.
1141 */
1142 static int
1143 PrintHosts(void)
1144 {
1145 FILE *bookmarks;
1146 FILE *pager;
1147 int nbm = 0;
1148 Bookmark bm;
1149 char url[128];
1150 #ifdef SIGPIPE
1151 sigproc_t osigpipe;
1152 #endif
1153
1154 bookmarks = OpenBookmarkFile(NULL);
1155 if (bookmarks == NULL)
1156 return (0);
1157
1158 #ifdef SIGPIPE
1159 osigpipe = (sigproc_t) NcSignal(SIGPIPE, SIG_IGN);
1160 #endif
1161 pager = OpenPager();
1162
1163 while (GetNextBookmark(bookmarks, &bm) == 0) {
1164 nbm++;
1165 if (nbm == 1) {
1166 /* header */
1167 (void) fprintf(pager, "--BOOKMARK----------URL--------------------------------------------------------\n");
1168 }
1169 BookmarkToURL(&bm, url, sizeof(url));
1170 (void) fprintf(pager, " %-16s %s\n", bm.bookmarkName, url);
1171 }
1172
1173 ClosePager(pager);
1174 CloseBookmarkFile(bookmarks);
1175
1176 #ifdef SIGPIPE
1177 (void) NcSignal(SIGPIPE, osigpipe);
1178 #endif
1179 return (nbm);
1180 } /* PrintHosts */
1181
1182
1183
1184
1185
1186 static int
1187 RunBookmarkEditor(char *selectedBmName, size_t dsize)
1188 {
1189 #if defined(WIN32) || defined(_WINDOWS)
1190 char ncftpbookmarks[260];
1191 const char *prog;
1192 int winExecResult;
1193 HANDLE hMailSlot;
1194 char msg[kNcFTPBookmarksMailslotMsgSize];
1195 DWORD dwRead;
1196 BOOL rc;
1197
1198 if (selectedBmName != NULL)
1199 memset(selectedBmName, 0, dsize);
1200 if (gOurInstallationPath[0] == '\0') {
1201 (void) fprintf(stderr, "Cannot find path to %s. Please re-run Setup.\n", "ncftpbookmarks.exe");
1202 return (-1);
1203 }
1204 prog = ncftpbookmarks;
1205 OurInstallationPath(ncftpbookmarks, sizeof(ncftpbookmarks), "ncftpbookmarks.exe");
1206
1207
1208 hMailSlot = CreateMailslot(kNcFTPBookmarksMailslot, kNcFTPBookmarksMailslotMsgSize, MAILSLOT_WAIT_FOREVER, NULL);
1209
1210 if (hMailSlot == INVALID_HANDLE_VALUE) {
1211 SysPerror("CreateMailslot");
1212 (void) fprintf(stderr, "Could not create communication channel with %s.\n", "ncftpbookmarks.exe");
1213 (void) fprintf(stderr, "%s", "This means if you select a bookmark to connect to that NcFTP\n");
1214 (void) fprintf(stderr, "%s", "will not get the message from %s.\n", "ncftpbookmarks.exe");
1215 }
1216
1217 winExecResult = WinExec(prog, SW_SHOWNORMAL);
1218 if (winExecResult <= 31) switch (winExecResult) {
1219 case ERROR_BAD_FORMAT:
1220 fprintf(stderr, "Could not run %s: %s\n", prog, "The .EXE file is invalid");
1221 return (-1);
1222 case ERROR_FILE_NOT_FOUND:
1223 fprintf(stderr, "Could not run %s: %s\n", prog, "The specified file was not found.");
1224 return (-1);
1225 case ERROR_PATH_NOT_FOUND:
1226 fprintf(stderr, "Could not run %s: %s\n", prog, "The specified path was not found.");
1227 return (-1);
1228 default:
1229 fprintf(stderr, "Could not run %s: Unknown error #%d.\n", prog, winExecResult);
1230 return (-1);
1231 }
1232
1233 if (hMailSlot != INVALID_HANDLE_VALUE) {
1234 fprintf(stdout, "Waiting for %s to exit...\n", "ncftpbookmarks.exe");
1235 ZeroMemory(msg, sizeof(msg));
1236 dwRead = 0;
1237 rc = ReadFile(
1238 hMailSlot,
1239 msg,
1240 sizeof(msg),
1241 &dwRead,
1242 NULL
1243 );
1244
1245 if (!rc) {
1246 SysPerror("ReadFile");
1247 } else {
1248 msg[sizeof(msg) - 1] = '\0';
1249 Strncpy(selectedBmName, msg, dsize);
1250 Trace(0, "Selected bookmark from editor: [%s]\n", selectedBmName);
1251 }
1252 CloseHandle(hMailSlot);
1253 }
1254 return (0);
1255
1256 #else
1257 #ifdef BINDIR
1258 char ncftpbookmarks[256];
1259 char *av[8];
1260 int pid;
1261 int status;
1262 char bmSelectionFile[256];
1263 char pidStr[32];
1264 FILE *fp;
1265
1266 if (selectedBmName != NULL)
1267 memset(selectedBmName, 0, dsize);
1268 STRNCPY(ncftpbookmarks, BINDIR);
1269 STRNCAT(ncftpbookmarks, "/");
1270 STRNCAT(ncftpbookmarks, "ncftpbookmarks");
1271
1272 STRNCPY(bmSelectionFile, "view");
1273 if ((selectedBmName != NULL) && (gOurDirectoryPath[0] != '\0')) {
1274 sprintf(pidStr, ".%u", (unsigned int) getpid());
1275 OurDirectoryPath(bmSelectionFile, sizeof(bmSelectionFile), kOpenSelectedBookmarkFileName);
1276 STRNCAT(bmSelectionFile, pidStr);
1277 }
1278
1279 if (access(ncftpbookmarks, X_OK) == 0) {
1280 pid = (int) fork();
1281 if (pid < 0) {
1282 return (-1);
1283 } else if (pid == 0) {
1284 /* child */
1285
1286 av[0] = (char *) "ncftpbookmarks";
1287 av[1] = bmSelectionFile;
1288 av[2] = NULL;
1289 execv(ncftpbookmarks, av);
1290 exit(1);
1291 } else {
1292 /* parent NcFTP */
1293 for (;;) {
1294 #ifdef HAVE_WAITPID
1295 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1296 break;
1297 #else
1298 if ((wait(&status) < 0) && (errno != EINTR))
1299 break;
1300 #endif
1301 if (WIFEXITED(status) || WIFSIGNALED(status))
1302 break; /* done */
1303 }
1304
1305 if (strcmp(bmSelectionFile, "view") != 0) {
1306 fp = fopen(bmSelectionFile, FOPEN_READ_TEXT);
1307 if (fp != NULL) {
1308 (void) FGets(selectedBmName, dsize, fp);
1309 (void) fclose(fp);
1310 (void) unlink(bmSelectionFile);
1311 Trace(0, "Selected bookmark from editor: [%s]\n", selectedBmName);
1312 }
1313 }
1314 return (0);
1315 }
1316 }
1317 return (-1);
1318 #else /* BINDIR */
1319 /* Not installed. */
1320 return (-1);
1321 #endif /* BINDIR */
1322 #endif /* Windows */
1323 } /* RunBookmarkEditor */
1324
1325
1326
1327 /* This just shows the list of saved bookmarks. */
1328 void
1329 HostsCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1330 {
1331 const char *av[3];
1332 char bm[128];
1333
1334 ARGSUSED(gUnusedArg);
1335 /* Skip visual mode if "-l". */
1336 if ((argc == 1) && (RunBookmarkEditor(bm, sizeof(bm)) == 0)) {
1337 if (bm[0] != '\0') {
1338 av[0] = "open";
1339 av[1] = bm;
1340 av[2] = NULL;
1341 OpenCmd(2, av, (CommandPtr) 0, (ArgvInfoPtr) 0);
1342 }
1343 return;
1344 }
1345 if (PrintHosts() <= 0) {
1346 (void) printf("You haven't bookmarked any FTP sites.\n");
1347 (void) printf("Before closing a site, you can use the \"bookmark\" command to save the current\nhost and directory for next time.\n");
1348 } else {
1349 (void) printf("\nTo use a bookmark, use the \"open\" command with the name of the bookmark.\n");
1350 }
1351 } /* HostsCmd */
1352
1353
1354
1355
1356 /* Show active background ncftp (ncftpbatch) jobs. */
1357 void
1358 JobsCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1359 {
1360 ARGSUSED(gUnusedArg);
1361 Jobs();
1362 } /* JobsCmd */
1363
1364
1365
1366
1367 /* Do a "ls" on the remote system and display the directory contents to our
1368 * user. This command handles "dir" (ls -l), "ls", and "nlist" (ls -1).
1369 */
1370 void
1371 ListCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1372 {
1373 volatile int i;
1374 int j;
1375 char options[32];
1376 char option[2];
1377 volatile int listmode;
1378 FILE *volatile stream;
1379 volatile int paging;
1380 #if defined(WIN32) || defined(_WINDOWS)
1381 #else
1382 int sj;
1383 vsigproc_t osigpipe, osigint;
1384 #endif
1385
1386 ARGSUSED(gUnusedArg);
1387 stream = stdout;
1388 paging = 0;
1389
1390 if (argv[0][0] == 'd') {
1391 /* dir */
1392 listmode = 'l';
1393 } else if (argv[0][0] == 'n') {
1394 /* nlist */
1395 listmode = '1';
1396 } else if (argv[0][0] == 'p') {
1397 /* pager */
1398 paging = 1;
1399
1400 if (argv[0][1] == 'd') {
1401 /* dir */
1402 listmode = 'l';
1403 } else if (argv[0][1] == 'n') {
1404 /* nlist */
1405 listmode = '1';
1406 } else {
1407 /* ls */
1408 listmode = 'C';
1409 }
1410 } else {
1411 /* ls */
1412 listmode = 'C';
1413 }
1414 options[0] = '\0';
1415 option[1] = '\0';
1416
1417 for (i=1; i<argc; i++) {
1418 if (argv[i][0] != '-')
1419 break;
1420 if (argv[i][1] == '-') {
1421 if (argv[i][2] == '\0') {
1422 /* end of options. */
1423 ++i;
1424 break;
1425 } else {
1426 /* GNU-esque long --option? */
1427 PrintCmdUsage(cmdp);
1428 }
1429 } else {
1430 for (j=1; ; j++) {
1431 option[0] = argv[i][j];
1432 if (argv[i][j] == '\0')
1433 break;
1434 switch (argv[i][j]) {
1435 case 'l':
1436 listmode = 'l';
1437 break;
1438 case '1':
1439 listmode = '1';
1440 break;
1441 case 'C':
1442 listmode = 'C';
1443 break;
1444 default:
1445 (void) STRNCAT(options, option);
1446 break;
1447 }
1448 }
1449 }
1450 }
1451
1452
1453 if (paging != 0) {
1454 stream = OpenPager();
1455 if (stream == NULL) {
1456 return;
1457 }
1458
1459 #if defined(WIN32) || defined(_WINDOWS)
1460 #elif defined(HAVE_SIGSETJMP)
1461 osigpipe = osigint = (sigproc_t) 0;
1462 sj = sigsetjmp(gCancelJmp, 1);
1463 #else /* HAVE_SIGSETJMP */
1464 osigpipe = osigint = (sigproc_t) 0;
1465 sj = setjmp(gCancelJmp);
1466 #endif /* HAVE_SIGSETJMP */
1467
1468 #if defined(WIN32) || defined(_WINDOWS)
1469 #else
1470 if (sj != 0) {
1471 /* Caught a signal. */
1472 (void) NcSignal(SIGPIPE, (FTPSigProc) SIG_IGN);
1473 ClosePager(stream);
1474 (void) NcSignal(SIGPIPE, osigpipe);
1475 (void) NcSignal(SIGINT, osigint);
1476 Trace(0, "Canceled because of signal %d.\n", gGotSig);
1477 (void) fprintf(stderr, "Canceled.\n");
1478 gMayCancelJmp = 0;
1479 return;
1480 } else {
1481 osigpipe = NcSignal(SIGPIPE, Cancel);
1482 osigint = NcSignal(SIGINT, Cancel);
1483 gMayCancelJmp = 1;
1484 }
1485 #endif
1486 }
1487
1488 if (argv[i] == NULL) {
1489 /* No directory specified, use cwd. */
1490 Ls(NULL, listmode, options, stream);
1491 } else {
1492 /* List each item. */
1493 for ( ; i<argc; i++) {
1494 Ls(argv[i], listmode, options, stream);
1495 }
1496 }
1497
1498 if (paging != 0) {
1499 ClosePager(stream);
1500 #if defined(WIN32) || defined(_WINDOWS)
1501 #else
1502 (void) NcSignal(SIGPIPE, osigpipe);
1503 (void) NcSignal(SIGINT, osigint);
1504 #endif
1505 }
1506 gMayCancelJmp = 0;
1507 } /* ListCmd */
1508
1509
1510
1511
1512 /* Does a change of working directory on the local host. */
1513 void
1514 LocalChdirCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1515 {
1516 int result;
1517 const char *dir;
1518 char buf[512];
1519
1520 ARGSUSED(gUnusedArg);
1521 dir = ((argc < 2) ? gHome : argv[1]);
1522 if ((dir[0] == '-') && (dir[1] == '\0')) {
1523 if (gPrevLocalCWD[0] == '\0') {
1524 (void) fprintf(stderr, "No previous local working directory to switch to.\n");
1525 return;
1526 } else {
1527 dir = gPrevLocalCWD;
1528 }
1529 } else if (dir[0] == '~') {
1530 if (dir[1] == '\0') {
1531 dir = gHome;
1532 } else if (dir[1] == '/') {
1533 (void) STRNCPY(buf, gHome);
1534 dir = STRNCAT(buf, dir + 1);
1535 }
1536 }
1537 result = chdir(dir);
1538 if (result < 0) {
1539 perror(dir);
1540 } else {
1541 (void) STRNCPY(gPrevLocalCWD, gLocalCWD);
1542 (void) FTPGetLocalCWD(gLocalCWD, sizeof(gLocalCWD));
1543
1544 }
1545 } /* LocalChdirCmd */
1546
1547
1548
1549
1550 /* Does directory listing on the local host. */
1551 void
1552 LocalListCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1553 {
1554 #if defined(WIN32) || defined(_WINDOWS)
1555 volatile int i;
1556 int j;
1557 char options[32];
1558 char option[2];
1559 volatile int listmode;
1560 FILE *volatile stream;
1561 volatile int paging;
1562
1563
1564 ARGSUSED(gUnusedArg);
1565 stream = stdout;
1566 paging = 0;
1567
1568 if (argv[0][1] == 'd') {
1569 /* dir */
1570 listmode = 'l';
1571 } else if (argv[0][1] == 'n') {
1572 /* nlist */
1573 listmode = '1';
1574 } else {
1575 /* ls */
1576 listmode = 'C';
1577 }
1578 options[0] = '\0';
1579 option[1] = '\0';
1580
1581 for (i=1; i<argc; i++) {
1582 if (argv[i][0] != '-')
1583 break;
1584 if (argv[i][1] == '-') {
1585 if (argv[i][2] == '\0') {
1586 /* end of options. */
1587 ++i;
1588 break;
1589 } else {
1590 /* GNU-esque long --option? */
1591 PrintCmdUsage(cmdp);
1592 }
1593 } else {
1594 for (j=1; ; j++) {
1595 option[0] = argv[i][j];
1596 if (argv[i][j] == '\0')
1597 break;
1598 switch (argv[i][j]) {
1599 case 'l':
1600 listmode = 'l';
1601 break;
1602 case '1':
1603 listmode = '1';
1604 break;
1605 case 'C':
1606 listmode = 'C';
1607 break;
1608 default:
1609 (void) STRNCAT(options, option);
1610 break;
1611 }
1612 }
1613 }
1614 }
1615
1616
1617 if (argv[i] == NULL) {
1618 /* No directory specified, use cwd. */
1619 LLs(NULL, listmode, options, stream);
1620 } else {
1621 /* List each item. */
1622 for ( ; i<argc; i++) {
1623 LLs(argv[i], listmode, options, stream);
1624 }
1625 }
1626
1627 #else
1628 FILE *volatile outfp;
1629 FILE *volatile infp;
1630 int i;
1631 int sj;
1632 int dashopts;
1633 char incmd[256];
1634 char line[256];
1635 vsigproc_t osigpipe, osigint;
1636
1637 ARGSUSED(gUnusedArg);
1638 (void) fflush(stdin);
1639 outfp = OpenPager();
1640
1641 (void) STRNCPY(incmd, "/bin/ls");
1642 for (i=1, dashopts=0; i<argc; i++) {
1643 (void) STRNCAT(incmd, " ");
1644 if (argv[i][0] == '-')
1645 dashopts++;
1646 (void) STRNCAT(incmd, argv[i]);
1647 }
1648
1649 if (dashopts == 0) {
1650 (void) STRNCPY(incmd, "/bin/ls -CF");
1651 for (i=1; i<argc; i++) {
1652 (void) STRNCAT(incmd, " ");
1653 (void) STRNCAT(incmd, argv[i]);
1654 }
1655 }
1656
1657 infp = popen(incmd, "r");
1658 if (infp == NULL) {
1659 ClosePager(outfp);
1660 return;
1661 }
1662
1663
1664 #ifdef HAVE_SIGSETJMP
1665 sj = sigsetjmp(gCancelJmp, 1);
1666 #else /* HAVE_SIGSETJMP */
1667 sj = setjmp(gCancelJmp);
1668 #endif /* HAVE_SIGSETJMP */
1669
1670 if (sj != 0) {
1671 /* Caught a signal. */
1672 (void) NcSignal(SIGPIPE, (FTPSigProc) SIG_IGN);
1673 ClosePager(outfp);
1674 if (infp != NULL)
1675 (void) pclose(infp);
1676 (void) NcSignal(SIGPIPE, osigpipe);
1677 (void) NcSignal(SIGINT, osigint);
1678 (void) fprintf(stderr, "Canceled.\n");
1679 Trace(0, "Canceled because of signal %d.\n", gGotSig);
1680 gMayCancelJmp = 0;
1681 return;
1682 } else {
1683 osigpipe = NcSignal(SIGPIPE, Cancel);
1684 osigint = NcSignal(SIGINT, Cancel);
1685 gMayCancelJmp = 1;
1686 }
1687
1688 while (fgets(line, sizeof(line) - 1, infp) != NULL)
1689 (void) fputs(line, outfp);
1690 (void) fflush(outfp);
1691
1692 (void) pclose(infp);
1693 infp = NULL;
1694 ClosePager(outfp);
1695 outfp = NULL;
1696
1697 (void) NcSignal(SIGPIPE, osigpipe);
1698 (void) NcSignal(SIGINT, osigint);
1699 gMayCancelJmp = 0;
1700 #endif
1701 } /* LocalListCmd */
1702
1703
1704
1705
1706 static void
1707 Sys(const int argc, const char **const argv, const ArgvInfoPtr aip, const char *syscmd, int noDQuote)
1708 {
1709 char cmd[256];
1710 int i;
1711
1712 (void) STRNCPY(cmd, syscmd);
1713 for (i = 1; i < argc; i++) {
1714 if (aip->noglobargv[i] != 0) {
1715 (void) STRNCAT(cmd, " '");
1716 (void) STRNCAT(cmd, argv[i]);
1717 (void) STRNCAT(cmd, "'");
1718 } else if (noDQuote != 0) {
1719 (void) STRNCAT(cmd, " ");
1720 (void) STRNCAT(cmd, argv[i]);
1721 } else {
1722 (void) STRNCAT(cmd, " \"");
1723 (void) STRNCAT(cmd, argv[i]);
1724 (void) STRNCAT(cmd, "\" ");
1725 }
1726 }
1727 #if defined(WIN32) || defined(_WINDOWS)
1728 fprintf(stderr, "Cannot run command: %s\n", cmd);
1729 #else
1730 Trace(0, "Sys: %s\n", cmd);
1731 (void) system(cmd);
1732 #endif
1733 } /* Sys */
1734
1735
1736
1737
1738 #if defined(WIN32) || defined(_WINDOWS)
1739 #else
1740 void
1741 LocalChmodCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1742 {
1743 ARGSUSED(gUnusedArg);
1744 Sys(argc, argv, aip, "/bin/chmod", 1);
1745 } /* LocalChmodCmd */
1746 #endif
1747
1748
1749
1750 void
1751 LocalMkdirCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1752 {
1753 #if defined(WIN32) || defined(_WINDOWS)
1754 const char *arg;
1755 int i;
1756
1757 for (i = 1; i < argc; i++) {
1758 arg = argv[i];
1759 if (MkDirs(arg, 00755) < 0) {
1760 perror(arg);
1761 }
1762 }
1763 #else
1764 ARGSUSED(gUnusedArg);
1765 Sys(argc, argv, aip, "/bin/mkdir", 0);
1766 #endif
1767 } /* LocalMkdirCmd */
1768
1769
1770
1771
1772 #if defined(WIN32) || defined(_WINDOWS)
1773 #else
1774 void
1775 LocalPageCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1776 {
1777 ARGSUSED(gUnusedArg);
1778 Sys(argc, argv, aip, gPager, 0);
1779 } /* LocalPageCmd */
1780 #endif
1781
1782
1783
1784
1785 void
1786 LocalRenameCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1787 {
1788 #if defined(WIN32) || defined(_WINDOWS)
1789 if (rename(argv[1], argv[2]) < 0) {
1790 perror("rename");
1791 }
1792 #else
1793 ARGSUSED(gUnusedArg);
1794 Sys(argc, argv, aip, "/bin/mv", 1);
1795 #endif
1796 } /* LocalRenameCmd */
1797
1798
1799
1800
1801 void
1802 LocalRmCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1803 {
1804 #if defined(WIN32) || defined(_WINDOWS)
1805 int i;
1806 int result;
1807 LineList ll;
1808 LinePtr lp;
1809
1810 ARGSUSED(gUnusedArg);
1811 for (i=1; i<argc; i++) {
1812 InitLineList(&ll);
1813 result = FTPLocalGlob(&gConn, &ll, argv[i], (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
1814 if (result < 0) {
1815 FTPPerror(&gConn, result, kErrGlobFailed, "local glob", argv[i]);
1816 } else {
1817 for (lp = ll.first; lp != NULL; lp = lp->next) {
1818 if (lp->line != NULL) {
1819 if (remove(lp->line) < 0)
1820 perror(lp->line);
1821 }
1822 }
1823 }
1824 DisposeLineListContents(&ll);
1825 }
1826 #else
1827 ARGSUSED(gUnusedArg);
1828 Sys(argc, argv, aip, "/bin/rm", 1);
1829 #endif
1830 } /* LocalRmCmd */
1831
1832
1833
1834
1835 void
1836 LocalRmdirCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1837 {
1838 #if defined(WIN32) || defined(_WINDOWS)
1839 int i;
1840 int result;
1841 LineList ll;
1842 LinePtr lp;
1843
1844 ARGSUSED(gUnusedArg);
1845 for (i=1; i<argc; i++) {
1846 InitLineList(&ll);
1847 result = FTPLocalGlob(&gConn, &ll, argv[i], (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
1848 if (result < 0) {
1849 FTPPerror(&gConn, result, kErrGlobFailed, "local glob", argv[i]);
1850 } else {
1851 for (lp = ll.first; lp != NULL; lp = lp->next) {
1852 if (lp->line != NULL) {
1853 if (rmdir(lp->line) < 0)
1854 perror(lp->line);
1855 }
1856 }
1857 }
1858 DisposeLineListContents(&ll);
1859 }
1860 #else
1861 ARGSUSED(gUnusedArg);
1862 Sys(argc, argv, aip, "/bin/rmdir", 1);
1863 #endif
1864 } /* LocalRmdirCmd */
1865
1866
1867
1868
1869 /* Displays the current local working directory. */
1870 void
1871 LocalPwdCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1872 {
1873 ARGSUSED(gUnusedArg);
1874 if (FTPGetLocalCWD(gLocalCWD, sizeof(gLocalCWD)) != NULL) {
1875 Trace(-1, "%s\n", gLocalCWD);
1876 }
1877 } /* LocalPwdCmd */
1878
1879
1880
1881
1882 /* This is a simple interface to name service. I prefer using this instead
1883 * of nslookup.
1884 */
1885 void
1886 LookupCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1887 {
1888 int i, j;
1889 struct hostent *hp;
1890 const char *host;
1891 char **cpp;
1892 struct in_addr ip_address;
1893 int shortMode, opt;
1894 char ipStr[16];
1895
1896 ARGSUSED(gUnusedArg);
1897 shortMode = 1;
1898
1899 GetoptReset();
1900 while ((opt = Getopt(argc, argv, "v")) >= 0) {
1901 if (opt == 'v')
1902 shortMode = 0;
1903 else {
1904 PrintCmdUsage(cmdp);
1905 return;
1906 }
1907 }
1908
1909 for (i=gOptInd; i<argc; i++) {
1910 hp = GetHostEntry((host = argv[i]), &ip_address);
1911 if ((i > gOptInd) && (shortMode == 0))
1912 Trace(-1, "\n");
1913 if (hp == NULL) {
1914 Trace(-1, "Unable to get information about site %s.\n", host);
1915 } else if (shortMode) {
1916 MyInetAddr(ipStr, sizeof(ipStr), hp->h_addr_list, 0);
1917 Trace(-1, "%-40s %s\n", hp->h_name, ipStr);
1918 } else {
1919 Trace(-1, "%s:\n", host);
1920 Trace(-1, " Name: %s\n", hp->h_name);
1921 for (cpp = hp->h_aliases; *cpp != NULL; cpp++)
1922 Trace(-1, " Alias: %s\n", *cpp);
1923 for (j = 0, cpp = hp->h_addr_list; *cpp != NULL; cpp++, ++j) {
1924 MyInetAddr(ipStr, sizeof(ipStr), hp->h_addr_list, j);
1925 Trace(-1, " Address: %s\n", ipStr);
1926 }
1927 }
1928 }
1929 } /* LookupCmd */
1930
1931
1932
1933 /* Directory listing in a machine-readable format;
1934 * Mostly for debugging, since NcFTP uses MLSD automatically when it needs to.
1935 */
1936 void
1937 MlsCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
1938 {
1939 int i;
1940 int opt;
1941 LinePtr linePtr, nextLinePtr;
1942 int result;
1943 LineList dirContents;
1944 int mlsd = 1, x;
1945 const char *item;
1946
1947 ARGSUSED(gUnusedArg);
1948 GetoptReset();
1949 while ((opt = Getopt(argc, argv, "dt")) >= 0) {
1950 if ((opt == 'd') || (opt == 't')) {
1951 /* Use MLST instead of MLSD,
1952 * which is similar to using "ls -d" instead of "ls".
1953 */
1954 mlsd = 0;
1955 } else {
1956 PrintCmdUsage(cmdp);
1957 return;
1958 }
1959 }
1960
1961 i = gOptInd;
1962 if (i == argc) {
1963 /* No args, do current directory. */
1964 x = 1;
1965 item = "";
1966 if ((result = FTPListToMemory2(&gConn, item, &dirContents, (mlsd == 0) ? "-d" : "", 1, &x)) < 0) {
1967 if (mlsd != 0) {
1968 FTPPerror(&gConn, result, 0, "Could not MLSD", item);
1969 } else {
1970 FTPPerror(&gConn, result, 0, "Could not MLST", item);
1971 }
1972 } else {
1973 for (linePtr = dirContents.first;
1974 linePtr != NULL;
1975 linePtr = nextLinePtr)
1976 {
1977 nextLinePtr = linePtr->next;
1978 (void) fprintf(stdout, "%s\n", linePtr->line);
1979 Trace(0, "%s\n", linePtr->line);
1980 }
1981 }
1982 }
1983
1984 for ( ; i<argc; i++) {
1985 x = 1;
1986 item = argv[i];
1987 if ((result = FTPListToMemory2(&gConn, item, &dirContents, (mlsd == 0) ? "-d" : "", 1, &x)) < 0) {
1988 if (mlsd != 0) {
1989 FTPPerror(&gConn, result, 0, "Could not MLSD", item);
1990 } else {
1991 FTPPerror(&gConn, result, 0, "Could not MLST", item);
1992 }
1993 } else {
1994 for (linePtr = dirContents.first;
1995 linePtr != NULL;
1996 linePtr = nextLinePtr)
1997 {
1998 nextLinePtr = linePtr->next;
1999 (void) fprintf(stdout, "%s\n", linePtr->line);
2000 Trace(0, "%s\n", linePtr->line);
2001 }
2002 }
2003 }
2004 } /* MlsCmd */
2005
2006
2007
2008
2009 /* Create directories on the remote system. */
2010 void
2011 MkdirCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2012 {
2013 int i;
2014 int opt;
2015 int result;
2016 int recurseFlag = kRecursiveNo;
2017
2018 ARGSUSED(gUnusedArg);
2019 GetoptReset();
2020 while ((opt = Getopt(argc, argv, "p")) >= 0) {
2021 if (opt == 'p') {
2022 /* Try creating intermediate directories if they
2023 * don't exist.
2024 *
2025 * For example if only /pub/stuff existed, and you
2026 * do a "mkdir -p /pub/stuff/a/b/c", the "a" and "b"
2027 * directories would also be created.
2028 */
2029 recurseFlag = kRecursiveYes;
2030 } else {
2031 PrintCmdUsage(cmdp);
2032 return;
2033 }
2034 }
2035
2036 for (i=gOptInd; i<argc; i++) {
2037 result = FTPMkdir(&gConn, argv[i], recurseFlag);
2038 if (result < 0)
2039 FTPPerror(&gConn, result, kErrMKDFailed, "Could not mkdir", argv[i]);
2040 }
2041
2042 /* Really should just flush only the modified directories... */
2043 FlushLsCache();
2044 } /* MkdirCmd */
2045
2046
2047
2048 /*VARARGS*/
2049 static void
2050 OpenMsg(const char *const fmt, ...)
2051 {
2052 va_list ap;
2053 char buf[512];
2054 size_t len, padlim;
2055
2056 padlim = (size_t) gScreenColumns;
2057 if ((size_t) gScreenColumns > (sizeof(buf) - 1))
2058 padlim = sizeof(buf) - 1;
2059
2060 va_start(ap, fmt);
2061 #ifdef HAVE_VSNPRINTF
2062 len = (size_t) vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
2063 va_end(ap);
2064 buf[sizeof(buf) - 1] = '\0';
2065 #else
2066 (void) vsprintf(buf, fmt, ap);
2067 va_end(ap);
2068 len = strlen(buf);
2069 #endif
2070 buf[len] = '\0';
2071 Trace(9, "%s\n", buf);
2072 for (; len < padlim; len++)
2073 buf[len] = ' ';
2074 buf[len] = '\0';
2075
2076 #if 0
2077 (void) fprintf(stdout, "\r%s", buf);
2078 #else
2079 (void) fprintf(stdout, "%s\r", buf);
2080 #endif
2081 (void) fflush(stdout);
2082 } /* OpenMsg */
2083
2084
2085
2086
2087 static void
2088 NcFTPOpenPrintResponseProc(const FTPCIPtr cipUNUSED, ResponsePtr rp)
2089 {
2090 gUnusedArg = (cipUNUSED != NULL);
2091 if ((rp->printMode & kResponseNoPrint) != 0)
2092 return;
2093 #if 0
2094 if (rp->code == 331) /* Skip: "331 User name okay, need password." */
2095 return;
2096 #else
2097 /* This is only used to print errors. */
2098 if (rp->code < 400)
2099 return;
2100 #endif
2101 PrintResp(&rp->msg);
2102 } /* NcFTPOpenPrintResponseProc */
2103
2104
2105
2106 static void
2107 NcFTPOnConnectMessageProc(const FTPCIPtr cipUNUSED, ResponsePtr rp)
2108 {
2109 gUnusedArg = (cipUNUSED != NULL);
2110 (void) printf("\n");
2111 PrintResp(&rp->msg);
2112 OpenMsg("Logging in...");
2113 } /* NcFTPOnConnectMessageProc */
2114
2115
2116
2117 static void
2118 NcFTPOnLoginMessageProc(const FTPCIPtr cipUNUSED, ResponsePtr rp)
2119 {
2120 gUnusedArg = (cipUNUSED != NULL);
2121 (void) printf("\n");
2122 PrintResp(&rp->msg);
2123 OpenMsg("Logging in...");
2124 } /* NcFTPOnLoginMessageProc */
2125
2126
2127
2128
2129 static void
2130 NcFTPRedialStatusProc(const FTPCIPtr cipUNUSED, int mode, int val)
2131 {
2132 gUnusedArg = (cipUNUSED != NULL);
2133 if (mode == kRedialStatusDialing) {
2134 if (val > 0) {
2135 OpenMsg("Redialing (try %d)...", val);
2136 sleep(1); /* Give time for message to stay */
2137 }
2138 } else if (mode == kRedialStatusSleeping) {
2139 OpenMsg("Sleeping %d seconds...", val);
2140 }
2141 } /* NcFTPRedialStatusProc */
2142
2143
2144
2145
2146 static void
2147 NcFTPGetPassphraseProc(const FTPCIPtr cip, LineListPtr pwPrompt, char *pass, size_t dsize)
2148 {
2149 LinePtr lp;
2150
2151 (void) printf("\nPassword requested by %s for user \"%s\".\n\n",
2152 cip->host,
2153 cip->user
2154 );
2155
2156 for (lp = pwPrompt->first; lp != NULL; lp = lp->next) {
2157 (void) printf(" %s\n", lp->line);
2158 }
2159 (void) printf("\n");
2160 (void) gl_getpass("Password: ", pass, (int) dsize);
2161 } /* NcFTPGetPassphraseProc */
2162
2163
2164
2165
2166 /* Attempts to establish a new FTP connection to a remote host. */
2167 int
2168 DoOpen(void)
2169 {
2170 int result;
2171 char ipstr[128];
2172 char ohost[128];
2173 #ifdef SIGALRM
2174 sigproc_t osigalrm;
2175 #endif
2176 char prompt[256];
2177
2178 if (gConn.firewallType == kFirewallNotInUse) {
2179 (void) STRNCPY(ohost, gConn.host);
2180 OpenMsg("Resolving %s...", ohost);
2181 if ((gLoadedBm != 0) && (gBm.lastIP[0] != '\0')) {
2182 result = GetHostByName(ipstr, sizeof(ipstr), ohost, 3);
2183 if (result < 0) {
2184 (void) STRNCPY(ipstr, gBm.lastIP);
2185 result = 0;
2186 } else {
2187 result = GetHostByName(ipstr, sizeof(ipstr), ohost, 7);
2188 }
2189 } else {
2190 result = GetHostByName(ipstr, sizeof(ipstr), ohost, 10);
2191 }
2192 if (result < 0) {
2193 (void) printf("\n");
2194 (void) printf("Unknown host \"%s\".\n", ohost);
2195 return (-1);
2196 }
2197 (void) STRNCPY(gConn.host, ipstr);
2198 OpenMsg("Connecting to %s...", ipstr);
2199 } else {
2200 OpenMsg("Connecting to %s via %s...", gConn.host, gConn.firewallHost);
2201 Trace(0, "Fw: %s Type: %d User: %s Pass: %s Port: %u\n",
2202 gConn.firewallHost,
2203 gConn.firewallType,
2204 gConn.firewallUser,
2205 (gConn.firewallPass[0] == '\0') ? "(none)" : "********",
2206 gConn.firewallPort
2207 );
2208 Trace(0, "FwExceptions: %s\n", gFirewallExceptionList);
2209 if (strchr(gLib.ourHostName, '.') == NULL) {
2210 Trace(0, "NOTE: Your domain name could not be detected.\n");
2211 if (gConn.firewallType != kFirewallNotInUse) {
2212 Trace(0, " Make sure you manually add your domain name to firewall-exception-list.\n");
2213 }
2214 }
2215 }
2216
2217 if (gConn.firewallPass[0] == '\0') {
2218 switch (gConn.firewallType) {
2219 case kFirewallNotInUse:
2220 break;
2221 case kFirewallUserAtSite:
2222 break;
2223 case kFirewallLoginThenUserAtSite:
2224 case kFirewallSiteSite:
2225 case kFirewallOpenSite:
2226 case kFirewallUserAtUserPassAtPass:
2227 case kFirewallFwuAtSiteFwpUserPass:
2228 case kFirewallUserAtSiteFwuPassFwp:
2229 (void) printf("\n");
2230 (void) STRNCPY(prompt, "Password for firewall user \"");
2231 (void) STRNCAT(prompt, gConn.firewallUser);
2232 (void) STRNCAT(prompt, "\" at ");
2233 (void) STRNCAT(prompt, gConn.firewallHost);
2234 (void) STRNCAT(prompt, ": ");
2235 (void) gl_getpass(prompt, gConn.firewallPass, sizeof(gConn.firewallPass));
2236 break;
2237 }
2238 }
2239
2240 if ((gConn.user[0] != '\0') && (strcmp(gConn.user, "anonymous") != 0) && (strcmp(gConn.user, "ftp") != 0)) {
2241 gConn.passphraseProc = NcFTPGetPassphraseProc;
2242 }
2243
2244 /* Register our callbacks. */
2245 gConn.printResponseProc = NcFTPOpenPrintResponseProc;
2246 gConn.onConnectMsgProc = NcFTPOnConnectMessageProc;
2247 gConn.onLoginMsgProc = NcFTPOnLoginMessageProc;
2248 gConn.redialStatusProc = NcFTPRedialStatusProc;
2249
2250 #ifdef SIGALRM
2251 osigalrm = NcSignal(SIGALRM, (FTPSigProc) SIG_IGN);
2252 result = FTPOpenHost(&gConn);
2253 (void) NcSignal(SIGALRM, osigalrm);
2254 #else
2255 result = FTPOpenHost(&gConn);
2256 #endif
2257
2258 if (gConn.firewallType == kFirewallNotInUse)
2259 (void) STRNCPY(gConn.host, ohost); /* Put it back. */
2260 if (result >= 0) {
2261 (void) time(&gBm.lastCall);
2262 LogOpen(gConn.host);
2263 OpenMsg("Logged in to %s.", gConn.host);
2264 (void) printf("\n");
2265
2266 /* Remove callback. */
2267 gConn.printResponseProc = 0;
2268
2269 /* Need to note what our "root" was before we change it. */
2270 if (gConn.startingWorkingDirectory == NULL) {
2271 (void) STRNCPY(gRemoteCWD, "/"); /* Guess! */
2272 } else {
2273 (void) STRNCPY(gRemoteCWD, gConn.startingWorkingDirectory);
2274 }
2275 (void) STRNCPY(gStartDir, gRemoteCWD);
2276
2277 /* If the bookmark specified a remote directory, change to it now. */
2278 if ((gLoadedBm != 0) && (gBm.dir[0] != '\0')) {
2279 result = Chdirs(&gConn, gBm.dir);
2280 if (result < 0) {
2281 FTPPerror(&gConn, result, kErrCWDFailed, "Could not chdir to previous directory", gBm.dir);
2282 }
2283 Trace(-1, "Current remote directory is %s.\n", gRemoteCWD);
2284 }
2285
2286 /* If the bookmark specified a local directory, change to it now. */
2287 if ((gLoadedBm != 0) && (gBm.ldir[0] != '\0')) {
2288 (void) chdir(gBm.ldir);
2289 (void) STRNCPY(gPrevLocalCWD, gLocalCWD);
2290 if (FTPGetLocalCWD(gLocalCWD, sizeof(gLocalCWD)) != NULL) {
2291 Trace(-1, "Current local directory is %s.\n", gLocalCWD);
2292 }
2293 }
2294
2295 /* Identify the FTP client type to the server. Most don't understand this yet. */
2296 if (gConn.hasCLNT != kCommandNotAvailable)
2297 (void) FTPCmd(&gConn, "CLNT NcFTP %.5s %s", gVersion + 11, gOS);
2298 return (0);
2299 } else {
2300 FTPPerror(&gConn, result, 0, "Could not open host", gConn.host);
2301 }
2302
2303 /* Remove callback. */
2304 gConn.printResponseProc = 0;
2305 (void) printf("\n");
2306 return (-1);
2307 } /* Open */
2308
2309
2310
2311
2312 /* Chooses a new remote system to connect to, and attempts to establish
2313 * a new FTP connection. This function is in charge of collecting the
2314 * information needed to do the open, and then doing it.
2315 */
2316 void
2317 OpenCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2318 {
2319 int c;
2320 int opts = 0;
2321 int uOptInd = 0;
2322 int n;
2323 int rc;
2324 char url[256];
2325 char urlfile[128];
2326 int directoryURL = 0;
2327 LineList cdlist;
2328 LinePtr lp;
2329 char prompt[256];
2330
2331 ARGSUSED(gUnusedArg);
2332 FlushLsCache();
2333 CloseHost();
2334 gLoadedBm = 0;
2335 InitConnectionInfo();
2336
2337 /* Need to find the host argument first. */
2338 GetoptReset();
2339 while ((c = Getopt(argc, argv, "aP:u:p:J:rd:g:")) > 0) switch(c) {
2340 case 'u':
2341 uOptInd = gOptInd + 1;
2342 opts++;
2343 break;
2344 default:
2345 opts++;
2346 }
2347 if (gOptInd < argc) {
2348 (void) STRNCPY(gConn.host, argv[gOptInd]);
2349 (void) STRNCPY(url, argv[gOptInd]);
2350 } else if (uOptInd > argc) {
2351 /* Special hack for v2.4.2 compatibility */
2352 (void) STRNCPY(gConn.host, argv[argc - 1]);
2353 (void) STRNCPY(url, argv[argc - 1]);
2354 } else {
2355 /* No host arg */
2356 if (opts > 0) {
2357 PrintCmdUsage(cmdp);
2358 } else if (RunBookmarkEditor(gConn.host, sizeof(gConn.host)) == 0) {
2359 if (gConn.host[0] != '\0') {
2360 gLoadedBm = 1;
2361 /* okay, now fall through */
2362 } else {
2363 return;
2364 }
2365 } else if (PrintHosts() <= 0) {
2366 (void) printf("You haven't bookmarked any FTP sites.\n");
2367 (void) printf("Before closing a site, you can use the \"bookmark\" command to save the current\nhost and directory for next time.\n");
2368 return;
2369 } else {
2370 (void) printf("\nTo use a bookmark, use the \"open\" command with the name of the bookmark.\n");
2371 return;
2372 }
2373 }
2374
2375 InitLineList(&cdlist);
2376
2377 if (GetBookmark(gConn.host, &gBm) >= 0) {
2378 gLoadedBm = 1;
2379 (void) STRNCPY(gConn.host, gBm.name);
2380 (void) STRNCPY(gConn.user, gBm.user);
2381 (void) STRNCPY(gConn.pass, gBm.pass);
2382 (void) STRNCPY(gConn.acct, gBm.acct);
2383 gConn.hasSIZE = gBm.hasSIZE;
2384 gConn.hasMDTM = gBm.hasMDTM;
2385 gConn.hasUTIME = gBm.hasUTIME;
2386 gConn.port = gBm.port;
2387
2388 /* Note: Version 3 only goes off of the
2389 * global "gDataPortMode" setting instead of
2390 * setting the dataPortMode on a per-site
2391 * basis.
2392 */
2393 gConn.hasPASV = gBm.hasPASV;
2394 } else {
2395 SetBookmarkDefaults(&gBm);
2396
2397 memcpy(&gTmpURLConn, &gConn, sizeof(gTmpURLConn));
2398 rc = DecodeDirectoryURL(&gTmpURLConn, url, &cdlist, urlfile, sizeof(urlfile));
2399 if (rc == kMalformedURL) {
2400 (void) fprintf(stdout, "Malformed URL: %s\n", url);
2401 DisposeLineListContents(&cdlist);
2402 return;
2403 } else if (rc == kNotURL) {
2404 directoryURL = 0;
2405 } else {
2406 /* It was a URL. */
2407 if (urlfile[0] != '\0') {
2408 /* It was obviously not a directory */
2409 (void) fprintf(stdout, "Use ncftpget or ncftpput to handle file URLs.\n");
2410 DisposeLineListContents(&cdlist);
2411 return;
2412 }
2413 memcpy(&gConn, &gTmpURLConn, sizeof(gConn));
2414 directoryURL = 1;
2415 }
2416 }
2417
2418 if (MayUseFirewall(gConn.host, gFirewallType, gFirewallExceptionList) != 0) {
2419 gConn.firewallType = gFirewallType;
2420 (void) STRNCPY(gConn.firewallHost, gFirewallHost);
2421 (void) STRNCPY(gConn.firewallUser, gFirewallUser);
2422 (void) STRNCPY(gConn.firewallPass, gFirewallPass);
2423 gConn.firewallPort = gFirewallPort;
2424 }
2425
2426 GetoptReset();
2427 while ((c = Getopt(argc, argv, "aP:u:p:J:j:rd:g:")) > 0) switch(c) {
2428 case 'J':
2429 case 'j':
2430 (void) STRNCPY(gConn.acct, gOptArg);
2431 break;
2432 case 'a':
2433 (void) STRNCPY(gConn.user, "anonymous");
2434 (void) STRNCPY(gConn.pass, "");
2435 (void) STRNCPY(gConn.acct, "");
2436 break;
2437 case 'P':
2438 gConn.port = atoi(gOptArg);
2439 break;
2440 case 'u':
2441 if (uOptInd <= argc)
2442 (void) STRNCPY(gConn.user, gOptArg);
2443 break;
2444 case 'p':
2445 (void) STRNCPY(gConn.pass, gOptArg); /* Don't recommend doing this! */
2446 break;
2447 case 'r':
2448 /* redial is on by default */
2449 break;
2450 case 'g':
2451 n = atoi(gOptArg);
2452 gConn.maxDials = n;
2453 break;
2454 case 'd':
2455 n = atoi(gOptArg);
2456 if (n >= 10)
2457 gConn.redialDelay = n;
2458 break;
2459 default:
2460 PrintCmdUsage(cmdp);
2461 DisposeLineListContents(&cdlist);
2462 return;
2463 }
2464
2465 if (uOptInd > argc) {
2466 (void) STRNCPY(prompt, "Username at ");
2467 (void) STRNCAT(prompt, gConn.host);
2468 (void) STRNCAT(prompt, ": ");
2469 (void) gl_getpass(prompt, gConn.user, sizeof(gConn.user));
2470 }
2471
2472 rc = DoOpen();
2473 if ((rc >= 0) && (directoryURL != 0)) {
2474 for (lp = cdlist.first; lp != NULL; lp = lp->next) {
2475 rc = FTPChdir(&gConn, lp->line);
2476 if (rc != kNoErr) {
2477 FTPPerror(&gConn, rc, kErrCWDFailed, "Could not chdir to", lp->line);
2478 break;
2479 }
2480 }
2481 rc = FTPGetCWD(&gConn, gRemoteCWD, sizeof(gRemoteCWD));
2482 if (rc != kNoErr) {
2483 FTPPerror(&gConn, rc, kErrPWDFailed, NULL, NULL);
2484 } else {
2485 (void) printf("Current remote directory is %s.\n", gRemoteCWD);
2486 }
2487 }
2488 DisposeLineListContents(&cdlist);
2489 } /* OpenCmd */
2490
2491
2492
2493
2494 /* View a remote file through the users $PAGER. */
2495 void
2496 PageCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2497 {
2498 int result;
2499 int i;
2500 FILE *volatile stream;
2501 #if defined(WIN32) || defined(_WINDOWS)
2502 #else
2503 int sj;
2504 vsigproc_t osigpipe, osigint;
2505 #endif
2506
2507 ARGSUSED(gUnusedArg);
2508 stream = OpenPager();
2509 if (stream == NULL) {
2510 return;
2511 }
2512
2513 #if defined(WIN32) || defined(_WINDOWS)
2514 #else
2515
2516 #ifdef HAVE_SIGSETJMP
2517 osigpipe = osigint = (sigproc_t) 0;
2518 sj = sigsetjmp(gCancelJmp, 1);
2519 #else /* HAVE_SIGSETJMP */
2520 osigpipe = osigint = (sigproc_t) 0;
2521 sj = setjmp(gCancelJmp);
2522 #endif /* HAVE_SIGSETJMP */
2523
2524 if (sj != 0) {
2525 /* Caught a signal. */
2526 (void) NcSignal(SIGPIPE, (FTPSigProc) SIG_IGN);
2527 ClosePager(stream);
2528 (void) NcSignal(SIGPIPE, osigpipe);
2529 (void) NcSignal(SIGINT, osigint);
2530 (void) fprintf(stderr, "Canceled.\n");
2531 Trace(0, "Canceled because of signal %d.\n", gGotSig);
2532 gMayCancelJmp = 0;
2533 return;
2534 } else {
2535 osigpipe = NcSignal(SIGPIPE, Cancel);
2536 osigint = NcSignal(SIGINT, Cancel);
2537 gMayCancelJmp = 1;
2538 }
2539
2540 #endif
2541
2542 for (i=1; i<argc; i++) {
2543 result = FTPGetOneFile2(&gConn, argv[i], NULL, kTypeAscii, _fileno(stream), kResumeNo, kAppendNo);
2544 if (result < 0) {
2545 if (errno != EPIPE) {
2546 ClosePager(stream);
2547 stream = NULL;
2548 FTPPerror(&gConn, result, kErrCouldNotStartDataTransfer, argv[0], argv[i]);
2549 }
2550 break;
2551 }
2552 }
2553
2554 #if defined(WIN32) || defined(_WINDOWS)
2555 ClosePager(stream);
2556 #else
2557 (void) NcSignal(SIGPIPE, (FTPSigProc) SIG_IGN);
2558 ClosePager(stream);
2559 (void) NcSignal(SIGPIPE, osigpipe);
2560 (void) NcSignal(SIGINT, osigint);
2561 #endif
2562 gMayCancelJmp = 0;
2563 } /* PageCmd */
2564
2565
2566
2567
2568 static int
2569 NcFTPConfirmResumeUploadProc(
2570 const char *volatile localpath,
2571 volatile longest_int localsize,
2572 volatile time_t localmtime,
2573 const char *volatile *remotepath,
2574 volatile longest_int remotesize,
2575 volatile time_t remotemtime,
2576 volatile longest_int *volatile startPoint)
2577 {
2578 int zaction = kConfirmResumeProcSaidBestGuess;
2579 char tstr[80], ans[32];
2580 static char newname[128]; /* arrggh... static. */
2581
2582 if (gResumeAnswerAll != kConfirmResumeProcNotUsed)
2583 return (gResumeAnswerAll);
2584
2585 if (gAutoResume != 0)
2586 return (kConfirmResumeProcSaidBestGuess);
2587
2588 printf("\nThe remote file \"%s\" already exists.\n", *remotepath);
2589
2590 if ((localmtime != kModTimeUnknown) && (localsize != kSizeUnknown)) {
2591 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &localmtime));
2592 (void) printf(
2593 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2594 "\tLocal: %12lld bytes, dated %s.\n",
2595 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2596 "\tLocal: %12qd bytes, dated %s.\n",
2597 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2598 "\tLocal: %12I64d bytes, dated %s.\n",
2599 #else
2600 "\tLocal: %12ld bytes, dated %s.\n",
2601 #endif
2602 localsize,
2603 tstr
2604 );
2605 if ((remotemtime == localmtime) && (remotesize == localsize)) {
2606 (void) printf("\t(Files are identical, skipped)\n\n");
2607 return (kConfirmResumeProcSaidSkip);
2608 }
2609 } else if (localsize != kSizeUnknown) {
2610 (void) printf(
2611 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2612 "\tLocal: %12lld bytes, date unknown.\n",
2613 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2614 "\tLocal: %12qd bytes, date unknown.\n",
2615 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2616 "\tLocal: %12I64d bytes, date unknown.\n",
2617 #else
2618 "\tLocal: %12ld bytes, date unknown.\n",
2619 #endif
2620 localsize
2621 );
2622 } else if (localmtime != kModTimeUnknown) {
2623 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &localmtime));
2624 (void) printf(
2625 "\tLocal: size unknown, dated %s.\n",
2626 tstr
2627 );
2628 }
2629
2630 tstr[sizeof(tstr) - 1] = '\0';
2631 if ((remotemtime != kModTimeUnknown) && (remotesize != kSizeUnknown)) {
2632 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &remotemtime));
2633 (void) printf(
2634 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2635 "\tRemote: %12lld bytes, dated %s.\n",
2636 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2637 "\tRemote: %12qd bytes, dated %s.\n",
2638 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2639 "\tRemote: %12I64d bytes, dated %s.\n",
2640 #else
2641 "\tRemote: %12ld bytes, dated %s.\n",
2642 #endif
2643 remotesize,
2644 tstr
2645 );
2646 } else if (remotesize != kSizeUnknown) {
2647 (void) printf(
2648 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2649 "\tRemote: %12lld bytes, date unknown.\n",
2650 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2651 "\tRemote: %12qd bytes, date unknown.\n",
2652 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2653 "\tRemote: %12I64d bytes, date unknown.\n",
2654 #else
2655 "\tRemote: %12ld bytes, date unknown.\n",
2656 #endif
2657 remotesize
2658 );
2659 } else if (remotemtime != kModTimeUnknown) {
2660 (void) strftime(tstr, sizeof(tstr) - 1, "%c", localtime((time_t *) &remotemtime));
2661 (void) printf(
2662 "\tRemote: size unknown, dated %s.\n",
2663 tstr
2664 );
2665 }
2666
2667 printf("\n");
2668 fflush(stdin);
2669 (void) memset(ans, 0, sizeof(ans));
2670 for (;;) {
2671 (void) printf("\t[O]verwrite?");
2672 if ((gConn.hasREST == kCommandAvailable) && (remotesize < localsize))
2673 printf(" [R]esume?");
2674 printf(" [A]ppend to? [S]kip? [N]ew Name?\n");
2675 (void) printf("\t[O!]verwrite all?");
2676 if ((gConn.hasREST == kCommandAvailable) && (remotesize < localsize))
2677 printf(" [R!]esume all?");
2678 printf(" [S!]kip all? [C]ancel > ");
2679 (void) fgets(ans, sizeof(ans) - 1, stdin);
2680 switch ((int) ans[0]) {
2681 case 'c':
2682 case 'C':
2683 ans[0] = 'C';
2684 ans[1] = '\0';
2685 zaction = kConfirmResumeProcSaidCancel;
2686 break;
2687 case 'o':
2688 case 'O':
2689 ans[0] = 'O';
2690 zaction = kConfirmResumeProcSaidOverwrite;
2691 break;
2692 case 'r':
2693 case 'R':
2694 if (gConn.hasREST != kCommandAvailable) {
2695 printf("\tResume is not available on this server.\n\n");
2696 ans[0] = '\0';
2697 break;
2698 } else if (remotesize > localsize) {
2699 printf("\tCannot resume when remote file is already larger than the local file.\n\n");
2700 ans[0] = '\0';
2701 break;
2702 } else if (remotesize == localsize) {
2703 printf("\tRemote file is already the same size as the local file.\n\n");
2704 ans[0] = '\0';
2705 break;
2706 }
2707 ans[0] = 'R';
2708 *startPoint = remotesize;
2709 zaction = kConfirmResumeProcSaidResume;
2710 if (OneTimeMessage("auto-resume") != 0) {
2711 printf("\n\tNOTE: If you want NcFTP to guess automatically, \"set auto-resume yes\"\n\n");
2712 }
2713 break;
2714 case 's':
2715 case 'S':
2716 ans[0] = 'S';
2717 zaction = kConfirmResumeProcSaidSkip;
2718 break;
2719 case 'n':
2720 case 'N':
2721 ans[0] = 'N';
2722 ans[1] = '\0';
2723 zaction = kConfirmResumeProcSaidOverwrite;
2724 break;
2725 case 'a':
2726 case 'A':
2727 ans[0] = 'A';
2728 ans[1] = '\0';
2729 zaction = kConfirmResumeProcSaidAppend;
2730 break;
2731 case 'g':
2732 case 'G':
2733 ans[0] = 'G';
2734 zaction = kConfirmResumeProcSaidBestGuess;
2735 break;
2736 default:
2737 ans[0] = '\0';
2738 }
2739 if (ans[0] != '\0')
2740 break;
2741 }
2742
2743 if (ans[0] == 'N') {
2744 (void) memset(newname, 0, sizeof(newname));
2745 printf("\tSave as: ");
2746 fflush(stdin);
2747 (void) fgets(newname, sizeof(newname) - 1, stdin);
2748 newname[strlen(newname) - 1] = '\0';
2749 if (newname[0] == '\0') {
2750 /* Nevermind. */
2751 printf("Skipped %s.\n", localpath);
2752 zaction = kConfirmResumeProcSaidSkip;
2753 } else {
2754 *remotepath = newname;
2755 }
2756 }
2757
2758 if (ans[1] == '!')
2759 gResumeAnswerAll = zaction;
2760 return (zaction);
2761 } /* NcFTPConfirmResumeUploadProc */
2762
2763
2764
2765
2766 /* Upload files to the remote system, permissions permitting. */
2767 void
2768 PutCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2769 {
2770 int opt;
2771 int renameMode = 0;
2772 int recurseFlag = kRecursiveNo;
2773 int appendFlag = kAppendNo;
2774 const char *dstdir = NULL;
2775 int rc;
2776 int i;
2777 int doGlob;
2778 int xtype = gBm.xferType;
2779 int nD = 0;
2780 int deleteFlag = kDeleteNo;
2781 int resumeFlag = kResumeYes;
2782 char pattern[256];
2783 vsigproc_t osigint;
2784 ConfirmResumeUploadProc confirmProc;
2785
2786 confirmProc = NcFTPConfirmResumeUploadProc;
2787 gResumeAnswerAll = kConfirmResumeProcNotUsed; /* Ask at least once each time */
2788 ARGSUSED(gUnusedArg);
2789 GetoptReset();
2790 while ((opt = Getopt(argc, argv, "AafZzrRD")) >= 0) switch (opt) {
2791 case 'a':
2792 xtype = kTypeAscii;
2793 break;
2794 case 'A':
2795 /* Append to remote files, instead of truncating
2796 * them first.
2797 */
2798 appendFlag = kAppendYes;
2799 break;
2800 case 'f':
2801 case 'Z':
2802 /* Do not try to resume a download, even if it
2803 * appeared that some of the file was transferred
2804 * already.
2805 */
2806 resumeFlag = kResumeNo;
2807 confirmProc = NoConfirmResumeUploadProc;
2808 break;
2809 case 'z':
2810 /* Special flag that lets you specify the
2811 * destination file. Normally a "put" will
2812 * write the file by the same name as the
2813 * local file's basename.
2814 */
2815 renameMode = 1;
2816 break;
2817 case 'r':
2818 case 'R':
2819 recurseFlag = kRecursiveYes;
2820 /* If the item is a directory, get the
2821 * directory and all its contents.
2822 */
2823 recurseFlag = kRecursiveYes;
2824 break;
2825 case 'D':
2826 /* You can delete the local file after
2827 * you uploaded it successfully by using
2828 * the -DD option. It requires two -D's
2829 * to minimize the odds of accidentally
2830 * using a single -D.
2831 */
2832 nD++;
2833 break;
2834 default:
2835 PrintCmdUsage(cmdp);
2836 return;
2837 }
2838
2839 if (nD >= 2)
2840 deleteFlag = kDeleteYes;
2841
2842 if (renameMode != 0) {
2843 if (gOptInd > (argc - 2)) {
2844 PrintCmdUsage(cmdp);
2845 (void) fprintf(stderr, "\nFor put with rename, try \"put -z local-path-name remote-path-name\".\n");
2846 return;
2847 }
2848 osigint = NcSignal(SIGINT, XferCanceller);
2849 rc = FTPPutOneFile3(&gConn, argv[gOptInd], argv[gOptInd + 1], xtype, (-1), appendFlag, NULL, NULL, resumeFlag, deleteFlag, confirmProc, 0);
2850 if (rc < 0)
2851 FTPPerror(&gConn, rc, kErrCouldNotStartDataTransfer, "put", argv[gOptInd + 1]);
2852 } else {
2853 osigint = NcSignal(SIGINT, XferCanceller);
2854 for (i=gOptInd; i<argc; i++) {
2855 doGlob = (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes;
2856 STRNCPY(pattern, argv[i]);
2857 StrRemoveTrailingSlashes(pattern);
2858 rc = FTPPutFiles3(&gConn, pattern, dstdir, recurseFlag, doGlob, xtype, appendFlag, NULL, NULL, resumeFlag, deleteFlag, confirmProc, 0);
2859 if (rc < 0)
2860 FTPPerror(&gConn, rc, kErrCouldNotStartDataTransfer, "put", argv[i]);
2861 }
2862 }
2863
2864 /* Really should just flush only the modified directories... */
2865 FlushLsCache();
2866
2867 (void) NcSignal(SIGINT, osigint);
2868 (void) fflush(stdin);
2869 } /* PutCmd */
2870
2871
2872
2873
2874 /* Displays the current remote working directory path. */
2875 void
2876 PwdCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2877 {
2878 int result;
2879 char url[256];
2880 char olddir[256];
2881
2882 ARGSUSED(gUnusedArg);
2883 #ifdef USE_WHAT_SERVER_SAYS_IS_CWD
2884 result = FTPGetCWD(&gConn, gRemoteCWD, sizeof(gRemoteCWD));
2885 CurrentURL(url, sizeof(url), 0);
2886 if (result < 0) {
2887 FTPPerror(&gConn, result, kErrPWDFailed, "Could not get remote working directory", NULL);
2888 } else {
2889 Trace(-1, "%s\n", url);
2890 }
2891 #else /* USE_CLIENT_SIDE_CALCULATED_CWD */
2892
2893 /* Display the current working directory, as
2894 * maintained by us.
2895 */
2896 CurrentURL(url, sizeof(url), 0);
2897 Trace(-1, " %s\n", url);
2898 olddir[sizeof(olddir) - 2] = '\0';
2899 STRNCPY(olddir, gRemoteCWD);
2900
2901 /* Now see what the server reports as the CWD.
2902 * Because of symlinks, it could be different
2903 * from what we are using.
2904 */
2905 result = FTPGetCWD(&gConn, gRemoteCWD, sizeof(gRemoteCWD));
2906 if ((result == kNoErr) && (strcmp(gRemoteCWD, olddir) != 0)) {
2907 Trace(-1, "This URL is also valid on this server:\n");
2908 CurrentURL(url, sizeof(url), 0);
2909 Trace(-1, " %s\n", url);
2910 if (olddir[sizeof(olddir) - 2] == '\0') {
2911 /* Go back to using the non-resolved version. */
2912 STRNCPY(gRemoteCWD, olddir);
2913 }
2914 }
2915 #endif
2916 } /* PwdCmd */
2917
2918
2919
2920
2921 /* Sets a flag so that the command shell exits. */
2922 void
2923 QuitCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2924 {
2925 ARGSUSED(gUnusedArg);
2926 gDoneApplication = 1;
2927 } /* QuitCmd */
2928
2929
2930
2931
2932 /* Send a command string to the FTP server, verbatim. */
2933 void
2934 QuoteCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2935 {
2936 char cmdbuf[256];
2937 int i;
2938
2939 ARGSUSED(gUnusedArg);
2940 (void) STRNCPY(cmdbuf, argv[1]);
2941 for (i=2; i<argc; i++) {
2942 (void) STRNCAT(cmdbuf, " ");
2943 (void) STRNCAT(cmdbuf, argv[i]);
2944 }
2945 (void) FTPCmd(&gConn, "%s", cmdbuf);
2946 PrintResp(&gConn.lastFTPCmdResultLL);
2947 } /* QuoteCmd */
2948
2949
2950
2951
2952 /* Expands a remote regex. Mostly a debugging command. */
2953 void
2954 RGlobCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2955 {
2956 int i;
2957 int result;
2958 int np = 0;
2959 LineList ll;
2960 LinePtr lp;
2961
2962 ARGSUSED(gUnusedArg);
2963 for (i=1; i<argc; i++) {
2964 InitLineList(&ll);
2965 result = FTPRemoteGlob(&gConn, &ll, argv[i], (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
2966 if (result < 0) {
2967 FTPPerror(&gConn, result, kErrGlobFailed, "remote glob", argv[i]);
2968 } else {
2969 for (lp = ll.first; lp != NULL; lp = lp->next) {
2970 if (lp->line != NULL) {
2971 if (np > 0)
2972 (void) printf(" ");
2973 (void) printf("%s", lp->line);
2974 np++;
2975 }
2976 }
2977 }
2978 DisposeLineListContents(&ll);
2979 }
2980 (void) printf("\n");
2981 } /* RGlobCmd */
2982
2983
2984
2985
2986 /* Renames a remote file. */
2987 void
2988 RenameCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
2989 {
2990 int result;
2991
2992 ARGSUSED(gUnusedArg);
2993 result = FTPRename(&gConn, argv[1], argv[2]);
2994 if (result < 0)
2995 FTPPerror(&gConn, result, kErrRenameFailed, "rename", argv[1]);
2996 else {
2997 /* Really should just flush only the modified directories... */
2998 FlushLsCache();
2999 }
3000 } /* RenameCmd */
3001
3002
3003
3004
3005 /* Removes a directory on the remote host. */
3006 void
3007 RmdirCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3008 {
3009 int result;
3010 int i, c;
3011 int recursive = kRecursiveNo;
3012
3013 ARGSUSED(gUnusedArg);
3014 GetoptReset();
3015 while ((c = Getopt(argc, argv, "rf")) > 0) switch(c) {
3016 case 'r':
3017 recursive = kRecursiveYes;
3018 break;
3019 case 'f':
3020 /* ignore */
3021 break;
3022 default:
3023 PrintCmdUsage(cmdp);
3024 return;
3025 }
3026 for (i=gOptInd; i<argc; i++) {
3027 result = FTPRmdir(&gConn, argv[i], recursive, (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
3028 if (result < 0)
3029 FTPPerror(&gConn, result, kErrRMDFailed, "rmdir", argv[i]);
3030 }
3031
3032 /* Really should just flush only the modified directories... */
3033 FlushLsCache();
3034 } /* RmdirCmd */
3035
3036
3037
3038
3039 /* Asks the remote server for help on how to use its server. */
3040 void
3041 RmtHelpCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3042 {
3043 int i, result;
3044 LineList ll;
3045 LinePtr lp;
3046
3047 ARGSUSED(gUnusedArg);
3048 if (argc == 1) {
3049 result = FTPRemoteHelp(&gConn, NULL, &ll);
3050 if (result < 0)
3051 FTPPerror(&gConn, result, kErrHELPFailed, "HELP failed", NULL);
3052 else {
3053 for (lp = ll.first; lp != NULL; lp = lp->next)
3054 (void) printf("%s\n", lp->line);
3055 }
3056 DisposeLineListContents(&ll);
3057 } else {
3058 for (i=1; i<argc; i++) {
3059 result = FTPRemoteHelp(&gConn, argv[i], &ll);
3060 if (result < 0)
3061 FTPPerror(&gConn, result, kErrHELPFailed, "HELP failed for", argv[i]);
3062 else {
3063 for (lp = ll.first; lp != NULL; lp = lp->next)
3064 (void) printf("%s\n", lp->line);
3065 }
3066 DisposeLineListContents(&ll);
3067 }
3068 }
3069 } /* RmtHelpCmd */
3070
3071
3072
3073
3074 /* Show and/or change customizable program settings. These changes are saved
3075 * at the end of the program's run.
3076 */
3077 void
3078 SetCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3079 {
3080 ARGSUSED(gUnusedArg);
3081 if (argc < 2)
3082 Set(NULL, NULL);
3083 else if (argc == 2)
3084 Set(argv[1], NULL);
3085 else
3086 Set(argv[1], argv[2]);
3087 } /* SetCmd */
3088
3089
3090
3091
3092 /* Local shell escape. */
3093 void
3094 ShellCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3095 {
3096 #if defined(WIN32) || defined(_WINDOWS)
3097 #else
3098 const char *cp;
3099 pid_t pid;
3100 int status;
3101 vsigproc_t osigint;
3102
3103 osigint = NcSignal(SIGINT, (FTPSigProc) SIG_IGN);
3104 ARGSUSED(gUnusedArg);
3105 pid = fork();
3106 if (pid < (pid_t) 0) {
3107 perror("fork");
3108 } else if (pid == 0) {
3109 cp = strrchr(gShell, '/');
3110 if (cp == NULL)
3111 cp = gShell; /* bug */
3112 else
3113 cp++;
3114 if (argc == 1) {
3115 execl(gShell, cp, NULL);
3116 perror(gShell);
3117 exit(1);
3118 } else {
3119 execvp(argv[1], (char **) argv + 1);
3120 perror(gShell);
3121 exit(1);
3122 }
3123 } else {
3124 /* parent */
3125 for (;;) {
3126 #ifdef HAVE_WAITPID
3127 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
3128 break;
3129 #else
3130 if ((wait(&status) < 0) && (errno != EINTR))
3131 break;
3132 #endif
3133 if (WIFEXITED(status) || WIFSIGNALED(status))
3134 break; /* done */
3135 }
3136 }
3137 (void) NcSignal(SIGINT, osigint);
3138 #endif
3139 } /* ShellCmd */
3140
3141
3142
3143
3144 /* Send a command string to the FTP server, verbatim. */
3145 void
3146 SiteCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3147 {
3148 char cmdbuf[256];
3149 int i;
3150
3151 ARGSUSED(gUnusedArg);
3152 (void) STRNCPY(cmdbuf, "SITE");
3153 for (i=1; i<argc; i++) {
3154 (void) STRNCAT(cmdbuf, " ");
3155 (void) STRNCAT(cmdbuf, argv[i]);
3156 }
3157 (void) FTPCmd(&gConn, "%s", cmdbuf);
3158 PrintResp(&gConn.lastFTPCmdResultLL);
3159 } /* SiteCmd */
3160
3161
3162
3163
3164 static time_t
3165 GetStartSpoolDate(const char *s)
3166 {
3167 char *cp;
3168 char s2[64];
3169 time_t now, when;
3170 int toff, n, c, hr, min;
3171 struct tm lt, *ltp;
3172
3173 STRNCPY(s2, s);
3174 cp = strchr(s2, ':');
3175 if ((s2[0] == 'n') || (s2[0] == '+')) {
3176 /* "now + XX hours" or
3177 * "+ XX hours"
3178 */
3179 cp = strchr(s2, '+');
3180 if (cp == NULL)
3181 return ((time_t) -1);
3182 ++cp;
3183 toff = 0;
3184 n = 0;
3185 (void) sscanf(cp, "%d%n", &toff, &n);
3186 if ((n <= 0) || (toff <= 0))
3187 return ((time_t) -1);
3188 cp += n;
3189 while ((*cp != '\0') && (!isalpha(*cp)))
3190 cp++;
3191 c = *cp;
3192 if (isupper(c))
3193 c = tolower(c);
3194 if (c == 's') {
3195 /* seconds */
3196 } else if (c == 'm') {
3197 /* minutes */
3198 toff *= 60;
3199 } else if (c == 'h') {
3200 /* hours */
3201 toff *= 3600;
3202 } else if (c == 'd') {
3203 /* days */
3204 toff *= 86400;
3205 } else {
3206 /* unrecognized unit */
3207 return ((time_t) -1);
3208 }
3209 time(&now);
3210 when = now + (time_t) toff;
3211 } else if (cp != NULL) {
3212 /* HH:MM, as in "23:38" */
3213 time(&now);
3214 ltp = localtime(&now);
3215 if (ltp == NULL)
3216 return ((time_t) -1); /* impossible */
3217 lt = *ltp;
3218 *cp = ' ';
3219 hr = -1;
3220 min = -1;
3221 (void) sscanf(s2, "%d%d", &hr, &min);
3222 if ((hr < 0) || (min < 0))
3223 return ((time_t) -1);
3224 lt.tm_hour = hr;
3225 lt.tm_min = min;
3226 when = mktime(&lt);
3227 if ((when == (time_t) -1) || (when == (time_t) 0))
3228 return (when);
3229 if (when < now)
3230 when += (time_t) 86400;
3231 } else {
3232 when = UnDate(s2);
3233 }
3234 return (when);
3235 } /* GetStartSpoolDate */
3236
3237
3238
3239 static int
3240 SpoolCheck(void)
3241 {
3242 if (CanSpool() < 0) {
3243 #if defined(WIN32) || defined(_WINDOWS)
3244 (void) printf("Sorry, spooling isn't allowed until you run Setup.exe.\n");
3245 #else
3246 (void) printf("Sorry, spooling isn't allowed because this user requires that the NCFTPDIR\nenvironment variable be set to a directory to write datafiles to.\n");
3247 #endif
3248 return (-1);
3249 } else if (HaveSpool() == 0) {
3250 #if defined(WIN32) || defined(_WINDOWS)
3251 (void) printf("Sorry, the \"ncftpbatch\" program could not be found.\nPlease re-run Setup to correct this problem.\n");
3252 #else
3253 (void) printf("Sorry, the \"ncftpbatch\" program could not be found.\nThis program must be installed and in your PATH in order to use this feature.\n");