/***************************************************************************** * * ftpresp.cpp - Parsing FTP responses * *****************************************************************************/ #include "priv.h" /*****************************************************************************\ FUNCTION: FindEndOfStrOrLine DESCRIPTION: Find the end of the line ('\n') or the end of the string ('\0'). \*****************************************************************************/ LPWIRESTR FindEndOfStrOrLine(LPWIRESTR pszString) { while (*pszString != '\0') { if (('\n' == pszString[0])) { while (('\n' == pszString[0])) pszString++; break; } pszString++; } return pszString; } /*****************************************************************************\ FUNCTION: FindFirstMajorResponse DESCRIPTION: \*****************************************************************************/ LPWIRESTR FindFirstMajorResponse(LPWIRESTR pszResponse) { while ((pszResponse[0]) && ('-' != pszResponse[3])) pszResponse = FindEndOfStrOrLine(pszResponse); return pszResponse; } /*****************************************************************************\ FUNCTION: GetNextResponseSection DESCRIPTION: \*****************************************************************************/ LPWIRESTR GetNextResponseSection(LPWIRESTR pszCompleteResponse, LPWIRESTR * ppszResponseStart) { LPWIRESTR pszNextResponse = NULL; // There may be a few minor responses. Skip over them... pszCompleteResponse = FindFirstMajorResponse(pszCompleteResponse); // Were we never able to fine a major response? if (!pszCompleteResponse[0]) return NULL; // No, so return failure. // We are off to find the next major response. // We should be looking at a response code. ASSERT('-' == pszCompleteResponse[3]); // Slop saves us here // Extended response. Copy until we see the match. // As we copy, we also clean up the lines, removing // the random punctuation servers prepend to continuations. // // wu-ftp prepends the extended response code to each line: // // 230-Welcome to ftp.foo.com. Please read the rules // 230-and regulations in the file RULES. // 230 Guest login ok, access restrictions apply. // // Microsoft Internet Information Server prepends a space: // // 230-This is ftp.microsoft.com. See the index.txt file // in the root directory for more information. // 230 Anonymous user logged in as anonymous. // WIRECHAR szResponseNumber[5]; // example: "230-" WIRECHAR szResponseEnd[5]; // example: "230 " StrCpyNA(szResponseNumber, pszCompleteResponse, ARRAYSIZE(szResponseNumber)); ASSERT(4 == lstrlenA(szResponseNumber)); StrCpyNA(szResponseEnd, szResponseNumber, ARRAYSIZE(szResponseEnd)); szResponseEnd[3] = ' '; pszNextResponse = pszCompleteResponse; *ppszResponseStart = pszCompleteResponse; do { // Skip past the header. if (!StrCmpNA(szResponseNumber, pszNextResponse, 4)) pszNextResponse += 4; // wu-ftp else if ((pszNextResponse[0] == ' ') && (!StrCmpNA(szResponseNumber, &pszNextResponse[1], 4))) pszNextResponse += 5; // ftp.microsoft.com else if (pszNextResponse[0] == ' ') pszNextResponse++; // IIS // Skip the rest of the line. pszNextResponse = FindEndOfStrOrLine(pszNextResponse); } while (pszNextResponse[0] && StrCmpNA(pszNextResponse, szResponseEnd, 4)); /* Now gobble the trailer */ if ('\0' == pszNextResponse[0]) pszNextResponse = NULL; // We are at the end. return pszNextResponse; } /*****************************************************************************\ FUNCTION: StripResponseHeaders DESCRIPTION: \*****************************************************************************/ void StripResponseHeaders(LPWIRESTR pszResponse) { // We should be looking at a response code. if ((3 < lstrlenA(pszResponse)) && (pszResponse[3] == '-')) { LPWIRESTR pszIterator = pszResponse; WIRECHAR szResponseNumber[5]; // example: "230-" WIRECHAR szResponseEnd[5]; // example: "230 " BOOL fFirstPass = TRUE; StrCpyNA(szResponseNumber, pszResponse, ARRAYSIZE(szResponseNumber)); ASSERT(4 == lstrlenA(szResponseNumber)); StrCpyNA(szResponseEnd, szResponseNumber, ARRAYSIZE(szResponseEnd)); szResponseEnd[3] = ' '; do { // Skip past the header. if (!StrCmpNA(szResponseNumber, pszIterator, 4)) RemoveCharsFromStringA(pszIterator, 3); // wu-ftp else if ((pszIterator[0] == ' ') && (!StrCmpNA(szResponseNumber, &pszIterator[1], 4))) RemoveCharsFromStringA(pszIterator, 4); // ftp.microsoft.com else if (pszIterator[0] == ' ') NULL; // IIS if (fFirstPass) { fFirstPass = FALSE; RemoveCharsFromStringA(pszIterator, 1); // IIS } else pszIterator[0] = ' '; // Make that new line a space. // Skip the rest of the line. pszIterator = FindEndOfStrOrLine(pszIterator); } while (pszIterator[0] && StrCmpNA(pszIterator, szResponseEnd, 4)); RemoveCharsFromStringA(pszIterator, 4); // Now gobble the trailer } } /*****************************************************************************\ FUNCTION: GetMOTDMessage DESCRIPTION: \*****************************************************************************/ LPWIRESTR GetMOTDMessage(LPWIRESTR pwResponse, DWORD cchResponse) { LPWIRESTR pszMOTD = NULL; LPWIRESTR pszLast = &pwResponse[lstrlenA(pwResponse)]; LPWIRESTR pszNext = pwResponse; LPWIRESTR pszEnd = NULL; while (pszNext = GetNextResponseSection(pszNext, &pszLast)) pszEnd = pszNext; if (pszEnd) pszEnd[0] = '\0'; // Terminate it so we don't get the minor responses after our response. pszMOTD = (LPWIRESTR) GlobalAlloc(GPTR, (lstrlenA(pszLast) + 1) * sizeof(WIRECHAR)); if (pszMOTD) { StrCpyA(pszMOTD, pszLast); StripResponseHeaders(pszMOTD); } return pszMOTD; } /*****************************************************************************\ FUNCTION: GetFtpResponse DESCRIPTION: Get the MOTD from the Response \*****************************************************************************/ CFtpGlob * GetFtpResponse(CWireEncoding * pwe) { CFtpGlob * pfg = NULL; DWORD cchResponse = 0; LPWIRESTR pwWireResponse; DWORD dwError; InternetGetLastResponseInfoWrap(TRUE, &dwError, NULL, &cchResponse); cchResponse++; /* +1 for the terminating 0 */ pwWireResponse = (LPWIRESTR)LocalAlloc(LPTR, cchResponse * sizeof(WIRECHAR)); if (pwWireResponse) { if (SUCCEEDED(InternetGetLastResponseInfoWrap(TRUE, &dwError, pwWireResponse, &cchResponse))) { LPWIRESTR pwMOTD = GetMOTDMessage(pwWireResponse, cchResponse); if (pwMOTD) { LPWSTR pwzDisplayMOTD; DWORD cchSize = (lstrlenA(pwMOTD) + 1); pwzDisplayMOTD = (LPWSTR)GlobalAlloc(LPTR, cchSize * sizeof(WCHAR)); if (pwzDisplayMOTD) { pwe->WireBytesToUnicode(NULL, pwMOTD, WIREENC_IMPROVE_ACCURACY, pwzDisplayMOTD, cchSize); pfg = CFtpGlob_CreateStr(pwzDisplayMOTD); if (!(pfg)) GlobalFree(pwzDisplayMOTD); // Couldn't track message } GlobalFree(pwMOTD); } } LocalFree(pwWireResponse); } return pfg; }