int UnLslR(const FTPCIPtr cip, FTPFileInfoListPtr filp, FTPLineListPtr llp, int serverType) { char curdir[512]; char line[512]; int len; size_t curdirlen = 0; char fname[256]; char linkto[256]; char *cp; longest_int fsize; int ftype; time_t ftime, now; int thisyear; struct tm nowtm; int rc; FTPLinePtr lp; FTPFileInfo fi; int linesread = 0; int linesconverted = 0; size_t maxFileLen = 0; size_t maxPlugLen = 0; size_t fileLen; int plugend; int skipdir = 0; size_t cwdlen; if (Localtime(time(&now), &nowtm) == NULL) thisyear = 1970; /* should never happen */ else thisyear = nowtm.tm_year + 1900; curdir[0] = '\0'; cip->buf[0] = '\0'; cwdlen = 0; InitFileInfoList(filp); for (lp = llp->first; lp != NULL; lp = lp->next) { len = (int) strlen(STRNCPY(line, lp->line)); for (cp = line; ; cp++) { if ((*cp == '\0') || (!isspace((int) *cp))) break; } if (*cp == '\0') { /* Entire line was blank. */ /* separator line between dirs */ continue; } if (serverType == kServerTypeMicrosoftFTP) { /* IIS runs on WinNT only, and NT * can use / as a separator rather * that \. We will convert them * here. */ for (cp = line; *cp != '\0'; cp++) { if (*cp == '\\') *cp = '/'; } } linesread++; rc = UnLslRLine(line, curdir, curdirlen, fname, sizeof(fname), linkto, sizeof(linkto), &ftype, &fsize, &ftime, now, thisyear, &plugend); if ((rc < 0) && (serverType == kServerTypeMicrosoftFTP)) { rc = UnDosLine(line, curdir, curdirlen, fname, sizeof(fname), &ftype, &fsize, &ftime); if (rc == 0) { *linkto = '\0'; plugend = 0; } } if (rc == 0) { if (skipdir != 0) continue; cp = fname + curdirlen; if ((cp[0] == '.') && ((cp[1] == '\0') || ((cp[1] == '.') && (cp[2] == '\0')))) continue; /* ignore . and .. */ linesconverted++; cp = fname; if ((cp[0] == '.') && ((cp[1] == '/') || (cp[1] == '\\'))) cp += 2; if ((*cp == '/') || (*cp == '\\')) { /* Absolute pathnames not allowed unless * they are in the cwd. */ if (cip->buf[0] == '\0') { (void) FTPGetCWD(cip, cip->buf, cip->bufSize); cwdlen = strlen(cip->buf); } /* In root directory (cwdlen == 1), paths * from root are OK. */ if (cwdlen > 1) { if (memcmp(cp, cip->buf, cwdlen) == 0) { /* Abs path prefixed with cwd */ cp += cwdlen; if ((*cp == '/') || (*cp == '\\')) cp++; } else { /* Abs path not prefixed with cwd */ continue; } } } if (PathContainsIntermediateDotDotSubDir(cp)) continue; fileLen = strlen(cp); if (fileLen > maxFileLen) maxFileLen = fileLen; fi.relnameLen = fileLen; fi.relname = StrDup(cp); fi.rname = NULL; fi.lname = NULL; fi.rlinkto = (linkto[0] == '\0') ? NULL : StrDup(linkto); fi.mdtm = ftime; fi.size = (longest_int) fsize; fi.type = ftype; fi.mode = -1; if (plugend > 0) { fi.plug = (char *) malloc((size_t) plugend + 1); if (fi.plug != NULL) { (void) memcpy(fi.plug, line, (size_t) plugend); fi.plug[plugend] = '\0'; if ((size_t) plugend > maxPlugLen) maxPlugLen = (size_t) plugend; } } else { fi.plug = (char *) malloc(32 + 1); if (fi.plug != NULL) { (void) strcpy(fi.plug, "---------- 1 ftpuser ftpusers"); fi.plug[0] = (char) ftype; if (32 > maxPlugLen) maxPlugLen = (size_t) 32; } } (void) AddFileInfo(filp, &fi); } else if ((rc < 0) && (line[len - 1] == ':')) { if ((line[0] == '.') && (line[1] == '/')) { line[len - 1] = '/'; (void) memcpy(curdir, line + 2, (size_t) len + 1 - 2); curdirlen = (size_t) (len - 2); } else if ((line[0] == '.') && (line[1] == '\\')) { line[len - 1] = '\\'; (void) memcpy(curdir, line + 2, (size_t) len + 1 - 2); curdirlen = (size_t) (len - 2); } else { line[len - 1] = '/'; (void) memcpy(curdir, line, (size_t) len + 1); curdirlen = (size_t) len; } skipdir = 0; cp = curdir; if ((*cp == '/') || (*cp == '\\')) { skipdir = 1; /* absolute pathnames not allowed */ if (cip->buf[0] == '\0') { (void) FTPGetCWD(cip, cip->buf, cip->bufSize); cwdlen = strlen(cip->buf); } if (cwdlen == 1) { /* In root directory, so paths * from root are OK. */ skipdir = 0; } else { if (memcmp(cp, cip->buf, cwdlen) == 0) { cp += cwdlen; if ((*cp == '/') || (*cp == '\\')) cp++; memmove(curdir, cp, strlen(cp) + 1); skipdir = 0; cp = curdir; } } } if (PathContainsIntermediateDotDotSubDir(cp)) { skipdir = 1; } } } filp->maxFileLen = maxFileLen; filp->maxPlugLen = maxPlugLen; if (linesread == 0) return (0); return ((linesconverted > 0) ? linesconverted : (-1)); } /* UnLslR */
int FTPLoginHost(const FTPCIPtr cip) { ResponsePtr rp; int result = kErrLoginFailed; int anonLogin; int sentpass = 0; int fwloggedin; int firstTime; char cwd[512]; if (cip == NULL) return (kErrBadParameter); if ((cip->firewallType < kFirewallNotInUse) || (cip->firewallType > kFirewallLastType)) return (kErrBadParameter); if (strcmp(cip->magic, kLibraryMagic)) return (kErrBadMagic); anonLogin = 0; if (cip->user[0] == '\0') (void) STRNCPY(cip->user, "anonymous"); if ((strcmp(cip->user, "anonymous") == 0) || (strcmp(cip->user, "ftp") == 0)) { anonLogin = 1; /* Try to get the email address if you didn't specify * a password when the user is anonymous. */ if (cip->pass[0] == '\0') { FTPInitializeAnonPassword(cip->lip); (void) STRNCPY(cip->pass, cip->lip->defaultAnonPassword); } } rp = InitResponse(); if (rp == NULL) { result = kErrMallocFailed; cip->errNo = kErrMallocFailed; goto done2; } for (firstTime = 1, fwloggedin = 0; ; ) { /* Here's a mini finite-automaton for the login process. * * Originally, the FTP protocol was designed to be entirely * implementable from a FA. It could be done, but I don't think * it's something an interactive process could be the most * effective with. */ if (firstTime != 0) { rp->code = 220; firstTime = 0; } else if (result < 0) { goto done; } switch (rp->code) { case 220: /* Welcome, ready for new user. */ if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0)) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s", cip->user); } else if (cip->firewallType == kFirewallUserAtSite) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s@%s", cip->user, cip->host); } else if (cip->firewallType == kFirewallUserAtUserPassAtPass) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s@%s@%s", cip->user, cip->firewallUser, cip->host); } else if (cip->firewallType == kFirewallUserAtSiteFwuPassFwp) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s@%s %s", cip->user, cip->host, cip->firewallUser); } else if (cip->firewallType == kFirewallFwuAtSiteFwpUserPass) { /* only reached when !fwloggedin */ ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s@%s", cip->firewallUser, cip->host); } else if (cip->firewallType > kFirewallNotInUse) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s", cip->firewallUser); } else { goto unknown; } break; case 230: /* 230 User logged in, proceed. */ case 231: /* User name accepted. */ case 202: /* Command not implemented, superfluous at this site. */ if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0)) goto okay; /* Now logged in to the firewall. */ fwloggedin++; if (cip->firewallType == kFirewallLoginThenUserAtSite) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s@%s", cip->user, cip->host); } else if (cip->firewallType == kFirewallUserAtUserPassAtPass) { goto okay; } else if (cip->firewallType == kFirewallOpenSite) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "OPEN %s", cip->host); } else if (cip->firewallType == kFirewallSiteSite) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "SITE %s", cip->host); } else if (cip->firewallType == kFirewallFwuAtSiteFwpUserPass) { /* only reached when !fwloggedin */ ReInitResponse(cip, rp); result = RCmd(cip, rp, "USER %s", cip->user); } else /* kFirewallUserAtSite */ { goto okay; } break; case 421: /* 421 Service not available, closing control connection. */ result = kErrHostDisconnectedDuringLogin; goto done; case 331: /* 331 User name okay, need password. */ if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0)) { if ((cip->pass[0] == '\0') && (cip->passphraseProc != NoGetPassphraseProc)) (*cip->passphraseProc)(cip, &rp->msg, cip->pass, sizeof(cip->pass)); ReInitResponse(cip, rp); result = RCmd(cip, rp, "PASS %s", cip->pass); } else if (cip->firewallType == kFirewallUserAtSite) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "PASS %s", cip->pass); } else if (cip->firewallType == kFirewallUserAtUserPassAtPass) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "PASS %s@%s", cip->pass, cip->firewallPass); } else if (cip->firewallType == kFirewallUserAtSiteFwuPassFwp) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "PASS %s", cip->pass); } else if (cip->firewallType == kFirewallFwuAtSiteFwpUserPass) { /* only reached when !fwloggedin */ ReInitResponse(cip, rp); result = RCmd(cip, rp, "PASS %s", cip->firewallPass); } else if (cip->firewallType > kFirewallNotInUse) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "PASS %s", cip->firewallPass); } else { goto unknown; } sentpass++; break; case 332: /* 332 Need account for login. */ case 532: /* 532 Need account for storing files. */ if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0)) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "ACCT %s", cip->acct); } else if (cip->firewallType == kFirewallUserAtSiteFwuPassFwp) { ReInitResponse(cip, rp); result = RCmd(cip, rp, "ACCT %s", cip->firewallPass); } else { /* ACCT not supported on firewall. */ goto unknown; } break; case 530: /* Not logged in. */ result = (sentpass != 0) ? kErrBadRemoteUserOrPassword : kErrBadRemoteUser; goto done; case 501: /* Syntax error in parameters or arguments. */ case 503: /* Bad sequence of commands. */ case 550: /* Can't set guest privileges. */ goto done; default: unknown: if (rp->msg.first == NULL) { Error(cip, kDontPerror, "Lost connection during login.\n"); } else { Error(cip, kDontPerror, "Unexpected response: %s\n", rp->msg.first->line ); } goto done; } } okay: /* Do the application's connect message callback, if present. */ if (cip->onLoginMsgProc != 0) (*cip->onLoginMsgProc)(cip, rp); DoneWithResponse(cip, rp); result = 0; cip->loggedIn = 1; /* Make a note of what our root directory is. * This is often different from "/" when not * logged in anonymously. */ if (cip->startingWorkingDirectory != NULL) { free(cip->startingWorkingDirectory); cip->startingWorkingDirectory = NULL; } if ((cip->doNotGetStartingWorkingDirectory == 0) && (FTPGetCWD(cip, cwd, sizeof(cwd)) == kNoErr)) { cip->startingWorkingDirectory = StrDup(cwd); } /* When a new site is opened, ASCII mode is assumed (by protocol). */ cip->curTransferType = 'A'; PrintF(cip, "Logged in to %s as %s.\n", cip->host, cip->user); /* Don't leave cleartext password in memory. */ if ((anonLogin == 0) && (cip->leavePass == 0)) (void) memset(cip->pass, '*', strlen(cip->pass)); if (result < 0) cip->errNo = result; return result; done: DoneWithResponse(cip, rp); done2: /* Don't leave cleartext password in memory. */ if ((anonLogin == 0) && (cip->leavePass == 0)) (void) memset(cip->pass, '*', strlen(cip->pass)); if (result < 0) cip->errNo = result; return result; } /* FTPLoginHost */