diff --git a/netwerk/streamconv/converters/nsFTPDirListingConv.cpp b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp new file mode 100644 index 00000000000..e3240bf8b50 --- /dev/null +++ b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp @@ -0,0 +1,976 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nsFTPDirListingConv.h" +#include "nsIAllocator.h" +#include "plstr.h" +#include "nsIStringStream.h" +#include "nsIHTTPChannel.h" +#include "nsIAtom.h" +#include "nsIServiceManager.h" +#include "nsIGenericFactory.h" +#include "nsCOMPtr.h" +#include "nsIIOService.h" +#include "nsIStringStream.h" +#include "nsILocaleService.h" +#include "nsIComponentManager.h" +#include "nsDateTimeFormatCID.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); + +PRBool is_charAlpha(char chr) { + if ( ( ((chr >= 'a') && (chr <= 'z')) || ((chr >= 'A') && (chr <= 'Z')) ) ) + return PR_TRUE; + return PR_FALSE; +} + +#define NET_IS_SPACE(x) ((((unsigned int) (x)) > 0x7f) ? 0 : (x == ' ' || x == '\r' || x == '\n' || x == '\t') ) + +#define IS_DIGITx(x) ( (PRBool) ( x >= '0' && x <= '9' ) ? PR_TRUE : PR_FALSE ) + + +// nsISupports implementation +NS_IMPL_ISUPPORTS2(nsFTPDirListingConv, nsIStreamConverter, nsIStreamListener); + + +// nsIStreamConverter implementation + +// No syncronous conversion at this time. +NS_IMETHODIMP +nsFTPDirListingConv::Convert(nsIInputStream *aFromStream, + const PRUnichar *aFromType, + const PRUnichar *aToType, + nsISupports *aCtxt, nsIInputStream **_retval) { + 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, + 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; + + NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = serv->NewInputStreamChannel(uri, "application/http-index-format", nsnull, &mPartChannel); + NS_RELEASE(uri); + if (NS_FAILED(rv)) return rv; + + 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; + nsCString indexFormat; + + rv = inStr->GetLength(&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'; + + 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(); + } + + 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('\n'); + nsAllocator::Free(spec); + // END 300: + + // build up the column heading; 200: + indexFormat.Append("200: Filename\tContent-Length\tContent-Type\tFile-type\tLast-Modified\n"); + // END 200: + + mSentHeading = PR_TRUE; + } + + char *line = buffer; + char *eol; + + // while we have new lines, parse 'em into application/http-index-format. + while ( line && (eol = PL_strchr(line, '\n')) ) { + *eol = '\0'; + indexEntry *thisEntry = nsnull; + NS_NEWXPCOM(thisEntry, indexEntry); + if (!thisEntry) return NS_ERROR_OUT_OF_MEMORY; + + // XXX we need to handle comments in the raw stream. + + 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); + 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 (toupper(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) && (!NET_IS_SPACE(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 == "..") || (thisEntry->mName == ".") ) { + NS_DELETEXPCOM(thisEntry); + 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: + { + thisEntry->mName = line; + if (thisEntry->mName.Last() == '/') + thisEntry->mType = Dir; + + break; // END NCSA, TCPC + } + + case CMS: + { + thisEntry->mName = line; + 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); + 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, "")) { + thisEntry->mType = Dir; + } else { + nsCAutoString size(size_s); + size.StripWhitespace(); + thisEntry->mContentLen = atol(size.GetBuffer()); + } + + thisEntry->mMDTM = ConvertDOSDate(date); + + thisEntry->mName = name; + } + else + { + thisEntry->mName = line; + } + break; // END NT + } + default: + { + thisEntry->mName = line; + break; // END default (catches GENERIC, DCTS) + } + + } // end switch (mServerType) + + // blast the index entry into the indexFormat buffer as a 201: line. + indexFormat.Append("201: "); + // FILENAME + indexFormat.Append(thisEntry->mName); + indexFormat.Append('\t'); + + // CONTENT LENGTH + indexFormat.Append(thisEntry->mContentLen); + indexFormat.Append('\t'); + + // CONTENT TYPE (not very useful for ftp listings) + // XXX this field is currently meaningless for FTP + //indexFormat.Append(thisEntry->mContentType); + indexFormat.Append("n/a"); + indexFormat.Append('\t'); + + // ENTRY TYPE + switch (thisEntry->mType) { + case Dir: + indexFormat.Append("Directory"); + break; + case Link: + indexFormat.Append("Sym-Link"); + break; + default: + indexFormat.Append("File"); + } + indexFormat.Append('\t'); + + // MODIFIED DATE + nsString lDate; + rv = mDateTimeFormat->FormatPRTime(mLocale, kDateFormatShort, kTimeFormatNoSeconds, thisEntry->mMDTM, lDate); + if (NS_FAILED(rv)) return rv; + indexFormat.Append(lDate); + + + indexFormat.Append('\n'); // complete this line + // END 201: + + NS_DELETEXPCOM(thisEntry); + line = eol+1; + } // end while(eol) + + + + // if there's any data left over, buffer it. + if (line && *line) { + mBuffer.Append(line); + } + + nsAllocator::Free(buffer); + + // send the converted data out. + nsIInputStream *inputData = nsnull; + nsISupports *inputDataSup = nsnull; + + rv = NS_NewStringInputStream(&inputDataSup, indexFormat); + if (NS_FAILED(rv)) return rv; + + rv = inputDataSup->QueryInterface(NS_GET_IID(nsIInputStream), (void**)&inputData); + NS_RELEASE(inputDataSup); + if (NS_FAILED(rv)) return rv; + + rv = mFinalListener->OnDataAvailable(mPartChannel, ctxt, inputData, 0, indexFormat.Length()); + NS_RELEASE(inputData); + 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; + + 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]; + + switch (*month) { + case 'f': case 'F': + return 1; + case 'm': case 'M': + if (c1 == 'a' || c1 == 'A') + if (c2 == 'r' || c2 == 'R') + return 2; + else if (c2 == 'y' || c2 == 'Y') + return 4; + break; + case 'a': case 'A': + if (c1 == 'p' || c1 == 'P') + return 3; + else if (c1 == 'u' || c1 == 'U') + return 7; + break; + + case 'j': case 'J': + if (c1 == 'u' || c1 == 'U') + if (c2 == 'n' || c2 == 'N') + return 5; + else if (c2 == 'l' || c2 == 'L') + return 6; + else if (c1 == 'a' || c1 == 'A') + return 0; + break; + case 's': case 'S': + return 8; + case 'o': case 'O': + return 9; + case 'n': case 'N': + return 10; + case 'd': case 'D': + return 11; + default: + return -1; + } + + return -1; +} + + +// 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 (!is_charAlpha(*aCStr++) || !is_charAlpha(*aCStr++) || !is_charAlpha(*aCStr++)) + return PR_FALSE; + + /* space */ + if (*aCStr != ' ') + return PR_FALSE; + aCStr++; + + /* space or digit */ + if ((*aCStr != ' ') && !IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* digit */ + if (!IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* space */ + if (*aCStr != ' ') + return PR_FALSE; + aCStr++; + + /* space or digit */ + if ((*aCStr != ' ') && !IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* digit */ + if (!IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* colon or digit */ + if ((*aCStr != ':') && !IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* digit */ + if (!IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* space or digit */ + if ((*aCStr != ' ') && !IS_DIGITx(*aCStr)) + return PR_FALSE; + aCStr++; + + /* space */ + if (*aCStr != ' ') + return PR_FALSE; + aCStr++; + + return PR_TRUE; +} + + +// Converts a date string from 'ls -l' to a time_t number +// "Sep 1 1990 " or +// "Sep 11 11:59 " or +// "Dec 12 1989 " or +// "FCv 23 1990 " ... +// Returns 0 on error. +PRTime +nsFTPDirListingConv::ConvertUNIXDate(char *aCStr) { + + PRExplodedTime curTime; + + PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &curTime); + + char *bcol = aCStr; /* Column begin */ + char *ecol; /* Column end */ + + // MONTH + char tmpChar = bcol[3]; + bcol[3] = '\0'; + nsCAutoString lMonth(bcol); + bcol[3] = tmpChar; + lMonth.ToUpperCase(); + + if ((curTime.tm_month = MonthNumber(lMonth.GetBuffer())) < 0) + return (PRTime) 0; + + // DAY + ecol = &bcol[3]; + while (*ecol++ == ' ') ; /* Spool to other side of day */ + while (*ecol++ != ' ') ; + *--ecol = '\0'; + + PRInt32 error; + nsCAutoString day(bcol); + curTime.tm_mday = day.ToInteger(&error, 10); + //time_info->tm_mday = atoi(bcol); + curTime.tm_wday = 0; + //time_info->tm_wday = 0; + curTime.tm_yday = 0; + //time_info->tm_yday = 0; + + // YEAR + bcol = ++ecol; + if ((ecol = PL_strchr(bcol, ':')) == NULL) { + nsCAutoString intStr(bcol); + curTime.tm_year = intStr.ToInteger(&error, 10); + //time_info->tm_year = atoi(bcol)-1900; + curTime.tm_sec = 0; + //time_info->tm_sec = 0; + curTime.tm_min = 0; + //time_info->tm_min = 0; + curTime.tm_hour = 0; + //time_info->tm_hour = 0; + } 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'; + curTime.tm_sec = 0; + //time_info->tm_sec = 0; + nsCAutoString intStr(++ecol); + curTime.tm_min = intStr.ToInteger(&error, 10); + //time_info->tm_min = atoi(++ecol); /* Right side of ':' */ + + intStr = bcol; + curTime.tm_hour = intStr.ToInteger(&error, 10); + //time_info->tm_hour = atoi(bcol); /* Left side of ':' */ + //if (mktime(time_info) > curtime) + // --time_info->tm_year; + } + + return PR_ImplodeTime(&curTime); // compacts the curTime struct into a PRTime (64 bit int) + //return ((tval = mktime(time_info)) == -1 ? (time_t) 0 : tval); +} + +PRTime +nsFTPDirListingConv::ConvertVMSDate(char *aCStr) { + + PRExplodedTime curTime; + + PR_ExplodeTime(PR_Now(), PR_GMTParameters, &curTime); + + char *col; + + if ((col = strtok(aCStr, "-")) == NULL) + return (PRTime) 0; + + // DAY + nsCAutoString intStr(col); + PRInt32 error; + curTime.tm_mday = intStr.ToInteger(&error, 10); + //time_info->tm_mday = atoi(col); + curTime.tm_wday = 0; + //time_info->tm_wday = 0; + curTime.tm_yday = 0; + //time_info->tm_yday = 0; + + if ((col = strtok(nsnull, "-")) == nsnull || (curTime.tm_month = MonthNumber(col)) < 0) + //if ((col = strtok(NULL, "-")) == NULL || (time_info->tm_mon = MonthNumber(col)) < 0) + return (PRTime) 0; + + // YEAR + if ((col = strtok(NULL, " ")) == NULL) + return (PRTime) 0; + + + intStr = col; + curTime.tm_year = intStr.ToInteger(&error, 10); + //time_info->tm_year = atoi(col)-1900; + + // HOUR + if ((col = strtok(NULL, ":")) == NULL) + return (PRTime) 0; + + intStr = col; + curTime.tm_hour = intStr.ToInteger(&error, 10); + //time_info->tm_hour = atoi(col); + + // MINS + if ((col = strtok(NULL, " ")) == NULL) + return (PRTime) 0; + + intStr = col; + curTime.tm_min = intStr.ToInteger(&error, 10); + //time_info->tm_min = atoi(col); + curTime.tm_sec = 0; + //time_info->tm_sec = 0; + + return PR_ImplodeTime(&curTime); + //return ((tval = mktime(time_info)) < 0 ? (time_t) 0 : tval); +} + +PRTime +nsFTPDirListingConv::ConvertDOSDate(char *aCStr) { + + PRExplodedTime curTime; + + PR_ExplodeTime(PR_Now(), PR_GMTParameters, &curTime); + + curTime.tm_month = (aCStr[1]-'0')-1; + + curTime.tm_mday = (((aCStr[3]-'0')*10) + aCStr[4]-'0'); + curTime.tm_year = (((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'); + + curTime.tm_wday = 0; + curTime.tm_yday = 0; + curTime.tm_sec = 0; + + return PR_ImplodeTime(&curTime); +} + +nsresult +nsFTPDirListingConv::ParseLSLine(char *aLine, indexEntry *aEntry) { + + PRInt32 base=1; + PRInt32 size_num=0; + char save_char; + char *ptr; + + for (ptr = &aLine[PL_strlen(aLine) - 1]; + (ptr > aLine+13) && (!NET_IS_SPACE(*ptr) || !IsLSDate(ptr-12)); ptr--) + ; /* null body */ + save_char = *ptr; + *ptr = '\0'; + if (ptr > aLine+13) { + aEntry->mMDTM = ConvertUNIXDate(ptr-12); + } 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(NET_IS_SPACE(*ptr)) { + *ptr = '\0'; + break; + } + aEntry->mName = aLine; + + return NS_OK; + } + + // escape and copy + aEntry->mName = ptr+1; + + // parse size + if(ptr > aLine+15) { + ptr -= 14; + while (IS_DIGITx(*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 = " "; + 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'; + /* escape and copy + */ + aEntry->mName = aLine; + + /** 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 && IS_DIGITx(*(cpd-1)) && + is_charAlpha(*(cpd+1)) && *(cpd+4) == '-') + { + + /* Month + */ + *(cpd+4) = '\0'; + *(cpd+2) = tolower(*(cpd+2)); + *(cpd+3) = tolower(*(cpd+3)); + sprintf(date, "%.32s ", cpd+1); + *(cpd+4) = '-'; + + /** Day **/ + *cpd = '\0'; + if (IS_DIGITx(*(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) = ' '; + } + + aEntry->mMDTM = ConvertVMSDate(date); + } + + /* get the size + */ + if ((cpd=PL_strchr(cp, '/')) != NULL) + { + /* Appears be in used/allocated format */ + cps = cpd; + while (IS_DIGITx(*(cps-1))) + cps--; + if (cps < cpd) + *cpd = '\0'; + aEntry->mContentLen = atol(cps); + cps = cpd+1; + while (IS_DIGITx(*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 (IS_DIGITx(*cpd)) + cpd++; + if (*cpd == '\0') + { + /* Assume it's blocks */ + aEntry->mContentLen = atol(cps) * 512; + break; + } + } + } + return NS_OK; +} + + +//////////////////////////////////////////////////////////////////////// +// Factory +//////////////////////////////////////////////////////////////////////// +FTPDirListingFactory::FTPDirListingFactory(const nsCID &aClass, + const char* className, + const char* progID) + : mClassID(aClass), mClassName(className), mProgID(progID) +{ + NS_INIT_ISUPPORTS(); +} + +FTPDirListingFactory::~FTPDirListingFactory() +{ + NS_ASSERTION(mRefCnt == 0, "non-zero refcnt at destruction"); +} + +NS_IMPL_ISUPPORTS(FTPDirListingFactory, NS_GET_IID(nsIFactory)); + +NS_IMETHODIMP +FTPDirListingFactory::CreateInstance(nsISupports *aOuter, + const nsIID &aIID, + void **aResult) +{ + if (! aResult) + return NS_ERROR_NULL_POINTER; + + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + *aResult = nsnull; + + nsresult rv = NS_OK; + + nsISupports *inst = nsnull; + if (mClassID.Equals(kFTPDirListingConverterCID)) { + nsFTPDirListingConv *conv = new nsFTPDirListingConv(); + if (!conv) return NS_ERROR_OUT_OF_MEMORY; + rv = conv->Init(); + if (NS_FAILED(rv)) return rv; + + rv = conv->QueryInterface(NS_GET_IID(nsISupports), (void**)&inst); + if (NS_FAILED(rv)) return rv; + } + else { + return NS_ERROR_NO_INTERFACE; + } + + if (!inst) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(inst); + *aResult = inst; + NS_RELEASE(inst); + return rv; +} + +nsresult +FTPDirListingFactory::LockFactory(PRBool aLock){ + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////// +// Factory END +//////////////////////////////////////////////////////////////////////// + diff --git a/netwerk/streamconv/converters/nsFTPDirListingConv.h b/netwerk/streamconv/converters/nsFTPDirListingConv.h new file mode 100644 index 00000000000..7d9219c5d69 --- /dev/null +++ b/netwerk/streamconv/converters/nsFTPDirListingConv.h @@ -0,0 +1,184 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef __nsftpdirlistingdconv__h__ +#define __nsftpdirlistingdconv__h__ + +#include "nsIStreamConverter.h" +#include "nsIChannel.h" +#include "nsIURI.h" +#include "nsString2.h" +#include "nsILocale.h" +#include "nsIDateTimeFormat.h" + +#include "nsIFactory.h" + +#define NS_FTPDIRLISTINGCONVERTER_CID \ +{ /* 14C0E880-623E-11d3-A178-0050041CAF44 */ \ + 0x14c0e880, \ + 0x623e, \ + 0x11d3, \ + {0xa1, 0x78, 0x00, 0x50, 0x04, 0x1c, 0xaf, 0x44} \ +} +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 +// +// 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 +} 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; mMDTM = 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 + PRTime mMDTM; // modified time + PRBool mSupressSize; // supress the size info from display +}; + +class nsFTPDirListingConv : public nsIStreamConverter { +public: + // nsISupports methods + NS_DECL_ISUPPORTS + + // nsIStreamConverter methods + NS_DECL_NSISTREAMCONVERTER + + // nsIStreamListener methods + NS_DECL_NSISTREAMLISTENER + + // nsIStreamObserver methods + NS_DECL_NSISTREAMOBSERVER + + // nsFTPDirListingConv methods + nsFTPDirListingConv(); + 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: + // util parsing methods + PRInt8 MonthNumber(const char *aCStr); + PRBool IsLSDate(char *aCStr); + + // date conversion/parsing methods + PRTime ConvertUNIXDate(char *aCStr); + PRTime ConvertVMSDate(char *aCStr); + PRTime ConvertDOSDate(char *aCStr); + + // line parsing methods + nsresult ParseLSLine(char *aLine, indexEntry *aEntry); + nsresult ParseVMSLine(char *aLine, indexEntry *aEntry); + + // 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. + nsILocale *mLocale; // the application locale for date formating + nsIDateTimeFormat *mDateTimeFormat; // for the actual date time formatting. +}; + +////////////////////////////////////////////////// +// FACTORY +class FTPDirListingFactory : public nsIFactory +{ +public: + FTPDirListingFactory(const nsCID &aClass, const char* className, const char* progID); + + // nsISupports methods + NS_DECL_ISUPPORTS + + // nsIFactory methods + NS_IMETHOD CreateInstance(nsISupports *aOuter, + const nsIID &aIID, + void **aResult); + + NS_IMETHOD LockFactory(PRBool aLock); + +protected: + virtual ~FTPDirListingFactory(); + +protected: + nsCID mClassID; + const char* mClassName; + const char* mProgID; +}; + +#endif /* __nsftpdirlistingdconv__h__ */