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 "nsIGenericFactory.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/prompts.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
, GNOME_VFS_FILE_INFO_DEFAULT
);
427 if (rv
== GNOME_VFS_OK
)
429 if (info
.type
== GNOME_VFS_FILE_TYPE_DIRECTORY
)
431 rv
= gnome_vfs_directory_list_load(&mDirList
, mSpec
.get(),
432 GNOME_VFS_FILE_INFO_DEFAULT
);
434 LOG(("gnomevfs: gnome_vfs_directory_list_load returned %d (%s) [spec=\"%s\"]\n",
435 rv
, gnome_vfs_result_to_string(rv
), mSpec
.get()));
439 rv
= gnome_vfs_open(&mHandle
, mSpec
.get(), GNOME_VFS_OPEN_READ
);
441 LOG(("gnomevfs: gnome_vfs_open returned %d (%s) [spec=\"%s\"]\n",
442 rv
, gnome_vfs_result_to_string(rv
), mSpec
.get()));
446 gnome_vfs_module_callback_pop(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION
);
448 if (rv
== GNOME_VFS_OK
)
452 // Here we set the content type of the channel to the value of the mime
453 // type determined by GnomeVFS. However, if GnomeVFS is telling us that
454 // the document is binary, we'll ignore that and keep the channel's
455 // content type unspecified. That will enable our content type sniffing
456 // algorithms. This should provide more consistent mime type handling.
458 if (info
.mime_type
&& (strcmp(info
.mime_type
, APPLICATION_OCTET_STREAM
) != 0))
459 SetContentTypeOfChannel(info
.mime_type
);
461 // XXX truncates size from 64-bit to 32-bit
462 mBytesRemaining
= (PRUint32
) info
.size
;
464 // Update the content length attribute on the channel. We do this
465 // synchronously without proxying. This hack is not as bad as it looks!
466 if (mBytesRemaining
!= PR_UINT32_MAX
)
467 mChannel
->SetContentLength(mBytesRemaining
);
474 mDirList
= g_list_sort(mDirList
, FileInfoComparator
);
475 mDirListPtr
= mDirList
;
477 // Write base URL (make sure it ends with a '/')
478 mDirBuf
.Append("300: ");
479 mDirBuf
.Append(mSpec
);
480 if (mSpec
.get()[mSpec
.Length() - 1] != '/')
482 mDirBuf
.Append('\n');
484 // Write column names
485 mDirBuf
.Append("200: filename content-length last-modified file-type\n");
487 // Write charset (assume UTF-8)
488 // XXX is this correct?
489 mDirBuf
.Append("301: UTF-8\n");
491 SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT
);
495 gnome_vfs_file_info_clear(&info
);
500 nsGnomeVFSInputStream::DoRead(char *aBuf
, PRUint32 aCount
, PRUint32
*aCountRead
)
506 GnomeVFSFileSize bytesRead
;
507 rv
= gnome_vfs_read(mHandle
, aBuf
, aCount
, &bytesRead
);
508 if (rv
== GNOME_VFS_OK
)
510 *aCountRead
= (PRUint32
) bytesRead
;
511 mBytesRemaining
-= *aCountRead
;
518 while (aCount
&& rv
!= GNOME_VFS_ERROR_EOF
)
520 // Copy data out of our buffer
521 PRUint32 bufLen
= mDirBuf
.Length() - mDirBufCursor
;
524 PRUint32 n
= PR_MIN(bufLen
, aCount
);
525 memcpy(aBuf
, mDirBuf
.get() + mDirBufCursor
, n
);
532 if (!mDirListPtr
) // Are we at the end of the directory list?
534 rv
= GNOME_VFS_ERROR_EOF
;
536 else if (aCount
) // Do we need more data?
538 GnomeVFSFileInfo
*info
= (GnomeVFSFileInfo
*) mDirListPtr
->data
;
540 // Prune '.' and '..' from directory listing.
541 if (info
->name
[0] == '.' &&
542 (info
->name
[1] == '\0' ||
543 (info
->name
[1] == '.' && info
->name
[2] == '\0')))
545 mDirListPtr
= mDirListPtr
->next
;
549 mDirBuf
.Assign("201: ");
551 // The "filename" field
553 nsCOMPtr
<nsINetUtil
> nu
= do_GetService(NS_NETUTIL_CONTRACTID
);
555 nu
->EscapeString(nsDependentCString(info
->name
),
556 nsINetUtil::ESCAPE_URL_PATH
, escName
);
558 mDirBuf
.Append(escName
);
562 // The "content-length" field
563 // XXX truncates size from 64-bit to 32-bit
564 mDirBuf
.AppendInt(PRInt32(info
->size
));
567 // The "last-modified" field
569 // NSPR promises: PRTime is compatible with time_t
570 // we just need to convert from seconds to microseconds
572 PRTime pt
= ((PRTime
) info
->mtime
) * 1000000;
573 PR_ExplodeTime(pt
, PR_GMTParameters
, &tm
);
576 PR_FormatTimeUSEnglish(buf
, sizeof(buf
),
577 "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm
);
581 // The "file-type" field
584 case GNOME_VFS_FILE_TYPE_REGULAR
:
585 mDirBuf
.Append("FILE ");
587 case GNOME_VFS_FILE_TYPE_DIRECTORY
:
588 mDirBuf
.Append("DIRECTORY ");
590 case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK
:
591 mDirBuf
.Append("SYMBOLIC-LINK ");
597 mDirBuf
.Append('\n');
600 mDirListPtr
= mDirListPtr
->next
;
606 NS_NOTREACHED("reading from what?");
607 rv
= GNOME_VFS_ERROR_GENERIC
;
613 // This class is used to implement SetContentTypeOfChannel.
614 class nsGnomeVFSSetContentTypeEvent
: public nsRunnable
617 nsGnomeVFSSetContentTypeEvent(nsIChannel
*channel
, const char *contentType
)
618 : mChannel(channel
), mContentType(contentType
)
620 // stash channel reference in mChannel. no AddRef here! see note
621 // in SetContentTypeOfchannel.
626 mChannel
->SetContentType(mContentType
);
631 nsIChannel
*mChannel
;
632 nsCString mContentType
;
636 nsGnomeVFSInputStream::SetContentTypeOfChannel(const char *contentType
)
638 // We need to proxy this call over to the main thread. We post an
639 // asynchronous event in this case so that we don't delay reading data, and
640 // we know that this is safe to do since the channel's reference will be
641 // released asynchronously as well. We trust the ordering of the main
642 // thread's event queue to protect us against memory corruption.
645 nsCOMPtr
<nsIRunnable
> ev
=
646 new nsGnomeVFSSetContentTypeEvent(mChannel
, contentType
);
649 rv
= NS_ERROR_OUT_OF_MEMORY
;
653 rv
= NS_DispatchToMainThread(ev
);
658 NS_IMPL_THREADSAFE_ISUPPORTS1(nsGnomeVFSInputStream
, nsIInputStream
)
661 nsGnomeVFSInputStream::Close()
665 gnome_vfs_close(mHandle
);
671 // Destroy the list of GnomeVFSFileInfo objects...
672 g_list_foreach(mDirList
, (GFunc
) gnome_vfs_file_info_unref
, nsnull
);
673 g_list_free(mDirList
);
675 mDirListPtr
= nsnull
;
682 nsCOMPtr
<nsIThread
> thread
= do_GetMainThread();
684 rv
= NS_ProxyRelease(thread
, mChannel
);
686 NS_ASSERTION(thread
&& NS_SUCCEEDED(rv
), "leaking channel reference");
690 mSpec
.Truncate(); // free memory
692 // Prevent future reads from re-opening the handle.
693 if (NS_SUCCEEDED(mStatus
))
694 mStatus
= NS_BASE_STREAM_CLOSED
;
700 nsGnomeVFSInputStream::Available(PRUint32
*aResult
)
702 if (NS_FAILED(mStatus
))
705 *aResult
= mBytesRemaining
;
710 nsGnomeVFSInputStream::Read(char *aBuf
,
712 PRUint32
*aCountRead
)
716 if (mStatus
== NS_BASE_STREAM_CLOSED
)
718 if (NS_FAILED(mStatus
))
721 GnomeVFSResult rv
= GNOME_VFS_OK
;
723 // If this is our first-time through here, then open the URI.
724 if (!mHandle
&& !mDirOpen
)
727 if (rv
== GNOME_VFS_OK
)
728 rv
= DoRead(aBuf
, aCount
, aCountRead
);
730 if (rv
!= GNOME_VFS_OK
)
732 // If we reach here, we hit some kind of error. EOF is not an error.
733 mStatus
= MapGnomeVFSResult(rv
);
734 if (mStatus
== NS_BASE_STREAM_CLOSED
)
737 LOG(("gnomevfs: result %d [%s] mapped to 0x%x\n",
738 rv
, gnome_vfs_result_to_string(rv
), mStatus
));
744 nsGnomeVFSInputStream::ReadSegments(nsWriteSegmentFun aWriter
,
749 // There is no way to implement this using GnomeVFS, but fortunately
750 // that doesn't matter. Because we are a blocking input stream, Necko
751 // isn't going to call our ReadSegments method.
752 NS_NOTREACHED("nsGnomeVFSInputStream::ReadSegments");
753 return NS_ERROR_NOT_IMPLEMENTED
;
757 nsGnomeVFSInputStream::IsNonBlocking(PRBool
*aResult
)
763 //-----------------------------------------------------------------------------
765 class nsGnomeVFSProtocolHandler
: public nsIProtocolHandler
770 NS_DECL_NSIPROTOCOLHANDLER
776 void InitSupportedProtocolsPref(nsIPrefBranch
*prefs
);
777 PRBool
IsSupportedProtocol(const nsCString
&spec
);
779 nsCString mSupportedProtocols
;
782 NS_IMPL_ISUPPORTS2(nsGnomeVFSProtocolHandler
, nsIProtocolHandler
, nsIObserver
)
785 nsGnomeVFSProtocolHandler::Init()
788 sGnomeVFSLog
= PR_NewLogModule("gnomevfs");
791 if (!gnome_vfs_initialized())
793 if (!gnome_vfs_init())
795 NS_WARNING("gnome_vfs_init failed");
796 return NS_ERROR_UNEXPECTED
;
800 nsCOMPtr
<nsIPrefBranch2
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
803 InitSupportedProtocolsPref(prefs
);
804 prefs
->AddObserver(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS
, this, PR_FALSE
);
811 nsGnomeVFSProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch
*prefs
)
814 nsresult rv
= prefs
->GetCharPref(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS
,
815 getter_Copies(mSupportedProtocols
));
816 if (NS_SUCCEEDED(rv
)) {
817 mSupportedProtocols
.StripWhitespace();
818 ToLowerCase(mSupportedProtocols
);
821 mSupportedProtocols
.Assign("smb:,sftp:"); // use defaults
823 LOG(("gnomevfs: supported protocols \"%s\"\n", mSupportedProtocols
.get()));
827 nsGnomeVFSProtocolHandler::IsSupportedProtocol(const nsCString
&aSpec
)
829 const char *specString
= aSpec
.get();
830 const char *colon
= strchr(specString
, ':');
834 PRUint32 length
= colon
- specString
+ 1;
837 nsCString
scheme(specString
, length
);
839 char *found
= PL_strcasestr(mSupportedProtocols
.get(), scheme
.get());
843 if (found
[length
] != ',' && found
[length
] != '\0')
850 nsGnomeVFSProtocolHandler::GetScheme(nsACString
&aScheme
)
852 aScheme
.Assign(MOZ_GNOMEVFS_SCHEME
);
857 nsGnomeVFSProtocolHandler::GetDefaultPort(PRInt32
*aDefaultPort
)
864 nsGnomeVFSProtocolHandler::GetProtocolFlags(PRUint32
*aProtocolFlags
)
866 // Is URI_STD true of all GnomeVFS URI types?
867 *aProtocolFlags
= URI_STD
| URI_DANGEROUS_TO_LOAD
;
872 nsGnomeVFSProtocolHandler::NewURI(const nsACString
&aSpec
,
873 const char *aOriginCharset
,
877 const nsCString
flatSpec(aSpec
);
878 LOG(("gnomevfs: NewURI [spec=%s]\n", flatSpec
.get()));
883 // XXX This check is used to limit the gnome-vfs protocols we support. For
884 // security reasons, it is best that we limit the protocols we support to
885 // those with known characteristics. We might want to lessen this
886 // restriction if it proves to be too heavy handed. A black list of
887 // protocols we don't want to support might be better. For example, we
888 // probably don't want to try to load "start-here:" inside the browser.
889 // There are others that fall into this category, which are best handled
890 // externally by Nautilus (or another app like it).
892 if (!IsSupportedProtocol(flatSpec
))
893 return NS_ERROR_UNKNOWN_PROTOCOL
;
895 // Verify that GnomeVFS supports this URI scheme.
896 GnomeVFSURI
*uri
= gnome_vfs_uri_new(flatSpec
.get());
898 return NS_ERROR_UNKNOWN_PROTOCOL
;
902 // XXX Can we really assume that all gnome-vfs URIs can be parsed using
903 // nsStandardURL? We probably really need to implement nsIURI/nsIURL
904 // in terms of the gnome_vfs_uri_XXX methods, but at least this works
905 // correctly for smb:// URLs ;-)
907 // Also, it might not be possible to fully implement nsIURI/nsIURL in
908 // terms of GnomeVFSURI since some Necko methods have no GnomeVFS
912 nsCOMPtr
<nsIStandardURL
> url
=
913 do_CreateInstance(NS_STANDARDURL_CONTRACTID
, &rv
);
917 rv
= url
->Init(nsIStandardURL::URLTYPE_STANDARD
, -1, flatSpec
,
918 aOriginCharset
, aBaseURI
);
919 if (NS_SUCCEEDED(rv
))
920 rv
= CallQueryInterface(url
, aResult
);
926 nsGnomeVFSProtocolHandler::NewChannel(nsIURI
*aURI
, nsIChannel
**aResult
)
928 NS_ENSURE_ARG_POINTER(aURI
);
932 rv
= aURI
->GetSpec(spec
);
936 nsRefPtr
<nsGnomeVFSInputStream
> stream
= new nsGnomeVFSInputStream(spec
);
939 rv
= NS_ERROR_OUT_OF_MEMORY
;
943 // start out assuming an unknown content-type. we'll set the content-type
944 // to something better once we open the URI.
945 rv
= NS_NewInputStreamChannel(aResult
, aURI
, stream
,
946 NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE
));
947 if (NS_SUCCEEDED(rv
))
948 stream
->SetChannel(*aResult
);
954 nsGnomeVFSProtocolHandler::AllowPort(PRInt32 aPort
,
958 // Don't override anything.
964 nsGnomeVFSProtocolHandler::Observe(nsISupports
*aSubject
,
966 const PRUnichar
*aData
)
968 if (strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0) {
969 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
970 InitSupportedProtocolsPref(prefs
);
975 //-----------------------------------------------------------------------------
977 #define NS_GNOMEVFSPROTOCOLHANDLER_CID \
978 { /* 9b6dc177-a2e4-49e1-9c98-0a8384de7f6c */ \
982 {0x9c, 0x98, 0x0a, 0x83, 0x84, 0xde, 0x7f, 0x6c} \
985 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGnomeVFSProtocolHandler
, Init
)
987 static const nsModuleComponentInfo components
[] =
989 { "nsGnomeVFSProtocolHandler",
990 NS_GNOMEVFSPROTOCOLHANDLER_CID
,
991 NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GNOMEVFS_SCHEME
,
992 nsGnomeVFSProtocolHandlerConstructor
996 NS_IMPL_NSGETMODULE(nsGnomeVFSModule
, components
)