/* May need this later. */ static void CheckForLS_d(FTPCIPtr cip) { FTPLineList lines; char *cp; if (cip->hasNLST_d == kCommandAvailabilityUnknown) { if (FTPListToMemory2(cip, ".", &lines, "-d ", 0, (int *) 0) == kNoErr) { if ((lines.first != NULL) && (lines.first == lines.last)) { /* If we have only one item in the list, see if it really was * an error message we would recognize. */ cp = strchr(lines.first->line, ':'); if ((cp != NULL) && STREQ(cp, ": No such file or directory")) { cip->hasNLST_d = kCommandNotAvailable; } else { cip->hasNLST_d = kCommandAvailable; } } else { cip->hasNLST_d = kCommandNotAvailable; } } else { cip->hasNLST_d = kCommandNotAvailable; } DisposeLineListContents(&lines); } } /* CheckForLS_d */
/* This mess is essentially the local version of Ftw with FTP * grafted onto it, so see that and grok that before studying this. */ static int FTPFtwTraverse(const FtwInfoPtr ftwip, size_t dirPathLen, int depth) { char *cp; size_t fnLen; mode_t m; char *filename; char *newBuf; char *path = ftwip->curPath; int nSubdirs; FtwSubDirListPtr head = NULL, tail = NULL, sdp, nextsdp; int rc = (-1); int lsl, mls, unlsrc; FTPCIPtr cip = (FTPCIPtr) ftwip->cip; FTPLineList ll; FTPFileInfoList fil; FTPLinePtr filePtr; FTPFileInfoPtr fip; int result; int isRootDir; longest_int sz; isRootDir = ((dirPathLen == 1) && ((path[0] == '/') || (path[0] == '\\'))) ? 1 : 0; filePtr = NULL; fip = NULL; mls = 0; lsl = 0; if (cip->hasMLSD == kCommandAvailable) { mls = 1; if (((result = FTPListToMemory2(cip, dirPathLen ? path : ".", &ll, "-a", 0, &mls)) < 0) || (ll.first == NULL)) { /* Not an error unless the first directory could not be opened. */ DisposeLineListContents(&ll); return (0); } /* "MLSD" succeeded */ unlsrc = UnMlsD(cip, &fil, &ll); if (unlsrc < 0) { DisposeLineListContents(&ll); return (cip->errNo = kErrInvalidMLSTResponse); } else if (unlsrc == 0) { /* empty */ DisposeLineListContents(&ll); return (0); } fip = fil.first; DisposeLineListContents(&ll); } else { if (((result = FTPListToMemory2(cip, dirPathLen ? path : ".", &ll, "-la", 0, &mls)) < 0) || (ll.first == NULL)) { DisposeLineListContents(&ll); if (((result = FTPListToMemory2(cip, dirPathLen ? path : ".", &ll, (cip->hasNLST_a == kCommandNotAvailable) ? "" : "-a", 0, &mls)) < 0) || (ll.first == NULL)) { DisposeLineListContents(&ll); return (0); } else { /* "NLST -a" succeeded */ RemoteGlobCollapse(cip, path, &ll); filePtr = ll.first; } } else { /* "LIST -a" succeeded */ lsl = 1; unlsrc = UnLslR(cip, &fil, &ll, cip->serverType); if (unlsrc < 0) { DisposeLineListContents(&ll); return (cip->errNo = kErrInvalidMLSTResponse); } else if (unlsrc == 0) { /* empty */ DisposeLineListContents(&ll); return (0); } fip = fil.first; DisposeLineListContents(&ll); } } nSubdirs = 0; ++ftwip->numDirs; ftwip->depth = depth; if (ftwip->maxDepth < ftwip->depth) { ftwip->maxDepth = ftwip->depth; } filename = path + dirPathLen; if (isRootDir == 0) { /* Root directory is a separator. */ *filename++ = (char) ftwip->dirSeparator; dirPathLen++; } *filename = '\0'; /* Path now contains dir/ */ for (;;) { if ((mls != 0) || (lsl != 0)) { if (fip == NULL) break; cp = fip->relname; } else { if (filePtr == NULL) break; cp = filePtr->line; } if ((cp[0] == '.') && ((cp[1] == '\0') || ((cp[1] == '.') && (cp[2] == '\0')))) goto nxt; /* Skip . and .. */ ftwip->rlinkto = NULL; *filename = '\0'; fnLen = strlen(cp) + 1 /* include \0 */; if ((fnLen + dirPathLen) > ftwip->curPathAllocSize) { if (ftwip->autoGrow == kFtwNoAutoGrowAndFail) { goto panic; } else if (ftwip->autoGrow == kFtwNoAutoGrowButContinue) { goto nxt; } newBuf = (char *) realloc(ftwip->curPath, fnLen + dirPathLen + 30 + 2 /* room for / and \0 */); if (newBuf == NULL) goto panic; ftwip->curPath = newBuf; ftwip->curPathAllocSize = fnLen + dirPathLen + 30; path = ftwip->curPath; filename = path + dirPathLen; if (isRootDir == 0) /* Root directory is a separator. */ *filename++ = (char) ftwip->dirSeparator; *filename = '\0'; } memcpy(filename, cp, fnLen); ftwip->curPathLen = dirPathLen + fnLen - 1; ftwip->curFile = filename; ftwip->curFileLen = fnLen - 1; memset(&ftwip->curStat, 0, sizeof(ftwip->curStat)); if (mls != 0) { ftwip->curType = fip->type; if (fip->type == 'd') { ftwip->curStat.st_mode = S_IFDIR; ftwip->curStat.st_size = (longest_int) -1; #ifdef S_IFLNK } else if (fip->type == 'l') { ftwip->curStat.st_mode = S_IFLNK; ftwip->rlinkto = fip->rlinkto; #endif } else if (fip->type == '-') { ftwip->curStat.st_mode = S_IFREG; ftwip->curStat.st_size = fip->size; } else { /* unknown type, skip */ goto nxt; } if (fip->mode != (-1)) ftwip->curStat.st_mode |= (fip->mode & 00777); ftwip->curStat.st_mtime = fip->mdtm; } else if (lsl != 0) { ftwip->curType = fip->type; if (fip->type == 'd') { ftwip->curStat.st_mode = S_IFDIR; ftwip->curStat.st_size = (longest_int) -1; #ifdef S_IFLNK } else if (fip->type == 'l') { ftwip->curStat.st_mode = S_IFLNK; ftwip->rlinkto = fip->rlinkto; #endif } else if (fip->type == '-') { ftwip->curStat.st_mode = S_IFREG; ftwip->curStat.st_size = fip->size; } else { /* unknown type, skip */ goto nxt; } if (fip->mode != (-1)) ftwip->curStat.st_mode |= (fip->mode & 00777); ftwip->curStat.st_mtime = fip->mdtm; /* Override local times in LS output! */ result = FTPFileModificationTime(cip, path, &fip->mdtm); if (fip->mdtm != kModTimeUnknown) { ftwip->curStat.st_mtime = fip->mdtm; } } else { result = FTPIsDir(cip, path); if (result < 0) { /* error */ /* could be just a stat error, so continue */ goto nxt; } else if (result == 1) { /* directory */ ftwip->curType = 'd'; ftwip->curStat.st_mode = S_IFDIR | 00755; result = FTPFileModificationTime(cip, path, &ftwip->curStat.st_mtime); } else { /* file */ ftwip->curType = '-'; ftwip->curStat.st_mode = S_IFREG | 00644; result = FTPFileSizeAndModificationTime(cip, path, &sz, kTypeBinary, &ftwip->curStat.st_mtime); #if defined(TRU64UNIX) || defined(DIGITAL_UNIX) ftwip->curStat.st_size = (off_t) sz; #else ftwip->curStat.st_size = sz; #endif } } { m = ftwip->curStat.st_mode; if (S_ISREG(m)) { ++ftwip->numFiles; ftwip->curType = '-'; if ((*ftwip->proc)(ftwip) < 0) { goto panic; } } else if (S_ISLNK(m)) { ++ftwip->numLinks; ftwip->curType = 'l'; if ((*ftwip->proc)(ftwip) < 0) { goto panic; } } else if (S_ISDIR(m)) { /* We delay entering the subdirectories * until we have closed this directory. * This will conserve file descriptors * and also have the effect of having * the files processed first. */ sdp = (FtwSubDirListPtr) malloc(sizeof(FtwSubDirList) + fnLen); if (sdp == NULL) goto panic; memcpy(&sdp->st, &ftwip->curStat, sizeof(sdp->st)); memcpy(sdp->name, cp, fnLen); sdp->fnLen = fnLen; sdp->next = NULL; if (head == NULL) { head = tail = sdp; } else { tail->next = sdp; tail = sdp; } nSubdirs++; } } nxt: if ((mls != 0) || (lsl != 0)) { fip = fip->next; } else { filePtr = filePtr->next; } } if ((mls != 0) || (lsl != 0)) { DisposeFileInfoListContents(&fil); } else { DisposeLineListContents(&ll); } /* Now enter each subdirectory. */ for (sdp = head; sdp != NULL; sdp = nextsdp) { nextsdp = sdp->next; memcpy(&ftwip->curStat, &sdp->st, sizeof(ftwip->curStat)); fnLen = sdp->fnLen; memcpy(filename, sdp->name, fnLen); ftwip->curFile = filename; ftwip->curFileLen = fnLen - 1; ftwip->curPathLen = dirPathLen + fnLen - 1; head = nextsdp; free(sdp); ftwip->curType = 'd'; if ((*ftwip->proc)(ftwip) < 0) { goto panic; } if (FTPFtwTraverse(ftwip, dirPathLen + fnLen - 1, depth + 1) < 0) goto panic; /* Reset these, since buffer could have * been reallocated. */ path = ftwip->curPath; filename = path + dirPathLen; *filename = '\0'; } head = NULL; rc = 0; panic: if (mls != 0) { DisposeFileInfoListContents(&fil); } else { DisposeLineListContents(&ll); } for (sdp = head; sdp != NULL; sdp = nextsdp) { nextsdp = sdp->next; free(sdp); } return (rc); } /* FTPFtwTraverse */
int FTPRemoteGlob(FTPCIPtr cip, FTPLineListPtr fileList, const char *pattern, int doGlob) { char *cp; const char *lsflags; FTPLinePtr lp; int result; if (cip == NULL) return (kErrBadParameter); if (strcmp(cip->magic, kLibraryMagic)) return (kErrBadMagic); if (fileList == NULL) return (kErrBadParameter); InitLineList(fileList); if ((pattern == NULL) || (pattern[0] == '\0')) return (kErrBadParameter); /* Note that we do attempt to use glob characters even if the remote * host isn't UNIX. Most non-UNIX remote FTP servers look for UNIX * style wildcards. */ if ((doGlob == 1) && (GLOBCHARSINSTR(pattern))) { /* Use NLST, which lists files one per line. */ lsflags = ""; /* Optimize for "NLST *" case which is same as "NLST". */ if (strcmp(pattern, "*") == 0) { pattern = ""; lsflags = (cip->hasNLST_a == kCommandNotAvailable) ? "" : "-a"; } else if (strcmp(pattern, "**") == 0) { /* Hack; Lets you try "NLST -a" if you're daring. */ /* Need to use "NLST -a" whenever possible, * because wu-ftpd doesn't do NLST right, IMHO. * (It doesn't include directories in the NLST * if you do "NLST /the/dir" without -a.) */ pattern = ""; lsflags = (cip->hasNLST_a == kCommandNotAvailable) ? "" : "-a"; } if ((result = FTPListToMemory2(cip, pattern, fileList, lsflags, 0, (int *) 0)) < 0) { if (*lsflags == '\0') return (result); if (strchr(lsflags, 'a') != NULL) { /* Try again, without "-a" */ cip->hasNLST_a = kCommandNotAvailable; lsflags = ""; if ((result = FTPListToMemory2(cip, pattern, fileList, lsflags, 0, (int *) 0)) < 0) { return (result); } /* else proceed */ } else { return (result); } } #if 0 DisposeLineListContents(fileList); InitLineList(fileList); AddLine(fileList, "../FAKEME1.txt"); AddLine(fileList, "../../FAKEME2.txt"); AddLine(fileList, "..\\FAKEME3.txt"); AddLine(fileList, "..\\..\\FAKEME4.txt"); AddLine(fileList, "...\\FAKEME5.txt"); AddLine(fileList, "/tmp/bad/FAKEME6.txt"); AddLine(fileList, "c:\\temp\\FAKEME7.txt"); AddLine(fileList, "foo/../FAKEME8.txt"); AddLine(fileList, "foo\\bar\\...\\FAKEME9.txt"); #endif if (fileList->first == NULL) { cip->errNo = kErrGlobNoMatch; return (kErrGlobNoMatch); } if (fileList->first == fileList->last) { #define glberr(a) (ISTRNEQ(cp, a, strlen(a))) /* If we have only one item in the list, see if it really was * an error message we would recognize. */ cp = strchr(fileList->first->line, ':'); if (cp != NULL) { if (glberr(": No such file or directory")) { (void) RemoveLine(fileList, fileList->first); cip->errNo = kErrGlobFailed; return (kErrGlobFailed); } else if (glberr(": No match")) { cip->errNo = kErrGlobNoMatch; return (kErrGlobNoMatch); } } } StripUnneccesaryGlobEntries(cip, fileList); RemoteGlobCollapse(cip, pattern, fileList); for (lp=fileList->first; lp != NULL; lp = lp->next) PrintF(cip, " Rglob [%s]\n", lp->line); } else { /* Or, if there were no globbing characters in 'pattern', then the * pattern is really just a filename. So for this case the * file list is really just a single file. */ fileList->first = fileList->last = NULL; (void) AddLine(fileList, pattern); } return (kNoErr); } /* FTPRemoteGlob */