extensions/gio/nsGIOProtocolHandler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial