extensions/gnomevfs/nsGnomeVFSProtocolHandler.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     6 // GnomeVFS v2.2.2 is missing G_BEGIN_DECLS in gnome-vfs-module-callback.h
     7 extern "C" {
     8 #include <libgnomevfs/gnome-vfs.h>
     9 #include <libgnomevfs/gnome-vfs-standard-callbacks.h>
    10 #include <libgnomevfs/gnome-vfs-mime-utils.h>
    11 }
    13 #include <algorithm>
    15 #include "nsServiceManagerUtils.h"
    16 #include "nsComponentManagerUtils.h"
    17 #include "mozilla/ModuleUtils.h"
    18 #include "nsIInterfaceRequestorUtils.h"
    19 #include "nsIPrefService.h"
    20 #include "nsIPrefBranch.h"
    21 #include "nsIObserver.h"
    22 #include "nsThreadUtils.h"
    23 #include "nsProxyRelease.h"
    24 #include "nsIAuthPrompt.h"
    25 #include "nsIStringBundle.h"
    26 #include "nsIStandardURL.h"
    27 #include "nsIURL.h"
    28 #include "nsMimeTypes.h"
    29 #include "nsNetUtil.h"
    30 #include "nsINetUtil.h"
    31 #include "nsAutoPtr.h"
    32 #include "nsError.h"
    33 #include "prlog.h"
    34 #include "prtime.h"
    35 #include "prprf.h"
    36 #include "plstr.h"
    37 #include "mozilla/Attributes.h"
    39 #define MOZ_GNOMEVFS_SCHEME              "moz-gnomevfs"
    40 #define MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS "network.gnomevfs.supported-protocols"
    42 //-----------------------------------------------------------------------------
    44 // NSPR_LOG_MODULES=gnomevfs:5
    45 #ifdef PR_LOGGING
    46 static PRLogModuleInfo *sGnomeVFSLog;
    47 #define LOG(args) PR_LOG(sGnomeVFSLog, PR_LOG_DEBUG, args)
    48 #else
    49 #define LOG(args)
    50 #endif
    52 //-----------------------------------------------------------------------------
    54 static nsresult
    55 MapGnomeVFSResult(GnomeVFSResult result)
    56 {
    57   switch (result)
    58   {
    59     case GNOME_VFS_OK:                           return NS_OK;
    60     case GNOME_VFS_ERROR_NOT_FOUND:              return NS_ERROR_FILE_NOT_FOUND;
    61     case GNOME_VFS_ERROR_INTERNAL:               return NS_ERROR_UNEXPECTED;
    62     case GNOME_VFS_ERROR_BAD_PARAMETERS:         return NS_ERROR_INVALID_ARG;
    63     case GNOME_VFS_ERROR_NOT_SUPPORTED:          return NS_ERROR_NOT_AVAILABLE;
    64     case GNOME_VFS_ERROR_CORRUPTED_DATA:         return NS_ERROR_FILE_CORRUPTED;
    65     case GNOME_VFS_ERROR_TOO_BIG:                return NS_ERROR_FILE_TOO_BIG;
    66     case GNOME_VFS_ERROR_NO_SPACE:               return NS_ERROR_FILE_NO_DEVICE_SPACE;
    67     case GNOME_VFS_ERROR_READ_ONLY:
    68     case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM:  return NS_ERROR_FILE_READ_ONLY;
    69     case GNOME_VFS_ERROR_INVALID_URI:
    70     case GNOME_VFS_ERROR_INVALID_HOST_NAME:      return NS_ERROR_MALFORMED_URI;
    71     case GNOME_VFS_ERROR_ACCESS_DENIED:
    72     case GNOME_VFS_ERROR_NOT_PERMITTED:
    73     case GNOME_VFS_ERROR_LOGIN_FAILED:           return NS_ERROR_FILE_ACCESS_DENIED;
    74     case GNOME_VFS_ERROR_EOF:                    return NS_BASE_STREAM_CLOSED;
    75     case GNOME_VFS_ERROR_NOT_A_DIRECTORY:        return NS_ERROR_FILE_NOT_DIRECTORY;
    76     case GNOME_VFS_ERROR_IN_PROGRESS:            return NS_ERROR_IN_PROGRESS;
    77     case GNOME_VFS_ERROR_FILE_EXISTS:            return NS_ERROR_FILE_ALREADY_EXISTS;
    78     case GNOME_VFS_ERROR_IS_DIRECTORY:           return NS_ERROR_FILE_IS_DIRECTORY;
    79     case GNOME_VFS_ERROR_NO_MEMORY:              return NS_ERROR_OUT_OF_MEMORY;
    80     case GNOME_VFS_ERROR_HOST_NOT_FOUND:
    81     case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS:    return NS_ERROR_UNKNOWN_HOST;
    82     case GNOME_VFS_ERROR_CANCELLED:
    83     case GNOME_VFS_ERROR_INTERRUPTED:            return NS_ERROR_ABORT;
    84     case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY:    return NS_ERROR_FILE_DIR_NOT_EMPTY;
    85     case GNOME_VFS_ERROR_NAME_TOO_LONG:          return NS_ERROR_FILE_NAME_TOO_LONG;
    86     case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE:  return NS_ERROR_UNKNOWN_PROTOCOL;
    88     /* No special mapping for these error codes...
    90     case GNOME_VFS_ERROR_GENERIC:
    91     case GNOME_VFS_ERROR_IO:
    92     case GNOME_VFS_ERROR_WRONG_FORMAT:
    93     case GNOME_VFS_ERROR_BAD_FILE:
    94     case GNOME_VFS_ERROR_NOT_OPEN:
    95     case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
    96     case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
    97     case GNOME_VFS_ERROR_LOOP:
    98     case GNOME_VFS_ERROR_DIRECTORY_BUSY:
    99     case GNOME_VFS_ERROR_TOO_MANY_LINKS:
   100     case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
   101     case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
   102     case GNOME_VFS_ERROR_PROTOCOL_ERROR:
   103     case GNOME_VFS_ERROR_NO_MASTER_BROWSER:
   105     */
   107     // Make GCC happy
   108     default:
   109       return NS_ERROR_FAILURE;
   110   }
   112   return NS_ERROR_FAILURE;
   113 }
   115 //-----------------------------------------------------------------------------
   117 static void
   118 ProxiedAuthCallback(gconstpointer in,
   119                     gsize         in_size,
   120                     gpointer      out,
   121                     gsize         out_size,
   122                     gpointer      callback_data)
   123 {
   124   GnomeVFSModuleCallbackAuthenticationIn *authIn =
   125       (GnomeVFSModuleCallbackAuthenticationIn *) in;
   126   GnomeVFSModuleCallbackAuthenticationOut *authOut =
   127       (GnomeVFSModuleCallbackAuthenticationOut *) out;
   129   LOG(("gnomevfs: ProxiedAuthCallback [uri=%s]\n", authIn->uri));
   131   // Without a channel, we have no way of getting a prompter.
   132   nsIChannel *channel = (nsIChannel *) callback_data;
   133   if (!channel)
   134     return;
   136   nsCOMPtr<nsIAuthPrompt> prompt;
   137   NS_QueryNotificationCallbacks(channel, prompt);
   139   // If no auth prompt, then give up.  We could failover to using the
   140   // WindowWatcher service, but that might defeat a consumer's purposeful
   141   // attempt to disable authentication (for whatever reason).
   142   if (!prompt)
   143     return;
   145   // Parse out the host and port...
   146   nsCOMPtr<nsIURI> uri;
   147   channel->GetURI(getter_AddRefs(uri));
   148   if (!uri)
   149     return;
   151 #ifdef DEBUG
   152   {
   153     //
   154     // Make sure authIn->uri is consistent with the channel's URI.
   155     //
   156     // XXX This check is probably not IDN safe, and it might incorrectly
   157     //     fire as a result of escaping differences.  It's unclear what
   158     //     kind of transforms GnomeVFS might have applied to the URI spec
   159     //     that we originally gave to it.  In spite of the likelihood of
   160     //     false hits, this check is probably still valuable.
   161     //
   162     nsAutoCString spec;
   163     uri->GetSpec(spec);
   164     int uriLen = strlen(authIn->uri);
   165     if (!StringHead(spec, uriLen).Equals(nsDependentCString(authIn->uri, uriLen)))
   166     {
   167       LOG(("gnomevfs: [spec=%s authIn->uri=%s]\n", spec.get(), authIn->uri));
   168       NS_ERROR("URI mismatch");
   169     }
   170   }
   171 #endif
   173   nsAutoCString scheme, hostPort;
   174   uri->GetScheme(scheme);
   175   uri->GetHostPort(hostPort);
   177   // It doesn't make sense for either of these strings to be empty.  What kind
   178   // of funky URI is this?
   179   if (scheme.IsEmpty() || hostPort.IsEmpty())
   180     return;
   182   // Construct the single signon key.  Altering the value of this key will
   183   // cause people's remembered passwords to be forgotten.  Think carefully
   184   // before changing the way this key is constructed.
   185   nsAutoString key, realm;
   187   NS_ConvertUTF8toUTF16 dispHost(scheme);
   188   dispHost.Append(NS_LITERAL_STRING("://"));
   189   dispHost.Append(NS_ConvertUTF8toUTF16(hostPort));
   191   key = dispHost;
   192   if (authIn->realm)
   193   {
   194     // We assume the realm string is ASCII.  That might be a bogus assumption,
   195     // but we have no idea what encoding GnomeVFS is using, so for now we'll
   196     // limit ourselves to ISO-Latin-1.  XXX What is a better solution?
   197     realm.Append('"');
   198     realm.Append(NS_ConvertASCIItoUTF16(authIn->realm));
   199     realm.Append('"');
   200     key.Append(' ');
   201     key.Append(realm);
   202   }
   204   // Construct the message string...
   205   //
   206   // We use Necko's string bundle here.  This code really should be encapsulated
   207   // behind some Necko API, after all this code is based closely on the code in
   208   // nsHttpChannel.cpp.
   210   nsCOMPtr<nsIStringBundleService> bundleSvc =
   211       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   212   if (!bundleSvc)
   213     return;
   215   nsCOMPtr<nsIStringBundle> bundle;
   216   bundleSvc->CreateBundle("chrome://global/locale/commonDialogs.properties",
   217                           getter_AddRefs(bundle));
   218   if (!bundle)
   219     return;
   221   nsString message;
   222   if (!realm.IsEmpty())
   223   {
   224     const char16_t *strings[] = { realm.get(), dispHost.get() };
   225     bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordForRealm"),
   226                                  strings, 2, getter_Copies(message));
   227   }
   228   else
   229   {
   230     const char16_t *strings[] = { dispHost.get() };
   231     bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor"),
   232                                  strings, 1, getter_Copies(message));
   233   }
   234   if (message.IsEmpty())
   235     return;
   237   // Prompt the user...
   238   nsresult rv;
   239   bool retval = false;
   240   char16_t *user = nullptr, *pass = nullptr;
   242   rv = prompt->PromptUsernameAndPassword(nullptr, message.get(),
   243                                          key.get(),
   244                                          nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
   245                                          &user, &pass, &retval);
   246   if (NS_FAILED(rv))
   247     return;
   248   if (!retval || !user || !pass)
   249     return;
   251   // XXX We need to convert the UTF-16 username and password from our dialog to
   252   // strings that GnomeVFS can understand.  It's unclear what encoding GnomeVFS
   253   // expects, so for now we assume 7-bit ASCII.  Hopefully, we can get a better
   254   // solution at some point.
   256   // One copy is never enough...
   257   authOut->username = g_strdup(NS_LossyConvertUTF16toASCII(user).get());
   258   authOut->password = g_strdup(NS_LossyConvertUTF16toASCII(pass).get());
   260   nsMemory::Free(user);
   261   nsMemory::Free(pass);
   262 }
   264 struct nsGnomeVFSAuthCallbackEvent : public nsRunnable
   265 {
   266   gconstpointer in;
   267   gsize         in_size;
   268   gpointer      out;
   269   gsize         out_size;
   270   gpointer      callback_data;
   272   NS_IMETHOD Run() {
   273     ProxiedAuthCallback(in, in_size, out, out_size, callback_data);
   274     return NS_OK;
   275   }
   276 };
   278 static void
   279 AuthCallback(gconstpointer in,
   280              gsize         in_size,
   281              gpointer      out,
   282              gsize         out_size,
   283              gpointer      callback_data)
   284 {
   285   // Need to proxy this callback over to the main thread.  Synchronous dispatch
   286   // is required in order to provide data to the GnomeVFS callback.
   288   nsRefPtr<nsGnomeVFSAuthCallbackEvent> ev = new nsGnomeVFSAuthCallbackEvent();
   289   if (!ev)
   290     return;  // OOM
   292   ev->in = in;
   293   ev->in_size = in_size;
   294   ev->out = out;
   295   ev->out_size = out_size;
   296   ev->callback_data = callback_data;
   298   NS_DispatchToMainThread(ev, NS_DISPATCH_SYNC);
   299 }
   301 //-----------------------------------------------------------------------------
   303 static gint
   304 FileInfoComparator(gconstpointer a, gconstpointer b)
   305 {
   306   const GnomeVFSFileInfo *ia = (const GnomeVFSFileInfo *) a;
   307   const GnomeVFSFileInfo *ib = (const GnomeVFSFileInfo *) b;
   309   return strcasecmp(ia->name, ib->name);
   310 }
   312 //-----------------------------------------------------------------------------
   314 class nsGnomeVFSInputStream MOZ_FINAL : public nsIInputStream
   315 {
   316   public:
   317     NS_DECL_THREADSAFE_ISUPPORTS
   318     NS_DECL_NSIINPUTSTREAM
   320     nsGnomeVFSInputStream(const nsCString &uriSpec)
   321       : mSpec(uriSpec)
   322       , mChannel(nullptr)
   323       , mHandle(nullptr)
   324       , mBytesRemaining(UINT64_MAX)
   325       , mStatus(NS_OK)
   326       , mDirList(nullptr)
   327       , mDirListPtr(nullptr)
   328       , mDirBufCursor(0)
   329       , mDirOpen(false) {}
   331    ~nsGnomeVFSInputStream() { Close(); }
   333     void SetChannel(nsIChannel *channel)
   334     {
   335       // We need to hold an owning reference to our channel.  This is done
   336       // so we can access the channel's notification callbacks to acquire
   337       // a reference to a nsIAuthPrompt if we need to handle a GnomeVFS
   338       // authentication callback.
   339       //
   340       // However, the channel can only be accessed on the main thread, so
   341       // we have to be very careful with ownership.  Moreover, it doesn't
   342       // support threadsafe addref/release, so proxying is the answer.
   343       //
   344       // Also, it's important to note that this likely creates a reference
   345       // cycle since the channel likely owns this stream.  This reference
   346       // cycle is broken in our Close method.
   348       NS_ADDREF(mChannel = channel);
   349     }
   351   private:
   352     GnomeVFSResult DoOpen();
   353     GnomeVFSResult DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead);
   354     nsresult       SetContentTypeOfChannel(const char *contentType);
   356   private:
   357     nsCString                mSpec;
   358     nsIChannel              *mChannel; // manually refcounted
   359     GnomeVFSHandle          *mHandle;
   360     uint64_t                 mBytesRemaining;
   361     nsresult                 mStatus;
   362     GList                   *mDirList;
   363     GList                   *mDirListPtr;
   364     nsCString                mDirBuf;
   365     uint32_t                 mDirBufCursor;
   366     bool                     mDirOpen;
   367 };
   369 GnomeVFSResult
   370 nsGnomeVFSInputStream::DoOpen()
   371 {
   372   GnomeVFSResult rv;
   374   NS_ASSERTION(mHandle == nullptr, "already open");
   376   // Push a callback handler on the stack for this thread, so we can intercept
   377   // authentication requests from GnomeVFS.  We'll use the channel to get a
   378   // nsIAuthPrompt instance.
   380   gnome_vfs_module_callback_push(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION,
   381                                  AuthCallback, mChannel, nullptr);
   383   // Query the mime type first (this could return nullptr). 
   384   //
   385   // XXX We need to do this up-front in order to determine how to open the URI.
   386   //     Unfortunately, the error code GNOME_VFS_ERROR_IS_DIRECTORY is not
   387   //     always returned by gnome_vfs_open when we pass it a URI to a directory!
   388   //     Otherwise, we could have used that as a way to failover to opening the
   389   //     URI as a directory.  Also, it would have been ideal if
   390   //     gnome_vfs_get_file_info_from_handle were actually implemented by the
   391   //     smb:// module, since that would have allowed us to potentially save a
   392   //     round trip to the server to discover the mime type of the document in
   393   //     the case where gnome_vfs_open would have been used.  (Oh well!  /me
   394   //     throws hands up in the air and moves on...)
   396   GnomeVFSFileInfo info = {0};
   397   rv = gnome_vfs_get_file_info(mSpec.get(), &info, GnomeVFSFileInfoOptions(
   398                                GNOME_VFS_FILE_INFO_DEFAULT |
   399                                GNOME_VFS_FILE_INFO_FOLLOW_LINKS));
   400   if (rv == GNOME_VFS_OK)
   401   {
   402     if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY)
   403     {
   404       rv = gnome_vfs_directory_list_load(&mDirList, mSpec.get(),
   405                                          GNOME_VFS_FILE_INFO_DEFAULT);
   407       LOG(("gnomevfs: gnome_vfs_directory_list_load returned %d (%s) [spec=\"%s\"]\n",
   408           rv, gnome_vfs_result_to_string(rv), mSpec.get()));
   409     }
   410     else
   411     {
   412       rv = gnome_vfs_open(&mHandle, mSpec.get(), GNOME_VFS_OPEN_READ);
   414       LOG(("gnomevfs: gnome_vfs_open returned %d (%s) [spec=\"%s\"]\n",
   415           rv, gnome_vfs_result_to_string(rv), mSpec.get()));
   416     }
   417   }
   419   gnome_vfs_module_callback_pop(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION);
   421   if (rv == GNOME_VFS_OK)
   422   {
   423     if (mHandle)
   424     {
   425       // Here we set the content type of the channel to the value of the mime
   426       // type determined by GnomeVFS.  However, if GnomeVFS is telling us that
   427       // the document is binary, we'll ignore that and keep the channel's
   428       // content type unspecified.  That will enable our content type sniffing
   429       // algorithms.  This should provide more consistent mime type handling.
   431       if (info.mime_type && (strcmp(info.mime_type, APPLICATION_OCTET_STREAM) != 0))
   432         SetContentTypeOfChannel(info.mime_type);
   434       mBytesRemaining = info.size;
   436       // Update the content length attribute on the channel.  We do this
   437       // synchronously without proxying.  This hack is not as bad as it looks!
   438       if (mBytesRemaining > INT64_MAX) {
   439         mChannel->SetContentLength(-1);
   440       } else {
   441         mChannel->SetContentLength(mBytesRemaining);
   442       }
   443     }
   444     else
   445     {
   446       mDirOpen = true;
   448       // Sort mDirList
   449       mDirList = g_list_sort(mDirList, FileInfoComparator);
   450       mDirListPtr = mDirList;
   452       // Write base URL (make sure it ends with a '/')
   453       mDirBuf.Append("300: ");
   454       mDirBuf.Append(mSpec);
   455       if (mSpec.get()[mSpec.Length() - 1] != '/')
   456         mDirBuf.Append('/');
   457       mDirBuf.Append('\n');
   459       // Write column names
   460       mDirBuf.Append("200: filename content-length last-modified file-type\n");
   462       // Write charset (assume UTF-8)
   463       // XXX is this correct?
   464       mDirBuf.Append("301: UTF-8\n");
   466       SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT);
   467     }
   468   }
   470   gnome_vfs_file_info_clear(&info);
   471   return rv;
   472 }
   474 GnomeVFSResult
   475 nsGnomeVFSInputStream::DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead)
   476 {
   477   GnomeVFSResult rv;
   479   if (mHandle)
   480   {
   481     GnomeVFSFileSize bytesRead;
   482     rv = gnome_vfs_read(mHandle, aBuf, aCount, &bytesRead);
   483     if (rv == GNOME_VFS_OK)
   484     {
   485       // aCount is 32-bit, so aCountRead is under 32-bit value.
   486       *aCountRead = (uint32_t) bytesRead;
   487       mBytesRemaining -= *aCountRead;
   488     }
   489   }
   490   else if (mDirOpen)
   491   {
   492     rv = GNOME_VFS_OK;
   494     while (aCount && rv != GNOME_VFS_ERROR_EOF)
   495     {
   496       // Copy data out of our buffer
   497       uint32_t bufLen = mDirBuf.Length() - mDirBufCursor;
   498       if (bufLen)
   499       {
   500         uint32_t n = std::min(bufLen, aCount);
   501         memcpy(aBuf, mDirBuf.get() + mDirBufCursor, n);
   502         *aCountRead += n;
   503         aBuf += n;
   504         aCount -= n;
   505         mDirBufCursor += n;
   506       }
   508       if (!mDirListPtr)    // Are we at the end of the directory list?
   509       {
   510         rv = GNOME_VFS_ERROR_EOF;
   511       }
   512       else if (aCount)     // Do we need more data?
   513       {
   514         GnomeVFSFileInfo *info = (GnomeVFSFileInfo *) mDirListPtr->data;
   516         // Prune '.' and '..' from directory listing.
   517         if (info->name[0] == '.' &&
   518                (info->name[1] == '\0' ||
   519                    (info->name[1] == '.' && info->name[2] == '\0')))
   520         {
   521           mDirListPtr = mDirListPtr->next;
   522           continue;
   523         }
   525         mDirBuf.Assign("201: ");
   527         // The "filename" field
   528         nsCString escName;
   529         nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID);
   530         if (nu) {
   531           nu->EscapeString(nsDependentCString(info->name),
   532                            nsINetUtil::ESCAPE_URL_PATH, escName);
   534           mDirBuf.Append(escName);
   535           mDirBuf.Append(' ');
   536         }
   538         // The "content-length" field
   539         // XXX truncates size from 64-bit to 32-bit
   540         mDirBuf.AppendInt(int32_t(info->size));
   541         mDirBuf.Append(' ');
   543         // The "last-modified" field
   544         // 
   545         // NSPR promises: PRTime is compatible with time_t
   546         // we just need to convert from seconds to microseconds
   547         PRExplodedTime tm;
   548         PRTime pt = ((PRTime) info->mtime) * 1000000;
   549         PR_ExplodeTime(pt, PR_GMTParameters, &tm);
   550         {
   551           char buf[64];
   552           PR_FormatTimeUSEnglish(buf, sizeof(buf),
   553               "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
   554           mDirBuf.Append(buf);
   555         }
   557         // The "file-type" field
   558         switch (info->type)
   559         {
   560           case GNOME_VFS_FILE_TYPE_REGULAR:
   561             mDirBuf.Append("FILE ");
   562             break;
   563           case GNOME_VFS_FILE_TYPE_DIRECTORY:
   564             mDirBuf.Append("DIRECTORY ");
   565             break;
   566           case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK:
   567             mDirBuf.Append("SYMBOLIC-LINK ");
   568             break;
   569           default:
   570             break;
   571         }
   573         mDirBuf.Append('\n');
   575         mDirBufCursor = 0;
   576         mDirListPtr = mDirListPtr->next;
   577       }
   578     }
   579   }
   580   else
   581   {
   582     NS_NOTREACHED("reading from what?");
   583     rv = GNOME_VFS_ERROR_GENERIC;
   584   }
   586   return rv;
   587 }
   589 // This class is used to implement SetContentTypeOfChannel.
   590 class nsGnomeVFSSetContentTypeEvent : public nsRunnable
   591 {
   592   public:
   593     nsGnomeVFSSetContentTypeEvent(nsIChannel *channel, const char *contentType)
   594       : mChannel(channel), mContentType(contentType)
   595     {
   596       // stash channel reference in mChannel.  no AddRef here!  see note
   597       // in SetContentTypeOfchannel.
   598     }
   600     NS_IMETHOD Run()
   601     {
   602       mChannel->SetContentType(mContentType);
   603       return NS_OK;
   604     }
   606   private: 
   607     nsIChannel *mChannel;
   608     nsCString   mContentType;
   609 };
   611 nsresult
   612 nsGnomeVFSInputStream::SetContentTypeOfChannel(const char *contentType)
   613 {
   614   // We need to proxy this call over to the main thread.  We post an
   615   // asynchronous event in this case so that we don't delay reading data, and
   616   // we know that this is safe to do since the channel's reference will be
   617   // released asynchronously as well.  We trust the ordering of the main
   618   // thread's event queue to protect us against memory corruption.
   620   nsresult rv;
   621   nsCOMPtr<nsIRunnable> ev =
   622       new nsGnomeVFSSetContentTypeEvent(mChannel, contentType);
   623   if (!ev)
   624   {
   625     rv = NS_ERROR_OUT_OF_MEMORY;
   626   }
   627   else
   628   {
   629     rv = NS_DispatchToMainThread(ev);
   630   }
   631   return rv;
   632 }
   634 NS_IMPL_ISUPPORTS(nsGnomeVFSInputStream, nsIInputStream)
   636 NS_IMETHODIMP
   637 nsGnomeVFSInputStream::Close()
   638 {
   639   if (mHandle)
   640   {
   641     gnome_vfs_close(mHandle);
   642     mHandle = nullptr;
   643   }
   645   if (mDirList)
   646   {
   647     // Destroy the list of GnomeVFSFileInfo objects...
   648     g_list_foreach(mDirList, (GFunc) gnome_vfs_file_info_unref, nullptr);
   649     g_list_free(mDirList);
   650     mDirList = nullptr;
   651     mDirListPtr = nullptr;
   652   }
   654   if (mChannel)
   655   {
   656     nsresult rv = NS_OK;
   658     nsCOMPtr<nsIThread> thread = do_GetMainThread();
   659     if (thread)
   660       rv = NS_ProxyRelease(thread, mChannel);
   662     NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference");
   663     mChannel = nullptr;
   664   }
   666   mSpec.Truncate(); // free memory
   668   // Prevent future reads from re-opening the handle.
   669   if (NS_SUCCEEDED(mStatus))
   670     mStatus = NS_BASE_STREAM_CLOSED;
   672   return NS_OK;
   673 }
   675 NS_IMETHODIMP
   676 nsGnomeVFSInputStream::Available(uint64_t *aResult)
   677 {
   678   if (NS_FAILED(mStatus))
   679     return mStatus;
   681   *aResult = mBytesRemaining;
   682   return NS_OK;
   683 }
   685 NS_IMETHODIMP
   686 nsGnomeVFSInputStream::Read(char *aBuf,
   687                             uint32_t aCount,
   688                             uint32_t *aCountRead)
   689 {
   690   *aCountRead = 0;
   692   if (mStatus == NS_BASE_STREAM_CLOSED)
   693     return NS_OK;
   694   if (NS_FAILED(mStatus))
   695     return mStatus;
   697   GnomeVFSResult rv = GNOME_VFS_OK;
   699   // If this is our first-time through here, then open the URI.
   700   if (!mHandle && !mDirOpen)
   701     rv = DoOpen();
   703   if (rv == GNOME_VFS_OK)
   704     rv = DoRead(aBuf, aCount, aCountRead);
   706   if (rv != GNOME_VFS_OK)
   707   {
   708     // If we reach here, we hit some kind of error.  EOF is not an error.
   709     mStatus = MapGnomeVFSResult(rv);
   710     if (mStatus == NS_BASE_STREAM_CLOSED)
   711       return NS_OK;
   713     LOG(("gnomevfs: result %d [%s] mapped to 0x%x\n",
   714         rv, gnome_vfs_result_to_string(rv), mStatus));
   715   }
   716   return mStatus;
   717 }
   719 NS_IMETHODIMP
   720 nsGnomeVFSInputStream::ReadSegments(nsWriteSegmentFun aWriter,
   721                                     void *aClosure,
   722                                     uint32_t aCount,
   723                                     uint32_t *aResult)
   724 {
   725   // There is no way to implement this using GnomeVFS, but fortunately
   726   // that doesn't matter.  Because we are a blocking input stream, Necko
   727   // isn't going to call our ReadSegments method.
   728   NS_NOTREACHED("nsGnomeVFSInputStream::ReadSegments");
   729   return NS_ERROR_NOT_IMPLEMENTED;
   730 }
   732 NS_IMETHODIMP
   733 nsGnomeVFSInputStream::IsNonBlocking(bool *aResult)
   734 {
   735   *aResult = false;
   736   return NS_OK;
   737 }
   739 //-----------------------------------------------------------------------------
   741 class nsGnomeVFSProtocolHandler MOZ_FINAL : public nsIProtocolHandler
   742                                           , public nsIObserver
   743 {
   744   public:
   745     NS_DECL_ISUPPORTS
   746     NS_DECL_NSIPROTOCOLHANDLER
   747     NS_DECL_NSIOBSERVER
   749     nsresult Init();
   751   private:
   752     void   InitSupportedProtocolsPref(nsIPrefBranch *prefs);
   753     bool IsSupportedProtocol(const nsCString &spec);
   755     nsCString mSupportedProtocols;
   756 };
   758 NS_IMPL_ISUPPORTS(nsGnomeVFSProtocolHandler, nsIProtocolHandler, nsIObserver)
   760 nsresult
   761 nsGnomeVFSProtocolHandler::Init()
   762 {
   763 #ifdef PR_LOGGING
   764   sGnomeVFSLog = PR_NewLogModule("gnomevfs");
   765 #endif
   767   if (!gnome_vfs_initialized())
   768   {
   769     if (!gnome_vfs_init())
   770     {
   771       NS_WARNING("gnome_vfs_init failed");
   772       return NS_ERROR_UNEXPECTED;
   773     }
   774   }
   776   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   777   if (prefs)
   778   {
   779     InitSupportedProtocolsPref(prefs);
   780     prefs->AddObserver(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS, this, false);
   781   }
   783   return NS_OK;
   784 }
   786 void
   787 nsGnomeVFSProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch *prefs)
   788 {
   789   // read preferences
   790   nsresult rv = prefs->GetCharPref(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS,
   791                                    getter_Copies(mSupportedProtocols));
   792   if (NS_SUCCEEDED(rv)) {
   793     mSupportedProtocols.StripWhitespace();
   794     ToLowerCase(mSupportedProtocols);
   795   }
   796   else
   797     mSupportedProtocols.Assign("smb:,sftp:"); // use defaults
   799   LOG(("gnomevfs: supported protocols \"%s\"\n", mSupportedProtocols.get()));
   800 }
   802 bool
   803 nsGnomeVFSProtocolHandler::IsSupportedProtocol(const nsCString &aSpec)
   804 {
   805   const char *specString = aSpec.get();
   806   const char *colon = strchr(specString, ':');
   807   if (!colon)
   808     return false;
   810   uint32_t length = colon - specString + 1;
   812   // <scheme> + ':'
   813   nsCString scheme(specString, length);
   815   char *found = PL_strcasestr(mSupportedProtocols.get(), scheme.get());
   816   if (!found)
   817     return false;
   819   if (found[length] != ',' && found[length] != '\0')
   820     return false;
   822   return true;
   823 }
   825 NS_IMETHODIMP
   826 nsGnomeVFSProtocolHandler::GetScheme(nsACString &aScheme)
   827 {
   828   aScheme.Assign(MOZ_GNOMEVFS_SCHEME);
   829   return NS_OK;
   830 }
   832 NS_IMETHODIMP
   833 nsGnomeVFSProtocolHandler::GetDefaultPort(int32_t *aDefaultPort)
   834 {
   835   *aDefaultPort = -1;
   836   return NS_OK;
   837 }
   839 NS_IMETHODIMP
   840 nsGnomeVFSProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
   841 {
   842   // Is URI_STD true of all GnomeVFS URI types?
   843   *aProtocolFlags = URI_STD | URI_DANGEROUS_TO_LOAD;
   844   return NS_OK;
   845 }
   847 NS_IMETHODIMP
   848 nsGnomeVFSProtocolHandler::NewURI(const nsACString &aSpec,
   849                                   const char *aOriginCharset,
   850                                   nsIURI *aBaseURI,
   851                                   nsIURI **aResult)
   852 {
   853   const nsCString flatSpec(aSpec);
   854   LOG(("gnomevfs: NewURI [spec=%s]\n", flatSpec.get()));
   856   if (!aBaseURI)
   857   {
   858     //
   859     // XXX This check is used to limit the gnome-vfs protocols we support.  For
   860     //     security reasons, it is best that we limit the protocols we support to
   861     //     those with known characteristics.  We might want to lessen this
   862     //     restriction if it proves to be too heavy handed.  A black list of
   863     //     protocols we don't want to support might be better.  For example, we
   864     //     probably don't want to try to load "start-here:" inside the browser.
   865     //     There are others that fall into this category, which are best handled
   866     //     externally by Nautilus (or another app like it).
   867     //
   868     if (!IsSupportedProtocol(flatSpec))
   869       return NS_ERROR_UNKNOWN_PROTOCOL;
   871     // Verify that GnomeVFS supports this URI scheme.
   872     GnomeVFSURI *uri = gnome_vfs_uri_new(flatSpec.get());
   873     if (!uri)
   874       return NS_ERROR_UNKNOWN_PROTOCOL;
   875   }
   877   //
   878   // XXX Can we really assume that all gnome-vfs URIs can be parsed using
   879   //     nsStandardURL?  We probably really need to implement nsIURI/nsIURL
   880   //     in terms of the gnome_vfs_uri_XXX methods, but at least this works
   881   //     correctly for smb:// URLs ;-)
   882   //
   883   //     Also, it might not be possible to fully implement nsIURI/nsIURL in
   884   //     terms of GnomeVFSURI since some Necko methods have no GnomeVFS
   885   //     equivalent.
   886   //
   887   nsresult rv;
   888   nsCOMPtr<nsIStandardURL> url =
   889       do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
   890   if (NS_FAILED(rv))
   891     return rv;
   893   rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, flatSpec,
   894                  aOriginCharset, aBaseURI);
   895   if (NS_SUCCEEDED(rv))
   896     rv = CallQueryInterface(url, aResult);
   898   return rv;
   899 }
   901 NS_IMETHODIMP
   902 nsGnomeVFSProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult)
   903 {
   904   NS_ENSURE_ARG_POINTER(aURI);
   905   nsresult rv;
   907   nsAutoCString spec;
   908   rv = aURI->GetSpec(spec);
   909   if (NS_FAILED(rv))
   910     return rv;
   912   nsRefPtr<nsGnomeVFSInputStream> stream = new nsGnomeVFSInputStream(spec);
   913   if (!stream)
   914   {
   915     rv = NS_ERROR_OUT_OF_MEMORY;
   916   }
   917   else
   918   {
   919     // start out assuming an unknown content-type.  we'll set the content-type
   920     // to something better once we open the URI.
   921     rv = NS_NewInputStreamChannel(aResult, aURI, stream,
   922                                   NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
   923     if (NS_SUCCEEDED(rv))
   924       stream->SetChannel(*aResult);
   925   }
   926   return rv;
   927 }
   929 NS_IMETHODIMP
   930 nsGnomeVFSProtocolHandler::AllowPort(int32_t aPort,
   931                                      const char *aScheme,
   932                                      bool *aResult)
   933 {
   934   // Don't override anything.
   935   *aResult = false; 
   936   return NS_OK;
   937 }
   939 NS_IMETHODIMP
   940 nsGnomeVFSProtocolHandler::Observe(nsISupports *aSubject,
   941                                    const char *aTopic,
   942                                    const char16_t *aData)
   943 {
   944   if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
   945     nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
   946     InitSupportedProtocolsPref(prefs);
   947   }
   948   return NS_OK;
   949 }
   951 //-----------------------------------------------------------------------------
   953 #define NS_GNOMEVFSPROTOCOLHANDLER_CID               \
   954 { /* 9b6dc177-a2e4-49e1-9c98-0a8384de7f6c */         \
   955     0x9b6dc177,                                      \
   956     0xa2e4,                                          \
   957     0x49e1,                                          \
   958     {0x9c, 0x98, 0x0a, 0x83, 0x84, 0xde, 0x7f, 0x6c} \
   959 }
   961 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGnomeVFSProtocolHandler, Init)
   962 NS_DEFINE_NAMED_CID(NS_GNOMEVFSPROTOCOLHANDLER_CID);
   964 static const mozilla::Module::CIDEntry kVFSCIDs[] = {
   965   { &kNS_GNOMEVFSPROTOCOLHANDLER_CID, false, nullptr, nsGnomeVFSProtocolHandlerConstructor },
   966   { nullptr }
   967 };
   969 static const mozilla::Module::ContractIDEntry kVFSContracts[] = {
   970   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GNOMEVFS_SCHEME, &kNS_GNOMEVFSPROTOCOLHANDLER_CID },
   971   { nullptr }
   972 };
   974 static const mozilla::Module kVFSModule = {
   975   mozilla::Module::kVersion,
   976   kVFSCIDs,
   977   kVFSContracts
   978 };
   980 NSMODULE_DEFN(nsGnomeVFSModule) = &kVFSModule;

mercurial