1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Bradley Baetz <bbaetz@student.usyd.edu.au>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsFTPDirListingConv.h"
43 #include "nsIServiceManager.h"
44 #include "nsIGenericFactory.h"
45 #include "nsXPIDLString.h"
46 #include "nsReadableUtils.h"
49 #include "nsNetUtil.h"
50 #include "nsStringStream.h"
51 #include "nsILocaleService.h"
52 #include "nsIComponentManager.h"
53 #include "nsDateTimeFormatCID.h"
54 #include "nsIStreamListener.h"
56 #include "nsMimeTypes.h"
57 #include "nsAutoPtr.h"
59 #include "ParseFTPList.h"
61 #if defined(PR_LOGGING)
63 // Log module for FTP dir listing stream converter logging...
65 // To enable logging (see prlog.h for full details):
67 // set NSPR_LOG_MODULES=nsFTPDirListConv:5
68 // set NSPR_LOG_FILE=nspr.log
70 // this enables PR_LOG_DEBUG level information and places all output in
73 PRLogModuleInfo
* gFTPDirListConvLog
= nsnull
;
75 #endif /* PR_LOGGING */
77 // nsISupports implementation
78 NS_IMPL_ISUPPORTS3(nsFTPDirListingConv
,
84 // nsIStreamConverter implementation
86 nsFTPDirListingConv::Convert(nsIInputStream
*aFromStream
,
87 const char *aFromType
,
89 nsISupports
*aCtxt
, nsIInputStream
**_retval
) {
90 return NS_ERROR_NOT_IMPLEMENTED
;
94 // Stream converter service calls this to initialize the actual stream converter (us).
96 nsFTPDirListingConv::AsyncConvertData(const char *aFromType
, const char *aToType
,
97 nsIStreamListener
*aListener
, nsISupports
*aCtxt
) {
98 NS_ASSERTION(aListener
&& aFromType
&& aToType
, "null pointer passed into FTP dir listing converter");
100 // hook up our final listener. this guy gets the various On*() calls we want to throw
102 mFinalListener
= aListener
;
103 NS_ADDREF(mFinalListener
);
105 PR_LOG(gFTPDirListConvLog
, PR_LOG_DEBUG
,
106 ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n"));
112 // nsIStreamListener implementation
114 nsFTPDirListingConv::OnDataAvailable(nsIRequest
* request
, nsISupports
*ctxt
,
115 nsIInputStream
*inStr
, PRUint32 sourceOffset
, PRUint32 count
) {
116 NS_ASSERTION(request
, "FTP dir listing stream converter needs a request");
120 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
, &rv
);
121 NS_ENSURE_SUCCESS(rv
, rv
);
123 PRUint32 read
, streamLen
;
125 rv
= inStr
->Available(&streamLen
);
126 NS_ENSURE_SUCCESS(rv
, rv
);
128 nsAutoArrayPtr
<char> buffer(new char[streamLen
+ 1]);
129 NS_ENSURE_TRUE(buffer
, NS_ERROR_OUT_OF_MEMORY
);
131 rv
= inStr
->Read(buffer
, streamLen
, &read
);
132 NS_ENSURE_SUCCESS(rv
, rv
);
134 // the dir listings are ascii text, null terminate this sucker.
135 buffer
[streamLen
] = '\0';
137 PR_LOG(gFTPDirListConvLog
, PR_LOG_DEBUG
, ("nsFTPDirListingConv::OnData(request = %x, ctxt = %x, inStr = %x, sourceOffset = %d, count = %d)\n", request
, ctxt
, inStr
, sourceOffset
, count
));
139 if (!mBuffer
.IsEmpty()) {
140 // we have data left over from a previous OnDataAvailable() call.
141 // combine the buffers so we don't lose any data.
142 mBuffer
.Append(buffer
);
144 buffer
= new char[mBuffer
.Length()+1];
145 NS_ENSURE_TRUE(buffer
, NS_ERROR_OUT_OF_MEMORY
);
147 strncpy(buffer
, mBuffer
.get(), mBuffer
.Length()+1);
152 PR_LOG(gFTPDirListConvLog
, PR_LOG_DEBUG
, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen
, buffer
.get()) );
154 printf("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen
, buffer
);
155 #endif // DEBUG_dougt
157 nsCAutoString indexFormat
;
159 // build up the 300: line
160 nsCOMPtr
<nsIURI
> uri
;
161 rv
= channel
->GetURI(getter_AddRefs(uri
));
162 NS_ENSURE_SUCCESS(rv
, rv
);
164 rv
= GetHeaders(indexFormat
, uri
);
165 NS_ENSURE_SUCCESS(rv
, rv
);
167 mSentHeading
= PR_TRUE
;
171 line
= DigestBufferLines(line
, indexFormat
);
174 PR_LOG(gFTPDirListConvLog
, PR_LOG_DEBUG
, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
175 indexFormat
.Length(), indexFormat
.get()) );
177 char *unescData
= ToNewCString(indexFormat
);
178 NS_ENSURE_TRUE(unescData
, NS_ERROR_OUT_OF_MEMORY
);
180 nsUnescape(unescData
);
181 printf("::OnData() sending the following %d bytes...\n\n%s\n\n", indexFormat
.Length(), unescData
);
182 nsMemory::Free(unescData
);
183 #endif // DEBUG_dougt
185 // if there's any data left over, buffer it.
187 mBuffer
.Append(line
);
188 PR_LOG(gFTPDirListConvLog
, PR_LOG_DEBUG
, ("::OnData() buffering the following %d bytes...\n\n%s\n\n",
189 PL_strlen(line
), line
) );
192 // send the converted data out.
193 nsCOMPtr
<nsIInputStream
> inputData
;
195 rv
= NS_NewCStringInputStream(getter_AddRefs(inputData
), indexFormat
);
196 NS_ENSURE_SUCCESS(rv
, rv
);
198 rv
= mFinalListener
->OnDataAvailable(request
, ctxt
, inputData
, 0, indexFormat
.Length());
204 // nsIRequestObserver implementation
206 nsFTPDirListingConv::OnStartRequest(nsIRequest
* request
, nsISupports
*ctxt
) {
207 // we don't care about start. move along... but start masqeurading
208 // as the http-index channel now.
209 return mFinalListener
->OnStartRequest(request
, ctxt
);
213 nsFTPDirListingConv::OnStopRequest(nsIRequest
* request
, nsISupports
*ctxt
,
215 // we don't care about stop. move along...
217 return mFinalListener
->OnStopRequest(request
, ctxt
, aStatus
);
221 // nsFTPDirListingConv methods
222 nsFTPDirListingConv::nsFTPDirListingConv() {
223 mFinalListener
= nsnull
;
224 mSentHeading
= PR_FALSE
;
227 nsFTPDirListingConv::~nsFTPDirListingConv() {
228 NS_IF_RELEASE(mFinalListener
);
232 nsFTPDirListingConv::Init() {
233 #if defined(PR_LOGGING)
235 // Initialize the global PRLogModule for FTP Protocol logging
238 if (nsnull
== gFTPDirListConvLog
) {
239 gFTPDirListConvLog
= PR_NewLogModule("nsFTPDirListingConv");
241 #endif /* PR_LOGGING */
247 nsFTPDirListingConv::GetHeaders(nsACString
& headers
,
252 headers
.AppendLiteral("300: ");
254 // Bug 111117 - don't print the password
257 uri
->GetPassword(pw
);
259 rv
= uri
->SetPassword(EmptyCString());
260 if (NS_FAILED(rv
)) return rv
;
261 rv
= uri
->GetAsciiSpec(spec
);
262 if (NS_FAILED(rv
)) return rv
;
263 headers
.Append(spec
);
264 rv
= uri
->SetPassword(pw
);
265 if (NS_FAILED(rv
)) return rv
;
267 rv
= uri
->GetAsciiSpec(spec
);
268 if (NS_FAILED(rv
)) return rv
;
270 headers
.Append(spec
);
272 headers
.Append(char(nsCRT::LF
));
275 // build up the column heading; 200:
276 headers
.AppendLiteral("200: filename content-length last-modified file-type\n");
282 nsFTPDirListingConv::DigestBufferLines(char *aBuffer
, nsCString
&aString
) {
283 char *line
= aBuffer
;
285 PRBool cr
= PR_FALSE
;
287 // while we have new lines, parse 'em into application/http-index-format.
288 while ( line
&& (eol
= PL_strchr(line
, nsCRT::LF
)) ) {
289 // yank any carriage returns too.
290 if (eol
> line
&& *(eol
-1) == nsCRT::CR
) {
302 int type
= ParseFTPList(line
, &state
, &result
);
304 // if it is other than a directory, file, or link -OR- if it is a
305 // directory named . or .., skip over this line.
306 if ((type
!= 'd' && type
!= 'f' && type
!= 'l') ||
307 (result
.fe_type
== 'd' && result
.fe_fname
[0] == '.' &&
308 (result
.fe_fnlen
== 1 || (result
.fe_fnlen
== 2 && result
.fe_fname
[1] == '.'))) )
318 // blast the index entry into the indexFormat buffer as a 201: line.
319 aString
.AppendLiteral("201: ");
322 // parsers for styles 'U' and 'W' handle sequence " -> " themself
323 if (state
.lstyle
!= 'U' && state
.lstyle
!= 'W') {
324 const char* offset
= strstr(result
.fe_fname
, " -> ");
326 result
.fe_fnlen
= offset
- result
.fe_fname
;
331 aString
.Append('\"');
332 aString
.Append(NS_EscapeURL(Substring(result
.fe_fname
,
333 result
.fe_fname
+result
.fe_fnlen
),
334 esc_Minimal
|esc_OnlyASCII
|esc_Forced
,buf
));
335 aString
.AppendLiteral("\" ");
341 for (int i
= 0; i
< int(sizeof(result
.fe_size
)); ++i
)
343 if (result
.fe_size
[i
] != '\0')
344 aString
.Append((const char*)&result
.fe_size
[i
], 1);
350 aString
.AppendLiteral("0 ");
354 char buffer
[256] = "";
355 // Note: The below is the RFC822/1123 format, as required by
356 // the application/http-index-format specs
357 // viewers of such a format can then reformat this into the
358 // current locale (or anything else they choose)
359 PR_FormatTimeUSEnglish(buffer
, sizeof(buffer
),
360 "%a, %d %b %Y %H:%M:%S", &result
.fe_time
);
362 char *escapedDate
= nsEscape(buffer
, url_Path
);
363 aString
.Append(escapedDate
);
364 nsMemory::Free(escapedDate
);
369 aString
.AppendLiteral("DIRECTORY");
370 else if (type
== 'l')
371 aString
.AppendLiteral("SYMBOLIC-LINK");
373 aString
.AppendLiteral("FILE");
377 aString
.Append(char(nsCRT::LF
)); // complete this line
390 NS_NewFTPDirListingConv(nsFTPDirListingConv
** aFTPDirListingConv
)
392 NS_PRECONDITION(aFTPDirListingConv
!= nsnull
, "null ptr");
393 if (! aFTPDirListingConv
)
394 return NS_ERROR_NULL_POINTER
;
396 *aFTPDirListingConv
= new nsFTPDirListingConv();
397 if (! *aFTPDirListingConv
)
398 return NS_ERROR_OUT_OF_MEMORY
;
400 NS_ADDREF(*aFTPDirListingConv
);
401 return (*aFTPDirListingConv
)->Init();