932 lines
27 KiB
C++
932 lines
27 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 {
|
|
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 {
|
|
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...
|
|
nsCOMPtr<nsILoadGroup> loadgroup;
|
|
nsresult rv = mPartChannel->GetLoadGroup(getter_AddRefs(loadgroup));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (loadgroup)
|
|
(void)loadgroup->RemoveChannel(mPartChannel, nsnull, nsnull, nsnull);
|
|
|
|
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, 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 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 = 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
thisEntry->mName.Truncate(thisEntry->mName.Length()-1);
|
|
}
|
|
|
|
break; // END NCSA, TCPC
|
|
}
|
|
|
|
case CMS:
|
|
{
|
|
escName = nsEscape(line, url_Path);
|
|
thisEntry->mName = escName;
|
|
nsAllocator::Free(escName);
|
|
break; // END CMS
|
|
}
|
|
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->FormatPRExplodedTime(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("SYMBOLIC-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();
|
|
}
|