delete .cvsignore
[reactos.git] / rosapps / net / ncftp / libncftp / cmds.c
1 /* cmds.c
2 *
3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
4 * All rights reserved.
5 *
6 */
7
8 #include "syshdrs.h"
9
10 int
11 FTPChdir(const FTPCIPtr cip, const char *const cdCwd)
12 {
13 int result;
14
15 if (cip == NULL)
16 return (kErrBadParameter);
17 if (strcmp(cip->magic, kLibraryMagic))
18 return (kErrBadMagic);
19
20 if (cdCwd == NULL) {
21 result = kErrInvalidDirParam;
22 cip->errNo = kErrInvalidDirParam;
23 } else {
24 if (cdCwd[0] == '\0') /* But allow FTPChdir(cip, ".") to go through. */
25 result = 2;
26 else if (strcmp(cdCwd, "..") == 0)
27 result = FTPCmd(cip, "CDUP");
28 else
29 result = FTPCmd(cip, "CWD %s", cdCwd);
30 if (result >= 0) {
31 if (result == 2) {
32 result = kNoErr;
33 } else {
34 result = kErrCWDFailed;
35 cip->errNo = kErrCWDFailed;
36 }
37 }
38 }
39 return (result);
40 } /* FTPChdir */
41
42
43
44
45 int
46 FTPChmod(const FTPCIPtr cip, const char *const pattern, const char *const mode, const int doGlob)
47 {
48 LineList fileList;
49 LinePtr filePtr;
50 char *file;
51 int onceResult, batchResult;
52
53 if (cip == NULL)
54 return (kErrBadParameter);
55 if (strcmp(cip->magic, kLibraryMagic))
56 return (kErrBadMagic);
57
58 batchResult = FTPRemoteGlob(cip, &fileList, pattern, doGlob);
59 if (batchResult != kNoErr)
60 return (batchResult);
61
62 for (batchResult = kNoErr, filePtr = fileList.first;
63 filePtr != NULL;
64 filePtr = filePtr->next)
65 {
66 file = filePtr->line;
67 if (file == NULL) {
68 batchResult = kErrBadLineList;
69 cip->errNo = kErrBadLineList;
70 break;
71 }
72 onceResult = FTPCmd(cip, "SITE CHMOD %s %s", mode, file);
73 if (onceResult < 0) {
74 batchResult = onceResult;
75 break;
76 }
77 if (onceResult != 2) {
78 batchResult = kErrChmodFailed;
79 cip->errNo = kErrChmodFailed;
80 }
81 }
82 DisposeLineListContents(&fileList);
83 return (batchResult);
84 } /* FTPChmod */
85
86
87
88
89 static int
90 FTPRmdirRecursiveL2(const FTPCIPtr cip)
91 {
92 LineList fileList;
93 LinePtr filePtr;
94 char *file;
95 int result;
96
97 result = FTPRemoteGlob(cip, &fileList, "**", kGlobYes);
98 if (result != kNoErr) {
99 return (result);
100 }
101
102 for (filePtr = fileList.first;
103 filePtr != NULL;
104 filePtr = filePtr->next)
105 {
106 file = filePtr->line;
107 if (file == NULL) {
108 cip->errNo = kErrBadLineList;
109 break;
110 }
111
112 if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0'))))
113 continue; /* Skip . and .. */
114
115 if (FTPChdir(cip, file) == kNoErr) {
116 /* It was a directory.
117 * Go in and wax it.
118 */
119 result = FTPRmdirRecursiveL2(cip);
120
121 if (FTPChdir(cip, "..") != kNoErr) {
122 /* Panic -- we can no longer
123 * cd back to the directory
124 * we were in before.
125 */
126 result = kErrCannotGoToPrevDir;
127 cip->errNo = kErrCannotGoToPrevDir;
128 return (result);
129 }
130
131 if ((result < 0) && (result != kErrGlobNoMatch))
132 return (result);
133
134 result = FTPRmdir(cip, file, kRecursiveNo, kGlobNo);
135 if (result != kNoErr) {
136 /* Well, we couldn't remove the empty
137 * directory. Perhaps we screwed up
138 * and the directory wasn't empty.
139 */
140 return (result);
141 }
142 } else {
143 /* Assume it was a file -- remove it. */
144 result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
145 /* Try continuing to remove the rest,
146 * even if this failed.
147 */
148 }
149 }
150 DisposeLineListContents(&fileList);
151
152 return (result);
153 } /* FTPRmdirRecursiveL2 */
154
155
156
157 static int
158 FTPRmdirRecursive(const FTPCIPtr cip, const char *const dir)
159 {
160 int result, result2;
161
162 /* Preserve old working directory. */
163 (void) FTPGetCWD(cip, cip->buf, cip->bufSize);
164
165 result = FTPChdir(cip, dir);
166 if (result != kNoErr) {
167 return (result);
168 }
169
170 result = FTPRmdirRecursiveL2(cip);
171
172 if (FTPChdir(cip, cip->buf) != kNoErr) {
173 /* Could not cd back to the original user directory -- bad. */
174 if (result != kNoErr) {
175 result = kErrCannotGoToPrevDir;
176 cip->errNo = kErrCannotGoToPrevDir;
177 }
178 return (result);
179 }
180
181 /* Now rmdir the last node, the root of the tree
182 * we just went through.
183 */
184 result2 = FTPRmdir(cip, dir, kRecursiveNo, kGlobNo);
185 if ((result2 != kNoErr) && (result == kNoErr))
186 result = result2;
187
188 return (result);
189 } /* FTPRmdirRecursive */
190
191
192
193
194 int
195 FTPDelete(const FTPCIPtr cip, const char *const pattern, const int recurse, const int doGlob)
196 {
197 LineList fileList;
198 LinePtr filePtr;
199 char *file;
200 int onceResult, batchResult;
201
202 if (cip == NULL)
203 return (kErrBadParameter);
204 if (strcmp(cip->magic, kLibraryMagic))
205 return (kErrBadMagic);
206
207 batchResult = FTPRemoteGlob(cip, &fileList, pattern, doGlob);
208 if (batchResult != kNoErr)
209 return (batchResult);
210
211 for (batchResult = kNoErr, filePtr = fileList.first;
212 filePtr != NULL;
213 filePtr = filePtr->next)
214 {
215 file = filePtr->line;
216 if (file == NULL) {
217 batchResult = kErrBadLineList;
218 cip->errNo = kErrBadLineList;
219 break;
220 }
221 onceResult = FTPCmd(cip, "DELE %s", file);
222 if (onceResult < 0) {
223 batchResult = onceResult;
224 break;
225 }
226 if (onceResult != 2) {
227 if (recurse != kRecursiveYes) {
228 batchResult = kErrDELEFailed;
229 cip->errNo = kErrDELEFailed;
230 } else {
231 onceResult = FTPCmd(cip, "RMD %s", file);
232 if (onceResult < 0) {
233 batchResult = onceResult;
234 break;
235 }
236 if (onceResult != 2) {
237 onceResult = FTPRmdirRecursive(cip, file);
238 if (onceResult < 0) {
239 batchResult = kErrRMDFailed;
240 cip->errNo = kErrRMDFailed;
241 }
242 }
243 }
244 }
245 }
246 DisposeLineListContents(&fileList);
247 return (batchResult);
248 } /* FTPDelete */
249
250
251
252
253 int
254 FTPGetCWD(const FTPCIPtr cip, char *const newCwd, const size_t newCwdSize)
255 {
256 ResponsePtr rp;
257 char *l, *r;
258 int result;
259
260 if (cip == NULL)
261 return (kErrBadParameter);
262 if (strcmp(cip->magic, kLibraryMagic))
263 return (kErrBadMagic);
264
265 if ((newCwd == NULL) || (newCwdSize == 0)) {
266 result = kErrInvalidDirParam;
267 cip->errNo = kErrInvalidDirParam;
268 } else {
269 rp = InitResponse();
270 if (rp == NULL) {
271 result = kErrMallocFailed;
272 cip->errNo = kErrMallocFailed;
273 Error(cip, kDontPerror, "Malloc failed.\n");
274 } else {
275 result = RCmd(cip, rp, "PWD");
276 if (result == 2) {
277 if ((r = strrchr(rp->msg.first->line, '"')) != NULL) {
278 /* "xxxx" is current directory.
279 * Strip out just the xxxx to copy into the remote cwd.
280 */
281 l = strchr(rp->msg.first->line, '"');
282 if ((l != NULL) && (l != r)) {
283 *r = '\0';
284 ++l;
285 (void) Strncpy(newCwd, l, newCwdSize);
286 *r = '"'; /* Restore, so response prints correctly. */
287 }
288 } else {
289 /* xxxx is current directory.
290 * Mostly for VMS.
291 */
292 if ((r = strchr(rp->msg.first->line, ' ')) != NULL) {
293 *r = '\0';
294 (void) Strncpy(newCwd, (rp->msg.first->line), newCwdSize);
295 *r = ' '; /* Restore, so response prints correctly. */
296 }
297 }
298 result = kNoErr;
299 } else if (result > 0) {
300 result = kErrPWDFailed;
301 cip->errNo = kErrPWDFailed;
302 }
303 DoneWithResponse(cip, rp);
304 }
305 }
306 return (result);
307 } /* FTPGetCWD */
308
309
310
311
312 int
313 FTPChdirAndGetCWD(const FTPCIPtr cip, const char *const cdCwd, char *const newCwd, const size_t newCwdSize)
314 {
315 ResponsePtr rp;
316 char *l, *r;
317 int result;
318
319 if (cip == NULL)
320 return (kErrBadParameter);
321 if (strcmp(cip->magic, kLibraryMagic))
322 return (kErrBadMagic);
323
324 if ((newCwd == NULL) || (cdCwd == NULL)) {
325 result = kErrInvalidDirParam;
326 cip->errNo = kErrInvalidDirParam;
327 } else {
328 if (cdCwd[0] == '\0') { /* But allow FTPChdir(cip, ".") to go through. */
329 result = FTPGetCWD(cip, newCwd, newCwdSize);
330 return (result);
331 }
332 rp = InitResponse();
333 if (rp == NULL) {
334 result = kErrMallocFailed;
335 cip->errNo = kErrMallocFailed;
336 Error(cip, kDontPerror, "Malloc failed.\n");
337 } else {
338 if (strcmp(cdCwd, "..") == 0)
339 result = RCmd(cip, rp, "CDUP");
340 else
341 result = RCmd(cip, rp, "CWD %s", cdCwd);
342 if (result == 2) {
343 l = strchr(rp->msg.first->line, '"');
344 if ((l == rp->msg.first->line) && ((r = strrchr(rp->msg.first->line, '"')) != NULL) && (l != r)) {
345 /* "xxxx" is current directory.
346 * Strip out just the xxxx to copy into the remote cwd.
347 *
348 * This is nice because we didn't have to do a PWD.
349 */
350 *r = '\0';
351 ++l;
352 (void) Strncpy(newCwd, l, newCwdSize);
353 *r = '"'; /* Restore, so response prints correctly. */
354 DoneWithResponse(cip, rp);
355 result = kNoErr;
356 } else {
357 DoneWithResponse(cip, rp);
358 result = FTPGetCWD(cip, newCwd, newCwdSize);
359 }
360 } else if (result > 0) {
361 result = kErrCWDFailed;
362 cip->errNo = kErrCWDFailed;
363 DoneWithResponse(cip, rp);
364 } else {
365 DoneWithResponse(cip, rp);
366 }
367 }
368 }
369 return (result);
370 } /* FTPChdirAndGetCWD */
371
372
373
374
375 int
376 FTPChdir3(FTPCIPtr cip, const char *const cdCwd, char *const newCwd, const size_t newCwdSize, int flags)
377 {
378 char *cp, *startcp;
379 int result;
380 int lastSubDir;
381 int mkd, pwd;
382
383 if (cip == NULL)
384 return (kErrBadParameter);
385 if (strcmp(cip->magic, kLibraryMagic))
386 return (kErrBadMagic);
387
388 if (cdCwd == NULL) {
389 result = kErrInvalidDirParam;
390 cip->errNo = kErrInvalidDirParam;
391 return result;
392 }
393
394 if (flags == kChdirOnly)
395 return (FTPChdir(cip, cdCwd));
396 if (flags == kChdirAndGetCWD) {
397 return (FTPChdirAndGetCWD(cip, cdCwd, newCwd, newCwdSize));
398 } else if (flags == kChdirAndMkdir) {
399 result = FTPMkdir(cip, cdCwd, kRecursiveYes);
400 if (result == kNoErr)
401 result = FTPChdir(cip, cdCwd);
402 return result;
403 } else if (flags == (kChdirAndMkdir|kChdirAndGetCWD)) {
404 result = FTPMkdir(cip, cdCwd, kRecursiveYes);
405 if (result == kNoErr)
406 result = FTPChdirAndGetCWD(cip, cdCwd, newCwd, newCwdSize);
407 return result;
408 }
409
410 /* else: (flags | kChdirOneSubdirAtATime) == true */
411
412 cp = cip->buf;
413 cp[cip->bufSize - 1] = '\0';
414 (void) Strncpy(cip->buf, cdCwd, cip->bufSize);
415 if (cp[cip->bufSize - 1] != '\0')
416 return (kErrBadParameter);
417
418 mkd = (flags & kChdirAndMkdir);
419 pwd = (flags & kChdirAndGetCWD);
420
421 if ((cdCwd[0] == '\0') || (strcmp(cdCwd, ".") == 0)) {
422 result = 0;
423 if (flags == kChdirAndGetCWD)
424 result = FTPGetCWD(cip, newCwd, newCwdSize);
425 return (result);
426 }
427
428 lastSubDir = 0;
429 do {
430 startcp = cp;
431 cp = StrFindLocalPathDelim(cp);
432 if (cp != NULL) {
433 /* If this is the first slash in an absolute
434 * path, then startcp will be empty. We will
435 * use this below to treat this as the root
436 * directory.
437 */
438 *cp++ = '\0';
439 } else {
440 lastSubDir = 1;
441 }
442 if (strcmp(startcp, ".") == 0) {
443 result = 0;
444 if ((lastSubDir != 0) && (pwd != 0))
445 result = FTPGetCWD(cip, newCwd, newCwdSize);
446 } else if ((lastSubDir != 0) && (pwd != 0)) {
447 result = FTPChdirAndGetCWD(cip, (*startcp != '\0') ? startcp : "/", newCwd, newCwdSize);
448 } else {
449 result = FTPChdir(cip, (*startcp != '\0') ? startcp : "/");
450 }
451 if (result < 0) {
452 if ((mkd != 0) && (*startcp != '\0')) {
453 if (FTPCmd(cip, "MKD %s", startcp) == 2) {
454 result = FTPChdir(cip, startcp);
455 } else {
456 /* couldn't change nor create */
457 cip->errNo = result;
458 }
459 } else {
460 cip->errNo = result;
461 }
462 }
463 } while ((!lastSubDir) && (result == 0));
464
465 return (result);
466 } /* FTPChdir3 */
467
468
469
470
471 int
472 FTPMkdir2(const FTPCIPtr cip, const char *const newDir, const int recurse, const char *const curDir)
473 {
474 int result, result2;
475 char *cp, *newTreeStart, *cp2;
476 char dir[512];
477 char dir2[512];
478 char c;
479
480 if (cip == NULL)
481 return (kErrBadParameter);
482 if (strcmp(cip->magic, kLibraryMagic))
483 return (kErrBadMagic);
484
485 if ((newDir == NULL) || (newDir[0] == '\0')) {
486 cip->errNo = kErrInvalidDirParam;
487 return (kErrInvalidDirParam);
488 }
489
490 /* Preserve old working directory. */
491 if ((curDir == NULL) || (curDir[0] == '\0')) {
492 /* This hack is nice so you can eliminate an
493 * unnecessary "PWD" command on the server,
494 * since if you already knew what directory
495 * you're in. We want to minimize the number
496 * of client-server exchanges when feasible.
497 */
498 (void) FTPGetCWD(cip, cip->buf, cip->bufSize);
499 }
500
501 result = FTPChdir(cip, newDir);
502 if (result == kNoErr) {
503 /* Directory already exists -- but we
504 * must now change back to where we were.
505 */
506 result2 = FTPChdir(cip, ((curDir == NULL) || (curDir[0] == '\0')) ? cip->buf : curDir);
507 if (result2 < 0) {
508 result = kErrCannotGoToPrevDir;
509 cip->errNo = kErrCannotGoToPrevDir;
510 return (result);
511 }
512
513 /* Don't need to create it. */
514 return (kNoErr);
515 }
516
517 if (recurse == kRecursiveNo) {
518 result = FTPCmd(cip, "MKD %s", newDir);
519 if (result > 0) {
520 if (result != 2) {
521 Error(cip, kDontPerror, "MKD %s failed; [%s]\n", newDir, cip->lastFTPCmdResultStr);
522 result = kErrMKDFailed;
523 cip->errNo = kErrMKDFailed;
524 return (result);
525 } else {
526 result = kNoErr;
527 }
528 }
529 } else {
530 (void) STRNCPY(dir, newDir);
531
532 /* Strip trailing slashes. */
533 cp = dir + strlen(dir) - 1;
534 for (;;) {
535 if (cp <= dir) {
536 if ((newDir == NULL) || (newDir[0] == '\0')) {
537 cip->errNo = kErrInvalidDirParam;
538 result = kErrInvalidDirParam;
539 return (result);
540 }
541 }
542 if ((*cp != '/') && (*cp != '\\')) {
543 cp[1] = '\0';
544 break;
545 }
546 --cp;
547 }
548 (void) STRNCPY(dir2, dir);
549
550 if ((strrchr(dir, '/') == dir) || (strrchr(dir, '\\') == dir)) {
551 /* Special case "mkdir /subdir" */
552 result = FTPCmd(cip, "MKD %s", dir);
553 if (result < 0) {
554 return (result);
555 }
556 if (result != 2) {
557 Error(cip, kDontPerror, "MKD %s failed; [%s]\n", dir, cip->lastFTPCmdResultStr);
558 result = kErrMKDFailed;
559 cip->errNo = kErrMKDFailed;
560 return (result);
561 }
562 /* Haven't chdir'ed, don't need to goto goback. */
563 return (kNoErr);
564 }
565
566 for (;;) {
567 cp = strrchr(dir, '/');
568 if (cp == NULL)
569 cp = strrchr(dir, '\\');
570 if (cp == NULL) {
571 cp = dir + strlen(dir) - 1;
572 if (dir[0] == '\0') {
573 result = kErrMKDFailed;
574 cip->errNo = kErrMKDFailed;
575 return (result);
576 }
577 /* Note: below we will refer to cp + 1
578 * which is why we set cp to point to
579 * the byte before the array begins!
580 */
581 cp = dir - 1;
582 break;
583 }
584 if (cp == dir) {
585 result = kErrMKDFailed;
586 cip->errNo = kErrMKDFailed;
587 return (result);
588 }
589 *cp = '\0';
590 result = FTPChdir(cip, dir);
591 if (result == 0) {
592 break; /* Found a valid parent dir. */
593 /* from this point, we need to preserve old dir. */
594 }
595 }
596
597 newTreeStart = dir2 + ((cp + 1) - dir);
598 for (cp = newTreeStart; ; ) {
599 cp2 = cp;
600 cp = strchr(cp2, '/');
601 c = '/';
602 if (cp == NULL)
603 cp = strchr(cp2, '\\');
604 if (cp != NULL) {
605 c = *cp;
606 *cp = '\0';
607 if (cp[1] == '\0') {
608 /* Done, if they did "mkdir /tmp/dir/" */
609 break;
610 }
611 }
612 result = FTPCmd(cip, "MKD %s", newTreeStart);
613 if (result < 0) {
614 return (result);
615 }
616 if (result != 2) {
617 Error(cip, kDontPerror, "Cwd=%s; MKD %s failed; [%s]\n", cip->buf, newTreeStart, cip->lastFTPCmdResultStr);
618 result = kErrMKDFailed;
619 cip->errNo = kErrMKDFailed;
620 goto goback;
621 }
622 if (cp == NULL)
623 break; /* No more to make, done. */
624 *cp++ = c;
625 }
626 result = kNoErr;
627
628 goback:
629 result2 = FTPChdir(cip, ((curDir == NULL) || (curDir[0] == '\0')) ? cip->buf : curDir);
630 if ((result == 0) && (result2 < 0)) {
631 result = kErrCannotGoToPrevDir;
632 cip->errNo = kErrCannotGoToPrevDir;
633 }
634 }
635 return (result);
636 } /* FTPMkdir2 */
637
638
639
640 int
641 FTPMkdir(const FTPCIPtr cip, const char *const newDir, const int recurse)
642 {
643 return (FTPMkdir2(cip, newDir, recurse, NULL));
644 } /* FTPMkdir */
645
646
647
648 int
649 FTPFileModificationTime(const FTPCIPtr cip, const char *const file, time_t *const mdtm)
650 {
651 int result;
652 ResponsePtr rp;
653
654 if (cip == NULL)
655 return (kErrBadParameter);
656 if (strcmp(cip->magic, kLibraryMagic))
657 return (kErrBadMagic);
658
659 if ((mdtm == NULL) || (file == NULL))
660 return (kErrBadParameter);
661 *mdtm = kModTimeUnknown;
662
663 if (cip->hasMDTM == kCommandNotAvailable) {
664 cip->errNo = kErrMDTMNotAvailable;
665 result = kErrMDTMNotAvailable;
666 } else {
667 rp = InitResponse();
668 if (rp == NULL) {
669 result = kErrMallocFailed;
670 cip->errNo = kErrMallocFailed;
671 Error(cip, kDontPerror, "Malloc failed.\n");
672 } else {
673 result = RCmd(cip, rp, "MDTM %s", file);
674 if (result < 0) {
675 DoneWithResponse(cip, rp);
676 return (result);
677 } else if (strncmp(rp->msg.first->line, "19100", 5) == 0) {
678 Error(cip, kDontPerror, "Warning: Server has Y2K Bug in \"MDTM\" command.\n");
679 cip->errNo = kErrMDTMFailed;
680 result = kErrMDTMFailed;
681 } else if (result == 2) {
682 *mdtm = UnMDTMDate(rp->msg.first->line);
683 cip->hasMDTM = kCommandAvailable;
684 result = kNoErr;
685 } else if (UNIMPLEMENTED_CMD(rp->code)) {
686 cip->hasMDTM = kCommandNotAvailable;
687 cip->errNo = kErrMDTMNotAvailable;
688 result = kErrMDTMNotAvailable;
689 } else {
690 cip->errNo = kErrMDTMFailed;
691 result = kErrMDTMFailed;
692 }
693 DoneWithResponse(cip, rp);
694 }
695 }
696 return (result);
697 } /* FTPFileModificationTime */
698
699
700
701
702 int
703 FTPRename(const FTPCIPtr cip, const char *const oldname, const char *const newname)
704 {
705 int result;
706
707 if (cip == NULL)
708 return (kErrBadParameter);
709 if (strcmp(cip->magic, kLibraryMagic))
710 return (kErrBadMagic);
711 if ((oldname == NULL) || (oldname[0] == '\0'))
712 return (kErrBadParameter);
713 if ((newname == NULL) || (oldname[0] == '\0'))
714 return (kErrBadParameter);
715
716
717 result = FTPCmd(cip, "RNFR %s", oldname);
718 if (result < 0)
719 return (result);
720 if (result != 3) {
721 cip->errNo = kErrRenameFailed;
722 return (cip->errNo);
723 }
724
725 result = FTPCmd(cip, "RNTO %s", newname);
726 if (result < 0)
727 return (result);
728 if (result != 2) {
729 cip->errNo = kErrRenameFailed;
730 return (cip->errNo);
731 }
732 return (kNoErr);
733 } /* FTPRename */
734
735
736
737
738 int
739 FTPRemoteHelp(const FTPCIPtr cip, const char *const pattern, const LineListPtr llp)
740 {
741 int result;
742 ResponsePtr rp;
743
744 if ((cip == NULL) || (llp == NULL))
745 return (kErrBadParameter);
746 if (strcmp(cip->magic, kLibraryMagic))
747 return (kErrBadMagic);
748
749 InitLineList(llp);
750 rp = InitResponse();
751 if (rp == NULL) {
752 result = kErrMallocFailed;
753 cip->errNo = kErrMallocFailed;
754 Error(cip, kDontPerror, "Malloc failed.\n");
755 } else {
756 if ((pattern == NULL) || (*pattern == '\0'))
757 result = RCmd(cip, rp, "HELP");
758 else
759 result = RCmd(cip, rp, "HELP %s", pattern);
760 if (result < 0) {
761 DoneWithResponse(cip, rp);
762 return (result);
763 } else if (result == 2) {
764 if (CopyLineList(llp, &rp->msg) < 0) {
765 result = kErrMallocFailed;
766 cip->errNo = kErrMallocFailed;
767 Error(cip, kDontPerror, "Malloc failed.\n");
768 } else {
769 result = kNoErr;
770 }
771 } else {
772 cip->errNo = kErrHELPFailed;
773 result = kErrHELPFailed;
774 }
775 DoneWithResponse(cip, rp);
776 }
777 return (result);
778 } /* FTPRemoteHelp */
779
780
781
782
783 int
784 FTPRmdir(const FTPCIPtr cip, const char *const pattern, const int recurse, const int doGlob)
785 {
786 LineList fileList;
787 LinePtr filePtr;
788 char *file;
789 int onceResult, batchResult;
790
791 if (cip == NULL)
792 return (kErrBadParameter);
793 if (strcmp(cip->magic, kLibraryMagic))
794 return (kErrBadMagic);
795
796 batchResult = FTPRemoteGlob(cip, &fileList, pattern, doGlob);
797 if (batchResult != kNoErr)
798 return (batchResult);
799
800 for (batchResult = kNoErr, filePtr = fileList.first;
801 filePtr != NULL;
802 filePtr = filePtr->next)
803 {
804 file = filePtr->line;
805 if (file == NULL) {
806 batchResult = kErrBadLineList;
807 cip->errNo = kErrBadLineList;
808 break;
809 }
810 onceResult = FTPCmd(cip, "RMD %s", file);
811 if (onceResult < 0) {
812 batchResult = onceResult;
813 break;
814 }
815 if (onceResult != 2) {
816 if (recurse == kRecursiveYes) {
817 onceResult = FTPRmdirRecursive(cip, file);
818 if (onceResult < 0) {
819 batchResult = kErrRMDFailed;
820 cip->errNo = kErrRMDFailed;
821 }
822 } else {
823 batchResult = kErrRMDFailed;
824 cip->errNo = kErrRMDFailed;
825 }
826 }
827 }
828 DisposeLineListContents(&fileList);
829 return (batchResult);
830 } /* FTPRmdir */
831
832
833
834
835 int
836 FTPSetTransferType(const FTPCIPtr cip, int type)
837 {
838 int result;
839
840 if (cip == NULL)
841 return (kErrBadParameter);
842 if (strcmp(cip->magic, kLibraryMagic))
843 return (kErrBadMagic);
844
845 if (cip->curTransferType != type) {
846 switch (type) {
847 case kTypeAscii:
848 case kTypeBinary:
849 case kTypeEbcdic:
850 break;
851 case 'i':
852 case 'b':
853 case 'B':
854 type = kTypeBinary;
855 break;
856 case 'e':
857 type = kTypeEbcdic;
858 break;
859 case 'a':
860 type = kTypeAscii;
861 break;
862 default:
863 /* Yeah, we don't support Tenex. Who cares? */
864 Error(cip, kDontPerror, "Bad transfer type [%c].\n", type);
865 cip->errNo = kErrBadTransferType;
866 return (kErrBadTransferType);
867 }
868 result = FTPCmd(cip, "TYPE %c", type);
869 if (result != 2) {
870 result = kErrTYPEFailed;
871 cip->errNo = kErrTYPEFailed;
872 return (result);
873 }
874 cip->curTransferType = type;
875 }
876 return (kNoErr);
877 } /* FTPSetTransferType */
878
879
880
881
882 /* If the remote host supports the SIZE command, we can find out the exact
883 * size of a remote file, depending on the transfer type in use. SIZE
884 * returns different values for ascii and binary modes!
885 */
886 int
887 FTPFileSize(const FTPCIPtr cip, const char *const file, longest_int *const size, const int type)
888 {
889 int result;
890 ResponsePtr rp;
891
892 if (cip == NULL)
893 return (kErrBadParameter);
894 if (strcmp(cip->magic, kLibraryMagic))
895 return (kErrBadMagic);
896
897 if ((size == NULL) || (file == NULL))
898 return (kErrBadParameter);
899 *size = kSizeUnknown;
900
901 result = FTPSetTransferType(cip, type);
902 if (result < 0)
903 return (result);
904
905 if (cip->hasSIZE == kCommandNotAvailable) {
906 cip->errNo = kErrSIZENotAvailable;
907 result = kErrSIZENotAvailable;
908 } else {
909 rp = InitResponse();
910 if (rp == NULL) {
911 result = kErrMallocFailed;
912 cip->errNo = kErrMallocFailed;
913 Error(cip, kDontPerror, "Malloc failed.\n");
914 } else {
915 result = RCmd(cip, rp, "SIZE %s", file);
916 if (result < 0) {
917 DoneWithResponse(cip, rp);
918 return (result);
919 } else if (result == 2) {
920 #if defined(HAVE_LONG_LONG) && defined(SCANF_LONG_LONG)
921 (void) sscanf(rp->msg.first->line, SCANF_LONG_LONG, size);
922 #elif defined(HAVE_LONG_LONG) && defined(HAVE_STRTOQ)
923 *size = (longest_int) strtoq(rp->msg.first->line, NULL, 0);
924 #else
925 (void) sscanf(rp->msg.first->line, "%ld", size);
926 #endif
927 cip->hasSIZE = kCommandAvailable;
928 result = kNoErr;
929 } else if (UNIMPLEMENTED_CMD(rp->code)) {
930 cip->hasSIZE = kCommandNotAvailable;
931 cip->errNo = kErrSIZENotAvailable;
932 result = kErrSIZENotAvailable;
933 } else {
934 cip->errNo = kErrSIZEFailed;
935 result = kErrSIZEFailed;
936 }
937 DoneWithResponse(cip, rp);
938 }
939 }
940 return (result);
941 } /* FTPFileSize */
942
943
944
945
946 int
947 FTPMListOneFile(const FTPCIPtr cip, const char *const file, const MLstItemPtr mlip)
948 {
949 int result;
950 ResponsePtr rp;
951
952 /* We do a special check for older versions of NcFTPd which
953 * are based off of an incompatible previous version of IETF
954 * extensions.
955 *
956 * Roxen also seems to be way outdated, where MLST was on the
957 * data connection among other things.
958 *
959 */
960 if (
961 (cip->hasMLST == kCommandNotAvailable) ||
962 ((cip->serverType == kServerTypeNcFTPd) && (cip->ietfCompatLevel < 19981201)) ||
963 (cip->serverType == kServerTypeRoxen)
964 ) {
965 cip->errNo = kErrMLSTNotAvailable;
966 return (cip->errNo);
967 }
968
969 rp = InitResponse();
970 if (rp == NULL) {
971 result = cip->errNo = kErrMallocFailed;
972 Error(cip, kDontPerror, "Malloc failed.\n");
973 } else {
974 result = RCmd(cip, rp, "MLST %s", file);
975 if (
976 (result == 2) &&
977 (rp->msg.first->line != NULL) &&
978 (rp->msg.first->next != NULL) &&
979 (rp->msg.first->next->line != NULL)
980 ) {
981 result = UnMlsT(rp->msg.first->next->line, mlip);
982 if (result < 0) {
983 cip->errNo = result = kErrInvalidMLSTResponse;
984 }
985 } else if (UNIMPLEMENTED_CMD(rp->code)) {
986 cip->hasMLST = kCommandNotAvailable;
987 cip->errNo = kErrMLSTNotAvailable;
988 result = kErrMLSTNotAvailable;
989 } else {
990 cip->errNo = kErrMLSTFailed;
991 result = kErrMLSTFailed;
992 }
993 DoneWithResponse(cip, rp);
994 }
995
996 return (result);
997 } /* FTPMListOneFile */
998
999
1000
1001
1002 /* We only use STAT to see if files or directories exist.
1003 * But since it is so rarely used in the wild, we need to
1004 * make sure the server supports the use where we pass
1005 * a pathname as a parameter.
1006 */
1007 int
1008 FTPFileExistsStat(const FTPCIPtr cip, const char *const file)
1009 {
1010 int result;
1011 ResponsePtr rp;
1012 LineList fileList;
1013 char savedCwd[512];
1014
1015 if (cip == NULL)
1016 return (kErrBadParameter);
1017 if (strcmp(cip->magic, kLibraryMagic))
1018 return (kErrBadMagic);
1019
1020 if (file == NULL)
1021 return (kErrBadParameter);
1022
1023 if (cip->STATfileParamWorks == kCommandNotAvailable) {
1024 cip->errNo = result = kErrSTATwithFileNotAvailable;
1025 return (result);
1026 }
1027
1028 if (cip->STATfileParamWorks == kCommandAvailabilityUnknown) {
1029 rp = InitResponse();
1030 if (rp == NULL) {
1031 result = kErrMallocFailed;
1032 cip->errNo = kErrMallocFailed;
1033 Error(cip, kDontPerror, "Malloc failed.\n");
1034 return (result);
1035
1036 }
1037
1038 /* First, make sure that when we STAT a pathname
1039 * that does not exist, that we get an error back.
1040 *
1041 * We also assume that a valid STAT response has
1042 * at least 3 lines of response text, typically
1043 * a "start" line, intermediate data, and then
1044 * a trailing line.
1045 *
1046 * We also can see a one-line case.
1047 */
1048 result = RCmd(cip, rp, "STAT %s", "NoSuchFile");
1049 if ((result == 2) && ((rp->msg.nLines >= 3) || (rp->msg.nLines == 1))) {
1050 /* Hmmm.... it gave back a positive
1051 * response. So STAT <file> does not
1052 * work correctly.
1053 */
1054 if (
1055 (rp->msg.first->next != NULL) &&
1056 (rp->msg.first->next->line != NULL) &&
1057 (
1058 (strstr(rp->msg.first->next->line, "o such file") != NULL) ||
1059 (strstr(rp->msg.first->next->line, "ot found") != NULL)
1060 )
1061 ) {
1062 /* OK, while we didn't want a 200
1063 * level response, some servers,
1064 * like wu-ftpd print an error
1065 * message "No such file or
1066 * directory" which we can special
1067 * case.
1068 */
1069 result = kNoErr;
1070 } else {
1071 cip->STATfileParamWorks = kCommandNotAvailable;
1072 cip->errNo = result = kErrSTATwithFileNotAvailable;
1073 DoneWithResponse(cip, rp);
1074 return (result);
1075 }
1076 }
1077 DoneWithResponse(cip, rp);
1078
1079 /* We can't assume that we can simply say STAT rootdir/firstfile,
1080 * since the remote host may not be using / as a directory
1081 * delimiter. So we have to change to the root directory
1082 * and then do the STAT on that file.
1083 */
1084 if (
1085 (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != kNoErr) ||
1086 (FTPChdir(cip, cip->startingWorkingDirectory) != kNoErr)
1087 ) {
1088 return (cip->errNo);
1089 }
1090
1091 /* OK, we get an error when we stat
1092 * a non-existant file, but now we need to
1093 * see if we get a positive reply when
1094 * we stat a file that does exist.
1095 *
1096 * To do this, we list the root directory,
1097 * which we assume has one or more items.
1098 * If it doesn't, the user can't do anything
1099 * anyway. Then we stat the first item
1100 * we found to see if STAT says it exists.
1101 */
1102 if (
1103 ((result = FTPListToMemory2(cip, "", &fileList, "", 0, (int *) 0)) < 0) ||
1104 (fileList.last == NULL) ||
1105 (fileList.last->line == NULL)
1106 ) {
1107 /* Hmmm... well, in any case we can't use STAT. */
1108 cip->STATfileParamWorks = kCommandNotAvailable;
1109 cip->errNo = result = kErrSTATwithFileNotAvailable;
1110 DisposeLineListContents(&fileList);
1111 (void) FTPChdir(cip, savedCwd);
1112 return (result);
1113 }
1114
1115 rp = InitResponse();
1116 if (rp == NULL) {
1117 result = kErrMallocFailed;
1118 cip->errNo = kErrMallocFailed;
1119 Error(cip, kDontPerror, "Malloc failed.\n");
1120 DisposeLineListContents(&fileList);
1121 (void) FTPChdir(cip, savedCwd);
1122 return (result);
1123
1124 }
1125
1126 result = RCmd(cip, rp, "STAT %s", fileList.last->line);
1127 DisposeLineListContents(&fileList);
1128
1129 if ((result != 2) || (rp->msg.nLines == 2)) {
1130 /* Hmmm.... it gave back a negative
1131 * response. So STAT <file> does not
1132 * work correctly.
1133 */
1134 cip->STATfileParamWorks = kCommandNotAvailable;
1135 cip->errNo = result = kErrSTATwithFileNotAvailable;
1136 DoneWithResponse(cip, rp);
1137 (void) FTPChdir(cip, savedCwd);
1138 return (result);
1139 } else if (
1140 (rp->msg.first->next != NULL) &&
1141 (rp->msg.first->next->line != NULL) &&
1142 (
1143 (strstr(rp->msg.first->next->line, "o such file") != NULL) ||
1144 (strstr(rp->msg.first->next->line, "ot found") != NULL)
1145 )
1146 ) {
1147 /* Same special-case of the second line of STAT response. */
1148 cip->STATfileParamWorks = kCommandNotAvailable;
1149 cip->errNo = result = kErrSTATwithFileNotAvailable;
1150 DoneWithResponse(cip, rp);
1151 (void) FTPChdir(cip, savedCwd);
1152 return (result);
1153 }
1154 DoneWithResponse(cip, rp);
1155 cip->STATfileParamWorks = kCommandAvailable;
1156
1157 /* Don't forget to change back to the original directory. */
1158 (void) FTPChdir(cip, savedCwd);
1159 }
1160
1161 rp = InitResponse();
1162 if (rp == NULL) {
1163 result = kErrMallocFailed;
1164 cip->errNo = kErrMallocFailed;
1165 Error(cip, kDontPerror, "Malloc failed.\n");
1166 return (result);
1167 }
1168
1169 result = RCmd(cip, rp, "STAT %s", file);
1170 if (result == 2) {
1171 result = kNoErr;
1172 if (((rp->msg.nLines >= 3) || (rp->msg.nLines == 1))) {
1173 if (
1174 (rp->msg.first->next != NULL) &&
1175 (rp->msg.first->next->line != NULL) &&
1176 (
1177 (strstr(rp->msg.first->next->line, "o such file") != NULL) ||
1178 (strstr(rp->msg.first->next->line, "ot found") != NULL)
1179 )
1180 ) {
1181 cip->errNo = kErrSTATFailed;
1182 result = kErrSTATFailed;
1183 } else {
1184 result = kNoErr;
1185 }
1186 } else if (rp->msg.nLines == 2) {
1187 cip->errNo = kErrSTATFailed;
1188 result = kErrSTATFailed;
1189 } else {
1190 result = kNoErr;
1191 }
1192 } else {
1193 cip->errNo = kErrSTATFailed;
1194 result = kErrSTATFailed;
1195 }
1196 DoneWithResponse(cip, rp);
1197 return (result);
1198 } /* FTPFileExistsStat */
1199
1200
1201
1202
1203 /* We only use STAT to see if files or directories exist.
1204 * But since it is so rarely used in the wild, we need to
1205 * make sure the server supports the use where we pass
1206 * a pathname as a parameter.
1207 */
1208 int
1209 FTPFileExistsNlst(const FTPCIPtr cip, const char *const file)
1210 {
1211 int result;
1212 LineList fileList, rootFileList;
1213 char savedCwd[512];
1214
1215 if (cip == NULL)
1216 return (kErrBadParameter);
1217 if (strcmp(cip->magic, kLibraryMagic))
1218 return (kErrBadMagic);
1219
1220 if (file == NULL)
1221 return (kErrBadParameter);
1222
1223 if (cip->NLSTfileParamWorks == kCommandNotAvailable) {
1224 cip->errNo = result = kErrNLSTwithFileNotAvailable;
1225 return (result);
1226 }
1227
1228 if (cip->NLSTfileParamWorks == kCommandAvailabilityUnknown) {
1229 /* First, make sure that when we NLST a pathname
1230 * that does not exist, that we get an error back.
1231 *
1232 * We also assume that a valid NLST response has
1233 * at least 3 lines of response text, typically
1234 * a "start" line, intermediate data, and then
1235 * a trailing line.
1236 *
1237 * We also can see a one-line case.
1238 */
1239 if (
1240 ((FTPListToMemory2(cip, "NoSuchFile", &fileList, "", 0, (int *) 0)) == kNoErr) &&
1241 (fileList.nLines >= 1) &&
1242 (strstr(fileList.last->line, "o such file") == NULL) &&
1243 (strstr(fileList.last->line, "ot found") == NULL) &&
1244 (strstr(fileList.last->line, "o Such File") == NULL) &&
1245 (strstr(fileList.last->line, "ot Found") == NULL)
1246
1247 ) {
1248 cip->NLSTfileParamWorks = kCommandNotAvailable;
1249 cip->errNo = result = kErrNLSTwithFileNotAvailable;
1250 DisposeLineListContents(&fileList);
1251 return (result);
1252 }
1253 DisposeLineListContents(&fileList);
1254
1255 /* We can't assume that we can simply say NLST rootdir/firstfile,
1256 * since the remote host may not be using / as a directory
1257 * delimiter. So we have to change to the root directory
1258 * and then do the NLST on that file.
1259 */
1260 if (
1261 (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != kNoErr) ||
1262 (FTPChdir(cip, cip->startingWorkingDirectory) != kNoErr)
1263 ) {
1264 return (cip->errNo);
1265 }
1266
1267 /* OK, we get an error when we list
1268 * a non-existant file, but now we need to
1269 * see if we get a positive reply when
1270 * we stat a file that does exist.
1271 *
1272 * To do this, we list the root directory,
1273 * which we assume has one or more items.
1274 * If it doesn't, the user can't do anything
1275 * anyway. Then we do the first item
1276 * we found to see if NLST says it exists.
1277 */
1278 if (
1279 ((result = FTPListToMemory2(cip, "", &rootFileList, "", 0, (int *) 0)) < 0) ||
1280 (rootFileList.last == NULL) ||
1281 (rootFileList.last->line == NULL)
1282 ) {
1283 /* Hmmm... well, in any case we can't use NLST. */
1284 cip->NLSTfileParamWorks = kCommandNotAvailable;
1285 cip->errNo = result = kErrNLSTwithFileNotAvailable;
1286 DisposeLineListContents(&rootFileList);
1287 (void) FTPChdir(cip, savedCwd);
1288 return (result);
1289 }
1290
1291 if (
1292 ((FTPListToMemory2(cip, rootFileList.last->line, &fileList, "", 0, (int *) 0)) == kNoErr) &&
1293 (fileList.nLines >= 1) &&
1294 (strstr(fileList.last->line, "o such file") == NULL) &&
1295 (strstr(fileList.last->line, "ot found") == NULL) &&
1296 (strstr(fileList.last->line, "o Such File") == NULL) &&
1297 (strstr(fileList.last->line, "ot Found") == NULL)
1298
1299 ) {
1300 /* Good. We listed the item. */
1301 DisposeLineListContents(&fileList);
1302 DisposeLineListContents(&rootFileList);
1303 cip->NLSTfileParamWorks = kCommandAvailable;
1304
1305 /* Don't forget to change back to the original directory. */
1306 (void) FTPChdir(cip, savedCwd);
1307 } else {
1308 cip->NLSTfileParamWorks = kCommandNotAvailable;
1309 cip->errNo = result = kErrNLSTwithFileNotAvailable;
1310 DisposeLineListContents(&fileList);
1311 DisposeLineListContents(&rootFileList);
1312 (void) FTPChdir(cip, savedCwd);
1313 return (result);
1314 }
1315 }
1316
1317 /* Check the requested item. */
1318 InitLineList(&fileList);
1319 if (
1320 ((FTPListToMemory2(cip, file, &fileList, "", 0, (int *) 0)) == kNoErr) &&
1321 (fileList.nLines >= 1) &&
1322 (strstr(fileList.last->line, "o such file") == NULL) &&
1323 (strstr(fileList.last->line, "ot found") == NULL) &&
1324 (strstr(fileList.last->line, "o Such File") == NULL) &&
1325 (strstr(fileList.last->line, "ot Found") == NULL)
1326
1327 ) {
1328 /* The item existed. */
1329 result = kNoErr;
1330 } else {
1331 cip->errNo = kErrNLSTFailed;
1332 result = kErrNLSTFailed;
1333 }
1334
1335 DisposeLineListContents(&fileList);
1336 return (result);
1337 } /* FTPFileExistsNlst*/
1338
1339
1340
1341
1342 /* This functions goes to a great deal of trouble to try and determine if the
1343 * remote file specified exists. Newer servers support commands that make
1344 * it relatively inexpensive to find the answer, but older servers do not
1345 * provide a standard way. This means we may try a whole bunch of things,
1346 * but the good news is that the library saves information about which things
1347 * worked so if you do this again it uses the methods that work.
1348 */
1349 int
1350 FTPFileExists2(const FTPCIPtr cip, const char *const file, const int tryMDTM, const int trySIZE, const int tryMLST, const int trySTAT, const int tryNLST)
1351 {
1352 int result;
1353 time_t mdtm;
1354 longest_int size;
1355 MLstItem mlsInfo;
1356
1357 if (tryMDTM != 0) {
1358 result = FTPFileModificationTime(cip, file, &mdtm);
1359 if (result == kNoErr)
1360 return (kNoErr);
1361 if (result == kErrMDTMFailed) {
1362 cip->errNo = kErrNoSuchFileOrDirectory;
1363 return (kErrNoSuchFileOrDirectory);
1364 }
1365 /* else keep going */
1366 }
1367
1368 if (trySIZE != 0) {
1369 result = FTPFileSize(cip, file, &size, kTypeBinary);
1370 if (result == kNoErr)
1371 return (kNoErr);
1372 /* SIZE could fail if the server does
1373 * not support it for directories.
1374 *
1375 * if (result == kErrSIZEFailed)
1376 * return (kErrNoSuchFileOrDirectory);
1377 */
1378 /* else keep going */
1379 }
1380
1381
1382 if (tryMLST != 0) {
1383 result = FTPMListOneFile(cip, file, &mlsInfo);
1384 if (result == kNoErr)
1385 return (kNoErr);
1386 if (result == kErrMLSTFailed) {
1387 cip->errNo = kErrNoSuchFileOrDirectory;
1388 return (kErrNoSuchFileOrDirectory);
1389 }
1390 /* else keep going */
1391 }
1392
1393 if (trySTAT != 0) {
1394 result = FTPFileExistsStat(cip, file);
1395 if (result == kNoErr)
1396 return (kNoErr);
1397 if (result == kErrSTATFailed) {
1398 cip->errNo = kErrNoSuchFileOrDirectory;
1399 return (kErrNoSuchFileOrDirectory);
1400 }
1401 /* else keep going */
1402 }
1403
1404 if (tryNLST != 0) {
1405 result = FTPFileExistsNlst(cip, file);
1406 if (result == kNoErr)
1407 return (kNoErr);
1408 if (result == kErrNLSTFailed) {
1409 cip->errNo = kErrNoSuchFileOrDirectory;
1410 return (kErrNoSuchFileOrDirectory);
1411 }
1412 /* else keep going */
1413 }
1414
1415 cip->errNo = kErrCantTellIfFileExists;
1416 return (kErrCantTellIfFileExists);
1417 } /* FTPFileExists2 */
1418
1419
1420
1421
1422 int
1423 FTPFileExists(const FTPCIPtr cip, const char *const file)
1424 {
1425 return (FTPFileExists2(cip, file, 1, 1, 1, 1, 1));
1426 } /* FTPFileExists */
1427
1428
1429
1430
1431
1432 int
1433 FTPFileSizeAndModificationTime(const FTPCIPtr cip, const char *const file, longest_int *const size, const int type, time_t *const mdtm)
1434 {
1435 MLstItem mlsInfo;
1436 int result;
1437
1438 if (cip == NULL)
1439 return (kErrBadParameter);
1440 if (strcmp(cip->magic, kLibraryMagic))
1441 return (kErrBadMagic);
1442
1443 if ((mdtm == NULL) || (size == NULL) || (file == NULL))
1444 return (kErrBadParameter);
1445
1446 *mdtm = kModTimeUnknown;
1447 *size = kSizeUnknown;
1448
1449 result = FTPSetTransferType(cip, type);
1450 if (result < 0)
1451 return (result);
1452
1453 result = FTPMListOneFile(cip, file, &mlsInfo);
1454 if (result < 0) {
1455 /* Do it the regular way, where
1456 * we do a SIZE and then a MDTM.
1457 */
1458 result = FTPFileSize(cip, file, size, type);
1459 if (result < 0)
1460 return (result);
1461 result = FTPFileModificationTime(cip, file, mdtm);
1462 return (result);
1463 } else {
1464 *mdtm = mlsInfo.ftime;
1465 *size = mlsInfo.fsize;
1466 }
1467
1468 return (result);
1469 } /* FTPFileSizeAndModificationTime */
1470
1471
1472
1473
1474 int
1475 FTPFileType(const FTPCIPtr cip, const char *const file, int *const ftype)
1476 {
1477 int result;
1478 MLstItem mlsInfo;
1479
1480 if (cip == NULL)
1481 return (kErrBadParameter);
1482 if (strcmp(cip->magic, kLibraryMagic))
1483 return (kErrBadMagic);
1484
1485 if ((file == NULL) || (file[0] == '\0')) {
1486 cip->errNo = kErrBadParameter;
1487 return (kErrBadParameter);
1488 }
1489
1490 if (ftype == NULL) {
1491 cip->errNo = kErrBadParameter;
1492 return (kErrBadParameter);
1493 }
1494
1495 *ftype = 0;
1496 result = FTPMListOneFile(cip, file, &mlsInfo);
1497 if (result == kNoErr) {
1498 *ftype = mlsInfo.ftype;
1499 return (kNoErr);
1500 }
1501
1502 /* Preserve old working directory. */
1503 (void) FTPGetCWD(cip, cip->buf, cip->bufSize);
1504
1505 result = FTPChdir(cip, file);
1506 if (result == kNoErr) {
1507 *ftype = 'd';
1508 /* Yes it was a directory, now go back to
1509 * where we were.
1510 */
1511 (void) FTPChdir(cip, cip->buf);
1512
1513 /* Note: This improperly assumes that we
1514 * will be able to chdir back, which is
1515 * not guaranteed.
1516 */
1517 return (kNoErr);
1518 }
1519
1520 result = FTPFileExists2(cip, file, 1, 1, 0, 1, 1);
1521 if (result != kErrNoSuchFileOrDirectory)
1522 result = kErrFileExistsButCannotDetermineType;
1523
1524 return (result);
1525 } /* FTPFileType */
1526
1527
1528
1529
1530 int
1531 FTPIsDir(const FTPCIPtr cip, const char *const dir)
1532 {
1533 int result, ftype;
1534
1535 if (cip == NULL)
1536 return (kErrBadParameter);
1537 if (strcmp(cip->magic, kLibraryMagic))
1538 return (kErrBadMagic);
1539
1540 if ((dir == NULL) || (dir[0] == '\0')) {
1541 cip->errNo = kErrInvalidDirParam;
1542 return (kErrInvalidDirParam);
1543 }
1544
1545 result = FTPFileType(cip, dir, &ftype);
1546 if ((result == kNoErr) || (result == kErrFileExistsButCannotDetermineType)) {
1547 result = 0;
1548 if (ftype == 'd')
1549 result = 1;
1550 }
1551 return (result);
1552 } /* FTPIsDir */
1553
1554
1555
1556
1557 int
1558 FTPIsRegularFile(const FTPCIPtr cip, const char *const file)
1559 {
1560 int result, ftype;
1561
1562 if (cip == NULL)
1563 return (kErrBadParameter);
1564 if (strcmp(cip->magic, kLibraryMagic))
1565 return (kErrBadMagic);
1566
1567 if ((file == NULL) || (file[0] == '\0')) {
1568 cip->errNo = kErrBadParameter;
1569 return (kErrBadParameter);
1570 }
1571
1572 result = FTPFileType(cip, file, &ftype);
1573 if ((result == kNoErr) || (result == kErrFileExistsButCannotDetermineType)) {
1574 result = 1;
1575 if (ftype == 'd')
1576 result = 0;
1577 }
1578 return (result);
1579 } /* FTPIsRegularFile */
1580
1581
1582
1583
1584 int
1585 FTPSymlink(const FTPCIPtr cip, const char *const lfrom, const char *const lto)
1586 {
1587 if (strcmp(cip->magic, kLibraryMagic))
1588 return (kErrBadMagic);
1589 if ((cip == NULL) || (lfrom == NULL) || (lto == NULL))
1590 return (kErrBadParameter);
1591 if ((lfrom[0] == '\0') || (lto[0] == '\0'))
1592 return (kErrBadParameter);
1593 if (FTPCmd(cip, "SITE SYMLINK %s %s", lfrom, lto) == 2)
1594 return (kNoErr);
1595 return (kErrSYMLINKFailed);
1596 } /* FTPSymlink */
1597
1598
1599
1600
1601 int
1602 FTPUmask(const FTPCIPtr cip, const char *const umsk)
1603 {
1604 if (cip == NULL)
1605 return (kErrBadParameter);
1606 if (strcmp(cip->magic, kLibraryMagic))
1607 return (kErrBadMagic);
1608 if ((umsk == NULL) || (umsk[0] == '\0'))
1609 return (kErrBadParameter);
1610 if (FTPCmd(cip, "SITE UMASK %s", umsk) == 2)
1611 return (kNoErr);
1612 return (kErrUmaskFailed);
1613 } /* FTPUmask */
1614
1615
1616
1617
1618 static void
1619 GmTimeStr(char *const dst, const size_t dstsize, time_t t)
1620 {
1621 char buf[64];
1622 struct tm *gtp;
1623
1624 gtp = gmtime(&t);
1625 if (gtp == NULL) {
1626 dst[0] = '\0';
1627 } else {
1628 #ifdef HAVE_SNPRINTF
1629 buf[sizeof(buf) - 1] = '\0';
1630 (void) snprintf(buf, sizeof(buf) - 1, "%04d%02d%02d%02d%02d%02d",
1631 #else
1632 (void) sprintf(buf, "%04d%02d%02d%02d%02d%02d",
1633 #endif
1634 gtp->tm_year + 1900,
1635 gtp->tm_mon + 1,
1636 gtp->tm_mday,
1637 gtp->tm_hour,
1638 gtp->tm_min,
1639 gtp->tm_sec
1640 );
1641 (void) Strncpy(dst, buf, dstsize);
1642 }
1643 } /* GmTimeStr */
1644
1645
1646
1647
1648 int
1649 FTPUtime(const FTPCIPtr cip, const char *const file, time_t actime, time_t modtime, time_t crtime)
1650 {
1651 char mstr[64], astr[64], cstr[64];
1652 int result;
1653 ResponsePtr rp;
1654
1655 if (cip == NULL)
1656 return (kErrBadParameter);
1657 if (strcmp(cip->magic, kLibraryMagic))
1658 return (kErrBadMagic);
1659
1660 if (cip->hasUTIME == kCommandNotAvailable) {
1661 cip->errNo = kErrUTIMENotAvailable;
1662 result = kErrUTIMENotAvailable;
1663 } else {
1664 if ((actime == (time_t) 0) || (actime == (time_t) -1))
1665 (void) time(&actime);
1666 if ((modtime == (time_t) 0) || (modtime == (time_t) -1))
1667 (void) time(&modtime);
1668 if ((crtime == (time_t) 0) || (crtime == (time_t) -1))
1669 crtime = modtime;
1670
1671 (void) GmTimeStr(astr, sizeof(astr), actime);
1672 (void) GmTimeStr(mstr, sizeof(mstr), modtime);
1673 (void) GmTimeStr(cstr, sizeof(cstr), crtime);
1674
1675 rp = InitResponse();
1676 if (rp == NULL) {
1677 result = kErrMallocFailed;
1678 cip->errNo = kErrMallocFailed;
1679 Error(cip, kDontPerror, "Malloc failed.\n");
1680 } else {
1681 result = RCmd(cip, rp, "SITE UTIME %s %s %s %s UTC", file, astr, mstr, cstr);
1682 if (result < 0) {
1683 DoneWithResponse(cip, rp);
1684 return (result);
1685 } else if (result == 2) {
1686 cip->hasUTIME = kCommandAvailable;
1687 result = kNoErr;
1688 } else if (UNIMPLEMENTED_CMD(rp->code)) {
1689 cip->hasUTIME = kCommandNotAvailable;
1690 cip->errNo = kErrUTIMENotAvailable;
1691 result = kErrUTIMENotAvailable;
1692 } else {
1693 cip->errNo = kErrUTIMEFailed;
1694 result = kErrUTIMEFailed;
1695 }
1696 DoneWithResponse(cip, rp);
1697 }
1698 }
1699 return (result);
1700 } /* FTPUtime */