- Merge the remaining portion of the wlan-bringup branch
[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");
3254 #endif
3255 return (-1);
3256 }
3257 return (0);
3258 } /* SpoolCheck */
3259
3260
3261
3262 /* Show and/or change customizable program settings. These changes are saved
3263 * at the end of the program's run.
3264 */
3265 void
3266 BGStartCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3267 {
3268 int i, n;
3269
3270 ARGSUSED(gUnusedArg);
3271 if (SpoolCheck() < 0)
3272 return;
3273
3274 if ((argc < 2) || ((n = atoi(argv[1])) < 2)) {
3275 RunBatch(0, &gConn);
3276 (void) printf("Background process started.\n");
3277 #if defined(WIN32) || defined(_WINDOWS)
3278 #else
3279 (void) printf("Watch the \"%s/batchlog\" file to see how it is progressing.\n", gOurDirectoryPath);
3280 #endif
3281 } else {
3282 for (i=0; i<n; i++)
3283 RunBatch(0, &gConn);
3284 (void) printf("Background processes started.\n");
3285 #if defined(WIN32) || defined(_WINDOWS)
3286 #else
3287 (void) printf("Watch the \"%s/batchlog\" file to see how it is progressing.\n", gOurDirectoryPath);
3288 #endif
3289 }
3290 } /* BGStart */
3291
3292
3293
3294
3295 /* This commands lets the user change the umask, if the server supports it. */
3296 /* (bgget) */
3297 void
3298 SpoolGetCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3299 {
3300 int opt;
3301 int renameMode = 0;
3302 int recurseFlag = kRecursiveNo;
3303 int rc;
3304 int i;
3305 int xtype = gBm.xferType;
3306 int nD = 0;
3307 int deleteFlag = kDeleteNo;
3308 time_t when = 0;
3309 char ldir[256];
3310 char pattern[256];
3311 char *lname;
3312 LineList ll;
3313 LinePtr lp;
3314
3315 ARGSUSED(gUnusedArg);
3316
3317 if ((gSavePasswords <= 0) && ((strcmp(gConn.user, "anonymous") != 0) && (strcmp(gConn.user, "ftp") != 0))) {
3318 (void) printf("Sorry, spooling isn't allowed when you're not logged in anonymously, because\nthe spool files would need to save your password.\n\nYou can override this by doing a \"set save-passwords yes\" if you're willing\nto live with the consequences.\n");
3319 return;
3320 } else if (SpoolCheck() < 0) {
3321 return;
3322 }
3323
3324 GetoptReset();
3325 while ((opt = Getopt(argc, argv, "@:azfrRD")) >= 0) switch (opt) {
3326 case '@':
3327 when = GetStartSpoolDate(gOptArg);
3328 if ((when == (time_t) -1) || (when == (time_t) 0)) {
3329 (void) fprintf(stderr, "Bad date. It must be expressed as one of the following:\n\tYYYYMMDDHHMMSS\t\n\t\"now + N hours|min|sec|days\"\n\tHH:MM\n\nNote: Do not forget to quote the entire argument for the offset option.\nExample: bgget -@ \"now + 15 min\" ...\n");
3330 return;
3331 }
3332 break;
3333 case 'a':
3334 xtype = kTypeAscii;
3335 break;
3336 case 'z':
3337 /* Special flag that lets you specify the
3338 * destination file. Normally a "get" will
3339 * write the file by the same name as the
3340 * remote file's basename.
3341 */
3342 renameMode = 1;
3343 break;
3344 case 'r':
3345 case 'R':
3346 /* If the item is a directory, get the
3347 * directory and all its contents.
3348 */
3349 recurseFlag = kRecursiveYes;
3350 break;
3351 case 'D':
3352 /* You can delete the remote file after
3353 * you downloaded it successfully by using
3354 * the -DD option. It requires two -D's
3355 * to minimize the odds of accidentally
3356 * using a single -D.
3357 */
3358 nD++;
3359 break;
3360 default:
3361 PrintCmdUsage(cmdp);
3362 return;
3363 }
3364
3365 if (nD >= 2)
3366 deleteFlag = kDeleteYes;
3367
3368 if (FTPGetLocalCWD(ldir, sizeof(ldir)) == NULL) {
3369 perror("could not get current local directory");
3370 return;
3371 }
3372
3373 if (renameMode != 0) {
3374 if (gOptInd > argc - 2) {
3375 PrintCmdUsage(cmdp);
3376 return;
3377 }
3378 rc = SpoolX(
3379 "get",
3380 argv[gOptInd],
3381 gRemoteCWD,
3382 argv[gOptInd + 1],
3383 ldir,
3384 gConn.host,
3385 gConn.ip,
3386 gConn.port,
3387 gConn.user,
3388 gConn.pass,
3389 xtype,
3390 recurseFlag,
3391 deleteFlag,
3392 gConn.dataPortMode,
3393 NULL,
3394 NULL,
3395 NULL,
3396 when
3397 );
3398 if (rc == 0) {
3399 Trace(-1, " + Spooled: get %s as %s\n", argv[gOptInd], argv[gOptInd]);
3400 }
3401 } else {
3402 for (i=gOptInd; i<argc; i++) {
3403 STRNCPY(pattern, argv[i]);
3404 StrRemoveTrailingSlashes(pattern);
3405 InitLineList(&ll);
3406 rc = FTPRemoteGlob(&gConn, &ll, pattern, (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
3407 if (rc < 0) {
3408 FTPPerror(&gConn, rc, kErrGlobFailed, argv[0], pattern);
3409 } else {
3410 for (lp = ll.first; lp != NULL; lp = lp->next) {
3411 if (lp->line != NULL) {
3412 lname = strrchr(lp->line, '/');
3413 if (lname == NULL)
3414 lname = lp->line;
3415 else
3416 lname++;
3417 rc = SpoolX(
3418 "get",
3419 lp->line,
3420 gRemoteCWD,
3421 lname,
3422 ldir,
3423 gConn.host,
3424 gConn.ip,
3425 gConn.port,
3426 gConn.user,
3427 gConn.pass,
3428 xtype,
3429 recurseFlag,
3430 deleteFlag,
3431 gConn.dataPortMode,
3432 NULL,
3433 NULL,
3434 NULL,
3435 when
3436 );
3437 if (rc == 0) {
3438 Trace(-1, " + Spooled: get %s\n", lp->line);
3439 }
3440 }
3441 }
3442 }
3443 DisposeLineListContents(&ll);
3444 }
3445 }
3446 } /* SpoolGetCmd */
3447
3448
3449
3450
3451 /* This commands lets the user change the umask, if the server supports it. */
3452 /* (bgput) */
3453 void
3454 SpoolPutCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3455 {
3456 int opt;
3457 int renameMode = 0;
3458 int recurseFlag = kRecursiveNo;
3459 int rc;
3460 int i;
3461 int xtype = gBm.xferType;
3462 int nD = 0;
3463 int deleteFlag = kDeleteNo;
3464 time_t when = 0;
3465 char ldir[256];
3466 char pattern[256];
3467 LineList ll;
3468 LinePtr lp;
3469 char *rname;
3470
3471 ARGSUSED(gUnusedArg);
3472
3473 if ((gSavePasswords <= 0) && ((strcmp(gConn.user, "anonymous") != 0) && (strcmp(gConn.user, "ftp") != 0))) {
3474 (void) printf("Sorry, spooling isn't allowed when you're not logged in anonymously, because\nthe spool files would need to save your password.\n\nYou can override this by doing a \"set save-passwords yes\" if you're willing\nto live with the consequences.\n");
3475 return;
3476 } else if (SpoolCheck() < 0) {
3477 return;
3478 }
3479
3480 GetoptReset();
3481 while ((opt = Getopt(argc, argv, "@:azrRD")) >= 0) switch (opt) {
3482 case '@':
3483 when = GetStartSpoolDate(gOptArg);
3484 if ((when == (time_t) -1) || (when == (time_t) 0)) {
3485 (void) fprintf(stderr, "Bad date. It must be expressed as one of the following:\n\tYYYYMMDDHHMMSS\t\n\t\"now + N hours|min|sec|days\"\n\tHH:MM\n\nNote: Do not forget to quote the entire argument for the offset option.\nExample: bgget -@ \"now + 15 min\" ...\n");
3486 return;
3487 }
3488 break;
3489 case 'a':
3490 xtype = kTypeAscii;
3491 break;
3492 case 'z':
3493 /* Special flag that lets you specify the
3494 * destination file. Normally a "get" will
3495 * write the file by the same name as the
3496 * remote file's basename.
3497 */
3498 renameMode = 1;
3499 break;
3500 case 'r':
3501 case 'R':
3502 /* If the item is a directory, get the
3503 * directory and all its contents.
3504 */
3505 recurseFlag = kRecursiveYes;
3506 break;
3507 case 'D':
3508 /* You can delete the remote file after
3509 * you downloaded it successfully by using
3510 * the -DD option. It requires two -D's
3511 * to minimize the odds of accidentally
3512 * using a single -D.
3513 */
3514 nD++;
3515 break;
3516 default:
3517 PrintCmdUsage(cmdp);
3518 return;
3519 }
3520
3521 if (nD >= 2)
3522 deleteFlag = kDeleteYes;
3523
3524 if (FTPGetLocalCWD(ldir, sizeof(ldir)) == NULL) {
3525 perror("could not get current local directory");
3526 return;
3527 }
3528
3529 if (renameMode != 0) {
3530 if (gOptInd > argc - 2) {
3531 PrintCmdUsage(cmdp);
3532 return;
3533 }
3534 rc = SpoolX(
3535 "put",
3536 argv[gOptInd + 1],
3537 gRemoteCWD,
3538 argv[gOptInd + 0],
3539 ldir,
3540 gConn.host,
3541 gConn.ip,
3542 gConn.port,
3543 gConn.user,
3544 gConn.pass,
3545 xtype,
3546 recurseFlag,
3547 deleteFlag,
3548 gConn.dataPortMode,
3549 NULL,
3550 NULL,
3551 NULL,
3552 when
3553 );
3554 if (rc == 0) {
3555 Trace(-1, " + Spooled: put %s as %s\n", argv[gOptInd], argv[gOptInd]);
3556 }
3557 } else {
3558 for (i=gOptInd; i<argc; i++) {
3559 STRNCPY(pattern, argv[i]);
3560 StrRemoveTrailingSlashes(pattern);
3561 InitLineList(&ll);
3562 rc = FTPLocalGlob(&gConn, &ll, pattern, (aip->noglobargv[i] != 0) ? kGlobNo: kGlobYes);
3563 if (rc < 0) {
3564 FTPPerror(&gConn, rc, kErrGlobFailed, "local glob", pattern);
3565 } else {
3566 for (lp = ll.first; lp != NULL; lp = lp->next) {
3567 if (lp->line != NULL) {
3568 rname = strrchr(lp->line, '/');
3569 if (rname == NULL)
3570 rname = lp->line;
3571 else
3572 rname++;
3573 rc = SpoolX(
3574 "put",
3575 rname,
3576 gRemoteCWD,
3577 lp->line,
3578 ldir,
3579 gConn.host,
3580 gConn.ip,
3581 gConn.port,
3582 gConn.user,
3583 gConn.pass,
3584 xtype,
3585 recurseFlag,
3586 deleteFlag,
3587 gConn.dataPortMode,
3588 NULL,
3589 NULL,
3590 NULL,
3591 when
3592 );
3593 if (rc == 0) {
3594 Trace(-1, " + Spooled: put %s\n", lp->line);
3595 }
3596 }
3597 }
3598 }
3599 DisposeLineListContents(&ll);
3600 }
3601 }
3602 } /* SpoolGetCmd */
3603
3604
3605
3606
3607 /* This commands lets the user change the umask, if the server supports it. */
3608 void
3609 SymlinkCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3610 {
3611 int result;
3612
3613 ARGSUSED(gUnusedArg);
3614 result = FTPSymlink(&gConn, argv[1], argv[2]);
3615 if (result < 0)
3616 FTPPerror(&gConn, result, kErrSYMLINKFailed, "symlink", argv[1]);
3617
3618 /* Really should just flush only the modified directories... */
3619 FlushLsCache();
3620 } /* SymlinkCmd */
3621
3622
3623
3624
3625 /* This commands lets the user change the transfer type to use. */
3626 void
3627 TypeCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3628 {
3629 int c;
3630 int result;
3631 const char *cs;
3632
3633 ARGSUSED(gUnusedArg);
3634 if (argc < 2) {
3635 c = argv[0][0];
3636 if (c == 't') {
3637 if (gBm.xferType == kTypeAscii) {
3638 c = kTypeAscii;
3639 cs = "ASCII";
3640 } else if (gBm.xferType == kTypeEbcdic) {
3641 c = kTypeEbcdic;
3642 cs = "EBCDIC";
3643 } else {
3644 c = kTypeBinary;
3645 cs = "binary/image";
3646 }
3647 Trace(-1, "Type is %c (%s).\n", c, cs);
3648 } else {
3649 result = FTPSetTransferType(&gConn, c);
3650 if (result < 0) {
3651 FTPPerror(&gConn, result, kErrTYPEFailed, "Type", argv[1]);
3652 } else {
3653 gBm.xferType = gConn.curTransferType;
3654 }
3655 }
3656 } else {
3657 c = argv[1][0];
3658 result = FTPSetTransferType(&gConn, c);
3659 if (result < 0) {
3660 FTPPerror(&gConn, result, kErrTYPEFailed, "Type", argv[1]);
3661 } else {
3662 gBm.xferType = gConn.curTransferType;
3663 }
3664 }
3665 } /* TypeCmd */
3666
3667
3668
3669
3670 /* This commands lets the user change the umask, if the server supports it. */
3671 void
3672 UmaskCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3673 {
3674 int result;
3675
3676 ARGSUSED(gUnusedArg);
3677 result = FTPUmask(&gConn, argv[1]);
3678 if (result < 0)
3679 FTPPerror(&gConn, result, kErrUmaskFailed, "umask", argv[1]);
3680 } /* UmaskCmd */
3681
3682
3683
3684
3685 /* Show the version information. */
3686 void
3687 VersionCmd(const int argc, const char **const argv, const CommandPtr cmdp, const ArgvInfoPtr aip)
3688 {
3689 ARGSUSED(gUnusedArg);
3690 (void) printf("Version: %s\n", gVersion + 5);
3691 (void) printf("Author: Mike Gleason (ncftp@ncftp.com)\n");
3692 #ifndef BETA
3693 (void) printf("Archived at: ftp://ftp.ncftp.com/ncftp/\n");
3694 #endif
3695 (void) printf("Library Version: %s\n", gLibNcFTPVersion + 5);
3696 #ifdef __DATE__
3697 (void) printf("Compile Date: %s\n", __DATE__);
3698 #endif
3699 if (gOS[0] != '\0')
3700 (void) printf("Platform: %s\n", gOS);
3701 } /* VersionCmd */