зеркало из https://github.com/mozilla/pjs.git
1131 строка
33 KiB
C++
1131 строка
33 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
|
*
|
|
* 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 Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "nsFTPDirListingConv.h"
|
|
#include "nsIAllocator.h"
|
|
#include "plstr.h"
|
|
#include "prlog.h"
|
|
#include "nsIHTTPChannel.h"
|
|
#include "nsIAtom.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIGenericFactory.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsEscape.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsIStringStream.h"
|
|
#include "nsILocaleService.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsDateTimeFormatCID.h"
|
|
#include "nsIStreamListener.h"
|
|
#include "nsCRT.h"
|
|
static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID);
|
|
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
|
|
static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID);
|
|
static NS_DEFINE_CID(kDateTimeCID, NS_DATETIMEFORMAT_CID);
|
|
|
|
#if defined(PR_LOGGING)
|
|
//
|
|
// Log module for FTP dir listing stream converter logging...
|
|
//
|
|
// To enable logging (see prlog.h for full details):
|
|
//
|
|
// set NSPR_LOG_MODULES=nsFTPDirListConv:5
|
|
// set NSPR_LOG_FILE=nspr.log
|
|
//
|
|
// this enables PR_LOG_DEBUG level information and places all output in
|
|
// the file nspr.log
|
|
//
|
|
PRLogModuleInfo* gFTPDirListConvLog = nsnull;
|
|
|
|
#endif /* PR_LOGGING */
|
|
|
|
// nsISupports implementation
|
|
NS_IMPL_ISUPPORTS3(nsFTPDirListingConv, nsIStreamConverter, nsIStreamListener, nsIStreamObserver);
|
|
|
|
// 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(aFromType);
|
|
const char *from = fromMIMEString.GetBuffer();
|
|
NS_ASSERTION(from, "nsCString/PRUnichar acceptance failed.");
|
|
|
|
from = PL_strstr(from, "/ftp-dir-");
|
|
if (!from) return NS_ERROR_FAILURE;
|
|
from += 9;
|
|
fromMIMEString = from;
|
|
|
|
if (-1 != fromMIMEString.Find("unix")) {
|
|
mServerType = UNIX;
|
|
} else if (-1 != fromMIMEString.Find("nt")) {
|
|
mServerType = NT;
|
|
} else if (-1 != fromMIMEString.Find("dcts")) {
|
|
mServerType = DCTS;
|
|
} else if (-1 != fromMIMEString.Find("ncsa")) {
|
|
mServerType = NCSA;
|
|
} else if (-1 != fromMIMEString.Find("peter_lewis")) {
|
|
mServerType = PETER_LEWIS;
|
|
} else if (-1 != fromMIMEString.Find("machten")) {
|
|
mServerType = MACHTEN;
|
|
} else if (-1 != fromMIMEString.Find("cms")) {
|
|
mServerType = CMS;
|
|
} else if (-1 != fromMIMEString.Find("tcpc")) {
|
|
mServerType = TCPC;
|
|
} else if (-1 != fromMIMEString.Find("vms")) {
|
|
mServerType = VMS;
|
|
} else {
|
|
mServerType = GENERIC;
|
|
}
|
|
|
|
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);
|
|
nsCAutoString convertedData;
|
|
|
|
NS_ASSERTION(aCtxt, "FTP dir conversion needs the context");
|
|
// build up the 300: line
|
|
nsXPIDLCString spec;
|
|
nsCOMPtr<nsIURI> uri(do_QueryInterface(aCtxt, &rv));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = uri->GetSpec(getter_Copies(spec));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
convertedData.Append("300: ");
|
|
convertedData.Append(spec);
|
|
convertedData.Append(LF);
|
|
// END 300:
|
|
|
|
// build up the column heading; 200:
|
|
convertedData.Append("200: filename content-length last-modified file-type\n");
|
|
// END 200:
|
|
|
|
|
|
// 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.GetBuffer()) );
|
|
#else
|
|
char *unescData = convertedData.ToNewCString();
|
|
nsUnescape(unescData);
|
|
printf("::OnData() sending the following %d bytes...\n\n%s\n\n", convertedData.Length(), unescData);
|
|
nsAllocator::Free(unescData);
|
|
#endif // DEBUG_valeski
|
|
|
|
// send the converted data out.
|
|
nsCOMPtr<nsIInputStream> inputData;
|
|
nsCOMPtr<nsISupports> inputDataSup;
|
|
|
|
rv = NS_NewStringInputStream(getter_AddRefs(inputDataSup), convertedData);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
inputData = do_QueryInterface(inputDataSup, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*_retval = inputData.get();
|
|
NS_ADDREF(*_retval);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stream converter service calls this to initialize the actual stream converter (us).
|
|
NS_IMETHODIMP
|
|
nsFTPDirListingConv::AsyncConvertData(const PRUnichar *aFromType, const PRUnichar *aToType,
|
|
nsIStreamListener *aListener, nsISupports *aCtxt) {
|
|
NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into FTP dir listing converter");
|
|
nsresult rv;
|
|
|
|
// hook up our final listener. this guy gets the various On*() calls we want to throw
|
|
// at him.
|
|
mFinalListener = aListener;
|
|
NS_ADDREF(mFinalListener);
|
|
|
|
// set our internal state to reflect the server type
|
|
nsCString fromMIMEString(aFromType);
|
|
const char *from = fromMIMEString.GetBuffer();
|
|
NS_ASSERTION(from, "nsCString/PRUnichar acceptance failed.");
|
|
|
|
from = PL_strstr(from, "/ftp-dir-");
|
|
if (!from) return NS_ERROR_FAILURE;
|
|
from += 9;
|
|
fromMIMEString = from;
|
|
|
|
if (-1 != fromMIMEString.Find("unix")) {
|
|
mServerType = UNIX;
|
|
} else if (-1 != fromMIMEString.Find("nt")) {
|
|
mServerType = NT;
|
|
} else if (-1 != fromMIMEString.Find("dcts")) {
|
|
mServerType = DCTS;
|
|
} else if (-1 != fromMIMEString.Find("ncsa")) {
|
|
mServerType = NCSA;
|
|
} else if (-1 != fromMIMEString.Find("peter_lewis")) {
|
|
mServerType = PETER_LEWIS;
|
|
} else if (-1 != fromMIMEString.Find("machten")) {
|
|
mServerType = MACHTEN;
|
|
} else if (-1 != fromMIMEString.Find("cms")) {
|
|
mServerType = CMS;
|
|
} else if (-1 != fromMIMEString.Find("tcpc")) {
|
|
mServerType = TCPC;
|
|
} else if (-1 != fromMIMEString.Find("vms")) {
|
|
mServerType = VMS;
|
|
} else {
|
|
mServerType = GENERIC;
|
|
}
|
|
|
|
|
|
// 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)");
|
|
nsIURI *uri;
|
|
rv = aCtxt->QueryInterface(NS_GET_IID(nsIURI), (void**)&uri);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = NS_NewInputStreamChannel(uri, "application/http-index-format", -1, // XXX fix contentLength
|
|
nsnull, // inStr
|
|
nsnull, // loadGroup
|
|
nsnull, // notificationCallbacks
|
|
nsIChannel::LOAD_NORMAL,
|
|
nsnull, // originalURI
|
|
0, 0,
|
|
&mPartChannel);
|
|
NS_RELEASE(uri);
|
|
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.GetBuffer()));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// nsIStreamListener implementation
|
|
NS_IMETHODIMP
|
|
nsFTPDirListingConv::OnDataAvailable(nsIChannel *channel, nsISupports *ctxt,
|
|
nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
|
|
NS_ASSERTION(channel, "FTP dir listing stream converter needs a channel");
|
|
nsresult rv;
|
|
PRUint32 read, streamLen;
|
|
nsCAutoString indexFormat;
|
|
indexFormat.SetCapacity(72); // quick guess
|
|
|
|
rv = inStr->Available(&streamLen);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
char *buffer = (char*)nsAllocator::Alloc(streamLen + 1);
|
|
rv = inStr->Read(buffer, streamLen, &read);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// the dir listings are ascii text, null terminate this sucker.
|
|
buffer[streamLen] = '\0';
|
|
|
|
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("nsFTPDirListingConv::OnData(channel = %x, ctxt = %x, inStr = %x, sourceOffset = %d, count = %d)\n", channel, ctxt, inStr, sourceOffset, count));
|
|
|
|
if (!mBuffer.IsEmpty()) {
|
|
// we have data left over from a previous OnDataAvailable() call.
|
|
// combine the buffers so we don't lose any data.
|
|
mBuffer.Append(buffer);
|
|
nsAllocator::Free(buffer);
|
|
buffer = mBuffer.ToNewCString();
|
|
mBuffer.Truncate();
|
|
}
|
|
|
|
#ifndef DEBUG_valeski
|
|
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
|
|
|
|
|
|
if (!mSentHeading) {
|
|
// build up the 300: line
|
|
char *spec = nsnull;
|
|
nsIURI *uri = nsnull;
|
|
rv = channel->GetURI(&uri);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = uri->GetSpec(&spec);
|
|
NS_RELEASE(uri);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
indexFormat.Append("300: ");
|
|
indexFormat.Append(spec);
|
|
indexFormat.Append(LF);
|
|
nsAllocator::Free(spec);
|
|
// END 300:
|
|
|
|
// build up the column heading; 200:
|
|
indexFormat.Append("200: filename content-length last-modified file-type\n");
|
|
// END 200:
|
|
|
|
mSentHeading = PR_TRUE;
|
|
}
|
|
|
|
char *line = buffer;
|
|
line = DigestBufferLines(line, indexFormat);
|
|
|
|
#ifndef DEBUG_valeski
|
|
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
|
|
indexFormat.Length(), indexFormat.GetBuffer()) );
|
|
#else
|
|
char *unescData = indexFormat.ToNewCString();
|
|
nsUnescape(unescData);
|
|
printf("::OnData() sending the following %d bytes...\n\n%s\n\n", indexFormat.Length(), unescData);
|
|
nsAllocator::Free(unescData);
|
|
#endif // DEBUG_valeski
|
|
|
|
// if there's any data left over, buffer it.
|
|
if (line && *line) {
|
|
mBuffer.Append(line);
|
|
PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() buffering the following %d bytes...\n\n%s\n\n",
|
|
PL_strlen(line), line) );
|
|
}
|
|
|
|
nsAllocator::Free(buffer);
|
|
|
|
// send the converted data out.
|
|
nsCOMPtr<nsIInputStream> inputData;
|
|
nsCOMPtr<nsISupports> inputDataSup;
|
|
|
|
rv = NS_NewStringInputStream(getter_AddRefs(inputDataSup), indexFormat);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
inputData = do_QueryInterface(inputDataSup, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = mFinalListener->OnDataAvailable(mPartChannel, ctxt, inputData, 0, indexFormat.Length());
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// nsIStreamObserver implementation
|
|
NS_IMETHODIMP
|
|
nsFTPDirListingConv::OnStartRequest(nsIChannel *channel, nsISupports *ctxt) {
|
|
// we don't care about start. move along... but start masqeurading
|
|
// as the http-index channel now.
|
|
return mFinalListener->OnStartRequest(mPartChannel, ctxt);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFTPDirListingConv::OnStopRequest(nsIChannel *channel, nsISupports *ctxt,
|
|
nsresult status, const PRUnichar *errorMsg) {
|
|
// we don't care about stop. move along...
|
|
return mFinalListener->OnStopRequest(mPartChannel, ctxt, status, errorMsg);
|
|
}
|
|
|
|
|
|
// nsFTPDirListingConv methods
|
|
nsFTPDirListingConv::nsFTPDirListingConv() {
|
|
NS_INIT_ISUPPORTS();
|
|
mFinalListener = nsnull;
|
|
mServerType = GENERIC;
|
|
mPartChannel = nsnull;
|
|
mSentHeading = PR_FALSE;
|
|
}
|
|
|
|
nsFTPDirListingConv::~nsFTPDirListingConv() {
|
|
NS_IF_RELEASE(mFinalListener);
|
|
NS_IF_RELEASE(mPartChannel);
|
|
NS_RELEASE(mLocale);
|
|
NS_RELEASE(mDateTimeFormat);
|
|
}
|
|
|
|
nsresult
|
|
nsFTPDirListingConv::Init() {
|
|
// Grab the nsILocale for date parsing.
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsILocaleService, localeSvc, kLocaleServiceCID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = localeSvc->GetApplicationLocale(&mLocale);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Grab the date/time formatter
|
|
nsIComponentManager *comMgr;
|
|
rv = NS_GetGlobalComponentManager(&comMgr);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = comMgr->CreateInstance(kDateTimeCID, nsnull, NS_GET_IID(nsIDateTimeFormat), (void**)&mDateTimeFormat);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
#if defined(PR_LOGGING)
|
|
//
|
|
// Initialize the global PRLogModule for FTP Protocol logging
|
|
// if necessary...
|
|
//
|
|
if (nsnull == gFTPDirListConvLog) {
|
|
gFTPDirListConvLog = PR_NewLogModule("nsFTPDirListingConv");
|
|
}
|
|
#endif /* PR_LOGGING */
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
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':
|
|
if (c1 == 'a' || c1 == 'A')
|
|
if (c2 == 'r' || c2 == 'R')
|
|
rv = 2;
|
|
else if (c2 == 'y' || c2 == 'Y')
|
|
rv = 4;
|
|
break;
|
|
case 'a': case 'A':
|
|
if (c1 == 'p' || c1 == 'P')
|
|
rv = 3;
|
|
else if (c1 == 'u' || c1 == 'U')
|
|
rv = 7;
|
|
break;
|
|
case 'j': case 'J':
|
|
if (c1 == 'u' || c1 == 'U') {
|
|
if (c2 == 'n' || c2 == 'N')
|
|
rv = 5;
|
|
else if (c2 == 'l' || c2 == 'L')
|
|
rv = 6;
|
|
} else if (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 (!nsString::IsAlpha(*aCStr++) || !nsString::IsAlpha(*aCStr++) || !nsString::IsAlpha(*aCStr++))
|
|
return PR_FALSE;
|
|
|
|
/* space */
|
|
if (*aCStr != ' ')
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* space or digit */
|
|
if ((*aCStr != ' ') && !nsString::IsDigit(*aCStr))
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* digit */
|
|
if (!nsString::IsDigit(*aCStr))
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* space */
|
|
if (*aCStr != ' ')
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* space or digit */
|
|
if ((*aCStr != ' ') && !nsString::IsDigit(*aCStr))
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* digit */
|
|
if (!nsString::IsDigit(*aCStr))
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* colon or digit */
|
|
if ((*aCStr != ':') && !nsString::IsDigit(*aCStr))
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* digit */
|
|
if (!nsString::IsDigit(*aCStr))
|
|
return PR_FALSE;
|
|
aCStr++;
|
|
|
|
/* space or digit */
|
|
if ((*aCStr != ' ') && !nsString::IsDigit(*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, PRTime& 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;
|
|
}
|
|
|
|
// set the out param
|
|
outDate = PR_ImplodeTime(&curTime);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool
|
|
nsFTPDirListingConv::ConvertVMSDate(char *aCStr, PRTime& outDate) {
|
|
|
|
PRExplodedTime curTime;
|
|
|
|
InitPRExplodedTime(curTime);
|
|
|
|
char *col;
|
|
|
|
if ((col = strtok(aCStr, "-")) == NULL)
|
|
return PR_FALSE;
|
|
|
|
// DAY
|
|
nsCAutoString intStr(col);
|
|
PRInt32 error;
|
|
curTime.tm_mday = intStr.ToInteger(&error, 10);
|
|
|
|
if ((col = strtok(nsnull, "-")) == nsnull || (curTime.tm_month = MonthNumber(col)) < 0)
|
|
return PR_FALSE;
|
|
|
|
// YEAR
|
|
if ((col = strtok(NULL, " ")) == NULL)
|
|
return PR_FALSE;
|
|
|
|
|
|
intStr = col;
|
|
curTime.tm_year = intStr.ToInteger(&error, 10);
|
|
|
|
// HOUR
|
|
if ((col = strtok(NULL, ":")) == NULL)
|
|
return PR_FALSE;
|
|
|
|
intStr = col;
|
|
curTime.tm_hour = intStr.ToInteger(&error, 10);
|
|
|
|
// MINS
|
|
if ((col = strtok(NULL, " ")) == NULL)
|
|
return PR_FALSE;
|
|
|
|
intStr = col;
|
|
curTime.tm_min = intStr.ToInteger(&error, 10);
|
|
|
|
outDate = PR_ImplodeTime(&curTime);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool
|
|
nsFTPDirListingConv::ConvertDOSDate(char *aCStr, PRTime& outDate) {
|
|
|
|
PRExplodedTime curTime, nowTime;
|
|
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &nowTime);
|
|
PRInt16 century = (nowTime.tm_year/1000 + nowTime.tm_year/100) * 100;
|
|
|
|
InitPRExplodedTime(curTime);
|
|
|
|
curTime.tm_month = (aCStr[1]-'0')-1;
|
|
|
|
curTime.tm_mday = (((aCStr[3]-'0')*10) + aCStr[4]-'0');
|
|
curTime.tm_year = century + (((aCStr[6]-'0')*10) + aCStr[7]-'0');
|
|
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 = PR_ImplodeTime(&curTime);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsresult
|
|
nsFTPDirListingConv::ParseLSLine(char *aLine, indexEntry *aEntry) {
|
|
|
|
PRInt32 base=1;
|
|
PRInt32 size_num=0;
|
|
char save_char;
|
|
char *ptr, *escName;
|
|
|
|
for (ptr = &aLine[PL_strlen(aLine) - 1];
|
|
(ptr > aLine+13) && (!nsString::IsSpace(*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(nsString::IsSpace(*ptr)) {
|
|
*ptr = '\0';
|
|
break;
|
|
}
|
|
escName = nsEscape(aLine, url_Path);
|
|
aEntry->mName = escName;
|
|
nsAllocator::Free(escName);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
escName = nsEscape(ptr+1, url_Path);
|
|
aEntry->mName = escName;
|
|
nsAllocator::Free(escName);
|
|
|
|
// parse size
|
|
if(ptr > aLine+15) {
|
|
ptr -= 14;
|
|
while (nsString::IsDigit(*ptr)) {
|
|
size_num += ((PRInt32) (*ptr - '0')) * base;
|
|
base *= 10;
|
|
ptr--;
|
|
}
|
|
|
|
aEntry->mContentLen = size_num;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFTPDirListingConv::ParseVMSLine(char *aLine, indexEntry *aEntry) {
|
|
int i, j;
|
|
int32 ialloc;
|
|
char *cp, *cpd, *cps, date[64], *sp = " ", *escName;
|
|
static char ThisYear[5];
|
|
static PRBool HaveYear = PR_FALSE;
|
|
|
|
// Get rid of blank lines, and information lines.
|
|
// Valid lines have the ';' version number token.
|
|
if (!PL_strlen(aLine) || (cp=PL_strchr(aLine, ';')) == NULL)
|
|
{
|
|
// entry_info->display = FALSE;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Cut out file or directory name at VMS version number.
|
|
*cp++ ='\0';
|
|
escName = nsEscape(aLine, url_Path);
|
|
aEntry->mName = escName;
|
|
nsAllocator::Free(escName);
|
|
|
|
// Cast VMS file and directory names to lowercase.
|
|
aEntry->mName.ToLowerCase();
|
|
|
|
/** Uppercase terminal .z's or _z's. **/
|
|
//if ((--i > 2) && entry_info->filename[i] == 'z' &&
|
|
// (entry_info->filename[i-1] == '.' || entry_info->filename[i-1] == '_'))
|
|
// entry_info->filename[i] = 'Z';
|
|
|
|
/** Convert any tabs in rest of line to spaces. **/
|
|
cps = cp-1;
|
|
while ((cps=PL_strchr(cps+1, '\t')) != NULL)
|
|
*cps = ' ';
|
|
|
|
/** Collapse serial spaces. **/
|
|
i = 0; j = 1;
|
|
cps = cp;
|
|
while (cps[j] != '\0')
|
|
{
|
|
if (cps[i] == ' ' && cps[j] == ' ')
|
|
j++;
|
|
else
|
|
cps[++i] = cps[j++];
|
|
}
|
|
cps[++i] = '\0';
|
|
|
|
/* Save the current year.
|
|
* It could be wrong on New Year's Eve.
|
|
*/
|
|
if (!HaveYear)
|
|
{
|
|
PRExplodedTime curTime;
|
|
PR_ExplodeTime(PR_Now(), PR_GMTParameters, &curTime);
|
|
nsCAutoString year(curTime.tm_year);
|
|
const char *yearCStr = year.GetBuffer();
|
|
for (PRInt8 x = 0; x < 4; x++)
|
|
ThisYear[x] = yearCStr[x];
|
|
HaveYear = PR_TRUE;
|
|
}
|
|
|
|
// get the date.
|
|
if ((cpd=PL_strchr(cp, '-')) != NULL &&
|
|
PL_strlen(cpd) > 9 && nsString::IsDigit(*(cpd-1)) &&
|
|
nsString::IsAlpha(*(cpd+1)) && *(cpd+4) == '-')
|
|
{
|
|
|
|
/* Month
|
|
*/
|
|
*(cpd+4) = '\0';
|
|
|
|
// lowercase these two chars
|
|
PRInt8 shift = 'A' - 'a';
|
|
if ( !(*(cpd+2) <= 'z') && !(*(cpd+2) >= 'a') )
|
|
*(cpd+2) = *(cpd+2) - shift;
|
|
|
|
if ( !(*(cpd+3) <= 'z') && !(*(cpd+2) >= 'a') )
|
|
*(cpd+3) = *(cpd+3) - shift;
|
|
|
|
sprintf(date, "%.32s ", cpd+1);
|
|
*(cpd+4) = '-';
|
|
|
|
/** Day **/
|
|
*cpd = '\0';
|
|
if (nsString::IsDigit(*(cpd-2)))
|
|
sprintf(date+4, "%.32s ", cpd-2);
|
|
else
|
|
sprintf(date+4, " %.32s ", cpd-1);
|
|
*cpd = '-';
|
|
|
|
/** Time or Year **/
|
|
if (!PL_strncmp(ThisYear, cpd+5, 4) && PL_strlen(cpd) > 15 && *(cpd+12) == ':')
|
|
{
|
|
*(cpd+15) = '\0';
|
|
sprintf(date+7, "%.32s", cpd+10);
|
|
*(cpd+15) = ' ';
|
|
}
|
|
else
|
|
{
|
|
*(cpd+9) = '\0';
|
|
sprintf(date+7, " %.32s", cpd+5);
|
|
*(cpd+9) = ' ';
|
|
}
|
|
|
|
ConvertVMSDate(date, aEntry->mMDTM);
|
|
}
|
|
|
|
/* get the size
|
|
*/
|
|
if ((cpd=PL_strchr(cp, '/')) != NULL)
|
|
{
|
|
/* Appears be in used/allocated format */
|
|
cps = cpd;
|
|
while (nsString::IsDigit(*(cps-1)))
|
|
cps--;
|
|
if (cps < cpd)
|
|
*cpd = '\0';
|
|
aEntry->mContentLen = atol(cps);
|
|
cps = cpd+1;
|
|
while (nsString::IsDigit(*cps))
|
|
cps++;
|
|
*cps = '\0';
|
|
ialloc = atoi(cpd+1);
|
|
/* Check if used is in blocks or bytes */
|
|
if (aEntry->mContentLen <= ialloc)
|
|
aEntry->mContentLen *= 512;
|
|
}
|
|
else if ((cps=strtok(cp, sp)) != NULL)
|
|
{
|
|
/* We just initialized on the version number
|
|
* Now let's find a lone, size number
|
|
*/
|
|
while ((cps=strtok(NULL, sp)) != NULL)
|
|
{
|
|
cpd = cps;
|
|
while (nsString::IsDigit(*cpd))
|
|
cpd++;
|
|
if (*cpd == '\0')
|
|
{
|
|
/* Assume it's blocks */
|
|
aEntry->mContentLen = atol(cps) * 512;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
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 = PR_LocalTimeParameters(&aTime);;
|
|
}
|
|
|
|
char *
|
|
nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCAutoString &aString) {
|
|
nsresult rv;
|
|
char *line = aBuffer;
|
|
char *eol;
|
|
PRBool cr = PR_FALSE;
|
|
|
|
// while we have new lines, parse 'em into application/http-index-format.
|
|
while ( line && (eol = PL_strchr(line, LF)) ) {
|
|
// yank any carriage returns too.
|
|
if (eol > line && *(eol-1) == CR) {
|
|
eol--;
|
|
*eol = '\0';
|
|
cr = PR_TRUE;
|
|
} else {
|
|
*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.
|
|
|
|
// special case windows servers who masquerade as unix servers
|
|
if (NT == mServerType && !nsString::IsSpace(line[8]))
|
|
mServerType = UNIX;
|
|
|
|
|
|
char *escName = nsnull;
|
|
switch (mServerType) {
|
|
|
|
case UNIX:
|
|
case PETER_LEWIS:
|
|
case MACHTEN:
|
|
{
|
|
// don't bother w/ these lines.
|
|
if(!PL_strncmp(line, "total ", 6)
|
|
|| (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.
|
|
if (line[0] == 'D' || line[0] == 'd') {
|
|
/* it's a directory */
|
|
thisEntry->mType = Dir;
|
|
thisEntry->mSupressSize = PR_TRUE;
|
|
} else if (line[0] == 'l') {
|
|
thisEntry->mType = Link;
|
|
thisEntry->mSupressSize = PR_TRUE;
|
|
|
|
/* strip off " -> pathname" */
|
|
PRInt32 i;
|
|
for (i = len - 1; (i > 3) && (!nsString::IsSpace(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;
|
|
}
|
|
|
|
/* add a trailing slash to all directories */
|
|
if(thisEntry->mType == Dir)
|
|
thisEntry->mName.Append('/');
|
|
|
|
break; // END UNIX, PETER_LEWIS, MACHTEN
|
|
}
|
|
|
|
case NCSA:
|
|
case TCPC:
|
|
{
|
|
escName = nsEscape(line, url_Path);
|
|
thisEntry->mName = escName;
|
|
nsAllocator::Free(escName);
|
|
|
|
if (thisEntry->mName.Last() == '/')
|
|
thisEntry->mType = Dir;
|
|
|
|
break; // END NCSA, TCPC
|
|
}
|
|
|
|
case CMS:
|
|
{
|
|
escName = nsEscape(line, url_Path);
|
|
thisEntry->mName = escName;
|
|
nsAllocator::Free(escName);
|
|
break; // END CMS
|
|
}
|
|
case VMS:
|
|
{
|
|
/* Interpret and edit LIST output from VMS server */
|
|
/* and convert information lines to zero length. */
|
|
rv = ParseVMSLine(line, thisEntry);
|
|
if (NS_FAILED(rv)) {
|
|
NS_DELETEXPCOM(thisEntry);
|
|
if (cr)
|
|
line = eol+2;
|
|
else
|
|
line = eol+1;
|
|
continue;
|
|
}
|
|
|
|
/** Trim off VMS directory extensions **/
|
|
//len = thisEntry->mName.Length();
|
|
// if ((len > 4) && !PL_strcmp(&entry_info->filename[len-4], ".dir"))
|
|
// {
|
|
// entry_info->filename[len-4] = '\0';
|
|
// entry_info->special_type = NET_DIRECTORY;
|
|
// remove_size=TRUE; /* size is not useful */
|
|
// /* add trailing slash to directories
|
|
// */
|
|
// StrAllocCat(entry_info->filename, "/");
|
|
// }
|
|
break; // END VMS
|
|
}
|
|
case NT:
|
|
{
|
|
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.GetBuffer());
|
|
}
|
|
|
|
ConvertDOSDate(date, thisEntry->mMDTM);
|
|
|
|
escName = nsEscape(name, url_Path);
|
|
thisEntry->mName = escName;
|
|
} else {
|
|
escName = nsEscape(line, url_Path);
|
|
thisEntry->mName = escName;
|
|
}
|
|
nsAllocator::Free(escName);
|
|
break; // END NT
|
|
}
|
|
default:
|
|
{
|
|
escName = nsEscape(line, url_Path);
|
|
thisEntry->mName = escName;
|
|
nsAllocator::Free(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);
|
|
aString.Append(' ');
|
|
|
|
// CONTENT LENGTH
|
|
if (!thisEntry->mSupressSize) {
|
|
aString.Append(thisEntry->mContentLen);
|
|
} else {
|
|
aString.Append('0');
|
|
}
|
|
aString.Append(' ');
|
|
|
|
// MODIFIED DATE
|
|
nsString lDate;
|
|
nsStr::Initialize(lDate, eOneByte);
|
|
rv = mDateTimeFormat->FormatPRTime(mLocale, kDateFormatShort, kTimeFormatNoSeconds, thisEntry->mMDTM, lDate);
|
|
if (NS_FAILED(rv)) {
|
|
NS_DELETEXPCOM(thisEntry);
|
|
return nsnull;
|
|
}
|
|
|
|
char *escapedDate = nsEscape(lDate.GetBuffer(), url_Path);
|
|
|
|
aString.Append(escapedDate);
|
|
nsAllocator::Free(escapedDate);
|
|
aString.Append(' ');
|
|
|
|
|
|
// ENTRY TYPE
|
|
switch (thisEntry->mType) {
|
|
case Dir:
|
|
aString.Append("DIRECTORY");
|
|
break;
|
|
case Link:
|
|
aString.Append("SYM-LINK");
|
|
break;
|
|
default:
|
|
aString.Append("FILE");
|
|
}
|
|
aString.Append(' ');
|
|
|
|
aString.Append(LF); // complete this line
|
|
// END 201:
|
|
|
|
NS_DELETEXPCOM(thisEntry);
|
|
|
|
if (cr)
|
|
line = eol+2;
|
|
else
|
|
line = eol+1;
|
|
} // end while(eol)
|
|
|
|
return line;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewFTPDirListingConv(nsFTPDirListingConv** aFTPDirListingConv)
|
|
{
|
|
NS_PRECONDITION(aFTPDirListingConv != nsnull, "null ptr");
|
|
if (! aFTPDirListingConv)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*aFTPDirListingConv = new nsFTPDirListingConv();
|
|
if (! *aFTPDirListingConv)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(*aFTPDirListingConv);
|
|
return (*aFTPDirListingConv)->Init();
|
|
}
|