/* -*- 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 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 inputData; nsCOMPtr 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 inputData; nsCOMPtr 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 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, "")) { 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(); }