Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / netwerk / streamconv / converters / nsFTPDirListingConv.cpp
blob27da76582f97d301437a4a5b1fc1febddeff5db0
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
13 * License.
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.
22 * Contributor(s):
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"
40 #include "nsMemory.h"
41 #include "plstr.h"
42 #include "prlog.h"
43 #include "nsIServiceManager.h"
44 #include "nsIGenericFactory.h"
45 #include "nsXPIDLString.h"
46 #include "nsReadableUtils.h"
47 #include "nsCOMPtr.h"
48 #include "nsEscape.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"
55 #include "nsCRT.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
71 // the file nspr.log
73 PRLogModuleInfo* gFTPDirListConvLog = nsnull;
75 #endif /* PR_LOGGING */
77 // nsISupports implementation
78 NS_IMPL_ISUPPORTS3(nsFTPDirListingConv,
79 nsIStreamConverter,
80 nsIStreamListener,
81 nsIRequestObserver)
84 // nsIStreamConverter implementation
85 NS_IMETHODIMP
86 nsFTPDirListingConv::Convert(nsIInputStream *aFromStream,
87 const char *aFromType,
88 const char *aToType,
89 nsISupports *aCtxt, nsIInputStream **_retval) {
90 return NS_ERROR_NOT_IMPLEMENTED;
94 // Stream converter service calls this to initialize the actual stream converter (us).
95 NS_IMETHODIMP
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
101 // at him.
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"));
108 return NS_OK;
112 // nsIStreamListener implementation
113 NS_IMETHODIMP
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");
118 nsresult rv;
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);
148 mBuffer.Truncate();
151 #ifndef DEBUG_dougt
152 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer.get()) );
153 #else
154 printf("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer);
155 #endif // DEBUG_dougt
157 nsCAutoString indexFormat;
158 if (!mSentHeading) {
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;
170 char *line = buffer;
171 line = DigestBufferLines(line, indexFormat);
173 #ifndef DEBUG_dougt
174 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
175 indexFormat.Length(), indexFormat.get()) );
176 #else
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.
186 if (line && *line) {
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());
200 return rv;
204 // nsIRequestObserver implementation
205 NS_IMETHODIMP
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);
212 NS_IMETHODIMP
213 nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
214 nsresult aStatus) {
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);
231 nsresult
232 nsFTPDirListingConv::Init() {
233 #if defined(PR_LOGGING)
235 // Initialize the global PRLogModule for FTP Protocol logging
236 // if necessary...
238 if (nsnull == gFTPDirListConvLog) {
239 gFTPDirListConvLog = PR_NewLogModule("nsFTPDirListingConv");
241 #endif /* PR_LOGGING */
243 return NS_OK;
246 nsresult
247 nsFTPDirListingConv::GetHeaders(nsACString& headers,
248 nsIURI* uri)
250 nsresult rv = NS_OK;
251 // build up 300 line
252 headers.AppendLiteral("300: ");
254 // Bug 111117 - don't print the password
255 nsCAutoString pw;
256 nsCAutoString spec;
257 uri->GetPassword(pw);
258 if (!pw.IsEmpty()) {
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;
266 } else {
267 rv = uri->GetAsciiSpec(spec);
268 if (NS_FAILED(rv)) return rv;
270 headers.Append(spec);
272 headers.Append(char(nsCRT::LF));
273 // END 300:
275 // build up the column heading; 200:
276 headers.AppendLiteral("200: filename content-length last-modified file-type\n");
277 // END 200:
278 return rv;
281 char *
282 nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
283 char *line = aBuffer;
284 char *eol;
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) {
291 eol--;
292 *eol = '\0';
293 cr = PR_TRUE;
294 } else {
295 *eol = '\0';
296 cr = PR_FALSE;
299 list_state state;
300 list_result result;
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] == '.'))) )
310 if (cr)
311 line = eol+2;
312 else
313 line = eol+1;
315 continue;
318 // blast the index entry into the indexFormat buffer as a 201: line.
319 aString.AppendLiteral("201: ");
320 // FILENAME
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, " -> ");
325 if (offset) {
326 result.fe_fnlen = offset - result.fe_fname;
330 nsCAutoString buf;
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("\" ");
337 // CONTENT LENGTH
339 if (type != 'd')
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);
347 aString.Append(' ');
349 else
350 aString.AppendLiteral("0 ");
353 // MODIFIED DATE
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);
365 aString.Append(' ');
367 // ENTRY TYPE
368 if (type == 'd')
369 aString.AppendLiteral("DIRECTORY");
370 else if (type == 'l')
371 aString.AppendLiteral("SYMBOLIC-LINK");
372 else
373 aString.AppendLiteral("FILE");
375 aString.Append(' ');
377 aString.Append(char(nsCRT::LF)); // complete this line
378 // END 201:
380 if (cr)
381 line = eol+2;
382 else
383 line = eol+1;
384 } // end while(eol)
386 return line;
389 nsresult
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();