Fixes 95590 FTP: SYST limitations. Intergrates Cyrus Patel LIST Parser. r=dougt, sr=darin@Netscape.com.

This commit is contained in:
dougt%netscape.com 2002-08-29 03:13:18 +00:00
Родитель 898f7454be
Коммит c7a5ce8c32
9 изменённых файлов: 2035 добавлений и 899 удалений

Просмотреть файл

@ -251,9 +251,7 @@ nsresult NS_NewHTTPCompressConv (nsHTTPCompressConv ** result);
nsresult NS_NewNSTXTToHTMLConv(nsTXTToHTMLConv** result);
nsresult NS_NewStreamConv(nsStreamConverterService **aStreamConv);
#define FTP_UNIX_TO_INDEX "?from=text/ftp-dir-unix&to=application/http-index-format"
#define FTP_NT_TO_INDEX "?from=text/ftp-dir-nt&to=application/http-index-format"
#define FTP_OS2_TO_INDEX "?from=text/ftp-dir-os2&to=application/http-index-format"
#define FTP_TO_INDEX "?from=text/ftp-dir&to=application/http-index-format"
#define GOPHER_TO_INDEX "?from=text/gopher-dir&to=application/http-index-format"
#define INDEX_TO_HTML "?from=application/http-index-format&to=text/html"
#define MULTI_MIXED_X "?from=multipart/x-mixed-replace&to=*/*"
@ -281,9 +279,7 @@ static PRUint32 g_StreamConverterCount = 16;
#endif
static const char *const g_StreamConverterArray[] = {
FTP_UNIX_TO_INDEX,
FTP_NT_TO_INDEX,
FTP_OS2_TO_INDEX,
FTP_TO_INDEX,
GOPHER_TO_INDEX,
INDEX_TO_HTML,
MULTI_MIXED_X,
@ -772,19 +768,7 @@ static const nsModuleComponentInfo gNetModuleInfo[] = {
// from netwerk/streamconv/converters:
{ "FTPDirListingConverter",
NS_FTPDIRLISTINGCONVERTER_CID,
NS_ISTREAMCONVERTER_KEY FTP_UNIX_TO_INDEX,
CreateNewFTPDirListingConv
},
{ "FTPDirListingConverter",
NS_FTPDIRLISTINGCONVERTER_CID,
NS_ISTREAMCONVERTER_KEY FTP_NT_TO_INDEX,
CreateNewFTPDirListingConv
},
{ "FTPDirListingConverter",
NS_FTPDIRLISTINGCONVERTER_CID,
NS_ISTREAMCONVERTER_KEY FTP_OS2_TO_INDEX,
NS_ISTREAMCONVERTER_KEY FTP_TO_INDEX,
CreateNewFTPDirListingConv
},

Просмотреть файл

@ -1212,6 +1212,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -1788,6 +1795,11 @@
<PATH>nsFTPDirListingConv.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -3234,6 +3246,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -3810,6 +3829,11 @@
<PATH>nsFTPDirListingConv.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -5256,6 +5280,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -5818,6 +5849,11 @@
<PATH>nsFTPDirListingConv.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -7254,6 +7290,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -7816,6 +7859,11 @@
<PATH>nsFTPDirListingConv.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>mozTXTToHTMLConv.cpp</PATH>
@ -8568,6 +8616,12 @@
<PATH>nsFTPDirListingConv.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>Necko.shlb</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>
<PATH>ParseFTPList.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>Necko.shlb</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>

Просмотреть файл

@ -1116,9 +1116,11 @@ nsFtpState::S_pass() {
nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
&passwd, &retval);
// we want to fail if the user canceled or didn't enter a password.
if (!retval || (passwd && !*passwd) )
// we want to fail if the user canceled. Note here that if they want
// a blank password, we will pass it along.
if (!retval)
return NS_ERROR_FAILURE;
mPassword = passwd;
}
// XXX mPassword may contain non-ASCII characters! what do we do?
@ -1255,7 +1257,6 @@ nsFtpState::R_syst() {
// since we just alerted the user, clear mResponseMsg,
// which is displayed to the user.
mResponseMsg = "";
return FTP_ERROR;
}
@ -1433,9 +1434,6 @@ nsFtpState::SetContentType()
switch (mListFormat) {
case nsIDirectoryListing::FORMAT_RAW:
{
nsAutoString fromStr(NS_LITERAL_STRING("text/ftp-dir-"));
SetDirMIMEType(fromStr);
contentType = NS_LITERAL_CSTRING("text/ftp-dir-");
}
break;
@ -2408,27 +2406,6 @@ nsFtpState::StopProcessing() {
return NS_OK;
}
void
nsFtpState::SetDirMIMEType(nsString& aString) {
// the from content type is a string of the form
// "text/ftp-dir-SERVER_TYPE" where SERVER_TYPE represents the server we're talking to.
switch (mServerType) {
case FTP_UNIX_TYPE:
aString.Append(NS_LITERAL_STRING("unix"));
break;
case FTP_NT_TYPE:
aString.Append(NS_LITERAL_STRING("nt"));
break;
case FTP_OS2_TYPE:
aString.Append(NS_LITERAL_STRING("os2"));
break;
case FTP_VMS_TYPE:
aString.Append(NS_LITERAL_STRING("vms"));
break;
default:
aString.Append(NS_LITERAL_STRING("generic"));
}
}
nsresult
nsFtpState::BuildStreamConverter(nsIStreamListener** convertStreamListener)
@ -2446,8 +2423,8 @@ nsFtpState::BuildStreamConverter(nsIStreamListener** convertStreamListener)
if (NS_FAILED(rv))
return rv;
nsAutoString fromStr(NS_LITERAL_STRING("text/ftp-dir-"));
SetDirMIMEType(fromStr);
nsAutoString fromStr(NS_LITERAL_STRING("text/ftp-dir"));
switch (mListFormat) {
case nsIDirectoryListing::FORMAT_RAW:
converterListener = listener;

Просмотреть файл

@ -157,7 +157,6 @@ private:
///////////////////////////////////
// internal methods
void SetDirMIMEType(nsString& aString);
void MoveToNextState(FTP_STATE nextState);
nsresult Process();

Просмотреть файл

@ -47,6 +47,7 @@ EXPORTS = \
$(NULL)
CPPSRCS = \
ParseFTPList.cpp \
nsMultiMixedConv.cpp \
nsFTPDirListingConv.cpp \
nsGopherDirListingConv.cpp \

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ----- BEGIN LICENSE BLOCK -----
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is
* Cyrus Patel <cyp@fb14.uni-mainz.de>
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Doug Turner <dougt@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the LGPL or the GPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ----- END LICENSE BLOCK ----- */
#include "nspr.h"
/* ParseFTPList() parses lines from an FTP LIST command.
**
** Written July 2002 by Cyrus Patel <cyp@fb14.uni-mainz.de>
** with acknowledgements to squid, lynx, wget and ftpmirror.
**
** Arguments:
** 'line': line of FTP data connection output. The line is assumed
** to end at the first '\0' or '\n' or '\r\n'.
** 'state': a structure used internally to track state between
** lines. Needs to be bzero()'d at LIST begin.
** 'result': where ParseFTPList will store the results of the parse
** if 'line' is not a comment and is not junk.
**
** Returns one of the following:
** 'd' - LIST line is a directory entry ('result' is valid)
** 'f' - LIST line is a file's entry ('result' is valid)
** 'l' - LIST line is a symlink's entry ('result' is valid)
** '?' - LIST line is junk. (cwd, non-file/dir/link, etc)
** '"' - its not a LIST line (its a "comment")
**
** It may be advisable to let the end-user see "comments" (particularly when
** the listing results in ONLY such lines) because such a listing may be:
** - an unknown LIST format (NLST or "custom" format for example)
** - an error msg (EPERM,ENOENT,ENFILE,EMFILE,ENOTDIR,ENOTBLK,EEXDEV etc).
** - an empty directory and the 'comment' is a "total 0" line or similar.
** (warning: a "total 0" can also mean the total size is unknown).
**
** ParseFTPList() supports all known FTP LISTing formats:
** - '/bin/ls -l' and all variants (including Hellsoft FTP for NetWare);
** - EPLF (Easily Parsable List Format);
** - Windows NT's default "DOS-dirstyle";
** - OS/2 basic server format LIST format;
** - VMS (MultiNet, UCX, and CMU) LIST format (including multi-line format);
** - IBM VM/CMS, VM/ESA LIST format (two known variants);
** - SuperTCP FTP Server for Win16 LIST format;
** - NetManage Chameleon (NEWT) for Win16 LIST format;
** - '/bin/dls' (two known variants, plus multi-line) LIST format;
** If there are others, then I'd like to hear about them (send me a sample).
**
** NLSTings are not supported explicitely because they cannot be machine
** parsed consistantly: NLSTings do not have unique characteristics - even
** the assumption that there won't be whitespace on the line does not hold
** because some nlistings have more than one filename per line and/or
** may have filenames that have spaces in them. Moreover, distinguishing
** between an error message and an NLST line would require ParseList() to
** recognize all the possible strerror() messages in the world.
*/
/* #undef anything you don't want to support */
#define SUPPORT_LSL /* /bin/ls -l and dozens of variations therof */
#define SUPPORT_DLS /* /bin/dls format (very, Very, VERY rare) */
#define SUPPORT_EPLF /* Extraordinarily Pathetic List Format */
#define SUPPORT_DOS /* WinNT server in 'site dirstyle' dos */
#define SUPPORT_VMS /* VMS (all: MultiNet, UCX, CMU-IP) */
#define SUPPORT_CMS /* IBM VM/CMS,VM/ESA (z/VM and LISTING forms) */
#define SUPPORT_OS2 /* IBM TCP/IP for OS/2 - FTP Server */
#define SUPPORT_W16 /* win16 hosts: SuperTCP or NetManage Chameleon */
struct list_state
{
void *magic; /* to determine if previously initialized */
PRTime now_time; /* needed for year determination */
PRExplodedTime now_tm; /* needed for year determination */
PRInt32 lstyle; /* LISTing style */
PRInt32 parsed_one; /* returned anything yet? */
char carry_buf[84]; /* for VMS multiline */
PRUint32 carry_buf_len; /* length of name in carry_buf */
PRUint32 numlines; /* number of lines seen */
};
struct list_result
{
PRInt32 fe_type; /* 'd'(dir) or 'l'(link) or 'f'(file) */
const char * fe_fname; /* pointer to filename */
PRUint32 fe_fnlen; /* length of filename */
const char * fe_lname; /* pointer to symlink name */
PRUint32 fe_lnlen; /* length of symlink name */
char fe_size[40]; /* size of file in bytes (<= (2^128 - 1)) */
PRExplodedTime fe_time; /* last-modified time */
PRInt32 fe_cinfs; /* file system is definitely case insensitive */
/* (converting all-upcase names may be desirable) */
};
int ParseFTPList(const char *line,
struct list_state *state,
struct list_result *result );

Просмотреть файл

@ -55,15 +55,7 @@
#include "nsCRT.h"
#include "nsMimeTypes.h"
#define IS_LWS(c) (PL_strchr(" \t\r\n",c) != 0)
#define IS_FTYPE(c) (PL_strchr("-dlbcsp",c) != 0)
#define IS_RPERM(c) (PL_strchr("r-",c) != 0)
#define IS_WPERM(c) (PL_strchr("w-",c) != 0)
#define IS_SPERM(c) (PL_strchr("sSx-",c) != 0)
#define IS_TPERM(c) (PL_strchr("tTx-",c) != 0)
static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID);
static NS_DEFINE_CID(kDateTimeCID, NS_DATETIMEFORMAT_CID);
#include "ParseFTPList.h"
#if defined(PR_LOGGING)
//
@ -87,111 +79,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsFTPDirListingConv,
nsIStreamListener,
nsIRequestObserver);
// Common code of Convert and AsyncConvertData function
static FTP_Server_Type
DetermineServerType (nsCString &fromMIMEString, const PRUnichar *aFromType)
{
fromMIMEString.AssignWithConversion(aFromType);
const char *from = fromMIMEString.get();
NS_ASSERTION(from, "nsCString/PRUnichar acceptance failed.");
from = PL_strstr(from, "/ftp-dir-");
if (!from) return ERROR_TYPE;
from += 9;
fromMIMEString = from;
if (-1 != fromMIMEString.Find("unix")) {
return UNIX;
} else if (-1 != fromMIMEString.Find("nt")) {
return NT;
} else if (-1 != fromMIMEString.Find("dcts")) {
return DCTS;
} else if (-1 != fromMIMEString.Find("ncsa")) {
return NCSA;
} else if (-1 != fromMIMEString.Find("peter_lewis")) {
return PETER_LEWIS;
} else if (-1 != fromMIMEString.Find("machten")) {
return MACHTEN;
} else if (-1 != fromMIMEString.Find("cms")) {
return CMS;
} else if (-1 != fromMIMEString.Find("tcpc")) {
return TCPC;
} else if (-1 != fromMIMEString.Find("os2")) {
return OS_2;
}
return GENERIC;
}
// nsIStreamConverter implementation
#define CONV_BUF_SIZE (4*1024)
NS_IMETHODIMP
nsFTPDirListingConv::Convert(nsIInputStream *aFromStream,
const PRUnichar *aFromType,
const PRUnichar *aToType,
nsISupports *aCtxt, nsIInputStream **_retval) {
nsresult rv;
// set our internal state to reflect the server type
nsCString fromMIMEString;
mServerType = DetermineServerType(fromMIMEString, aFromType);
if (mServerType == ERROR_TYPE) return NS_ERROR_FAILURE;
char buffer[CONV_BUF_SIZE];
int i = 0;
while (i < CONV_BUF_SIZE) {
buffer[i] = '\0';
i++;
}
CBufDescriptor desc(buffer, PR_TRUE, CONV_BUF_SIZE);
nsCAutoString aBuffer(desc);
nsCString convertedData;
NS_ASSERTION(aCtxt, "FTP dir conversion needs the context");
nsCOMPtr<nsIURI> uri(do_QueryInterface(aCtxt, &rv));
if (NS_FAILED(rv)) return rv;
rv = GetHeaders(convertedData, uri);
if (NS_FAILED(rv)) return rv;
// build up the body
while (1) {
PRUint32 amtRead = 0;
rv = aFromStream->Read(buffer+aBuffer.Length(),
CONV_BUF_SIZE-aBuffer.Length(), &amtRead);
if (NS_FAILED(rv)) return rv;
if (!amtRead) {
// EOF
break;
}
aBuffer = DigestBufferLines(buffer, convertedData);
}
// end body building
#ifndef DEBUG_valeski
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
convertedData.Length(), convertedData.get()) );
#else
char *unescData = ToNewCString(convertedData);
nsUnescape(unescData);
printf("::OnData() sending the following %d bytes...\n\n%s\n\n", convertedData.Length(), unescData);
nsMemory::Free(unescData);
#endif // DEBUG_valeski
// send the converted data out.
return NS_NewCStringInputStream(_retval, convertedData);
return NS_ERROR_NOT_IMPLEMENTED;
}
// Stream converter service calls this to initialize the actual stream converter (us).
NS_IMETHODIMP
nsFTPDirListingConv::AsyncConvertData(const PRUnichar *aFromType, const PRUnichar *aToType,
@ -204,13 +102,6 @@ nsFTPDirListingConv::AsyncConvertData(const PRUnichar *aFromType, const PRUnicha
mFinalListener = aListener;
NS_ADDREF(mFinalListener);
// set our internal state to reflect the server type
nsCString fromMIMEString;
mServerType = DetermineServerType(fromMIMEString, aFromType);
if (mServerType == ERROR_TYPE) return NS_ERROR_FAILURE;
// we need our own channel that represents the content-type of the
// converted data.
NS_ASSERTION(aCtxt, "FTP dir listing needs a context (the uri)");
@ -228,7 +119,7 @@ nsFTPDirListingConv::AsyncConvertData(const PRUnichar *aFromType, const PRUnicha
if (NS_FAILED(rv)) return rv;
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG,
("nsFTPDirListingConv::AsyncConvertData() converting FROM raw %s, TO application/http-index-format\n", fromMIMEString.get()));
("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n"));
return NS_OK;
}
@ -268,11 +159,11 @@ nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
mBuffer.Truncate();
}
#ifndef DEBUG_valeski
#ifndef DEBUG_dougt
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer) );
#else
printf("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer);
#endif // DEBUG_valeski
#endif // DEBUG_dougt
nsCString indexFormat;
if (!mSentHeading) {
@ -290,7 +181,7 @@ nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
char *line = buffer;
line = DigestBufferLines(line, indexFormat);
#ifndef DEBUG_valeski
#ifndef DEBUG_dougt
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
indexFormat.Length(), indexFormat.get()) );
#else
@ -298,7 +189,7 @@ nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
nsUnescape(unescData);
printf("::OnData() sending the following %d bytes...\n\n%s\n\n", indexFormat.Length(), unescData);
nsMemory::Free(unescData);
#endif // DEBUG_valeski
#endif // DEBUG_dougt
// if there's any data left over, buffer it.
if (line && *line) {
@ -356,7 +247,6 @@ nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
nsFTPDirListingConv::nsFTPDirListingConv() {
NS_INIT_ISUPPORTS();
mFinalListener = nsnull;
mServerType = GENERIC;
mPartChannel = nsnull;
mSentHeading = PR_FALSE;
}
@ -416,368 +306,8 @@ nsFTPDirListingConv::GetHeaders(nsACString& headers,
return rv;
}
PRInt8
nsFTPDirListingConv::MonthNumber(const char *month) {
NS_ASSERTION(month && month[1] && month[2], "bad month");
if (!month || !month[0] || !month[1] || !month[2])
return -1;
char c1 = month[1], c2 = month[2];
PRInt8 rv = -1;
//PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("nsFTPDirListingConv::MonthNumber(month = %s) ", month) );
switch (*month) {
case 'f': case 'F':
rv = 1; break;
case 'm': case 'M':
// c1 == 'a' || c1 == 'A'
if (c2 == 'r' || c2 == 'R')
rv = 2;
else
// c2 == 'y' || c2 == 'Y'
rv = 4;
break;
case 'a': case 'A':
if (c1 == 'p' || c1 == 'P')
rv = 3;
else
// c1 == 'u' || c1 == 'U'
rv = 7;
break;
case 'j': case 'J':
if (c1 == 'u' || c1 == 'U') {
if (c2 == 'n' || c2 == 'N')
rv = 5;
else
// c2 == 'l' || c2 == 'L'
rv = 6;
} else {
// c1 == 'a' || c1 == 'A'
rv = 0;
}
break;
case 's': case 'S':
rv = 8; break;
case 'o': case 'O':
rv = 9; break;
case 'n': case 'N':
rv = 10; break;
case 'd': case 'D':
rv = 11; break;
default:
rv = -1;
}
//PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("returning %d\n", rv) );
return rv;
}
// Return true if the string is of the form:
// "Sep 1 1990 " or
// "Sep 11 11:59 " or
// "Dec 12 1989 " or
// "FCv 23 1990 " ...
PRBool
nsFTPDirListingConv::IsLSDate(char *aCStr) {
/* must start with three alpha characters */
if (!nsCRT::IsAsciiAlpha(*aCStr++) || !nsCRT::IsAsciiAlpha(*aCStr++) || !nsCRT::IsAsciiAlpha(*aCStr++))
return PR_FALSE;
/* space */
if (*aCStr != ' ')
return PR_FALSE;
aCStr++;
/* space or digit */
if ((*aCStr != ' ') && !nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* digit */
if (!nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* space */
if (*aCStr != ' ')
return PR_FALSE;
aCStr++;
/* space or digit */
if ((*aCStr != ' ') && !nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* digit */
if (!nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* colon or digit */
if ((*aCStr != ':') && !nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* digit */
if (!nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* space or digit */
if ((*aCStr != ' ') && !nsCRT::IsAsciiDigit(*aCStr))
return PR_FALSE;
aCStr++;
/* space */
if (*aCStr != ' ')
return PR_FALSE;
aCStr++;
return PR_TRUE;
}
// Converts a date string from 'ls -l' to a PRTime number
// "Sep 1 1990 " or
// "Sep 11 11:59 " or
// "Dec 12 1989 " or
// "FCv 23 1990 " ...
// Returns 0 on error.
PRBool
nsFTPDirListingConv::ConvertUNIXDate(char *aCStr, PRExplodedTime& outDate) {
PRExplodedTime curTime;
InitPRExplodedTime(curTime);
char *bcol = aCStr; /* Column begin */
char *ecol; /* Column end */
// MONTH
char tmpChar = bcol[3];
bcol[3] = '\0';
if ((curTime.tm_month = MonthNumber(bcol)) < 0)
return PR_FALSE;
bcol[3] = tmpChar;
// DAY
ecol = &bcol[3];
while (*(++ecol) == ' ') ;
while (*(++ecol) != ' ') ;
*ecol = '\0';
bcol = ecol+1;
while (*(--ecol) != ' ') ;
PRInt32 error;
nsCAutoString day(ecol);
curTime.tm_mday = day.ToInteger(&error, 10);
// YEAR
if ((ecol = PL_strchr(bcol, ':')) == NULL) {
nsCAutoString intStr(bcol);
curTime.tm_year = intStr.ToInteger(&error, 10);
} else {
// TIME
/* If the time is given as hh:mm, then the file is less than 1 year
* old, but we might shift calandar year. This is avoided by checking
* if the date parsed is future or not.
*/
*ecol = '\0';
nsCAutoString intStr(++ecol);
curTime.tm_min = intStr.ToInteger(&error, 10); // Right side of ':'
intStr = bcol;
curTime.tm_hour = intStr.ToInteger(&error, 10); // Left side of ':'
PRExplodedTime nowETime;
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &nowETime);
curTime.tm_year = nowETime.tm_year;
PRBool thisCalendarYear = PR_FALSE;
if (nowETime.tm_month > curTime.tm_month) {
thisCalendarYear = PR_TRUE;
} else if (nowETime.tm_month == curTime.tm_month
&& nowETime.tm_mday > curTime.tm_mday) {
thisCalendarYear = PR_TRUE;
} else if (nowETime.tm_month == curTime.tm_month
&& nowETime.tm_mday == curTime.tm_mday
&& nowETime.tm_hour > curTime.tm_hour) {
thisCalendarYear = PR_TRUE;
} else if (nowETime.tm_month == curTime.tm_month
&& nowETime.tm_mday == curTime.tm_mday
&& nowETime.tm_hour == curTime.tm_hour
&& nowETime.tm_min >= curTime.tm_min) {
thisCalendarYear = PR_TRUE;
}
if (!thisCalendarYear) curTime.tm_year--;
}
// set the out param
outDate = curTime;
return PR_TRUE;
}
PRBool
nsFTPDirListingConv::ConvertDOSDate(char *aCStr, PRExplodedTime& outDate) {
PRExplodedTime curTime, nowTime;
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &nowTime);
PRInt16 currCentury = nowTime.tm_year / 100;
InitPRExplodedTime(curTime);
curTime.tm_month = ((aCStr[0]-'0')*10 + (aCStr[1]-'0')) - 1;
curTime.tm_mday = (((aCStr[3]-'0')*10) + aCStr[4]-'0');
curTime.tm_year = currCentury*100 + ((aCStr[6]-'0')*10) + aCStr[7]-'0';
// year is only 2-digit, so we've converted. If its in the future,
// it must have been last century
if (curTime.tm_year > nowTime.tm_year) {
curTime.tm_year -= 100;
}
curTime.tm_hour = (((aCStr[10]-'0')*10) + aCStr[11]-'0');
if (aCStr[15] == 'P')
curTime.tm_hour += 12;
curTime.tm_min = (((aCStr[13]-'0')*10) + aCStr[14]-'0');
outDate = curTime;
return PR_TRUE;
}
nsresult
nsFTPDirListingConv::ParseLSLine(char *aLine, indexEntry *aEntry) {
PRInt32 base=1;
PRInt32 size_num=0;
char save_char;
char *ptr, *escName;
// insure that we have enough data to parse here
// using 27 for a minimal LIST line
if (PL_strlen(aLine) <= 27) {
NS_WARNING("ls -l is incorrectly formatted");
aEntry->mName.Adopt(nsEscape(aLine, url_Path));
// initialize the time struct to 0
InitPRExplodedTime(aEntry->mMDTM);
return NS_OK;
}
for (ptr = &aLine[PL_strlen(aLine) - 1];
(ptr > aLine+13) && (!nsCRT::IsAsciiSpace(*ptr) || !IsLSDate(ptr-12)); ptr--)
; /* null body */
save_char = *ptr;
*ptr = '\0';
if (ptr > aLine+13) {
ConvertUNIXDate(ptr-12, aEntry->mMDTM);
} else {
// must be a dl listing
// unterminate the line
*ptr = save_char;
// find the first whitespace and terminate
for(ptr=aLine; *ptr != '\0'; ptr++)
if (nsCRT::IsAsciiSpace(*ptr)) {
*ptr = '\0';
break;
}
escName = nsEscape(aLine, url_Path);
aEntry->mName.Adopt(escName);
// initialize the time struct to 0 to be safe
InitPRExplodedTime(aEntry->mMDTM);
return NS_OK;
}
escName = nsEscape(ptr+1, url_Path);
aEntry->mName.Adopt(escName);
// parse size
if (ptr > aLine+15) {
ptr -= 14;
while (nsCRT::IsAsciiDigit(*ptr)) {
size_num += ((PRInt32) (*ptr - '0')) * base;
base *= 10;
ptr--;
}
aEntry->mContentLen = size_num;
}
return NS_OK;
}
void
nsFTPDirListingConv::InitPRExplodedTime(PRExplodedTime& aTime) {
aTime.tm_usec = 0;
aTime.tm_sec = 0;
aTime.tm_min = 0;
aTime.tm_hour = 0;
aTime.tm_mday = 0;
aTime.tm_month= 0;
aTime.tm_year = 0;
aTime.tm_wday = 0;
aTime.tm_yday = 0;
aTime.tm_params.tp_gmt_offset = 0;
aTime.tm_params.tp_dst_offset = 0;
}
/**
* Can this line possibly be a ls-l line?
* Ideally, one should parse the line into tokens, etc. but
* that's for later. At the moment, just test the line for
* length and examine the first token. An absolutely minimal
* line has the form
* -rwxrwxrwx 0 Dec 31 23:59 F
* which is 27 characters. If the line is at least 27 bytes
* long and the first token matches the regular expression
* [-dlbcps][r-][w-][sSx-][r-][w-][sSx-][r-][w-][tTx-]
* then accept the line *provided* there is another token on
* the line.
* As written, this function *assumes* leading whitespace has
* been removed. That's what the rest of the code assumes. It
* may not be a reasonable assumption.
* DigestBufferLines hints that the regular expression test should
* be done case insensitively. That would relax this test so it is
* *not* done. If definitive evidence exists of a server that does
* this, then it can be changed.
* This function uses a lot of macros for clarity. They probably
* should be improved.
*/
PRBool
nsFTPDirListingConv::ls_lCandidate(const char *lsLine) {
const char *cp;
if (PL_strlen(lsLine) < 27) return PR_FALSE;
if (!IS_FTYPE(lsLine[0])) return PR_FALSE;
// shorter tests first
if (!IS_RPERM(lsLine[1])) return PR_FALSE;
if (!IS_WPERM(lsLine[2])) return PR_FALSE;
if (!IS_RPERM(lsLine[4])) return PR_FALSE;
if (!IS_WPERM(lsLine[5])) return PR_FALSE;
if (!IS_RPERM(lsLine[7])) return PR_FALSE;
if (!IS_WPERM(lsLine[8])) return PR_FALSE;
if (!IS_SPERM(lsLine[3])) return PR_FALSE;
if (!IS_SPERM(lsLine[6])) return PR_FALSE;
for (cp = &lsLine[10]; *cp; ++cp)
if (!IS_LWS(*cp)) return PR_TRUE;
return PR_FALSE;
}
char *
nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
nsresult rv;
char *line = aBuffer;
char *eol;
PRBool cr = PR_FALSE;
@ -793,281 +323,53 @@ nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
*eol = '\0';
cr = PR_FALSE;
}
indexEntry *thisEntry = nsnull;
NS_NEWXPCOM(thisEntry, indexEntry);
if (!thisEntry) return nsnull;
// XXX we need to handle comments in the raw stream.
list_state state;
list_result result;
// special case windows servers who masquerade as unix servers
if (NT == mServerType && !nsCRT::IsAsciiSpace(line[8]))
mServerType = UNIX;
int type = ParseFTPList(line, &state, &result );
// check for an eplf response
if (line[0] == '+')
mServerType = EPLF;
char *escName = nsnull;
switch (mServerType) {
case UNIX:
case PETER_LEWIS:
case MACHTEN:
// if it is other than a directory, file, or link -OR- if it is a
// directory named . or .., skip over this line.
if ((type != 'd' && type != 'f' && type != 'l') ||
(result.fe_type == 'd' && result.fe_fname[0] == '.' &&
(result.fe_fnlen == 1 || (result.fe_fnlen == 2 && result.fe_fname[1] == '.'))) )
{
// don't bother w/ these lines.
if (!PL_strncmp(line, "total ", 6)
|| !PL_strncmp(line, "ls: total", 9)
|| (PL_strstr(line, "Permission denied") != NULL)
|| (PL_strstr(line, "not available") != NULL)) {
NS_DELETEXPCOM(thisEntry);
if (cr)
line = eol+2;
else
line = eol+1;
continue;
}
PRInt32 len = PL_strlen(line);
// check first character of ls -l output
// For example: "dr-x--x--x" is what we're starting with.
// sanity check for dir permission bits
if ((line[0] == 'D' || line[0] == 'd') && ls_lCandidate(line)) {
/* it's a directory */
thisEntry->mType = Dir;
thisEntry->mSupressSize = PR_TRUE;
} else if ((line[0] == 'L' || line[0] == 'l') && ls_lCandidate(line)) {
/**
* Dir Links are not displayed properly
* we need a more robust implementation
*/
thisEntry->mType = Link;
thisEntry->mSupressSize = PR_TRUE;
/* strip off " -> pathname" */
PRInt32 i;
for (i = len - 1; (i > 3) && (!nsCRT::IsAsciiSpace(line[i])
|| (line[i-1] != '>')
|| (line[i-2] != '-')
|| (line[i-3] != ' ')); i--)
; /* null body */
if (i > 3) {
line[i-3] = '\0';
len = i - 3;
}
}
rv = ParseLSLine(line, thisEntry);
if ( NS_FAILED(rv) || (thisEntry->mName.Equals("..")) || (thisEntry->mName.Equals(".")) ) {
NS_DELETEXPCOM(thisEntry);
if (cr)
line = eol+2;
else
line = eol+1;
continue;
}
break; // END UNIX, PETER_LEWIS, MACHTEN
}
case NCSA:
case TCPC:
{
escName = nsEscape(line, url_Path);
thisEntry->mName.Adopt(escName);
if (thisEntry->mName.Last() == '/') {
thisEntry->mType = Dir;
thisEntry->mName.Truncate(thisEntry->mName.Length()-1);
}
break; // END NCSA, TCPC
}
case CMS:
{
escName = nsEscape(line, url_Path);
thisEntry->mName.Adopt(escName);
break; // END CMS
}
case NT:
{
// don't bother w/ these lines.
if (!PL_strncmp(line, "total ", 6)
|| !PL_strncmp(line, "ls: total", 9)
|| (PL_strstr(line, "Permission denied") != NULL)
|| (PL_strstr(line, "not available") != NULL)) {
NS_DELETEXPCOM(thisEntry);
if (cr)
line = eol+2;
else
line = eol+1;
continue;
}
char *date, *size_s, *name;
if (PL_strlen(line) > 37) {
date = line;
line[17] = '\0';
size_s = &line[18];
line[38] = '\0';
name = &line[39];
if (PL_strstr(size_s, "<DIR>")) {
thisEntry->mType = Dir;
} else {
nsCAutoString size(size_s);
size.StripWhitespace();
thisEntry->mContentLen = atol(size.get());
}
ConvertDOSDate(date, thisEntry->mMDTM);
escName = nsEscape(name, url_Path);
thisEntry->mName.Adopt(escName);
} else {
escName = nsEscape(line, url_Path);
thisEntry->mName.Adopt(escName);
}
break; // END NT
}
case EPLF:
{
int flagcwd = 0;
int when = 0;
int flagsize = 0;
unsigned long size = 0;
PRBool processing = PR_TRUE;
while (*line && processing)
switch (*line) {
case '\t':
{
if (flagcwd) {
thisEntry->mType = Dir;
thisEntry->mContentLen = 0;
} else {
thisEntry->mType = File;
thisEntry->mContentLen = size;
}
escName = nsEscape(line+1, url_Path);
thisEntry->mName.Adopt(escName);
thisEntry->mSupressSize = !flagsize;
// Mutiply what the last modification date to get usecs.
PRInt64 usecs = LL_Zero();
PRInt64 seconds = LL_Zero();
PRInt64 multiplier = LL_Zero();
LL_I2L(seconds, when);
LL_I2L(multiplier, PR_USEC_PER_SEC);
LL_MUL(usecs, seconds, multiplier);
PR_ExplodeTime(usecs, PR_LocalTimeParameters, &thisEntry->mMDTM);
processing = PR_FALSE;
}
break;
case 's':
flagsize = 1;
size = 0;
while (*++line && nsCRT::IsAsciiDigit(*line))
size = size * 10 + (*line - '0');
break;
case 'm':
while (*++line && nsCRT::IsAsciiDigit(*line))
when = when * 10 + (*line - '0');
break;
case '/':
flagcwd = 1;
default:
while (*line) if (*line++ == ',') break;
}
break; //END EPLF
}
case OS_2:
{
if (!PL_strncmp(line, "total ", 6)
|| (PL_strstr(line, "not authorized") != NULL)
|| (PL_strstr(line, "Path not found") != NULL)
|| (PL_strstr(line, "No Files") != NULL)) {
NS_DELETEXPCOM(thisEntry);
if (cr)
line = eol+2;
else
line = eol+1;
continue;
}
char *name;
nsCAutoString str;
if (PL_strstr(line, "DIR")) {
thisEntry->mType = Dir;
thisEntry->mSupressSize = PR_TRUE;
}
else
thisEntry->mType = File;
PRInt32 error;
line[18] = '\0';
str = line;
str.StripWhitespace();
thisEntry->mContentLen = str.ToInteger(&error, 10);
InitPRExplodedTime(thisEntry->mMDTM);
line[37] = '\0';
str = &line[35];
thisEntry->mMDTM.tm_month = str.ToInteger(&error, 10) - 1;
line[40] = '\0';
str = &line[38];
thisEntry->mMDTM.tm_mday = str.ToInteger(&error, 10);
line[43] = '\0';
str = &line[41];
thisEntry->mMDTM.tm_year = str.ToInteger(&error, 10);
line[48] = '\0';
str = &line[46];
thisEntry->mMDTM.tm_hour = str.ToInteger(&error, 10);
line[51] = '\0';
str = &line[49];
thisEntry->mMDTM.tm_min = str.ToInteger(&error, 10);
name = &line[53];
escName = nsEscape(name, url_Path);
thisEntry->mName.Adopt(escName);
break;
}
default:
{
escName = nsEscape(line, url_Path);
thisEntry->mName.Adopt(escName);
break; // END default (catches GENERIC, DCTS)
}
} // end switch (mServerType)
// blast the index entry into the indexFormat buffer as a 201: line.
aString.Append("201: ");
// FILENAME
aString.Append(thisEntry->mName);
if (thisEntry->mType == Dir)
aString.Append(nsDependentCString(result.fe_fname, result.fe_fnlen));
/* Bug 143770
if (type == 'd')
aString.Append('/');
*/
aString.Append(' ');
// CONTENT LENGTH
if (!thisEntry->mSupressSize) {
aString.AppendInt(thisEntry->mContentLen);
} else {
aString.Append('0');
if (type == 'f')
{
for (int i = 0; i < sizeof(result.fe_size); i++)
{
if (result.fe_size[i] != '\0')
aString.Append((const char*)&result.fe_size[i], 1);
}
aString.Append(' ');
}
else
aString.Append("0 ");
// MODIFIED DATE
char buffer[256] = "";
@ -1075,40 +377,27 @@ nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
// the application/http-index-format specs
// viewers of such a format can then reformat this into the
// current locale (or anything else they choose)
// make sure we don't have a null time struct
if ((thisEntry->mMDTM.tm_month + thisEntry->mMDTM.tm_mday +
thisEntry->mMDTM.tm_year + thisEntry->mMDTM.tm_hour +
thisEntry->mMDTM.tm_min) != nsnull) {
PR_FormatTimeUSEnglish(buffer, sizeof(buffer),
"%a, %d %b %Y %H:%M:%S", &thisEntry->mMDTM );
}
"%a, %d %b %Y %H:%M:%S", &result.fe_time );
char *escapedDate = nsEscape(buffer, url_Path);
aString.Append(escapedDate);
nsMemory::Free(escapedDate);
aString.Append(' ');
// ENTRY TYPE
switch (thisEntry->mType) {
case Dir:
if (type == 'd')
aString.Append("DIRECTORY");
break;
case Link:
else if (type == 'l')
aString.Append("SYMBOLIC-LINK");
break;
default:
else
aString.Append("FILE");
}
aString.Append(' ');
aString.Append(char(nsCRT::LF)); // complete this line
// END 201:
NS_DELETEXPCOM(thisEntry);
if (cr)
line = eol+2;
else

Просмотреть файл

@ -53,62 +53,6 @@
}
static NS_DEFINE_CID(kFTPDirListingConverterCID, NS_FTPDIRLISTINGCONVERTER_CID);
// The nsFTPDirListingConv stream converter converts a stream of type "text/ftp-dir-SERVER_TYPE"
// (where SERVER_TYPE is one of the following):
//
// SERVER TYPES:
// generic
// unix
// dcts
// ncsa
// peter_lewis
// machten
// cms
// tcpc
// vms
// nt
// eplf
//
// nsFTPDirListingConv converts the raw ascii text directory generated via a FTP
// LIST or NLST command, to the application/http-index-format MIME-type.
// For more info see: http://www.area.com/~roeber/file_format.html
typedef enum _FTP_Server_Type {
GENERIC,
UNIX,
DCTS,
NCSA,
PETER_LEWIS,
MACHTEN,
CMS,
TCPC,
VMS,
NT,
EPLF,
OS_2,
ERROR_TYPE
} FTP_Server_Type;
typedef enum _FTPentryType {
Dir,
File,
Link
} FTPentryType;
// indexEntry is the data structure used to maintain directory entry information.
class indexEntry {
public:
indexEntry() { mContentLen = 0; mType = File; mSupressSize = PR_FALSE; };
nsCString mName; // the file or dir name
FTPentryType mType;
PRInt32 mContentLen; // length of the file
nsCString mContentType; // type of the file
PRExplodedTime mMDTM; // modified time
PRBool mSupressSize; // supress the size info from display
};
class nsFTPDirListingConv : public nsIStreamConverter {
public:
// nsISupports methods
@ -128,53 +72,15 @@ public:
virtual ~nsFTPDirListingConv();
nsresult Init();
// For factory creation.
static NS_METHOD
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
nsresult rv;
if (aOuter)
return NS_ERROR_NO_AGGREGATION;
nsFTPDirListingConv* _s = new nsFTPDirListingConv();
if (_s == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(_s);
rv = _s->Init();
if (NS_FAILED(rv)) {
delete _s;
return rv;
}
rv = _s->QueryInterface(aIID, aResult);
NS_RELEASE(_s);
return rv;
}
private:
// Get the application/http-index-format headers
nsresult GetHeaders(nsACString& str, nsIURI* uri);
// util parsing methods
PRInt8 MonthNumber(const char *aCStr);
PRBool IsLSDate(char *aCStr);
// date conversion/parsing methods
PRBool ConvertUNIXDate(char *aCStr, PRExplodedTime& outDate);
PRBool ConvertDOSDate(char *aCStr, PRExplodedTime& outDate);
// line parsing methods
nsresult ParseLSLine(char *aLine, indexEntry *aEntry);
nsresult ParseVMSLine(char *aLine, indexEntry *aEntry);
void InitPRExplodedTime(PRExplodedTime& aTime);
PRBool ls_lCandidate(const char *aLine);
char* DigestBufferLines(char *aBuffer, nsCString &aString);
// member data
FTP_Server_Type mServerType; // what kind of server is the data coming from?
nsCAutoString mBuffer; // buffered data.
PRBool mSentHeading; // have we sent 100, 101, 200, and 300 lines yet?
nsIStreamListener *mFinalListener; // this guy gets the converted data via his OnDataAvailable()
nsIChannel *mPartChannel; // the channel for the given part we're processing.
// one channel per part.