/src/mozilla-central/netwerk/protocol/gio/nsGIOProtocolHandler.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | /* |
7 | | * This code is based on original Mozilla gnome-vfs extension. It implements |
8 | | * input stream provided by GVFS/GIO. |
9 | | */ |
10 | | #include "mozilla/ModuleUtils.h" |
11 | | #include "mozilla/NullPrincipal.h" |
12 | | #include "nsIPrefService.h" |
13 | | #include "nsIPrefBranch.h" |
14 | | #include "nsIObserver.h" |
15 | | #include "nsThreadUtils.h" |
16 | | #include "nsProxyRelease.h" |
17 | | #include "nsIStringBundle.h" |
18 | | #include "nsIStandardURL.h" |
19 | | #include "nsMimeTypes.h" |
20 | | #include "nsNetCID.h" |
21 | | #include "nsNetUtil.h" |
22 | | #include "nsServiceManagerUtils.h" |
23 | | #include "nsIURI.h" |
24 | | #include "nsIURIMutator.h" |
25 | | #include "nsIAuthPrompt.h" |
26 | | #include "nsIChannel.h" |
27 | | #include "nsIInputStream.h" |
28 | | #include "nsIProtocolHandler.h" |
29 | | #include "mozilla/Monitor.h" |
30 | | #include "plstr.h" |
31 | | #include "prtime.h" |
32 | | #include <gio/gio.h> |
33 | | #include <algorithm> |
34 | | |
35 | 0 | #define MOZ_GIO_SCHEME "moz-gio" |
36 | 2 | #define MOZ_GIO_SUPPORTED_PROTOCOLS "network.gio.supported-protocols" |
37 | | |
38 | | //----------------------------------------------------------------------------- |
39 | | |
40 | | // NSPR_LOG_MODULES=gio:5 |
41 | | static mozilla::LazyLogModule sGIOLog("gio"); |
42 | 285k | #define LOG(args) MOZ_LOG(sGIOLog, mozilla::LogLevel::Debug, args) |
43 | | |
44 | | |
45 | | //----------------------------------------------------------------------------- |
46 | | static nsresult |
47 | | MapGIOResult(gint code) |
48 | | { |
49 | | switch (code) |
50 | | { |
51 | | case G_IO_ERROR_NOT_FOUND: return NS_ERROR_FILE_NOT_FOUND; // shows error |
52 | | case G_IO_ERROR_INVALID_ARGUMENT: return NS_ERROR_INVALID_ARG; |
53 | | case G_IO_ERROR_NOT_SUPPORTED: return NS_ERROR_NOT_AVAILABLE; |
54 | | case G_IO_ERROR_NO_SPACE: return NS_ERROR_FILE_NO_DEVICE_SPACE; |
55 | | case G_IO_ERROR_READ_ONLY: return NS_ERROR_FILE_READ_ONLY; |
56 | | case G_IO_ERROR_PERMISSION_DENIED: return NS_ERROR_FILE_ACCESS_DENIED; // wrong password/login |
57 | | case G_IO_ERROR_CLOSED: return NS_BASE_STREAM_CLOSED; // was EOF |
58 | | case G_IO_ERROR_NOT_DIRECTORY: return NS_ERROR_FILE_NOT_DIRECTORY; |
59 | | case G_IO_ERROR_PENDING: return NS_ERROR_IN_PROGRESS; |
60 | | case G_IO_ERROR_EXISTS: return NS_ERROR_FILE_ALREADY_EXISTS; |
61 | | case G_IO_ERROR_IS_DIRECTORY: return NS_ERROR_FILE_IS_DIRECTORY; |
62 | | case G_IO_ERROR_NOT_MOUNTED: return NS_ERROR_NOT_CONNECTED; // shows error |
63 | | case G_IO_ERROR_HOST_NOT_FOUND: return NS_ERROR_UNKNOWN_HOST; // shows error |
64 | | case G_IO_ERROR_CANCELLED: return NS_ERROR_ABORT; |
65 | | case G_IO_ERROR_NOT_EMPTY: return NS_ERROR_FILE_DIR_NOT_EMPTY; |
66 | | case G_IO_ERROR_FILENAME_TOO_LONG: return NS_ERROR_FILE_NAME_TOO_LONG; |
67 | | case G_IO_ERROR_INVALID_FILENAME: return NS_ERROR_FILE_INVALID_PATH; |
68 | | case G_IO_ERROR_TIMED_OUT: return NS_ERROR_NET_TIMEOUT; // shows error |
69 | | case G_IO_ERROR_WOULD_BLOCK: return NS_BASE_STREAM_WOULD_BLOCK; |
70 | | case G_IO_ERROR_FAILED_HANDLED: return NS_ERROR_ABORT; // Cancel on login dialog |
71 | | |
72 | | /* unhandled: |
73 | | G_IO_ERROR_NOT_REGULAR_FILE, |
74 | | G_IO_ERROR_NOT_SYMBOLIC_LINK, |
75 | | G_IO_ERROR_NOT_MOUNTABLE_FILE, |
76 | | G_IO_ERROR_TOO_MANY_LINKS, |
77 | | G_IO_ERROR_ALREADY_MOUNTED, |
78 | | G_IO_ERROR_CANT_CREATE_BACKUP, |
79 | | G_IO_ERROR_WRONG_ETAG, |
80 | | G_IO_ERROR_WOULD_RECURSE, |
81 | | G_IO_ERROR_BUSY, |
82 | | G_IO_ERROR_WOULD_MERGE, |
83 | | G_IO_ERROR_TOO_MANY_OPEN_FILES |
84 | | */ |
85 | | // Make GCC happy |
86 | | default: |
87 | | return NS_ERROR_FAILURE; |
88 | | } |
89 | | } |
90 | | |
91 | | static nsresult |
92 | | MapGIOResult(GError *result) |
93 | 0 | { |
94 | 0 | if (!result) |
95 | 0 | return NS_OK; |
96 | 0 | return MapGIOResult(result->code); |
97 | 0 | } |
98 | | /** Return values for mount operation. |
99 | | * These enums are used as mount operation return values. |
100 | | */ |
101 | | typedef enum { |
102 | | MOUNT_OPERATION_IN_PROGRESS, /** \enum operation in progress */ |
103 | | MOUNT_OPERATION_SUCCESS, /** \enum operation successful */ |
104 | | MOUNT_OPERATION_FAILED /** \enum operation not successful */ |
105 | | } MountOperationResult; |
106 | | //----------------------------------------------------------------------------- |
107 | | /** |
108 | | * Sort function compares according to file type (directory/file) |
109 | | * and alphabethical order |
110 | | * @param a pointer to GFileInfo object to compare |
111 | | * @param b pointer to GFileInfo object to compare |
112 | | * @return -1 when first object should be before the second, 0 when equal, |
113 | | * +1 when second object should be before the first |
114 | | */ |
115 | | static gint |
116 | | FileInfoComparator(gconstpointer a, gconstpointer b) |
117 | 0 | { |
118 | 0 | GFileInfo *ia = ( GFileInfo *) a; |
119 | 0 | GFileInfo *ib = ( GFileInfo *) b; |
120 | 0 | if (g_file_info_get_file_type(ia) == G_FILE_TYPE_DIRECTORY |
121 | 0 | && g_file_info_get_file_type(ib) != G_FILE_TYPE_DIRECTORY) |
122 | 0 | return -1; |
123 | 0 | if (g_file_info_get_file_type(ib) == G_FILE_TYPE_DIRECTORY |
124 | 0 | && g_file_info_get_file_type(ia) != G_FILE_TYPE_DIRECTORY) |
125 | 0 | return 1; |
126 | 0 | |
127 | 0 | return strcasecmp(g_file_info_get_name(ia), g_file_info_get_name(ib)); |
128 | 0 | } |
129 | | |
130 | | /* Declaration of mount callback functions */ |
131 | | static void mount_enclosing_volume_finished (GObject *source_object, |
132 | | GAsyncResult *res, |
133 | | gpointer user_data); |
134 | | static void mount_operation_ask_password (GMountOperation *mount_op, |
135 | | const char *message, |
136 | | const char *default_user, |
137 | | const char *default_domain, |
138 | | GAskPasswordFlags flags, |
139 | | gpointer user_data); |
140 | | //----------------------------------------------------------------------------- |
141 | | |
142 | | class nsGIOInputStream final : public nsIInputStream |
143 | | { |
144 | 0 | ~nsGIOInputStream() { Close(); } |
145 | | |
146 | | public: |
147 | | NS_DECL_THREADSAFE_ISUPPORTS |
148 | | NS_DECL_NSIINPUTSTREAM |
149 | | |
150 | | explicit nsGIOInputStream(const nsCString &uriSpec) |
151 | | : mSpec(uriSpec) |
152 | | , mChannel(nullptr) |
153 | | , mHandle(nullptr) |
154 | | , mStream(nullptr) |
155 | | , mBytesRemaining(UINT64_MAX) |
156 | | , mStatus(NS_OK) |
157 | | , mDirList(nullptr) |
158 | | , mDirListPtr(nullptr) |
159 | | , mDirBufCursor(0) |
160 | | , mDirOpen(false) |
161 | 0 | , mMonitorMountInProgress("GIOInputStream::MountFinished") { } |
162 | | |
163 | | void SetChannel(nsIChannel *channel) |
164 | 0 | { |
165 | 0 | // We need to hold an owning reference to our channel. This is done |
166 | 0 | // so we can access the channel's notification callbacks to acquire |
167 | 0 | // a reference to a nsIAuthPrompt if we need to handle an interactive |
168 | 0 | // mount operation. |
169 | 0 | // |
170 | 0 | // However, the channel can only be accessed on the main thread, so |
171 | 0 | // we have to be very careful with ownership. Moreover, it doesn't |
172 | 0 | // support threadsafe addref/release, so proxying is the answer. |
173 | 0 | // |
174 | 0 | // Also, it's important to note that this likely creates a reference |
175 | 0 | // cycle since the channel likely owns this stream. This reference |
176 | 0 | // cycle is broken in our Close method. |
177 | 0 |
|
178 | 0 | NS_ADDREF(mChannel = channel); |
179 | 0 | } |
180 | | void SetMountResult(MountOperationResult result, gint error_code); |
181 | | private: |
182 | | nsresult DoOpen(); |
183 | | nsresult DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead); |
184 | | nsresult SetContentTypeOfChannel(const char *contentType); |
185 | | nsresult MountVolume(); |
186 | | nsresult DoOpenDirectory(); |
187 | | nsresult DoOpenFile(GFileInfo *info); |
188 | | nsCString mSpec; |
189 | | nsIChannel *mChannel; // manually refcounted |
190 | | GFile *mHandle; |
191 | | GFileInputStream *mStream; |
192 | | uint64_t mBytesRemaining; |
193 | | nsresult mStatus; |
194 | | GList *mDirList; |
195 | | GList *mDirListPtr; |
196 | | nsCString mDirBuf; |
197 | | uint32_t mDirBufCursor; |
198 | | bool mDirOpen; |
199 | | MountOperationResult mMountRes; |
200 | | mozilla::Monitor mMonitorMountInProgress; |
201 | | gint mMountErrorCode; |
202 | | }; |
203 | | /** |
204 | | * Set result of mount operation and notify monitor waiting for results. |
205 | | * This method is called in main thread as long as it is used only |
206 | | * in mount_enclosing_volume_finished function. |
207 | | * @param result Result of mount operation |
208 | | */ |
209 | | void |
210 | | nsGIOInputStream::SetMountResult(MountOperationResult result, gint error_code) |
211 | 0 | { |
212 | 0 | mozilla::MonitorAutoLock mon(mMonitorMountInProgress); |
213 | 0 | mMountRes = result; |
214 | 0 | mMountErrorCode = error_code; |
215 | 0 | mon.Notify(); |
216 | 0 | } |
217 | | |
218 | | /** |
219 | | * Start mount operation and wait in loop until it is finished. This method is |
220 | | * called from thread which is trying to read from location. |
221 | | */ |
222 | | nsresult |
223 | 0 | nsGIOInputStream::MountVolume() { |
224 | 0 | GMountOperation* mount_op = g_mount_operation_new(); |
225 | 0 | g_signal_connect (mount_op, "ask-password", |
226 | 0 | G_CALLBACK (mount_operation_ask_password), mChannel); |
227 | 0 | mMountRes = MOUNT_OPERATION_IN_PROGRESS; |
228 | 0 | /* g_file_mount_enclosing_volume uses a dbus request to mount the volume. |
229 | 0 | Callback mount_enclosing_volume_finished is called in main thread |
230 | 0 | (not this thread on which this method is called). */ |
231 | 0 | g_file_mount_enclosing_volume(mHandle, |
232 | 0 | G_MOUNT_MOUNT_NONE, |
233 | 0 | mount_op, |
234 | 0 | nullptr, |
235 | 0 | mount_enclosing_volume_finished, |
236 | 0 | this); |
237 | 0 | mozilla::MonitorAutoLock mon(mMonitorMountInProgress); |
238 | 0 | /* Waiting for finish of mount operation thread */ |
239 | 0 | while (mMountRes == MOUNT_OPERATION_IN_PROGRESS) |
240 | 0 | mon.Wait(); |
241 | 0 |
|
242 | 0 | g_object_unref(mount_op); |
243 | 0 |
|
244 | 0 | if (mMountRes == MOUNT_OPERATION_FAILED) { |
245 | 0 | return MapGIOResult(mMountErrorCode); |
246 | 0 | } |
247 | 0 | return NS_OK; |
248 | 0 | } |
249 | | |
250 | | /** |
251 | | * Create list of infos about objects in opened directory |
252 | | * Return: NS_OK when list obtained, otherwise error code according |
253 | | * to failed operation. |
254 | | */ |
255 | | nsresult |
256 | | nsGIOInputStream::DoOpenDirectory() |
257 | 0 | { |
258 | 0 | GError *error = nullptr; |
259 | 0 |
|
260 | 0 | GFileEnumerator *f_enum = g_file_enumerate_children(mHandle, |
261 | 0 | "standard::*,time::*", |
262 | 0 | G_FILE_QUERY_INFO_NONE, |
263 | 0 | nullptr, |
264 | 0 | &error); |
265 | 0 | if (!f_enum) { |
266 | 0 | nsresult rv = MapGIOResult(error); |
267 | 0 | g_warning("Cannot read from directory: %s", error->message); |
268 | 0 | g_error_free(error); |
269 | 0 | return rv; |
270 | 0 | } |
271 | 0 | // fill list of file infos |
272 | 0 | GFileInfo *info = g_file_enumerator_next_file(f_enum, nullptr, &error); |
273 | 0 | while (info) { |
274 | 0 | mDirList = g_list_append(mDirList, info); |
275 | 0 | info = g_file_enumerator_next_file(f_enum, nullptr, &error); |
276 | 0 | } |
277 | 0 | g_object_unref(f_enum); |
278 | 0 | if (error) { |
279 | 0 | g_warning("Error reading directory content: %s", error->message); |
280 | 0 | nsresult rv = MapGIOResult(error); |
281 | 0 | g_error_free(error); |
282 | 0 | return rv; |
283 | 0 | } |
284 | 0 | mDirOpen = true; |
285 | 0 |
|
286 | 0 | // Sort list of file infos by using FileInfoComparator function |
287 | 0 | mDirList = g_list_sort(mDirList, FileInfoComparator); |
288 | 0 | mDirListPtr = mDirList; |
289 | 0 |
|
290 | 0 | // Write base URL (make sure it ends with a '/') |
291 | 0 | mDirBuf.AppendLiteral("300: "); |
292 | 0 | mDirBuf.Append(mSpec); |
293 | 0 | if (mSpec.get()[mSpec.Length() - 1] != '/') |
294 | 0 | mDirBuf.Append('/'); |
295 | 0 | mDirBuf.Append('\n'); |
296 | 0 |
|
297 | 0 | // Write column names |
298 | 0 | mDirBuf.AppendLiteral("200: filename content-length last-modified file-type\n"); |
299 | 0 |
|
300 | 0 | // Write charset (assume UTF-8) |
301 | 0 | // XXX is this correct? |
302 | 0 | mDirBuf.AppendLiteral("301: UTF-8\n"); |
303 | 0 | SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT); |
304 | 0 | return NS_OK; |
305 | 0 | } |
306 | | |
307 | | /** |
308 | | * Create file stream and set mime type for channel |
309 | | * @param info file info used to determine mime type |
310 | | * @return NS_OK when file stream created successfuly, error code otherwise |
311 | | */ |
312 | | nsresult |
313 | | nsGIOInputStream::DoOpenFile(GFileInfo *info) |
314 | 0 | { |
315 | 0 | GError *error = nullptr; |
316 | 0 |
|
317 | 0 | mStream = g_file_read(mHandle, nullptr, &error); |
318 | 0 | if (!mStream) { |
319 | 0 | nsresult rv = MapGIOResult(error); |
320 | 0 | g_warning("Cannot read from file: %s", error->message); |
321 | 0 | g_error_free(error); |
322 | 0 | return rv; |
323 | 0 | } |
324 | 0 | |
325 | 0 | const char * content_type = g_file_info_get_content_type(info); |
326 | 0 | if (content_type) { |
327 | 0 | char *mime_type = g_content_type_get_mime_type(content_type); |
328 | 0 | if (mime_type) { |
329 | 0 | if (strcmp(mime_type, APPLICATION_OCTET_STREAM) != 0) { |
330 | 0 | SetContentTypeOfChannel(mime_type); |
331 | 0 | } |
332 | 0 | g_free(mime_type); |
333 | 0 | } |
334 | 0 | } else { |
335 | 0 | g_warning("Missing content type."); |
336 | 0 | } |
337 | 0 |
|
338 | 0 | mBytesRemaining = g_file_info_get_size(info); |
339 | 0 | // Update the content length attribute on the channel. We do this |
340 | 0 | // synchronously without proxying. This hack is not as bad as it looks! |
341 | 0 | mChannel->SetContentLength(mBytesRemaining); |
342 | 0 |
|
343 | 0 | return NS_OK; |
344 | 0 | } |
345 | | |
346 | | /** |
347 | | * Start file open operation, mount volume when needed and according to file type |
348 | | * create file output stream or read directory content. |
349 | | * @return NS_OK when file or directory opened successfully, error code otherwise |
350 | | */ |
351 | | nsresult |
352 | | nsGIOInputStream::DoOpen() |
353 | 0 | { |
354 | 0 | nsresult rv; |
355 | 0 | GError *error = nullptr; |
356 | 0 |
|
357 | 0 | NS_ASSERTION(mHandle == nullptr, "already open"); |
358 | 0 |
|
359 | 0 | mHandle = g_file_new_for_uri( mSpec.get() ); |
360 | 0 |
|
361 | 0 | GFileInfo *info = g_file_query_info(mHandle, |
362 | 0 | "standard::*", |
363 | 0 | G_FILE_QUERY_INFO_NONE, |
364 | 0 | nullptr, |
365 | 0 | &error); |
366 | 0 |
|
367 | 0 | if (error) { |
368 | 0 | if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) { |
369 | 0 | // location is not yet mounted, try to mount |
370 | 0 | g_error_free(error); |
371 | 0 | if (NS_IsMainThread()) |
372 | 0 | return NS_ERROR_NOT_CONNECTED; |
373 | 0 | error = nullptr; |
374 | 0 | rv = MountVolume(); |
375 | 0 | if (rv != NS_OK) { |
376 | 0 | return rv; |
377 | 0 | } |
378 | 0 | // get info again |
379 | 0 | info = g_file_query_info(mHandle, |
380 | 0 | "standard::*", |
381 | 0 | G_FILE_QUERY_INFO_NONE, |
382 | 0 | nullptr, |
383 | 0 | &error); |
384 | 0 | // second try to get file info from remote files after media mount |
385 | 0 | if (!info) { |
386 | 0 | g_warning("Unable to get file info: %s", error->message); |
387 | 0 | rv = MapGIOResult(error); |
388 | 0 | g_error_free(error); |
389 | 0 | return rv; |
390 | 0 | } |
391 | 0 | } else { |
392 | 0 | g_warning("Unable to get file info: %s", error->message); |
393 | 0 | rv = MapGIOResult(error); |
394 | 0 | g_error_free(error); |
395 | 0 | return rv; |
396 | 0 | } |
397 | 0 | } |
398 | 0 | // Get file type to handle directories and file differently |
399 | 0 | GFileType f_type = g_file_info_get_file_type(info); |
400 | 0 | if (f_type == G_FILE_TYPE_DIRECTORY) { |
401 | 0 | // directory |
402 | 0 | rv = DoOpenDirectory(); |
403 | 0 | } else if (f_type != G_FILE_TYPE_UNKNOWN) { |
404 | 0 | // file |
405 | 0 | rv = DoOpenFile(info); |
406 | 0 | } else { |
407 | 0 | g_warning("Unable to get file type."); |
408 | 0 | rv = NS_ERROR_FILE_NOT_FOUND; |
409 | 0 | } |
410 | 0 | if (info) |
411 | 0 | g_object_unref(info); |
412 | 0 | return rv; |
413 | 0 | } |
414 | | |
415 | | /** |
416 | | * Read content of file or create file list from directory |
417 | | * @param aBuf read destination buffer |
418 | | * @param aCount length of destination buffer |
419 | | * @param aCountRead number of read characters |
420 | | * @return NS_OK when read successfully, NS_BASE_STREAM_CLOSED when end of file, |
421 | | * error code otherwise |
422 | | */ |
423 | | nsresult |
424 | | nsGIOInputStream::DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead) |
425 | 0 | { |
426 | 0 | nsresult rv = NS_ERROR_NOT_AVAILABLE; |
427 | 0 | if (mStream) { |
428 | 0 | // file read |
429 | 0 | GError *error = nullptr; |
430 | 0 | uint32_t bytes_read = g_input_stream_read(G_INPUT_STREAM(mStream), |
431 | 0 | aBuf, |
432 | 0 | aCount, |
433 | 0 | nullptr, |
434 | 0 | &error); |
435 | 0 | if (error) { |
436 | 0 | rv = MapGIOResult(error); |
437 | 0 | *aCountRead = 0; |
438 | 0 | g_warning("Cannot read from file: %s", error->message); |
439 | 0 | g_error_free(error); |
440 | 0 | return rv; |
441 | 0 | } |
442 | 0 | *aCountRead = bytes_read; |
443 | 0 | mBytesRemaining -= *aCountRead; |
444 | 0 | return NS_OK; |
445 | 0 | } |
446 | 0 | if (mDirOpen) { |
447 | 0 | // directory read |
448 | 0 | while (aCount && rv != NS_BASE_STREAM_CLOSED) |
449 | 0 | { |
450 | 0 | // Copy data out of our buffer |
451 | 0 | uint32_t bufLen = mDirBuf.Length() - mDirBufCursor; |
452 | 0 | if (bufLen) |
453 | 0 | { |
454 | 0 | uint32_t n = std::min(bufLen, aCount); |
455 | 0 | memcpy(aBuf, mDirBuf.get() + mDirBufCursor, n); |
456 | 0 | *aCountRead += n; |
457 | 0 | aBuf += n; |
458 | 0 | aCount -= n; |
459 | 0 | mDirBufCursor += n; |
460 | 0 | } |
461 | 0 |
|
462 | 0 | if (!mDirListPtr) // Are we at the end of the directory list? |
463 | 0 | { |
464 | 0 | rv = NS_BASE_STREAM_CLOSED; |
465 | 0 | } |
466 | 0 | else if (aCount) // Do we need more data? |
467 | 0 | { |
468 | 0 | GFileInfo *info = (GFileInfo *) mDirListPtr->data; |
469 | 0 |
|
470 | 0 | // Prune '.' and '..' from directory listing. |
471 | 0 | const char * fname = g_file_info_get_name(info); |
472 | 0 | if (fname && fname[0] == '.' && |
473 | 0 | (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) |
474 | 0 | { |
475 | 0 | mDirListPtr = mDirListPtr->next; |
476 | 0 | continue; |
477 | 0 | } |
478 | 0 | |
479 | 0 | mDirBuf.AssignLiteral("201: "); |
480 | 0 |
|
481 | 0 | // The "filename" field |
482 | 0 | nsCString escName; |
483 | 0 | nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID); |
484 | 0 | if (nu && fname) { |
485 | 0 | nu->EscapeString(nsDependentCString(fname), |
486 | 0 | nsINetUtil::ESCAPE_URL_PATH, escName); |
487 | 0 |
|
488 | 0 | mDirBuf.Append(escName); |
489 | 0 | mDirBuf.Append(' '); |
490 | 0 | } |
491 | 0 |
|
492 | 0 | // The "content-length" field |
493 | 0 | // XXX truncates size from 64-bit to 32-bit |
494 | 0 | mDirBuf.AppendInt(int32_t(g_file_info_get_size(info))); |
495 | 0 | mDirBuf.Append(' '); |
496 | 0 |
|
497 | 0 | // The "last-modified" field |
498 | 0 | // |
499 | 0 | // NSPR promises: PRTime is compatible with time_t |
500 | 0 | // we just need to convert from seconds to microseconds |
501 | 0 | GTimeVal gtime; |
502 | 0 | g_file_info_get_modification_time(info, >ime); |
503 | 0 |
|
504 | 0 | PRExplodedTime tm; |
505 | 0 | PRTime pt = ((PRTime) gtime.tv_sec) * 1000000; |
506 | 0 | PR_ExplodeTime(pt, PR_GMTParameters, &tm); |
507 | 0 | { |
508 | 0 | char buf[64]; |
509 | 0 | PR_FormatTimeUSEnglish(buf, sizeof(buf), |
510 | 0 | "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm); |
511 | 0 | mDirBuf.Append(buf); |
512 | 0 | } |
513 | 0 |
|
514 | 0 | // The "file-type" field |
515 | 0 | switch (g_file_info_get_file_type(info)) |
516 | 0 | { |
517 | 0 | case G_FILE_TYPE_REGULAR: |
518 | 0 | mDirBuf.AppendLiteral("FILE "); |
519 | 0 | break; |
520 | 0 | case G_FILE_TYPE_DIRECTORY: |
521 | 0 | mDirBuf.AppendLiteral("DIRECTORY "); |
522 | 0 | break; |
523 | 0 | case G_FILE_TYPE_SYMBOLIC_LINK: |
524 | 0 | mDirBuf.AppendLiteral("SYMBOLIC-LINK "); |
525 | 0 | break; |
526 | 0 | default: |
527 | 0 | break; |
528 | 0 | } |
529 | 0 | mDirBuf.Append('\n'); |
530 | 0 |
|
531 | 0 | mDirBufCursor = 0; |
532 | 0 | mDirListPtr = mDirListPtr->next; |
533 | 0 | } |
534 | 0 | } |
535 | 0 | } |
536 | 0 | return rv; |
537 | 0 | } |
538 | | |
539 | | /** |
540 | | * This class is used to implement SetContentTypeOfChannel. |
541 | | */ |
542 | | class nsGIOSetContentTypeEvent : public mozilla::Runnable |
543 | | { |
544 | | public: |
545 | | nsGIOSetContentTypeEvent(nsIChannel* channel, const char* contentType) |
546 | | : mozilla::Runnable("nsGIOSetContentTypeEvent") |
547 | | , mChannel(channel) |
548 | | , mContentType(contentType) |
549 | 0 | { |
550 | 0 | // stash channel reference in mChannel. no AddRef here! see note |
551 | 0 | // in SetContentTypeOfchannel. |
552 | 0 | } |
553 | | |
554 | | NS_IMETHOD Run() override |
555 | 0 | { |
556 | 0 | mChannel->SetContentType(mContentType); |
557 | 0 | return NS_OK; |
558 | 0 | } |
559 | | |
560 | | private: |
561 | | nsIChannel *mChannel; |
562 | | nsCString mContentType; |
563 | | }; |
564 | | |
565 | | nsresult |
566 | | nsGIOInputStream::SetContentTypeOfChannel(const char *contentType) |
567 | 0 | { |
568 | 0 | // We need to proxy this call over to the main thread. We post an |
569 | 0 | // asynchronous event in this case so that we don't delay reading data, and |
570 | 0 | // we know that this is safe to do since the channel's reference will be |
571 | 0 | // released asynchronously as well. We trust the ordering of the main |
572 | 0 | // thread's event queue to protect us against memory corruption. |
573 | 0 |
|
574 | 0 | nsresult rv; |
575 | 0 | nsCOMPtr<nsIRunnable> ev = |
576 | 0 | new nsGIOSetContentTypeEvent(mChannel, contentType); |
577 | 0 | if (!ev) |
578 | 0 | { |
579 | 0 | rv = NS_ERROR_OUT_OF_MEMORY; |
580 | 0 | } |
581 | 0 | else |
582 | 0 | { |
583 | 0 | rv = NS_DispatchToMainThread(ev); |
584 | 0 | } |
585 | 0 | return rv; |
586 | 0 | } |
587 | | |
588 | | NS_IMPL_ISUPPORTS(nsGIOInputStream, nsIInputStream) |
589 | | |
590 | | /** |
591 | | * Free all used memory and close stream. |
592 | | */ |
593 | | NS_IMETHODIMP |
594 | | nsGIOInputStream::Close() |
595 | 0 | { |
596 | 0 | if (mStream) |
597 | 0 | { |
598 | 0 | g_object_unref(mStream); |
599 | 0 | mStream = nullptr; |
600 | 0 | } |
601 | 0 |
|
602 | 0 | if (mHandle) |
603 | 0 | { |
604 | 0 | g_object_unref(mHandle); |
605 | 0 | mHandle = nullptr; |
606 | 0 | } |
607 | 0 |
|
608 | 0 | if (mDirList) |
609 | 0 | { |
610 | 0 | // Destroy the list of GIOFileInfo objects... |
611 | 0 | g_list_foreach(mDirList, (GFunc) g_object_unref, nullptr); |
612 | 0 | g_list_free(mDirList); |
613 | 0 | mDirList = nullptr; |
614 | 0 | mDirListPtr = nullptr; |
615 | 0 | } |
616 | 0 |
|
617 | 0 | if (mChannel) { |
618 | 0 | NS_ReleaseOnMainThreadSystemGroup( |
619 | 0 | "nsGIOInputStream::mChannel", dont_AddRef(mChannel)); |
620 | 0 |
|
621 | 0 | mChannel = nullptr; |
622 | 0 | } |
623 | 0 |
|
624 | 0 | mSpec.Truncate(); // free memory |
625 | 0 |
|
626 | 0 | // Prevent future reads from re-opening the handle. |
627 | 0 | if (NS_SUCCEEDED(mStatus)) |
628 | 0 | mStatus = NS_BASE_STREAM_CLOSED; |
629 | 0 |
|
630 | 0 | return NS_OK; |
631 | 0 | } |
632 | | |
633 | | /** |
634 | | * Return number of remaining bytes available on input |
635 | | * @param aResult remaining bytes |
636 | | */ |
637 | | NS_IMETHODIMP |
638 | | nsGIOInputStream::Available(uint64_t *aResult) |
639 | 0 | { |
640 | 0 | if (NS_FAILED(mStatus)) |
641 | 0 | return mStatus; |
642 | 0 | |
643 | 0 | *aResult = mBytesRemaining; |
644 | 0 |
|
645 | 0 | return NS_OK; |
646 | 0 | } |
647 | | |
648 | | /** |
649 | | * Trying to read from stream. When location is not available it tries to mount it. |
650 | | * @param aBuf buffer to put read data |
651 | | * @param aCount length of aBuf |
652 | | * @param aCountRead number of bytes actually read |
653 | | */ |
654 | | NS_IMETHODIMP |
655 | | nsGIOInputStream::Read(char *aBuf, |
656 | | uint32_t aCount, |
657 | | uint32_t *aCountRead) |
658 | 0 | { |
659 | 0 | *aCountRead = 0; |
660 | 0 | // Check if file is already opened, otherwise open it |
661 | 0 | if (!mStream && !mDirOpen && mStatus == NS_OK) { |
662 | 0 | mStatus = DoOpen(); |
663 | 0 | if (NS_FAILED(mStatus)) { |
664 | 0 | return mStatus; |
665 | 0 | } |
666 | 0 | } |
667 | 0 | |
668 | 0 | mStatus = DoRead(aBuf, aCount, aCountRead); |
669 | 0 | // Check if all data has been read |
670 | 0 | if (mStatus == NS_BASE_STREAM_CLOSED) |
671 | 0 | return NS_OK; |
672 | 0 | |
673 | 0 | // Check whenever any error appears while reading |
674 | 0 | return mStatus; |
675 | 0 | } |
676 | | |
677 | | NS_IMETHODIMP |
678 | | nsGIOInputStream::ReadSegments(nsWriteSegmentFun aWriter, |
679 | | void *aClosure, |
680 | | uint32_t aCount, |
681 | | uint32_t *aResult) |
682 | 0 | { |
683 | 0 | // There is no way to implement this using GnomeVFS, but fortunately |
684 | 0 | // that doesn't matter. Because we are a blocking input stream, Necko |
685 | 0 | // isn't going to call our ReadSegments method. |
686 | 0 | MOZ_ASSERT_UNREACHABLE("nsGIOInputStream::ReadSegments"); |
687 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
688 | 0 | } |
689 | | |
690 | | NS_IMETHODIMP |
691 | | nsGIOInputStream::IsNonBlocking(bool *aResult) |
692 | 0 | { |
693 | 0 | *aResult = false; |
694 | 0 | return NS_OK; |
695 | 0 | } |
696 | | |
697 | | //----------------------------------------------------------------------------- |
698 | | |
699 | | /** |
700 | | * Called when finishing mount operation. Result of operation is set in |
701 | | * nsGIOInputStream. This function is called in main thread as an async request |
702 | | * typically from dbus. |
703 | | * @param source_object GFile object which requested the mount |
704 | | * @param res result object |
705 | | * @param user_data pointer to nsGIOInputStream |
706 | | */ |
707 | | static void |
708 | | mount_enclosing_volume_finished (GObject *source_object, |
709 | | GAsyncResult *res, |
710 | | gpointer user_data) |
711 | 0 | { |
712 | 0 | GError *error = nullptr; |
713 | 0 |
|
714 | 0 | nsGIOInputStream* istream = static_cast<nsGIOInputStream*>(user_data); |
715 | 0 |
|
716 | 0 | g_file_mount_enclosing_volume_finish(G_FILE (source_object), res, &error); |
717 | 0 |
|
718 | 0 | if (error) { |
719 | 0 | g_warning("Mount failed: %s %d", error->message, error->code); |
720 | 0 | istream->SetMountResult(MOUNT_OPERATION_FAILED, error->code); |
721 | 0 | g_error_free(error); |
722 | 0 | } else { |
723 | 0 | istream->SetMountResult(MOUNT_OPERATION_SUCCESS, 0); |
724 | 0 | } |
725 | 0 | } |
726 | | |
727 | | /** |
728 | | * This function is called when username or password are requested from user. |
729 | | * This function is called in main thread as async request from dbus. |
730 | | * @param mount_op mount operation |
731 | | * @param message message to show to user |
732 | | * @param default_user preffered user |
733 | | * @param default_domain domain name |
734 | | * @param flags what type of information is required |
735 | | * @param user_data nsIChannel |
736 | | */ |
737 | | static void |
738 | | mount_operation_ask_password (GMountOperation *mount_op, |
739 | | const char *message, |
740 | | const char *default_user, |
741 | | const char *default_domain, |
742 | | GAskPasswordFlags flags, |
743 | | gpointer user_data) |
744 | 0 | { |
745 | 0 | nsIChannel *channel = (nsIChannel *) user_data; |
746 | 0 | if (!channel) { |
747 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
748 | 0 | return; |
749 | 0 | } |
750 | 0 | // We can't handle request for domain |
751 | 0 | if (flags & G_ASK_PASSWORD_NEED_DOMAIN) { |
752 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
753 | 0 | return; |
754 | 0 | } |
755 | 0 | |
756 | 0 | nsCOMPtr<nsIAuthPrompt> prompt; |
757 | 0 | NS_QueryNotificationCallbacks(channel, prompt); |
758 | 0 |
|
759 | 0 | // If no auth prompt, then give up. We could failover to using the |
760 | 0 | // WindowWatcher service, but that might defeat a consumer's purposeful |
761 | 0 | // attempt to disable authentication (for whatever reason). |
762 | 0 | if (!prompt) { |
763 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
764 | 0 | return; |
765 | 0 | } |
766 | 0 | // Parse out the host and port... |
767 | 0 | nsCOMPtr<nsIURI> uri; |
768 | 0 | channel->GetURI(getter_AddRefs(uri)); |
769 | 0 | if (!uri) { |
770 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
771 | 0 | return; |
772 | 0 | } |
773 | 0 | |
774 | 0 | nsAutoCString scheme, hostPort; |
775 | 0 | uri->GetScheme(scheme); |
776 | 0 | uri->GetHostPort(hostPort); |
777 | 0 |
|
778 | 0 | // It doesn't make sense for either of these strings to be empty. What kind |
779 | 0 | // of funky URI is this? |
780 | 0 | if (scheme.IsEmpty() || hostPort.IsEmpty()) { |
781 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
782 | 0 | return; |
783 | 0 | } |
784 | 0 | // Construct the single signon key. Altering the value of this key will |
785 | 0 | // cause people's remembered passwords to be forgotten. Think carefully |
786 | 0 | // before changing the way this key is constructed. |
787 | 0 | nsAutoString key, realm; |
788 | 0 |
|
789 | 0 | NS_ConvertUTF8toUTF16 dispHost(scheme); |
790 | 0 | dispHost.AppendLiteral("://"); |
791 | 0 | dispHost.Append(NS_ConvertUTF8toUTF16(hostPort)); |
792 | 0 |
|
793 | 0 | key = dispHost; |
794 | 0 | if (*default_domain != '\0') |
795 | 0 | { |
796 | 0 | // We assume the realm string is ASCII. That might be a bogus assumption, |
797 | 0 | // but we have no idea what encoding GnomeVFS is using, so for now we'll |
798 | 0 | // limit ourselves to ISO-Latin-1. XXX What is a better solution? |
799 | 0 | realm.Append('"'); |
800 | 0 | realm.Append(NS_ConvertASCIItoUTF16(default_domain)); |
801 | 0 | realm.Append('"'); |
802 | 0 | key.Append(' '); |
803 | 0 | key.Append(realm); |
804 | 0 | } |
805 | 0 | // Construct the message string... |
806 | 0 | // |
807 | 0 | // We use Necko's string bundle here. This code really should be encapsulated |
808 | 0 | // behind some Necko API, after all this code is based closely on the code in |
809 | 0 | // nsHttpChannel.cpp. |
810 | 0 | nsCOMPtr<nsIStringBundleService> bundleSvc = |
811 | 0 | do_GetService(NS_STRINGBUNDLE_CONTRACTID); |
812 | 0 | if (!bundleSvc) { |
813 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
814 | 0 | return; |
815 | 0 | } |
816 | 0 | nsCOMPtr<nsIStringBundle> bundle; |
817 | 0 | bundleSvc->CreateBundle("chrome://global/locale/commonDialogs.properties", |
818 | 0 | getter_AddRefs(bundle)); |
819 | 0 | if (!bundle) { |
820 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
821 | 0 | return; |
822 | 0 | } |
823 | 0 | nsAutoString nsmessage; |
824 | 0 |
|
825 | 0 | if (flags & G_ASK_PASSWORD_NEED_PASSWORD) { |
826 | 0 | if (flags & G_ASK_PASSWORD_NEED_USERNAME) { |
827 | 0 | if (!realm.IsEmpty()) { |
828 | 0 | const char16_t *strings[] = { realm.get(), dispHost.get() }; |
829 | 0 | bundle->FormatStringFromName("EnterLoginForRealm3", |
830 | 0 | strings, 2, nsmessage); |
831 | 0 | } else { |
832 | 0 | const char16_t *strings[] = { dispHost.get() }; |
833 | 0 | bundle->FormatStringFromName("EnterUserPasswordFor2", |
834 | 0 | strings, 1, nsmessage); |
835 | 0 | } |
836 | 0 | } else { |
837 | 0 | NS_ConvertUTF8toUTF16 userName(default_user); |
838 | 0 | const char16_t *strings[] = { userName.get(), dispHost.get() }; |
839 | 0 | bundle->FormatStringFromName("EnterPasswordFor", |
840 | 0 | strings, 2, nsmessage); |
841 | 0 | } |
842 | 0 | } else { |
843 | 0 | g_warning("Unknown mount operation request (flags: %x)", flags); |
844 | 0 | } |
845 | 0 |
|
846 | 0 | if (nsmessage.IsEmpty()) { |
847 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
848 | 0 | return; |
849 | 0 | } |
850 | 0 | // Prompt the user... |
851 | 0 | nsresult rv; |
852 | 0 | bool retval = false; |
853 | 0 | char16_t *user = nullptr, *pass = nullptr; |
854 | 0 | if (default_user) { |
855 | 0 | // user will be freed by PromptUsernameAndPassword |
856 | 0 | user = ToNewUnicode(NS_ConvertUTF8toUTF16(default_user)); |
857 | 0 | } |
858 | 0 | if (flags & G_ASK_PASSWORD_NEED_USERNAME) { |
859 | 0 | rv = prompt->PromptUsernameAndPassword(nullptr, nsmessage.get(), |
860 | 0 | key.get(), |
861 | 0 | nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, |
862 | 0 | &user, &pass, &retval); |
863 | 0 | } else { |
864 | 0 | rv = prompt->PromptPassword(nullptr, nsmessage.get(), |
865 | 0 | key.get(), |
866 | 0 | nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, |
867 | 0 | &pass, &retval); |
868 | 0 | } |
869 | 0 | if (NS_FAILED(rv) || !retval) { // was || user == '\0' || pass == '\0' |
870 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); |
871 | 0 | free(user); |
872 | 0 | free(pass); |
873 | 0 | return; |
874 | 0 | } |
875 | 0 | /* GIO should accept UTF8 */ |
876 | 0 | g_mount_operation_set_username(mount_op, NS_ConvertUTF16toUTF8(user).get()); |
877 | 0 | g_mount_operation_set_password(mount_op, NS_ConvertUTF16toUTF8(pass).get()); |
878 | 0 | free(user); |
879 | 0 | free(pass); |
880 | 0 | g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_HANDLED); |
881 | 0 | } |
882 | | |
883 | | //----------------------------------------------------------------------------- |
884 | | |
885 | | class nsGIOProtocolHandler final : public nsIProtocolHandler |
886 | | , public nsIObserver |
887 | | { |
888 | | public: |
889 | | NS_DECL_ISUPPORTS |
890 | | NS_DECL_NSIPROTOCOLHANDLER |
891 | | NS_DECL_NSIOBSERVER |
892 | | |
893 | | nsresult Init(); |
894 | | |
895 | | private: |
896 | 0 | ~nsGIOProtocolHandler() {} |
897 | | |
898 | | void InitSupportedProtocolsPref(nsIPrefBranch *prefs); |
899 | | bool IsSupportedProtocol(const nsCString &spec); |
900 | | |
901 | | nsCString mSupportedProtocols; |
902 | | }; |
903 | | |
904 | | NS_IMPL_ISUPPORTS(nsGIOProtocolHandler, nsIProtocolHandler, nsIObserver) |
905 | | |
906 | | nsresult |
907 | | nsGIOProtocolHandler::Init() |
908 | 1 | { |
909 | 1 | nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
910 | 1 | if (prefs) |
911 | 1 | { |
912 | 1 | InitSupportedProtocolsPref(prefs); |
913 | 1 | prefs->AddObserver(MOZ_GIO_SUPPORTED_PROTOCOLS, this, false); |
914 | 1 | } |
915 | 1 | |
916 | 1 | return NS_OK; |
917 | 1 | } |
918 | | |
919 | | void |
920 | | nsGIOProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch *prefs) |
921 | 1 | { |
922 | 1 | // Get user preferences to determine which protocol is supported. |
923 | 1 | // Gvfs/GIO has a set of supported protocols like obex, network, archive, |
924 | 1 | // computer, dav, cdda, gphoto2, trash, etc. Some of these seems to be |
925 | 1 | // irrelevant to process by browser. By default accept only smb and sftp |
926 | 1 | // protocols so far. |
927 | 1 | nsresult rv = prefs->GetCharPref(MOZ_GIO_SUPPORTED_PROTOCOLS, |
928 | 1 | mSupportedProtocols); |
929 | 1 | if (NS_SUCCEEDED(rv)) { |
930 | 0 | mSupportedProtocols.StripWhitespace(); |
931 | 0 | ToLowerCase(mSupportedProtocols); |
932 | 1 | } else { |
933 | 1 | mSupportedProtocols.AssignLiteral( |
934 | | #ifdef MOZ_PROXY_BYPASS_PROTECTION |
935 | | "" // use none |
936 | | #else |
937 | | "smb:,sftp:" // use defaults |
938 | 1 | #endif |
939 | 1 | ); |
940 | 1 | } |
941 | 1 | LOG(("gio: supported protocols \"%s\"\n", mSupportedProtocols.get())); |
942 | 1 | } |
943 | | |
944 | | bool |
945 | | nsGIOProtocolHandler::IsSupportedProtocol(const nsCString &aSpec) |
946 | 285k | { |
947 | 285k | const char *specString = aSpec.get(); |
948 | 285k | const char *colon = strchr(specString, ':'); |
949 | 285k | if (!colon) |
950 | 0 | return false; |
951 | 285k | |
952 | 285k | uint32_t length = colon - specString + 1; |
953 | 285k | |
954 | 285k | // <scheme> + ':' |
955 | 285k | nsCString scheme(specString, length); |
956 | 285k | |
957 | 285k | char *found = PL_strcasestr(mSupportedProtocols.get(), scheme.get()); |
958 | 285k | if (!found) |
959 | 285k | return false; |
960 | 456 | |
961 | 456 | if (found[length] != ',' && found[length] != '\0') |
962 | 0 | return false; |
963 | 456 | |
964 | 456 | return true; |
965 | 456 | } |
966 | | |
967 | | NS_IMETHODIMP |
968 | | nsGIOProtocolHandler::GetScheme(nsACString &aScheme) |
969 | 0 | { |
970 | 0 | aScheme.AssignLiteral(MOZ_GIO_SCHEME); |
971 | 0 | return NS_OK; |
972 | 0 | } |
973 | | |
974 | | NS_IMETHODIMP |
975 | | nsGIOProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) |
976 | 0 | { |
977 | 0 | *aDefaultPort = -1; |
978 | 0 | return NS_OK; |
979 | 0 | } |
980 | | |
981 | | NS_IMETHODIMP |
982 | | nsGIOProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags) |
983 | 0 | { |
984 | 0 | // Is URI_STD true of all GnomeVFS URI types? |
985 | 0 | *aProtocolFlags = URI_STD | URI_DANGEROUS_TO_LOAD; |
986 | 0 | return NS_OK; |
987 | 0 | } |
988 | | |
989 | | NS_IMETHODIMP |
990 | | nsGIOProtocolHandler::NewURI(const nsACString &aSpec, |
991 | | const char *aOriginCharset, |
992 | | nsIURI *aBaseURI, |
993 | | nsIURI **aResult) |
994 | 285k | { |
995 | 285k | const nsCString flatSpec(aSpec); |
996 | 285k | LOG(("gio: NewURI [spec=%s]\n", flatSpec.get())); |
997 | 285k | |
998 | 285k | if (!aBaseURI) |
999 | 285k | { |
1000 | 285k | // XXX Is it good to support all GIO protocols? |
1001 | 285k | if (!IsSupportedProtocol(flatSpec)) |
1002 | 285k | return NS_ERROR_UNKNOWN_PROTOCOL; |
1003 | 456 | |
1004 | 456 | int32_t colon_location = flatSpec.FindChar(':'); |
1005 | 456 | if (colon_location <= 0) |
1006 | 0 | return NS_ERROR_UNKNOWN_PROTOCOL; |
1007 | 456 | |
1008 | 456 | // Verify that GIO supports this URI scheme. |
1009 | 456 | bool uri_scheme_supported = false; |
1010 | 456 | |
1011 | 456 | GVfs *gvfs = g_vfs_get_default(); |
1012 | 456 | |
1013 | 456 | if (!gvfs) { |
1014 | 0 | g_warning("Cannot get GVfs object."); |
1015 | 0 | return NS_ERROR_UNKNOWN_PROTOCOL; |
1016 | 0 | } |
1017 | 456 | |
1018 | 456 | const gchar* const * uri_schemes = g_vfs_get_supported_uri_schemes(gvfs); |
1019 | 456 | |
1020 | 912 | while (*uri_schemes != nullptr) { |
1021 | 456 | // While flatSpec ends with ':' the uri_scheme does not. Therefore do not |
1022 | 456 | // compare last character. |
1023 | 456 | if (StringHead(flatSpec, colon_location).Equals(*uri_schemes)) { |
1024 | 0 | uri_scheme_supported = true; |
1025 | 0 | break; |
1026 | 0 | } |
1027 | 456 | uri_schemes++; |
1028 | 456 | } |
1029 | 456 | |
1030 | 456 | if (!uri_scheme_supported) { |
1031 | 456 | return NS_ERROR_UNKNOWN_PROTOCOL; |
1032 | 456 | } |
1033 | 0 | } |
1034 | 0 | |
1035 | 0 | nsCOMPtr<nsIURI> base(aBaseURI); |
1036 | 0 | return NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) |
1037 | 0 | .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init, |
1038 | 0 | nsIStandardURL::URLTYPE_STANDARD, |
1039 | 0 | -1, flatSpec, aOriginCharset, base, nullptr)) |
1040 | 0 | .Finalize(aResult); |
1041 | 0 | } |
1042 | | |
1043 | | NS_IMETHODIMP |
1044 | | nsGIOProtocolHandler::NewChannel2(nsIURI* aURI, |
1045 | | nsILoadInfo* aLoadInfo, |
1046 | | nsIChannel** aResult) |
1047 | 0 | { |
1048 | 0 | NS_ENSURE_ARG_POINTER(aURI); |
1049 | 0 | nsresult rv; |
1050 | 0 |
|
1051 | 0 | nsAutoCString spec; |
1052 | 0 | rv = aURI->GetSpec(spec); |
1053 | 0 | if (NS_FAILED(rv)) |
1054 | 0 | return rv; |
1055 | 0 | |
1056 | 0 | RefPtr<nsGIOInputStream> stream = new nsGIOInputStream(spec); |
1057 | 0 | if (!stream) { |
1058 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1059 | 0 | } |
1060 | 0 | |
1061 | 0 | RefPtr<nsGIOInputStream> tmpStream = stream; |
1062 | 0 | rv = NS_NewInputStreamChannelInternal(aResult, |
1063 | 0 | aURI, |
1064 | 0 | tmpStream.forget(), |
1065 | 0 | NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE), |
1066 | 0 | EmptyCString(), // aContentCharset |
1067 | 0 | aLoadInfo); |
1068 | 0 | if (NS_SUCCEEDED(rv)) { |
1069 | 0 | stream->SetChannel(*aResult); |
1070 | 0 | } |
1071 | 0 | return rv; |
1072 | 0 | } |
1073 | | |
1074 | | NS_IMETHODIMP |
1075 | | nsGIOProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult) |
1076 | 0 | { |
1077 | 0 | return NewChannel2(aURI, nullptr, aResult); |
1078 | 0 | } |
1079 | | |
1080 | | NS_IMETHODIMP |
1081 | | nsGIOProtocolHandler::AllowPort(int32_t aPort, |
1082 | | const char *aScheme, |
1083 | | bool *aResult) |
1084 | 0 | { |
1085 | 0 | // Don't override anything. |
1086 | 0 | *aResult = false; |
1087 | 0 | return NS_OK; |
1088 | 0 | } |
1089 | | |
1090 | | NS_IMETHODIMP |
1091 | | nsGIOProtocolHandler::Observe(nsISupports *aSubject, |
1092 | | const char *aTopic, |
1093 | | const char16_t *aData) |
1094 | 0 | { |
1095 | 0 | if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { |
1096 | 0 | nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject); |
1097 | 0 | InitSupportedProtocolsPref(prefs); |
1098 | 0 | } |
1099 | 0 | return NS_OK; |
1100 | 0 | } |
1101 | | |
1102 | | //----------------------------------------------------------------------------- |
1103 | | |
1104 | | #define NS_GIOPROTOCOLHANDLER_CID \ |
1105 | | { /* ee706783-3af8-4d19-9e84-e2ebfe213480 */ \ |
1106 | | 0xee706783, \ |
1107 | | 0x3af8, \ |
1108 | | 0x4d19, \ |
1109 | | {0x9e, 0x84, 0xe2, 0xeb, 0xfe, 0x21, 0x34, 0x80} \ |
1110 | | } |
1111 | | |
1112 | | NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGIOProtocolHandler, Init) |
1113 | | NS_DEFINE_NAMED_CID(NS_GIOPROTOCOLHANDLER_CID); |
1114 | | |
1115 | | static const mozilla::Module::CIDEntry kVFSCIDs[] = { |
1116 | | { &kNS_GIOPROTOCOLHANDLER_CID, false, nullptr, nsGIOProtocolHandlerConstructor }, |
1117 | | { nullptr } |
1118 | | }; |
1119 | | |
1120 | | static const mozilla::Module::ContractIDEntry kVFSContracts[] = { |
1121 | | { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GIO_SCHEME, &kNS_GIOPROTOCOLHANDLER_CID }, |
1122 | | { nullptr } |
1123 | | }; |
1124 | | |
1125 | | static const mozilla::Module kVFSModule = { |
1126 | | mozilla::Module::kVersion, |
1127 | | kVFSCIDs, |
1128 | | kVFSContracts |
1129 | | }; |
1130 | | |
1131 | | NSMODULE_DEFN(nsGIOModule) = &kVFSModule; |