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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Bradley Baetz <bbaetz@student.usyd.edu.au>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsFTPDirListingConv.h"
40 : #include "nsMemory.h"
41 : #include "plstr.h"
42 : #include "prlog.h"
43 : #include "nsIServiceManager.h"
44 : #include "nsXPIDLString.h"
45 : #include "nsReadableUtils.h"
46 : #include "nsCOMPtr.h"
47 : #include "nsEscape.h"
48 : #include "nsNetUtil.h"
49 : #include "nsStringStream.h"
50 : #include "nsIComponentManager.h"
51 : #include "nsDateTimeFormatCID.h"
52 : #include "nsIStreamListener.h"
53 : #include "nsCRT.h"
54 : #include "nsMimeTypes.h"
55 : #include "nsAutoPtr.h"
56 :
57 : #include "ParseFTPList.h"
58 :
59 : #if defined(PR_LOGGING)
60 : //
61 : // Log module for FTP dir listing stream converter logging...
62 : //
63 : // To enable logging (see prlog.h for full details):
64 : //
65 : // set NSPR_LOG_MODULES=nsFTPDirListConv:5
66 : // set NSPR_LOG_FILE=nspr.log
67 : //
68 : // this enables PR_LOG_DEBUG level information and places all output in
69 : // the file nspr.log
70 : //
71 : PRLogModuleInfo* gFTPDirListConvLog = nsnull;
72 :
73 : #endif /* PR_LOGGING */
74 :
75 : // nsISupports implementation
76 255 : NS_IMPL_ISUPPORTS3(nsFTPDirListingConv,
77 : nsIStreamConverter,
78 : nsIStreamListener,
79 : nsIRequestObserver)
80 :
81 :
82 : // nsIStreamConverter implementation
83 : NS_IMETHODIMP
84 0 : nsFTPDirListingConv::Convert(nsIInputStream *aFromStream,
85 : const char *aFromType,
86 : const char *aToType,
87 : nsISupports *aCtxt, nsIInputStream **_retval) {
88 0 : return NS_ERROR_NOT_IMPLEMENTED;
89 : }
90 :
91 :
92 : // Stream converter service calls this to initialize the actual stream converter (us).
93 : NS_IMETHODIMP
94 17 : nsFTPDirListingConv::AsyncConvertData(const char *aFromType, const char *aToType,
95 : nsIStreamListener *aListener, nsISupports *aCtxt) {
96 17 : NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into FTP dir listing converter");
97 :
98 : // hook up our final listener. this guy gets the various On*() calls we want to throw
99 : // at him.
100 17 : mFinalListener = aListener;
101 17 : NS_ADDREF(mFinalListener);
102 :
103 17 : PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG,
104 : ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n"));
105 :
106 17 : return NS_OK;
107 : }
108 :
109 :
110 : // nsIStreamListener implementation
111 : NS_IMETHODIMP
112 17 : nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
113 : nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
114 17 : NS_ASSERTION(request, "FTP dir listing stream converter needs a request");
115 :
116 : nsresult rv;
117 :
118 34 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
119 17 : NS_ENSURE_SUCCESS(rv, rv);
120 :
121 : PRUint32 read, streamLen;
122 :
123 17 : rv = inStr->Available(&streamLen);
124 17 : NS_ENSURE_SUCCESS(rv, rv);
125 :
126 51 : nsAutoArrayPtr<char> buffer(new char[streamLen + 1]);
127 17 : NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
128 :
129 17 : rv = inStr->Read(buffer, streamLen, &read);
130 17 : NS_ENSURE_SUCCESS(rv, rv);
131 :
132 : // the dir listings are ascii text, null terminate this sucker.
133 17 : buffer[streamLen] = '\0';
134 :
135 17 : PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("nsFTPDirListingConv::OnData(request = %x, ctxt = %x, inStr = %x, sourceOffset = %d, count = %d)\n", request, ctxt, inStr, sourceOffset, count));
136 :
137 17 : if (!mBuffer.IsEmpty()) {
138 : // we have data left over from a previous OnDataAvailable() call.
139 : // combine the buffers so we don't lose any data.
140 0 : mBuffer.Append(buffer);
141 :
142 0 : buffer = new char[mBuffer.Length()+1];
143 0 : NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
144 :
145 0 : strncpy(buffer, mBuffer.get(), mBuffer.Length()+1);
146 0 : mBuffer.Truncate();
147 : }
148 :
149 : #ifndef DEBUG_dougt
150 17 : PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer.get()) );
151 : #else
152 : printf("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer);
153 : #endif // DEBUG_dougt
154 :
155 34 : nsCAutoString indexFormat;
156 17 : if (!mSentHeading) {
157 : // build up the 300: line
158 34 : nsCOMPtr<nsIURI> uri;
159 17 : rv = channel->GetURI(getter_AddRefs(uri));
160 17 : NS_ENSURE_SUCCESS(rv, rv);
161 :
162 17 : rv = GetHeaders(indexFormat, uri);
163 17 : NS_ENSURE_SUCCESS(rv, rv);
164 :
165 34 : mSentHeading = true;
166 : }
167 :
168 17 : char *line = buffer;
169 17 : line = DigestBufferLines(line, indexFormat);
170 :
171 : #ifndef DEBUG_dougt
172 17 : PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
173 : indexFormat.Length(), indexFormat.get()) );
174 : #else
175 : char *unescData = ToNewCString(indexFormat);
176 : NS_ENSURE_TRUE(unescData, NS_ERROR_OUT_OF_MEMORY);
177 :
178 : nsUnescape(unescData);
179 : printf("::OnData() sending the following %d bytes...\n\n%s\n\n", indexFormat.Length(), unescData);
180 : nsMemory::Free(unescData);
181 : #endif // DEBUG_dougt
182 :
183 : // if there's any data left over, buffer it.
184 17 : if (line && *line) {
185 0 : mBuffer.Append(line);
186 0 : PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() buffering the following %d bytes...\n\n%s\n\n",
187 : PL_strlen(line), line) );
188 : }
189 :
190 : // send the converted data out.
191 34 : nsCOMPtr<nsIInputStream> inputData;
192 :
193 17 : rv = NS_NewCStringInputStream(getter_AddRefs(inputData), indexFormat);
194 17 : NS_ENSURE_SUCCESS(rv, rv);
195 :
196 17 : rv = mFinalListener->OnDataAvailable(request, ctxt, inputData, 0, indexFormat.Length());
197 :
198 17 : return rv;
199 : }
200 :
201 :
202 : // nsIRequestObserver implementation
203 : NS_IMETHODIMP
204 17 : nsFTPDirListingConv::OnStartRequest(nsIRequest* request, nsISupports *ctxt) {
205 : // we don't care about start. move along... but start masqeurading
206 : // as the http-index channel now.
207 17 : return mFinalListener->OnStartRequest(request, ctxt);
208 : }
209 :
210 : NS_IMETHODIMP
211 17 : nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
212 : nsresult aStatus) {
213 : // we don't care about stop. move along...
214 :
215 17 : return mFinalListener->OnStopRequest(request, ctxt, aStatus);
216 : }
217 :
218 :
219 : // nsFTPDirListingConv methods
220 17 : nsFTPDirListingConv::nsFTPDirListingConv() {
221 17 : mFinalListener = nsnull;
222 17 : mSentHeading = false;
223 17 : }
224 :
225 51 : nsFTPDirListingConv::~nsFTPDirListingConv() {
226 17 : NS_IF_RELEASE(mFinalListener);
227 68 : }
228 :
229 : nsresult
230 17 : nsFTPDirListingConv::Init() {
231 : #if defined(PR_LOGGING)
232 : //
233 : // Initialize the global PRLogModule for FTP Protocol logging
234 : // if necessary...
235 : //
236 17 : if (nsnull == gFTPDirListConvLog) {
237 4 : gFTPDirListConvLog = PR_NewLogModule("nsFTPDirListingConv");
238 : }
239 : #endif /* PR_LOGGING */
240 :
241 17 : return NS_OK;
242 : }
243 :
244 : nsresult
245 17 : nsFTPDirListingConv::GetHeaders(nsACString& headers,
246 : nsIURI* uri)
247 : {
248 17 : nsresult rv = NS_OK;
249 : // build up 300 line
250 17 : headers.AppendLiteral("300: ");
251 :
252 : // Bug 111117 - don't print the password
253 34 : nsCAutoString pw;
254 34 : nsCAutoString spec;
255 17 : uri->GetPassword(pw);
256 17 : if (!pw.IsEmpty()) {
257 0 : rv = uri->SetPassword(EmptyCString());
258 0 : if (NS_FAILED(rv)) return rv;
259 0 : rv = uri->GetAsciiSpec(spec);
260 0 : if (NS_FAILED(rv)) return rv;
261 0 : headers.Append(spec);
262 0 : rv = uri->SetPassword(pw);
263 0 : if (NS_FAILED(rv)) return rv;
264 : } else {
265 17 : rv = uri->GetAsciiSpec(spec);
266 17 : if (NS_FAILED(rv)) return rv;
267 :
268 17 : headers.Append(spec);
269 : }
270 17 : headers.Append(char(nsCRT::LF));
271 : // END 300:
272 :
273 : // build up the column heading; 200:
274 17 : headers.AppendLiteral("200: filename content-length last-modified file-type\n");
275 : // END 200:
276 17 : return rv;
277 : }
278 :
279 : char *
280 17 : nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
281 17 : char *line = aBuffer;
282 : char *eol;
283 17 : bool cr = false;
284 :
285 : list_state state;
286 17 : state.magic = 0;
287 :
288 : // while we have new lines, parse 'em into application/http-index-format.
289 83 : while ( line && (eol = PL_strchr(line, nsCRT::LF)) ) {
290 : // yank any carriage returns too.
291 49 : if (eol > line && *(eol-1) == nsCRT::CR) {
292 49 : eol--;
293 49 : *eol = '\0';
294 49 : cr = true;
295 : } else {
296 0 : *eol = '\0';
297 0 : cr = false;
298 : }
299 :
300 : list_result result;
301 :
302 49 : int type = ParseFTPList(line, &state, &result );
303 :
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 56 : if ((type != 'd' && type != 'f' && type != 'l') ||
307 7 : (result.fe_type == 'd' && result.fe_fname[0] == '.' &&
308 0 : (result.fe_fnlen == 1 || (result.fe_fnlen == 2 && result.fe_fname[1] == '.'))) )
309 : {
310 7 : if (cr)
311 7 : line = eol+2;
312 : else
313 0 : line = eol+1;
314 :
315 7 : continue;
316 : }
317 :
318 : // blast the index entry into the indexFormat buffer as a 201: line.
319 42 : aString.AppendLiteral("201: ");
320 : // FILENAME
321 :
322 : // parsers for styles 'U' and 'W' handle sequence " -> " themself
323 42 : if (state.lstyle != 'U' && state.lstyle != 'W') {
324 2 : const char* offset = strstr(result.fe_fname, " -> ");
325 2 : if (offset) {
326 0 : result.fe_fnlen = offset - result.fe_fname;
327 : }
328 : }
329 :
330 84 : nsCAutoString buf;
331 42 : aString.Append('\"');
332 : aString.Append(NS_EscapeURL(Substring(result.fe_fname,
333 42 : result.fe_fname+result.fe_fnlen),
334 42 : esc_Minimal|esc_OnlyASCII|esc_Forced,buf));
335 42 : aString.AppendLiteral("\" ");
336 :
337 : // CONTENT LENGTH
338 :
339 42 : if (type != 'd')
340 : {
341 1435 : for (int i = 0; i < int(sizeof(result.fe_size)); ++i)
342 : {
343 1400 : if (result.fe_size[i] != '\0')
344 85 : aString.Append((const char*)&result.fe_size[i], 1);
345 : }
346 :
347 35 : aString.Append(' ');
348 : }
349 : else
350 7 : aString.AppendLiteral("0 ");
351 :
352 :
353 : // MODIFIED DATE
354 42 : 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 42 : "%a, %d %b %Y %H:%M:%S", &result.fe_time );
361 :
362 42 : char *escapedDate = nsEscape(buffer, url_Path);
363 42 : aString.Append(escapedDate);
364 42 : nsMemory::Free(escapedDate);
365 42 : aString.Append(' ');
366 :
367 : // ENTRY TYPE
368 42 : if (type == 'd')
369 7 : aString.AppendLiteral("DIRECTORY");
370 35 : else if (type == 'l')
371 8 : aString.AppendLiteral("SYMBOLIC-LINK");
372 : else
373 27 : aString.AppendLiteral("FILE");
374 :
375 42 : aString.Append(' ');
376 :
377 42 : aString.Append(char(nsCRT::LF)); // complete this line
378 : // END 201:
379 :
380 42 : if (cr)
381 42 : line = eol+2;
382 : else
383 0 : line = eol+1;
384 : } // end while(eol)
385 :
386 17 : return line;
387 : }
388 :
389 : nsresult
390 17 : NS_NewFTPDirListingConv(nsFTPDirListingConv** aFTPDirListingConv)
391 : {
392 17 : NS_PRECONDITION(aFTPDirListingConv != nsnull, "null ptr");
393 17 : if (! aFTPDirListingConv)
394 0 : return NS_ERROR_NULL_POINTER;
395 :
396 17 : *aFTPDirListingConv = new nsFTPDirListingConv();
397 17 : if (! *aFTPDirListingConv)
398 0 : return NS_ERROR_OUT_OF_MEMORY;
399 :
400 17 : NS_ADDREF(*aFTPDirListingConv);
401 17 : return (*aFTPDirListingConv)->Init();
402 : }
|