1 /* vim:set ts=2 sw=2 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * This code is based on original Mozilla gnome-vfs extension. It implements
8 * input stream provided by GVFS/GIO.
10 #include "mozilla/ModuleUtils.h"
11 #include "nsIPrefService.h"
12 #include "nsIPrefBranch.h"
13 #include "nsIObserver.h"
14 #include "nsThreadUtils.h"
15 #include "nsProxyRelease.h"
16 #include "nsIStringBundle.h"
17 #include "nsIStandardURL.h"
18 #include "nsMimeTypes.h"
19 #include "nsNetUtil.h"
20 #include "mozilla/Monitor.h"
24 #define MOZ_GIO_SCHEME "moz-gio"
25 #define MOZ_GIO_SUPPORTED_PROTOCOLS "network.gio.supported-protocols"
27 //-----------------------------------------------------------------------------
29 // NSPR_LOG_MODULES=gio:5
31 static PRLogModuleInfo
*sGIOLog
;
32 #define LOG(args) PR_LOG(sGIOLog, PR_LOG_DEBUG, args)
38 //-----------------------------------------------------------------------------
40 MapGIOResult(gint code
)
44 case G_IO_ERROR_NOT_FOUND
: return NS_ERROR_FILE_NOT_FOUND
; // shows error
45 case G_IO_ERROR_INVALID_ARGUMENT
: return NS_ERROR_INVALID_ARG
;
46 case G_IO_ERROR_NOT_SUPPORTED
: return NS_ERROR_NOT_AVAILABLE
;
47 case G_IO_ERROR_NO_SPACE
: return NS_ERROR_FILE_NO_DEVICE_SPACE
;
48 case G_IO_ERROR_READ_ONLY
: return NS_ERROR_FILE_READ_ONLY
;
49 case G_IO_ERROR_PERMISSION_DENIED
: return NS_ERROR_FILE_ACCESS_DENIED
; // wrong password/login
50 case G_IO_ERROR_CLOSED
: return NS_BASE_STREAM_CLOSED
; // was EOF
51 case G_IO_ERROR_NOT_DIRECTORY
: return NS_ERROR_FILE_NOT_DIRECTORY
;
52 case G_IO_ERROR_PENDING
: return NS_ERROR_IN_PROGRESS
;
53 case G_IO_ERROR_EXISTS
: return NS_ERROR_FILE_ALREADY_EXISTS
;
54 case G_IO_ERROR_IS_DIRECTORY
: return NS_ERROR_FILE_IS_DIRECTORY
;
55 case G_IO_ERROR_NOT_MOUNTED
: return NS_ERROR_NOT_CONNECTED
; // shows error
56 case G_IO_ERROR_HOST_NOT_FOUND
: return NS_ERROR_UNKNOWN_HOST
; // shows error
57 case G_IO_ERROR_CANCELLED
: return NS_ERROR_ABORT
;
58 case G_IO_ERROR_NOT_EMPTY
: return NS_ERROR_FILE_DIR_NOT_EMPTY
;
59 case G_IO_ERROR_FILENAME_TOO_LONG
: return NS_ERROR_FILE_NAME_TOO_LONG
;
60 case G_IO_ERROR_INVALID_FILENAME
: return NS_ERROR_FILE_INVALID_PATH
;
61 case G_IO_ERROR_TIMED_OUT
: return NS_ERROR_NET_TIMEOUT
; // shows error
62 case G_IO_ERROR_WOULD_BLOCK
: return NS_BASE_STREAM_WOULD_BLOCK
;
63 case G_IO_ERROR_FAILED_HANDLED
: return NS_ERROR_ABORT
; // Cancel on login dialog
66 G_IO_ERROR_NOT_REGULAR_FILE,
67 G_IO_ERROR_NOT_SYMBOLIC_LINK,
68 G_IO_ERROR_NOT_MOUNTABLE_FILE,
69 G_IO_ERROR_TOO_MANY_LINKS,
70 G_IO_ERROR_ALREADY_MOUNTED,
71 G_IO_ERROR_CANT_CREATE_BACKUP,
72 G_IO_ERROR_WRONG_ETAG,
73 G_IO_ERROR_WOULD_RECURSE,
75 G_IO_ERROR_WOULD_MERGE,
76 G_IO_ERROR_TOO_MANY_OPEN_FILES
80 return NS_ERROR_FAILURE
;
83 return NS_ERROR_FAILURE
;
87 MapGIOResult(GError
*result
)
92 return MapGIOResult(result
->code
);
94 /** Return values for mount operation.
95 * These enums are used as mount operation return values.
98 MOUNT_OPERATION_IN_PROGRESS
, /** \enum operation in progress */
99 MOUNT_OPERATION_SUCCESS
, /** \enum operation successful */
100 MOUNT_OPERATION_FAILED
/** \enum operation not successful */
101 } MountOperationResult
;
102 //-----------------------------------------------------------------------------
104 * Sort function compares according to file type (directory/file)
105 * and alphabethical order
106 * @param a pointer to GFileInfo object to compare
107 * @param b pointer to GFileInfo object to compare
108 * @return -1 when first object should be before the second, 0 when equal,
109 * +1 when second object should be before the first
112 FileInfoComparator(gconstpointer a
, gconstpointer b
)
114 GFileInfo
*ia
= ( GFileInfo
*) a
;
115 GFileInfo
*ib
= ( GFileInfo
*) b
;
116 if (g_file_info_get_file_type(ia
) == G_FILE_TYPE_DIRECTORY
117 && g_file_info_get_file_type(ib
) != G_FILE_TYPE_DIRECTORY
)
119 if (g_file_info_get_file_type(ib
) == G_FILE_TYPE_DIRECTORY
120 && g_file_info_get_file_type(ia
) != G_FILE_TYPE_DIRECTORY
)
123 return strcasecmp(g_file_info_get_name(ia
), g_file_info_get_name(ib
));
126 /* Declaration of mount callback functions */
127 static void mount_enclosing_volume_finished (GObject
*source_object
,
130 static void mount_operation_ask_password (GMountOperation
*mount_op
,
132 const char *default_user
,
133 const char *default_domain
,
134 GAskPasswordFlags flags
,
136 //-----------------------------------------------------------------------------
138 class nsGIOInputStream MOZ_FINAL
: public nsIInputStream
140 ~nsGIOInputStream() { Close(); }
143 NS_DECL_THREADSAFE_ISUPPORTS
144 NS_DECL_NSIINPUTSTREAM
146 nsGIOInputStream(const nsCString
&uriSpec
)
151 , mBytesRemaining(UINT64_MAX
)
154 , mDirListPtr(nullptr)
157 , mMonitorMountInProgress("GIOInputStream::MountFinished") { }
159 void SetChannel(nsIChannel
*channel
)
161 // We need to hold an owning reference to our channel. This is done
162 // so we can access the channel's notification callbacks to acquire
163 // a reference to a nsIAuthPrompt if we need to handle an interactive
166 // However, the channel can only be accessed on the main thread, so
167 // we have to be very careful with ownership. Moreover, it doesn't
168 // support threadsafe addref/release, so proxying is the answer.
170 // Also, it's important to note that this likely creates a reference
171 // cycle since the channel likely owns this stream. This reference
172 // cycle is broken in our Close method.
174 NS_ADDREF(mChannel
= channel
);
176 void SetMountResult(MountOperationResult result
, gint error_code
);
179 nsresult
DoRead(char *aBuf
, uint32_t aCount
, uint32_t *aCountRead
);
180 nsresult
SetContentTypeOfChannel(const char *contentType
);
181 nsresult
MountVolume();
182 nsresult
DoOpenDirectory();
183 nsresult
DoOpenFile(GFileInfo
*info
);
185 nsIChannel
*mChannel
; // manually refcounted
187 GFileInputStream
*mStream
;
188 uint64_t mBytesRemaining
;
193 uint32_t mDirBufCursor
;
195 MountOperationResult mMountRes
;
196 mozilla::Monitor mMonitorMountInProgress
;
197 gint mMountErrorCode
;
200 * Set result of mount operation and notify monitor waiting for results.
201 * This method is called in main thread as long as it is used only
202 * in mount_enclosing_volume_finished function.
203 * @param result Result of mount operation
206 nsGIOInputStream::SetMountResult(MountOperationResult result
, gint error_code
)
208 mozilla::MonitorAutoLock
mon(mMonitorMountInProgress
);
210 mMountErrorCode
= error_code
;
215 * Start mount operation and wait in loop until it is finished. This method is
216 * called from thread which is trying to read from location.
219 nsGIOInputStream::MountVolume() {
220 GMountOperation
* mount_op
= g_mount_operation_new();
221 g_signal_connect (mount_op
, "ask-password",
222 G_CALLBACK (mount_operation_ask_password
), mChannel
);
223 mMountRes
= MOUNT_OPERATION_IN_PROGRESS
;
224 /* g_file_mount_enclosing_volume uses a dbus request to mount the volume.
225 Callback mount_enclosing_volume_finished is called in main thread
226 (not this thread on which this method is called). */
227 g_file_mount_enclosing_volume(mHandle
,
231 mount_enclosing_volume_finished
,
233 mozilla::MonitorAutoLock
mon(mMonitorMountInProgress
);
234 /* Waiting for finish of mount operation thread */
235 while (mMountRes
== MOUNT_OPERATION_IN_PROGRESS
)
238 g_object_unref(mount_op
);
240 if (mMountRes
== MOUNT_OPERATION_FAILED
) {
241 return MapGIOResult(mMountErrorCode
);
248 * Create list of infos about objects in opened directory
249 * Return: NS_OK when list obtained, otherwise error code according
250 * to failed operation.
253 nsGIOInputStream::DoOpenDirectory()
255 GError
*error
= nullptr;
257 GFileEnumerator
*f_enum
= g_file_enumerate_children(mHandle
,
258 "standard::*,time::*",
259 G_FILE_QUERY_INFO_NONE
,
263 nsresult rv
= MapGIOResult(error
);
264 g_warning("Cannot read from directory: %s", error
->message
);
268 // fill list of file infos
269 GFileInfo
*info
= g_file_enumerator_next_file(f_enum
, nullptr, &error
);
271 mDirList
= g_list_append(mDirList
, info
);
272 info
= g_file_enumerator_next_file(f_enum
, nullptr, &error
);
274 g_object_unref(f_enum
);
276 g_warning("Error reading directory content: %s", error
->message
);
277 nsresult rv
= MapGIOResult(error
);
283 // Sort list of file infos by using FileInfoComparator function
284 mDirList
= g_list_sort(mDirList
, FileInfoComparator
);
285 mDirListPtr
= mDirList
;
287 // Write base URL (make sure it ends with a '/')
288 mDirBuf
.AppendLiteral("300: ");
289 mDirBuf
.Append(mSpec
);
290 if (mSpec
.get()[mSpec
.Length() - 1] != '/')
292 mDirBuf
.Append('\n');
294 // Write column names
295 mDirBuf
.AppendLiteral("200: filename content-length last-modified file-type\n");
297 // Write charset (assume UTF-8)
298 // XXX is this correct?
299 mDirBuf
.AppendLiteral("301: UTF-8\n");
300 SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT
);
305 * Create file stream and set mime type for channel
306 * @param info file info used to determine mime type
307 * @return NS_OK when file stream created successfuly, error code otherwise
310 nsGIOInputStream::DoOpenFile(GFileInfo
*info
)
312 GError
*error
= nullptr;
314 mStream
= g_file_read(mHandle
, nullptr, &error
);
316 nsresult rv
= MapGIOResult(error
);
317 g_warning("Cannot read from file: %s", error
->message
);
322 const char * content_type
= g_file_info_get_content_type(info
);
324 char *mime_type
= g_content_type_get_mime_type(content_type
);
326 if (strcmp(mime_type
, APPLICATION_OCTET_STREAM
) != 0) {
327 SetContentTypeOfChannel(mime_type
);
332 g_warning("Missing content type.");
335 mBytesRemaining
= g_file_info_get_size(info
);
336 // Update the content length attribute on the channel. We do this
337 // synchronously without proxying. This hack is not as bad as it looks!
338 mChannel
->SetContentLength(mBytesRemaining
);
344 * Start file open operation, mount volume when needed and according to file type
345 * create file output stream or read directory content.
346 * @return NS_OK when file or directory opened successfully, error code otherwise
349 nsGIOInputStream::DoOpen()
352 GError
*error
= nullptr;
354 NS_ASSERTION(mHandle
== nullptr, "already open");
356 mHandle
= g_file_new_for_uri( mSpec
.get() );
358 GFileInfo
*info
= g_file_query_info(mHandle
,
360 G_FILE_QUERY_INFO_NONE
,
365 if (error
->domain
== G_IO_ERROR
&& error
->code
== G_IO_ERROR_NOT_MOUNTED
) {
366 // location is not yet mounted, try to mount
368 if (NS_IsMainThread())
369 return NS_ERROR_NOT_CONNECTED
;
376 info
= g_file_query_info(mHandle
,
378 G_FILE_QUERY_INFO_NONE
,
381 // second try to get file info from remote files after media mount
383 g_warning("Unable to get file info: %s", error
->message
);
384 rv
= MapGIOResult(error
);
389 g_warning("Unable to get file info: %s", error
->message
);
390 rv
= MapGIOResult(error
);
395 // Get file type to handle directories and file differently
396 GFileType f_type
= g_file_info_get_file_type(info
);
397 if (f_type
== G_FILE_TYPE_DIRECTORY
) {
399 rv
= DoOpenDirectory();
400 } else if (f_type
!= G_FILE_TYPE_UNKNOWN
) {
402 rv
= DoOpenFile(info
);
404 g_warning("Unable to get file type.");
405 rv
= NS_ERROR_FILE_NOT_FOUND
;
408 g_object_unref(info
);
413 * Read content of file or create file list from directory
414 * @param aBuf read destination buffer
415 * @param aCount length of destination buffer
416 * @param aCountRead number of read characters
417 * @return NS_OK when read successfully, NS_BASE_STREAM_CLOSED when end of file,
418 * error code otherwise
421 nsGIOInputStream::DoRead(char *aBuf
, uint32_t aCount
, uint32_t *aCountRead
)
423 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
426 GError
*error
= nullptr;
427 uint32_t bytes_read
= g_input_stream_read(G_INPUT_STREAM(mStream
),
433 rv
= MapGIOResult(error
);
435 g_warning("Cannot read from file: %s", error
->message
);
439 *aCountRead
= bytes_read
;
440 mBytesRemaining
-= *aCountRead
;
445 while (aCount
&& rv
!= NS_BASE_STREAM_CLOSED
)
447 // Copy data out of our buffer
448 uint32_t bufLen
= mDirBuf
.Length() - mDirBufCursor
;
451 uint32_t n
= std::min(bufLen
, aCount
);
452 memcpy(aBuf
, mDirBuf
.get() + mDirBufCursor
, n
);
459 if (!mDirListPtr
) // Are we at the end of the directory list?
461 rv
= NS_BASE_STREAM_CLOSED
;
463 else if (aCount
) // Do we need more data?
465 GFileInfo
*info
= (GFileInfo
*) mDirListPtr
->data
;
467 // Prune '.' and '..' from directory listing.
468 const char * fname
= g_file_info_get_name(info
);
469 if (fname
&& fname
[0] == '.' &&
470 (fname
[1] == '\0' || (fname
[1] == '.' && fname
[2] == '\0')))
472 mDirListPtr
= mDirListPtr
->next
;
476 mDirBuf
.AssignLiteral("201: ");
478 // The "filename" field
480 nsCOMPtr
<nsINetUtil
> nu
= do_GetService(NS_NETUTIL_CONTRACTID
);
482 nu
->EscapeString(nsDependentCString(fname
),
483 nsINetUtil::ESCAPE_URL_PATH
, escName
);
485 mDirBuf
.Append(escName
);
489 // The "content-length" field
490 // XXX truncates size from 64-bit to 32-bit
491 mDirBuf
.AppendInt(int32_t(g_file_info_get_size(info
)));
494 // The "last-modified" field
496 // NSPR promises: PRTime is compatible with time_t
497 // we just need to convert from seconds to microseconds
499 g_file_info_get_modification_time(info
, >ime
);
502 PRTime pt
= ((PRTime
) gtime
.tv_sec
) * 1000000;
503 PR_ExplodeTime(pt
, PR_GMTParameters
, &tm
);
506 PR_FormatTimeUSEnglish(buf
, sizeof(buf
),
507 "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm
);
511 // The "file-type" field
512 switch (g_file_info_get_file_type(info
))
514 case G_FILE_TYPE_REGULAR
:
515 mDirBuf
.AppendLiteral("FILE ");
517 case G_FILE_TYPE_DIRECTORY
:
518 mDirBuf
.AppendLiteral("DIRECTORY ");
520 case G_FILE_TYPE_SYMBOLIC_LINK
:
521 mDirBuf
.AppendLiteral("SYMBOLIC-LINK ");
526 mDirBuf
.Append('\n');
529 mDirListPtr
= mDirListPtr
->next
;
537 * This class is used to implement SetContentTypeOfChannel.
539 class nsGIOSetContentTypeEvent
: public nsRunnable
542 nsGIOSetContentTypeEvent(nsIChannel
*channel
, const char *contentType
)
543 : mChannel(channel
), mContentType(contentType
)
545 // stash channel reference in mChannel. no AddRef here! see note
546 // in SetContentTypeOfchannel.
551 mChannel
->SetContentType(mContentType
);
556 nsIChannel
*mChannel
;
557 nsCString mContentType
;
561 nsGIOInputStream::SetContentTypeOfChannel(const char *contentType
)
563 // We need to proxy this call over to the main thread. We post an
564 // asynchronous event in this case so that we don't delay reading data, and
565 // we know that this is safe to do since the channel's reference will be
566 // released asynchronously as well. We trust the ordering of the main
567 // thread's event queue to protect us against memory corruption.
570 nsCOMPtr
<nsIRunnable
> ev
=
571 new nsGIOSetContentTypeEvent(mChannel
, contentType
);
574 rv
= NS_ERROR_OUT_OF_MEMORY
;
578 rv
= NS_DispatchToMainThread(ev
);
583 NS_IMPL_ISUPPORTS(nsGIOInputStream
, nsIInputStream
)
586 * Free all used memory and close stream.
589 nsGIOInputStream::Close()
593 g_object_unref(mStream
);
599 g_object_unref(mHandle
);
605 // Destroy the list of GIOFileInfo objects...
606 g_list_foreach(mDirList
, (GFunc
) g_object_unref
, nullptr);
607 g_list_free(mDirList
);
609 mDirListPtr
= nullptr;
616 nsCOMPtr
<nsIThread
> thread
= do_GetMainThread();
618 rv
= NS_ProxyRelease(thread
, mChannel
);
620 NS_ASSERTION(thread
&& NS_SUCCEEDED(rv
), "leaking channel reference");
625 mSpec
.Truncate(); // free memory
627 // Prevent future reads from re-opening the handle.
628 if (NS_SUCCEEDED(mStatus
))
629 mStatus
= NS_BASE_STREAM_CLOSED
;
635 * Return number of remaining bytes available on input
636 * @param aResult remaining bytes
639 nsGIOInputStream::Available(uint64_t *aResult
)
641 if (NS_FAILED(mStatus
))
644 *aResult
= mBytesRemaining
;
650 * Trying to read from stream. When location is not available it tries to mount it.
651 * @param aBuf buffer to put read data
652 * @param aCount length of aBuf
653 * @param aCountRead number of bytes actually read
656 nsGIOInputStream::Read(char *aBuf
,
658 uint32_t *aCountRead
)
661 // Check if file is already opened, otherwise open it
662 if (!mStream
&& !mDirOpen
&& mStatus
== NS_OK
) {
664 if (NS_FAILED(mStatus
)) {
669 mStatus
= DoRead(aBuf
, aCount
, aCountRead
);
670 // Check if all data has been read
671 if (mStatus
== NS_BASE_STREAM_CLOSED
)
674 // Check whenever any error appears while reading
679 nsGIOInputStream::ReadSegments(nsWriteSegmentFun aWriter
,
684 // There is no way to implement this using GnomeVFS, but fortunately
685 // that doesn't matter. Because we are a blocking input stream, Necko
686 // isn't going to call our ReadSegments method.
687 NS_NOTREACHED("nsGIOInputStream::ReadSegments");
688 return NS_ERROR_NOT_IMPLEMENTED
;
692 nsGIOInputStream::IsNonBlocking(bool *aResult
)
698 //-----------------------------------------------------------------------------
701 * Called when finishing mount operation. Result of operation is set in
702 * nsGIOInputStream. This function is called in main thread as an async request
703 * typically from dbus.
704 * @param source_object GFile object which requested the mount
705 * @param res result object
706 * @param user_data pointer to nsGIOInputStream
709 mount_enclosing_volume_finished (GObject
*source_object
,
713 GError
*error
= nullptr;
715 nsGIOInputStream
* istream
= static_cast<nsGIOInputStream
*>(user_data
);
717 g_file_mount_enclosing_volume_finish(G_FILE (source_object
), res
, &error
);
720 g_warning("Mount failed: %s %d", error
->message
, error
->code
);
721 istream
->SetMountResult(MOUNT_OPERATION_FAILED
, error
->code
);
724 istream
->SetMountResult(MOUNT_OPERATION_SUCCESS
, 0);
729 * This function is called when username or password are requested from user.
730 * This function is called in main thread as async request from dbus.
731 * @param mount_op mount operation
732 * @param message message to show to user
733 * @param default_user preffered user
734 * @param default_domain domain name
735 * @param flags what type of information is required
736 * @param user_data nsIChannel
739 mount_operation_ask_password (GMountOperation
*mount_op
,
741 const char *default_user
,
742 const char *default_domain
,
743 GAskPasswordFlags flags
,
746 nsIChannel
*channel
= (nsIChannel
*) user_data
;
748 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
751 // We can't handle request for domain
752 if (flags
& G_ASK_PASSWORD_NEED_DOMAIN
) {
753 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
757 nsCOMPtr
<nsIAuthPrompt
> prompt
;
758 NS_QueryNotificationCallbacks(channel
, prompt
);
760 // If no auth prompt, then give up. We could failover to using the
761 // WindowWatcher service, but that might defeat a consumer's purposeful
762 // attempt to disable authentication (for whatever reason).
764 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
767 // Parse out the host and port...
768 nsCOMPtr
<nsIURI
> uri
;
769 channel
->GetURI(getter_AddRefs(uri
));
771 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
775 nsAutoCString scheme
, hostPort
;
776 uri
->GetScheme(scheme
);
777 uri
->GetHostPort(hostPort
);
779 // It doesn't make sense for either of these strings to be empty. What kind
780 // of funky URI is this?
781 if (scheme
.IsEmpty() || hostPort
.IsEmpty()) {
782 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
785 // Construct the single signon key. Altering the value of this key will
786 // cause people's remembered passwords to be forgotten. Think carefully
787 // before changing the way this key is constructed.
788 nsAutoString key
, realm
;
790 NS_ConvertUTF8toUTF16
dispHost(scheme
);
791 dispHost
.AppendLiteral("://");
792 dispHost
.Append(NS_ConvertUTF8toUTF16(hostPort
));
795 if (*default_domain
!= '\0')
797 // We assume the realm string is ASCII. That might be a bogus assumption,
798 // but we have no idea what encoding GnomeVFS is using, so for now we'll
799 // limit ourselves to ISO-Latin-1. XXX What is a better solution?
801 realm
.Append(NS_ConvertASCIItoUTF16(default_domain
));
806 // Construct the message string...
808 // We use Necko's string bundle here. This code really should be encapsulated
809 // behind some Necko API, after all this code is based closely on the code in
810 // nsHttpChannel.cpp.
811 nsCOMPtr
<nsIStringBundleService
> bundleSvc
=
812 do_GetService(NS_STRINGBUNDLE_CONTRACTID
);
814 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
817 nsCOMPtr
<nsIStringBundle
> bundle
;
818 bundleSvc
->CreateBundle("chrome://global/locale/commonDialogs.properties",
819 getter_AddRefs(bundle
));
821 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
824 nsAutoString nsmessage
;
826 if (flags
& G_ASK_PASSWORD_NEED_PASSWORD
) {
827 if (flags
& G_ASK_PASSWORD_NEED_USERNAME
) {
828 if (!realm
.IsEmpty()) {
829 const char16_t
*strings
[] = { realm
.get(), dispHost
.get() };
830 bundle
->FormatStringFromName(MOZ_UTF16("EnterLoginForRealm"),
831 strings
, 2, getter_Copies(nsmessage
));
833 const char16_t
*strings
[] = { dispHost
.get() };
834 bundle
->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor"),
835 strings
, 1, getter_Copies(nsmessage
));
838 NS_ConvertUTF8toUTF16
userName(default_user
);
839 const char16_t
*strings
[] = { userName
.get(), dispHost
.get() };
840 bundle
->FormatStringFromName(MOZ_UTF16("EnterPasswordFor"),
841 strings
, 2, getter_Copies(nsmessage
));
844 g_warning("Unknown mount operation request (flags: %x)", flags
);
847 if (nsmessage
.IsEmpty()) {
848 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
851 // Prompt the user...
854 char16_t
*user
= nullptr, *pass
= nullptr;
856 // user will be freed by PromptUsernameAndPassword
857 user
= ToNewUnicode(NS_ConvertUTF8toUTF16(default_user
));
859 if (flags
& G_ASK_PASSWORD_NEED_USERNAME
) {
860 rv
= prompt
->PromptUsernameAndPassword(nullptr, nsmessage
.get(),
862 nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY
,
863 &user
, &pass
, &retval
);
865 rv
= prompt
->PromptPassword(nullptr, nsmessage
.get(),
867 nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY
,
870 if (NS_FAILED(rv
) || !retval
) { // was || user == '\0' || pass == '\0'
871 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_ABORTED
);
874 /* GIO should accept UTF8 */
875 g_mount_operation_set_username(mount_op
, NS_ConvertUTF16toUTF8(user
).get());
876 g_mount_operation_set_password(mount_op
, NS_ConvertUTF16toUTF8(pass
).get());
877 nsMemory::Free(user
);
878 nsMemory::Free(pass
);
879 g_mount_operation_reply(mount_op
, G_MOUNT_OPERATION_HANDLED
);
882 //-----------------------------------------------------------------------------
884 class nsGIOProtocolHandler MOZ_FINAL
: public nsIProtocolHandler
889 NS_DECL_NSIPROTOCOLHANDLER
895 ~nsGIOProtocolHandler() {}
897 void InitSupportedProtocolsPref(nsIPrefBranch
*prefs
);
898 bool IsSupportedProtocol(const nsCString
&spec
);
900 nsCString mSupportedProtocols
;
903 NS_IMPL_ISUPPORTS(nsGIOProtocolHandler
, nsIProtocolHandler
, nsIObserver
)
906 nsGIOProtocolHandler::Init()
909 sGIOLog
= PR_NewLogModule("gio");
912 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
915 InitSupportedProtocolsPref(prefs
);
916 prefs
->AddObserver(MOZ_GIO_SUPPORTED_PROTOCOLS
, this, false);
923 nsGIOProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch
*prefs
)
925 // Get user preferences to determine which protocol is supported.
926 // Gvfs/GIO has a set of supported protocols like obex, network, archive,
927 // computer, dav, cdda, gphoto2, trash, etc. Some of these seems to be
928 // irrelevant to process by browser. By default accept only smb and sftp
930 nsresult rv
= prefs
->GetCharPref(MOZ_GIO_SUPPORTED_PROTOCOLS
,
931 getter_Copies(mSupportedProtocols
));
932 if (NS_SUCCEEDED(rv
)) {
933 mSupportedProtocols
.StripWhitespace();
934 ToLowerCase(mSupportedProtocols
);
937 mSupportedProtocols
.AssignLiteral("smb:,sftp:"); // use defaults
939 LOG(("gio: supported protocols \"%s\"\n", mSupportedProtocols
.get()));
943 nsGIOProtocolHandler::IsSupportedProtocol(const nsCString
&aSpec
)
945 const char *specString
= aSpec
.get();
946 const char *colon
= strchr(specString
, ':');
950 uint32_t length
= colon
- specString
+ 1;
953 nsCString
scheme(specString
, length
);
955 char *found
= PL_strcasestr(mSupportedProtocols
.get(), scheme
.get());
959 if (found
[length
] != ',' && found
[length
] != '\0')
966 nsGIOProtocolHandler::GetScheme(nsACString
&aScheme
)
968 aScheme
.Assign(MOZ_GIO_SCHEME
);
973 nsGIOProtocolHandler::GetDefaultPort(int32_t *aDefaultPort
)
980 nsGIOProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags
)
982 // Is URI_STD true of all GnomeVFS URI types?
983 *aProtocolFlags
= URI_STD
| URI_DANGEROUS_TO_LOAD
;
988 nsGIOProtocolHandler::NewURI(const nsACString
&aSpec
,
989 const char *aOriginCharset
,
993 const nsCString
flatSpec(aSpec
);
994 LOG(("gio: NewURI [spec=%s]\n", flatSpec
.get()));
998 // XXX Is it good to support all GIO protocols?
999 if (!IsSupportedProtocol(flatSpec
))
1000 return NS_ERROR_UNKNOWN_PROTOCOL
;
1002 int32_t colon_location
= flatSpec
.FindChar(':');
1003 if (colon_location
<= 0)
1004 return NS_ERROR_UNKNOWN_PROTOCOL
;
1006 // Verify that GIO supports this URI scheme.
1007 bool uri_scheme_supported
= false;
1009 GVfs
*gvfs
= g_vfs_get_default();
1012 g_warning("Cannot get GVfs object.");
1013 return NS_ERROR_UNKNOWN_PROTOCOL
;
1016 const gchar
* const * uri_schemes
= g_vfs_get_supported_uri_schemes(gvfs
);
1018 while (*uri_schemes
!= nullptr) {
1019 // While flatSpec ends with ':' the uri_scheme does not. Therefore do not
1020 // compare last character.
1021 if (StringHead(flatSpec
, colon_location
).Equals(*uri_schemes
)) {
1022 uri_scheme_supported
= true;
1028 if (!uri_scheme_supported
) {
1029 return NS_ERROR_UNKNOWN_PROTOCOL
;
1034 nsCOMPtr
<nsIStandardURL
> url
=
1035 do_CreateInstance(NS_STANDARDURL_CONTRACTID
, &rv
);
1039 rv
= url
->Init(nsIStandardURL::URLTYPE_STANDARD
, -1, flatSpec
,
1040 aOriginCharset
, aBaseURI
);
1041 if (NS_SUCCEEDED(rv
))
1042 rv
= CallQueryInterface(url
, aResult
);
1048 nsGIOProtocolHandler::NewChannel(nsIURI
*aURI
, nsIChannel
**aResult
)
1050 NS_ENSURE_ARG_POINTER(aURI
);
1054 rv
= aURI
->GetSpec(spec
);
1058 nsRefPtr
<nsGIOInputStream
> stream
= new nsGIOInputStream(spec
);
1061 rv
= NS_ERROR_OUT_OF_MEMORY
;
1065 // start out assuming an unknown content-type. we'll set the content-type
1066 // to something better once we open the URI.
1067 rv
= NS_NewInputStreamChannel(aResult
,
1070 NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE
));
1071 if (NS_SUCCEEDED(rv
))
1072 stream
->SetChannel(*aResult
);
1078 nsGIOProtocolHandler::AllowPort(int32_t aPort
,
1079 const char *aScheme
,
1082 // Don't override anything.
1088 nsGIOProtocolHandler::Observe(nsISupports
*aSubject
,
1090 const char16_t
*aData
)
1092 if (strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0) {
1093 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
1094 InitSupportedProtocolsPref(prefs
);
1099 //-----------------------------------------------------------------------------
1101 #define NS_GIOPROTOCOLHANDLER_CID \
1102 { /* ee706783-3af8-4d19-9e84-e2ebfe213480 */ \
1106 {0x9e, 0x84, 0xe2, 0xeb, 0xfe, 0x21, 0x34, 0x80} \
1109 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGIOProtocolHandler
, Init
)
1110 NS_DEFINE_NAMED_CID(NS_GIOPROTOCOLHANDLER_CID
);
1112 static const mozilla::Module::CIDEntry kVFSCIDs
[] = {
1113 { &kNS_GIOPROTOCOLHANDLER_CID
, false, nullptr, nsGIOProtocolHandlerConstructor
},
1117 static const mozilla::Module::ContractIDEntry kVFSContracts
[] = {
1118 { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GIO_SCHEME
, &kNS_GIOPROTOCOLHANDLER_CID
},
1122 static const mozilla::Module kVFSModule
= {
1123 mozilla::Module::kVersion
,
1128 NSMODULE_DEFN(nsGIOModule
) = &kVFSModule
;