1 /* vim:set ts=2 sw=2 et cindent: */
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 the Mozilla gnome-vfs extension.
17 * The Initial Developer of the Original Code is IBM Corporation.
18 * Portions created by IBM Corporation are Copyright (C) 2004
19 * IBM Corporation. All Rights Reserved.
22 * Darin Fisher <darin@meer.net>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 // GnomeVFS v2.2.2 is missing G_BEGIN_DECLS in gnome-vfs-module-callback.h
40 #include <libgnomevfs/gnome-vfs.h>
41 #include <libgnomevfs/gnome-vfs-standard-callbacks.h>
42 #include <libgnomevfs/gnome-vfs-mime-utils.h>
45 #include "nsServiceManagerUtils.h"
46 #include "nsComponentManagerUtils.h"
47 #include "mozilla/ModuleUtils.h"
48 #include "nsIInterfaceRequestorUtils.h"
49 #include "nsIPrefService.h"
50 #include "nsIPrefBranch2.h"
51 #include "nsIObserver.h"
52 #include "nsThreadUtils.h"
53 #include "nsProxyRelease.h"
54 #include "nsIAuthPrompt.h"
55 #include "nsIStringBundle.h"
56 #include "nsIStandardURL.h"
58 #include "nsMimeTypes.h"
59 #include "nsNetUtil.h"
60 #include "nsINetUtil.h"
61 #include "nsAutoPtr.h"
68 #define MOZ_GNOMEVFS_SCHEME "moz-gnomevfs"
69 #define MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS "network.gnomevfs.supported-protocols"
71 //-----------------------------------------------------------------------------
73 // NSPR_LOG_MODULES=gnomevfs:5
75 static PRLogModuleInfo
*sGnomeVFSLog
;
76 #define LOG(args) PR_LOG(sGnomeVFSLog, PR_LOG_DEBUG, args)
81 //-----------------------------------------------------------------------------
84 MapGnomeVFSResult(GnomeVFSResult result
)
88 case GNOME_VFS_OK
: return NS_OK
;
89 case GNOME_VFS_ERROR_NOT_FOUND
: return NS_ERROR_FILE_NOT_FOUND
;
90 case GNOME_VFS_ERROR_INTERNAL
: return NS_ERROR_UNEXPECTED
;
91 case GNOME_VFS_ERROR_BAD_PARAMETERS
: return NS_ERROR_INVALID_ARG
;
92 case GNOME_VFS_ERROR_NOT_SUPPORTED
: return NS_ERROR_NOT_AVAILABLE
;
93 case GNOME_VFS_ERROR_CORRUPTED_DATA
: return NS_ERROR_FILE_CORRUPTED
;
94 case GNOME_VFS_ERROR_TOO_BIG
: return NS_ERROR_FILE_TOO_BIG
;
95 case GNOME_VFS_ERROR_NO_SPACE
: return NS_ERROR_FILE_NO_DEVICE_SPACE
;
96 case GNOME_VFS_ERROR_READ_ONLY
:
97 case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM
: return NS_ERROR_FILE_READ_ONLY
;
98 case GNOME_VFS_ERROR_INVALID_URI
:
99 case GNOME_VFS_ERROR_INVALID_HOST_NAME
: return NS_ERROR_MALFORMED_URI
;
100 case GNOME_VFS_ERROR_ACCESS_DENIED
:
101 case GNOME_VFS_ERROR_NOT_PERMITTED
:
102 case GNOME_VFS_ERROR_LOGIN_FAILED
: return NS_ERROR_FILE_ACCESS_DENIED
;
103 case GNOME_VFS_ERROR_EOF
: return NS_BASE_STREAM_CLOSED
;
104 case GNOME_VFS_ERROR_NOT_A_DIRECTORY
: return NS_ERROR_FILE_NOT_DIRECTORY
;
105 case GNOME_VFS_ERROR_IN_PROGRESS
: return NS_ERROR_IN_PROGRESS
;
106 case GNOME_VFS_ERROR_FILE_EXISTS
: return NS_ERROR_FILE_ALREADY_EXISTS
;
107 case GNOME_VFS_ERROR_IS_DIRECTORY
: return NS_ERROR_FILE_IS_DIRECTORY
;
108 case GNOME_VFS_ERROR_NO_MEMORY
: return NS_ERROR_OUT_OF_MEMORY
;
109 case GNOME_VFS_ERROR_HOST_NOT_FOUND
:
110 case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS
: return NS_ERROR_UNKNOWN_HOST
;
111 case GNOME_VFS_ERROR_CANCELLED
:
112 case GNOME_VFS_ERROR_INTERRUPTED
: return NS_ERROR_ABORT
;
113 case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY
: return NS_ERROR_FILE_DIR_NOT_EMPTY
;
114 case GNOME_VFS_ERROR_NAME_TOO_LONG
: return NS_ERROR_FILE_NAME_TOO_LONG
;
115 case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE
: return NS_ERROR_UNKNOWN_PROTOCOL
;
117 /* No special mapping for these error codes...
119 case GNOME_VFS_ERROR_GENERIC:
120 case GNOME_VFS_ERROR_IO:
121 case GNOME_VFS_ERROR_WRONG_FORMAT:
122 case GNOME_VFS_ERROR_BAD_FILE:
123 case GNOME_VFS_ERROR_NOT_OPEN:
124 case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
125 case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
126 case GNOME_VFS_ERROR_LOOP:
127 case GNOME_VFS_ERROR_DIRECTORY_BUSY:
128 case GNOME_VFS_ERROR_TOO_MANY_LINKS:
129 case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
130 case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
131 case GNOME_VFS_ERROR_PROTOCOL_ERROR:
132 case GNOME_VFS_ERROR_NO_MASTER_BROWSER:
138 return NS_ERROR_FAILURE
;
141 return NS_ERROR_FAILURE
;
144 //-----------------------------------------------------------------------------
147 ProxiedAuthCallback(gconstpointer in
,
151 gpointer callback_data
)
153 GnomeVFSModuleCallbackAuthenticationIn
*authIn
=
154 (GnomeVFSModuleCallbackAuthenticationIn
*) in
;
155 GnomeVFSModuleCallbackAuthenticationOut
*authOut
=
156 (GnomeVFSModuleCallbackAuthenticationOut
*) out
;
158 LOG(("gnomevfs: ProxiedAuthCallback [uri=%s]\n", authIn
->uri
));
160 // Without a channel, we have no way of getting a prompter.
161 nsIChannel
*channel
= (nsIChannel
*) callback_data
;
165 nsCOMPtr
<nsIAuthPrompt
> prompt
;
166 NS_QueryNotificationCallbacks(channel
, prompt
);
168 // If no auth prompt, then give up. We could failover to using the
169 // WindowWatcher service, but that might defeat a consumer's purposeful
170 // attempt to disable authentication (for whatever reason).
174 // Parse out the host and port...
175 nsCOMPtr
<nsIURI
> uri
;
176 channel
->GetURI(getter_AddRefs(uri
));
183 // Make sure authIn->uri is consistent with the channel's URI.
185 // XXX This check is probably not IDN safe, and it might incorrectly
186 // fire as a result of escaping differences. It's unclear what
187 // kind of transforms GnomeVFS might have applied to the URI spec
188 // that we originally gave to it. In spite of the likelihood of
189 // false hits, this check is probably still valuable.
193 int uriLen
= strlen(authIn
->uri
);
194 if (!StringHead(spec
, uriLen
).Equals(nsDependentCString(authIn
->uri
, uriLen
)))
196 LOG(("gnomevfs: [spec=%s authIn->uri=%s]\n", spec
.get(), authIn
->uri
));
197 NS_ERROR("URI mismatch");
202 nsCAutoString scheme
, hostPort
;
203 uri
->GetScheme(scheme
);
204 uri
->GetHostPort(hostPort
);
206 // It doesn't make sense for either of these strings to be empty. What kind
207 // of funky URI is this?
208 if (scheme
.IsEmpty() || hostPort
.IsEmpty())
211 // Construct the single signon key. Altering the value of this key will
212 // cause people's remembered passwords to be forgotten. Think carefully
213 // before changing the way this key is constructed.
214 nsAutoString key
, realm
;
216 NS_ConvertUTF8toUTF16
dispHost(scheme
);
217 dispHost
.Append(NS_LITERAL_STRING("://"));
218 dispHost
.Append(NS_ConvertUTF8toUTF16(hostPort
));
223 // We assume the realm string is ASCII. That might be a bogus assumption,
224 // but we have no idea what encoding GnomeVFS is using, so for now we'll
225 // limit ourselves to ISO-Latin-1. XXX What is a better solution?
227 realm
.Append(NS_ConvertASCIItoUTF16(authIn
->realm
));
233 // Construct the message string...
235 // We use Necko's string bundle here. This code really should be encapsulated
236 // behind some Necko API, after all this code is based closely on the code in
237 // nsHttpChannel.cpp.
239 nsCOMPtr
<nsIStringBundleService
> bundleSvc
=
240 do_GetService(NS_STRINGBUNDLE_CONTRACTID
);
244 nsCOMPtr
<nsIStringBundle
> bundle
;
245 bundleSvc
->CreateBundle("chrome://global/locale/commonDialogs.properties",
246 getter_AddRefs(bundle
));
251 if (!realm
.IsEmpty())
253 const PRUnichar
*strings
[] = { realm
.get(), dispHost
.get() };
254 bundle
->FormatStringFromName(NS_LITERAL_STRING("EnterUserPasswordForRealm").get(),
255 strings
, 2, getter_Copies(message
));
259 const PRUnichar
*strings
[] = { dispHost
.get() };
260 bundle
->FormatStringFromName(NS_LITERAL_STRING("EnterUserPasswordFor").get(),
261 strings
, 1, getter_Copies(message
));
263 if (message
.IsEmpty())
266 // Prompt the user...
268 PRBool retval
= PR_FALSE
;
269 PRUnichar
*user
= nsnull
, *pass
= nsnull
;
271 rv
= prompt
->PromptUsernameAndPassword(nsnull
, message
.get(),
273 nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY
,
274 &user
, &pass
, &retval
);
277 if (!retval
|| !user
|| !pass
)
280 // XXX We need to convert the UTF-16 username and password from our dialog to
281 // strings that GnomeVFS can understand. It's unclear what encoding GnomeVFS
282 // expects, so for now we assume 7-bit ASCII. Hopefully, we can get a better
283 // solution at some point.
285 // One copy is never enough...
286 authOut
->username
= g_strdup(NS_LossyConvertUTF16toASCII(user
).get());
287 authOut
->password
= g_strdup(NS_LossyConvertUTF16toASCII(pass
).get());
289 nsMemory::Free(user
);
290 nsMemory::Free(pass
);
293 struct nsGnomeVFSAuthCallbackEvent
: public nsRunnable
299 gpointer callback_data
;
302 ProxiedAuthCallback(in
, in_size
, out
, out_size
, callback_data
);
308 AuthCallback(gconstpointer in
,
312 gpointer callback_data
)
314 // Need to proxy this callback over to the main thread. Synchronous dispatch
315 // is required in order to provide data to the GnomeVFS callback.
317 nsRefPtr
<nsGnomeVFSAuthCallbackEvent
> ev
= new nsGnomeVFSAuthCallbackEvent();
322 ev
->in_size
= in_size
;
324 ev
->out_size
= out_size
;
325 ev
->callback_data
= callback_data
;
327 NS_DispatchToMainThread(ev
, NS_DISPATCH_SYNC
);
330 //-----------------------------------------------------------------------------
333 FileInfoComparator(gconstpointer a
, gconstpointer b
)
335 const GnomeVFSFileInfo
*ia
= (const GnomeVFSFileInfo
*) a
;
336 const GnomeVFSFileInfo
*ib
= (const GnomeVFSFileInfo
*) b
;
338 return strcasecmp(ia
->name
, ib
->name
);
341 //-----------------------------------------------------------------------------
343 class nsGnomeVFSInputStream
: public nsIInputStream
347 NS_DECL_NSIINPUTSTREAM
349 nsGnomeVFSInputStream(const nsCString
&uriSpec
)
353 , mBytesRemaining(PR_UINT32_MAX
)
356 , mDirListPtr(nsnull
)
358 , mDirOpen(PR_FALSE
) {}
360 ~nsGnomeVFSInputStream() { Close(); }
362 void SetChannel(nsIChannel
*channel
)
364 // We need to hold an owning reference to our channel. This is done
365 // so we can access the channel's notification callbacks to acquire
366 // a reference to a nsIAuthPrompt if we need to handle a GnomeVFS
367 // authentication callback.
369 // However, the channel can only be accessed on the main thread, so
370 // we have to be very careful with ownership. Moreover, it doesn't
371 // support threadsafe addref/release, so proxying is the answer.
373 // Also, it's important to note that this likely creates a reference
374 // cycle since the channel likely owns this stream. This reference
375 // cycle is broken in our Close method.
377 NS_ADDREF(mChannel
= channel
);
381 GnomeVFSResult
DoOpen();
382 GnomeVFSResult
DoRead(char *aBuf
, PRUint32 aCount
, PRUint32
*aCountRead
);
383 nsresult
SetContentTypeOfChannel(const char *contentType
);
387 nsIChannel
*mChannel
; // manually refcounted
388 GnomeVFSHandle
*mHandle
;
389 PRUint32 mBytesRemaining
;
394 PRUint32 mDirBufCursor
;
395 PRPackedBool mDirOpen
;
399 nsGnomeVFSInputStream::DoOpen()
403 NS_ASSERTION(mHandle
== nsnull
, "already open");
405 // Push a callback handler on the stack for this thread, so we can intercept
406 // authentication requests from GnomeVFS. We'll use the channel to get a
407 // nsIAuthPrompt instance.
409 gnome_vfs_module_callback_push(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION
,
410 AuthCallback
, mChannel
, NULL
);
412 // Query the mime type first (this could return NULL).
414 // XXX We need to do this up-front in order to determine how to open the URI.
415 // Unfortunately, the error code GNOME_VFS_ERROR_IS_DIRECTORY is not
416 // always returned by gnome_vfs_open when we pass it a URI to a directory!
417 // Otherwise, we could have used that as a way to failover to opening the
418 // URI as a directory. Also, it would have been ideal if
419 // gnome_vfs_get_file_info_from_handle were actually implemented by the
420 // smb:// module, since that would have allowed us to potentially save a
421 // round trip to the server to discover the mime type of the document in
422 // the case where gnome_vfs_open would have been used. (Oh well! /me
423 // throws hands up in the air and moves on...)
425 GnomeVFSFileInfo info
= {0};
426 rv
= gnome_vfs_get_file_info(mSpec
.get(), &info
, GnomeVFSFileInfoOptions(
427 GNOME_VFS_FILE_INFO_DEFAULT
|
428 GNOME_VFS_FILE_INFO_FOLLOW_LINKS
));
429 if (rv
== GNOME_VFS_OK
)
431 if (info
.type
== GNOME_VFS_FILE_TYPE_DIRECTORY
)
433 rv
= gnome_vfs_directory_list_load(&mDirList
, mSpec
.get(),
434 GNOME_VFS_FILE_INFO_DEFAULT
);
436 LOG(("gnomevfs: gnome_vfs_directory_list_load returned %d (%s) [spec=\"%s\"]\n",
437 rv
, gnome_vfs_result_to_string(rv
), mSpec
.get()));
441 rv
= gnome_vfs_open(&mHandle
, mSpec
.get(), GNOME_VFS_OPEN_READ
);
443 LOG(("gnomevfs: gnome_vfs_open returned %d (%s) [spec=\"%s\"]\n",
444 rv
, gnome_vfs_result_to_string(rv
), mSpec
.get()));
448 gnome_vfs_module_callback_pop(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION
);
450 if (rv
== GNOME_VFS_OK
)
454 // Here we set the content type of the channel to the value of the mime
455 // type determined by GnomeVFS. However, if GnomeVFS is telling us that
456 // the document is binary, we'll ignore that and keep the channel's
457 // content type unspecified. That will enable our content type sniffing
458 // algorithms. This should provide more consistent mime type handling.
460 if (info
.mime_type
&& (strcmp(info
.mime_type
, APPLICATION_OCTET_STREAM
) != 0))
461 SetContentTypeOfChannel(info
.mime_type
);
463 // XXX truncates size from 64-bit to 32-bit
464 mBytesRemaining
= (PRUint32
) info
.size
;
466 // Update the content length attribute on the channel. We do this
467 // synchronously without proxying. This hack is not as bad as it looks!
468 if (mBytesRemaining
!= PR_UINT32_MAX
)
469 mChannel
->SetContentLength(mBytesRemaining
);
476 mDirList
= g_list_sort(mDirList
, FileInfoComparator
);
477 mDirListPtr
= mDirList
;
479 // Write base URL (make sure it ends with a '/')
480 mDirBuf
.Append("300: ");
481 mDirBuf
.Append(mSpec
);
482 if (mSpec
.get()[mSpec
.Length() - 1] != '/')
484 mDirBuf
.Append('\n');
486 // Write column names
487 mDirBuf
.Append("200: filename content-length last-modified file-type\n");
489 // Write charset (assume UTF-8)
490 // XXX is this correct?
491 mDirBuf
.Append("301: UTF-8\n");
493 SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT
);
497 gnome_vfs_file_info_clear(&info
);
502 nsGnomeVFSInputStream::DoRead(char *aBuf
, PRUint32 aCount
, PRUint32
*aCountRead
)
508 GnomeVFSFileSize bytesRead
;
509 rv
= gnome_vfs_read(mHandle
, aBuf
, aCount
, &bytesRead
);
510 if (rv
== GNOME_VFS_OK
)
512 *aCountRead
= (PRUint32
) bytesRead
;
513 mBytesRemaining
-= *aCountRead
;
520 while (aCount
&& rv
!= GNOME_VFS_ERROR_EOF
)
522 // Copy data out of our buffer
523 PRUint32 bufLen
= mDirBuf
.Length() - mDirBufCursor
;
526 PRUint32 n
= PR_MIN(bufLen
, aCount
);
527 memcpy(aBuf
, mDirBuf
.get() + mDirBufCursor
, n
);
534 if (!mDirListPtr
) // Are we at the end of the directory list?
536 rv
= GNOME_VFS_ERROR_EOF
;
538 else if (aCount
) // Do we need more data?
540 GnomeVFSFileInfo
*info
= (GnomeVFSFileInfo
*) mDirListPtr
->data
;
542 // Prune '.' and '..' from directory listing.
543 if (info
->name
[0] == '.' &&
544 (info
->name
[1] == '\0' ||
545 (info
->name
[1] == '.' && info
->name
[2] == '\0')))
547 mDirListPtr
= mDirListPtr
->next
;
551 mDirBuf
.Assign("201: ");
553 // The "filename" field
555 nsCOMPtr
<nsINetUtil
> nu
= do_GetService(NS_NETUTIL_CONTRACTID
);
557 nu
->EscapeString(nsDependentCString(info
->name
),
558 nsINetUtil::ESCAPE_URL_PATH
, escName
);
560 mDirBuf
.Append(escName
);
564 // The "content-length" field
565 // XXX truncates size from 64-bit to 32-bit
566 mDirBuf
.AppendInt(PRInt32(info
->size
));
569 // The "last-modified" field
571 // NSPR promises: PRTime is compatible with time_t
572 // we just need to convert from seconds to microseconds
574 PRTime pt
= ((PRTime
) info
->mtime
) * 1000000;
575 PR_ExplodeTime(pt
, PR_GMTParameters
, &tm
);
578 PR_FormatTimeUSEnglish(buf
, sizeof(buf
),
579 "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm
);
583 // The "file-type" field
586 case GNOME_VFS_FILE_TYPE_REGULAR
:
587 mDirBuf
.Append("FILE ");
589 case GNOME_VFS_FILE_TYPE_DIRECTORY
:
590 mDirBuf
.Append("DIRECTORY ");
592 case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK
:
593 mDirBuf
.Append("SYMBOLIC-LINK ");
599 mDirBuf
.Append('\n');
602 mDirListPtr
= mDirListPtr
->next
;
608 NS_NOTREACHED("reading from what?");
609 rv
= GNOME_VFS_ERROR_GENERIC
;
615 // This class is used to implement SetContentTypeOfChannel.
616 class nsGnomeVFSSetContentTypeEvent
: public nsRunnable
619 nsGnomeVFSSetContentTypeEvent(nsIChannel
*channel
, const char *contentType
)
620 : mChannel(channel
), mContentType(contentType
)
622 // stash channel reference in mChannel. no AddRef here! see note
623 // in SetContentTypeOfchannel.
628 mChannel
->SetContentType(mContentType
);
633 nsIChannel
*mChannel
;
634 nsCString mContentType
;
638 nsGnomeVFSInputStream::SetContentTypeOfChannel(const char *contentType
)
640 // We need to proxy this call over to the main thread. We post an
641 // asynchronous event in this case so that we don't delay reading data, and
642 // we know that this is safe to do since the channel's reference will be
643 // released asynchronously as well. We trust the ordering of the main
644 // thread's event queue to protect us against memory corruption.
647 nsCOMPtr
<nsIRunnable
> ev
=
648 new nsGnomeVFSSetContentTypeEvent(mChannel
, contentType
);
651 rv
= NS_ERROR_OUT_OF_MEMORY
;
655 rv
= NS_DispatchToMainThread(ev
);
660 NS_IMPL_THREADSAFE_ISUPPORTS1(nsGnomeVFSInputStream
, nsIInputStream
)
663 nsGnomeVFSInputStream::Close()
667 gnome_vfs_close(mHandle
);
673 // Destroy the list of GnomeVFSFileInfo objects...
674 g_list_foreach(mDirList
, (GFunc
) gnome_vfs_file_info_unref
, nsnull
);
675 g_list_free(mDirList
);
677 mDirListPtr
= nsnull
;
684 nsCOMPtr
<nsIThread
> thread
= do_GetMainThread();
686 rv
= NS_ProxyRelease(thread
, mChannel
);
688 NS_ASSERTION(thread
&& NS_SUCCEEDED(rv
), "leaking channel reference");
692 mSpec
.Truncate(); // free memory
694 // Prevent future reads from re-opening the handle.
695 if (NS_SUCCEEDED(mStatus
))
696 mStatus
= NS_BASE_STREAM_CLOSED
;
702 nsGnomeVFSInputStream::Available(PRUint32
*aResult
)
704 if (NS_FAILED(mStatus
))
707 *aResult
= mBytesRemaining
;
712 nsGnomeVFSInputStream::Read(char *aBuf
,
714 PRUint32
*aCountRead
)
718 if (mStatus
== NS_BASE_STREAM_CLOSED
)
720 if (NS_FAILED(mStatus
))
723 GnomeVFSResult rv
= GNOME_VFS_OK
;
725 // If this is our first-time through here, then open the URI.
726 if (!mHandle
&& !mDirOpen
)
729 if (rv
== GNOME_VFS_OK
)
730 rv
= DoRead(aBuf
, aCount
, aCountRead
);
732 if (rv
!= GNOME_VFS_OK
)
734 // If we reach here, we hit some kind of error. EOF is not an error.
735 mStatus
= MapGnomeVFSResult(rv
);
736 if (mStatus
== NS_BASE_STREAM_CLOSED
)
739 LOG(("gnomevfs: result %d [%s] mapped to 0x%x\n",
740 rv
, gnome_vfs_result_to_string(rv
), mStatus
));
746 nsGnomeVFSInputStream::ReadSegments(nsWriteSegmentFun aWriter
,
751 // There is no way to implement this using GnomeVFS, but fortunately
752 // that doesn't matter. Because we are a blocking input stream, Necko
753 // isn't going to call our ReadSegments method.
754 NS_NOTREACHED("nsGnomeVFSInputStream::ReadSegments");
755 return NS_ERROR_NOT_IMPLEMENTED
;
759 nsGnomeVFSInputStream::IsNonBlocking(PRBool
*aResult
)
765 //-----------------------------------------------------------------------------
767 class nsGnomeVFSProtocolHandler
: public nsIProtocolHandler
772 NS_DECL_NSIPROTOCOLHANDLER
778 void InitSupportedProtocolsPref(nsIPrefBranch
*prefs
);
779 PRBool
IsSupportedProtocol(const nsCString
&spec
);
781 nsCString mSupportedProtocols
;
784 NS_IMPL_ISUPPORTS2(nsGnomeVFSProtocolHandler
, nsIProtocolHandler
, nsIObserver
)
787 nsGnomeVFSProtocolHandler::Init()
790 sGnomeVFSLog
= PR_NewLogModule("gnomevfs");
793 if (!gnome_vfs_initialized())
795 if (!gnome_vfs_init())
797 NS_WARNING("gnome_vfs_init failed");
798 return NS_ERROR_UNEXPECTED
;
802 nsCOMPtr
<nsIPrefBranch2
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
805 InitSupportedProtocolsPref(prefs
);
806 prefs
->AddObserver(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS
, this, PR_FALSE
);
813 nsGnomeVFSProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch
*prefs
)
816 nsresult rv
= prefs
->GetCharPref(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS
,
817 getter_Copies(mSupportedProtocols
));
818 if (NS_SUCCEEDED(rv
)) {
819 mSupportedProtocols
.StripWhitespace();
820 ToLowerCase(mSupportedProtocols
);
823 mSupportedProtocols
.Assign("smb:,sftp:"); // use defaults
825 LOG(("gnomevfs: supported protocols \"%s\"\n", mSupportedProtocols
.get()));
829 nsGnomeVFSProtocolHandler::IsSupportedProtocol(const nsCString
&aSpec
)
831 const char *specString
= aSpec
.get();
832 const char *colon
= strchr(specString
, ':');
836 PRUint32 length
= colon
- specString
+ 1;
839 nsCString
scheme(specString
, length
);
841 char *found
= PL_strcasestr(mSupportedProtocols
.get(), scheme
.get());
845 if (found
[length
] != ',' && found
[length
] != '\0')
852 nsGnomeVFSProtocolHandler::GetScheme(nsACString
&aScheme
)
854 aScheme
.Assign(MOZ_GNOMEVFS_SCHEME
);
859 nsGnomeVFSProtocolHandler::GetDefaultPort(PRInt32
*aDefaultPort
)
866 nsGnomeVFSProtocolHandler::GetProtocolFlags(PRUint32
*aProtocolFlags
)
868 // Is URI_STD true of all GnomeVFS URI types?
869 *aProtocolFlags
= URI_STD
| URI_DANGEROUS_TO_LOAD
;
874 nsGnomeVFSProtocolHandler::NewURI(const nsACString
&aSpec
,
875 const char *aOriginCharset
,
879 const nsCString
flatSpec(aSpec
);
880 LOG(("gnomevfs: NewURI [spec=%s]\n", flatSpec
.get()));
885 // XXX This check is used to limit the gnome-vfs protocols we support. For
886 // security reasons, it is best that we limit the protocols we support to
887 // those with known characteristics. We might want to lessen this
888 // restriction if it proves to be too heavy handed. A black list of
889 // protocols we don't want to support might be better. For example, we
890 // probably don't want to try to load "start-here:" inside the browser.
891 // There are others that fall into this category, which are best handled
892 // externally by Nautilus (or another app like it).
894 if (!IsSupportedProtocol(flatSpec
))
895 return NS_ERROR_UNKNOWN_PROTOCOL
;
897 // Verify that GnomeVFS supports this URI scheme.
898 GnomeVFSURI
*uri
= gnome_vfs_uri_new(flatSpec
.get());
900 return NS_ERROR_UNKNOWN_PROTOCOL
;
904 // XXX Can we really assume that all gnome-vfs URIs can be parsed using
905 // nsStandardURL? We probably really need to implement nsIURI/nsIURL
906 // in terms of the gnome_vfs_uri_XXX methods, but at least this works
907 // correctly for smb:// URLs ;-)
909 // Also, it might not be possible to fully implement nsIURI/nsIURL in
910 // terms of GnomeVFSURI since some Necko methods have no GnomeVFS
914 nsCOMPtr
<nsIStandardURL
> url
=
915 do_CreateInstance(NS_STANDARDURL_CONTRACTID
, &rv
);
919 rv
= url
->Init(nsIStandardURL::URLTYPE_STANDARD
, -1, flatSpec
,
920 aOriginCharset
, aBaseURI
);
921 if (NS_SUCCEEDED(rv
))
922 rv
= CallQueryInterface(url
, aResult
);
928 nsGnomeVFSProtocolHandler::NewChannel(nsIURI
*aURI
, nsIChannel
**aResult
)
930 NS_ENSURE_ARG_POINTER(aURI
);
934 rv
= aURI
->GetSpec(spec
);
938 nsRefPtr
<nsGnomeVFSInputStream
> stream
= new nsGnomeVFSInputStream(spec
);
941 rv
= NS_ERROR_OUT_OF_MEMORY
;
945 // start out assuming an unknown content-type. we'll set the content-type
946 // to something better once we open the URI.
947 rv
= NS_NewInputStreamChannel(aResult
, aURI
, stream
,
948 NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE
));
949 if (NS_SUCCEEDED(rv
))
950 stream
->SetChannel(*aResult
);
956 nsGnomeVFSProtocolHandler::AllowPort(PRInt32 aPort
,
960 // Don't override anything.
966 nsGnomeVFSProtocolHandler::Observe(nsISupports
*aSubject
,
968 const PRUnichar
*aData
)
970 if (strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0) {
971 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
972 InitSupportedProtocolsPref(prefs
);
977 //-----------------------------------------------------------------------------
979 #define NS_GNOMEVFSPROTOCOLHANDLER_CID \
980 { /* 9b6dc177-a2e4-49e1-9c98-0a8384de7f6c */ \
984 {0x9c, 0x98, 0x0a, 0x83, 0x84, 0xde, 0x7f, 0x6c} \
987 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGnomeVFSProtocolHandler
, Init
)
988 NS_DEFINE_NAMED_CID(NS_GNOMEVFSPROTOCOLHANDLER_CID
);
990 static const mozilla::Module::CIDEntry kVFSCIDs
[] = {
991 { &kNS_GNOMEVFSPROTOCOLHANDLER_CID
, false, NULL
, nsGnomeVFSProtocolHandlerConstructor
},
995 static const mozilla::Module::ContractIDEntry kVFSContracts
[] = {
996 { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GNOMEVFS_SCHEME
, &kNS_GNOMEVFSPROTOCOLHANDLER_CID
},
1000 static const mozilla::Module kVFSModule
= {
1001 mozilla::Module::kVersion
,
1006 NSMODULE_DEFN(nsGnomeVFSModule
) = &kVFSModule
;