tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GetDirectoryListingTask.cpp (12845B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "GetDirectoryListingTask.h"
      8 
      9 #include "HTMLSplitOnSpacesTokenizer.h"
     10 #include "js/Value.h"
     11 #include "mozilla/dom/FileBlobImpl.h"
     12 #include "mozilla/dom/FileSystemBase.h"
     13 #include "mozilla/dom/FileSystemUtils.h"
     14 #include "mozilla/dom/IPCBlobUtils.h"
     15 #include "mozilla/dom/PFileSystemParams.h"
     16 #include "mozilla/dom/Promise.h"
     17 #include "mozilla/dom/UnionTypes.h"
     18 #include "mozilla/ipc/BackgroundParent.h"
     19 #include "nsCExternalHandlerService.h"
     20 #include "nsIFile.h"
     21 #include "nsIMIMEService.h"
     22 #include "nsString.h"
     23 
     24 namespace mozilla::dom {
     25 
     26 /**
     27 * GetDirectoryListingTaskChild
     28 */
     29 
     30 /* static */
     31 already_AddRefed<GetDirectoryListingTaskChild>
     32 GetDirectoryListingTaskChild::Create(FileSystemBase* aFileSystem,
     33                                     Directory* aDirectory,
     34                                     nsIFile* aTargetPath,
     35                                     const nsAString& aFilters,
     36                                     ErrorResult& aRv) {
     37  MOZ_ASSERT(aFileSystem);
     38  MOZ_ASSERT(aDirectory);
     39  aFileSystem->AssertIsOnOwningThread();
     40 
     41  nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject();
     42  MOZ_ASSERT(globalObject);
     43 
     44  RefPtr<GetDirectoryListingTaskChild> task = new GetDirectoryListingTaskChild(
     45      globalObject, aFileSystem, aDirectory, aTargetPath, aFilters);
     46 
     47  // aTargetPath can be null. In this case SetError will be called.
     48 
     49  task->mPromise = Promise::Create(globalObject, aRv);
     50  if (NS_WARN_IF(aRv.Failed())) {
     51    return nullptr;
     52  }
     53 
     54  return task.forget();
     55 }
     56 
     57 GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(
     58    nsIGlobalObject* aGlobalObject, FileSystemBase* aFileSystem,
     59    Directory* aDirectory, nsIFile* aTargetPath, const nsAString& aFilters)
     60    : FileSystemTaskChildBase(aGlobalObject, aFileSystem),
     61      mDirectory(aDirectory),
     62      mTargetPath(aTargetPath),
     63      mFilters(aFilters) {
     64  MOZ_ASSERT(aFileSystem);
     65  aFileSystem->AssertIsOnOwningThread();
     66 }
     67 
     68 GetDirectoryListingTaskChild::~GetDirectoryListingTaskChild() {
     69  mFileSystem->AssertIsOnOwningThread();
     70 }
     71 
     72 already_AddRefed<Promise> GetDirectoryListingTaskChild::GetPromise() {
     73  mFileSystem->AssertIsOnOwningThread();
     74  return RefPtr<Promise>(mPromise).forget();
     75 }
     76 
     77 FileSystemParams GetDirectoryListingTaskChild::GetRequestParams(
     78    const nsString& aSerializedDOMPath, ErrorResult& aRv) const {
     79  mFileSystem->AssertIsOnOwningThread();
     80 
     81  // this is the real path.
     82  nsAutoString path;
     83  aRv = mTargetPath->GetPath(path);
     84  if (NS_WARN_IF(aRv.Failed())) {
     85    return FileSystemGetDirectoryListingParams();
     86  }
     87 
     88  // this is the dom path.
     89  nsAutoString directoryPath;
     90  mDirectory->GetPath(directoryPath, aRv);
     91  if (NS_WARN_IF(aRv.Failed())) {
     92    return FileSystemGetDirectoryListingParams();
     93  }
     94 
     95  return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
     96                                             directoryPath, mFilters);
     97 }
     98 
     99 void GetDirectoryListingTaskChild::SetSuccessRequestResult(
    100    const FileSystemResponseValue& aValue, ErrorResult& aRv) {
    101  mFileSystem->AssertIsOnOwningThread();
    102  MOZ_ASSERT(aValue.type() ==
    103             FileSystemResponseValue::TFileSystemDirectoryListingResponse);
    104 
    105  FileSystemDirectoryListingResponse r = aValue;
    106  for (uint32_t i = 0; i < r.data().Length(); ++i) {
    107    const FileSystemDirectoryListingResponseData& data = r.data()[i];
    108 
    109    OwningFileOrDirectory* ofd = mTargetData.AppendElement(fallible);
    110    if (!ofd) {
    111      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    112      return;
    113    }
    114 
    115    if (data.type() == FileSystemDirectoryListingResponseData::
    116                           TFileSystemDirectoryListingResponseFile) {
    117      const FileSystemDirectoryListingResponseFile& d =
    118          data.get_FileSystemDirectoryListingResponseFile();
    119 
    120      RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(d.blob());
    121      MOZ_ASSERT(blobImpl);
    122 
    123      nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject();
    124      MOZ_ASSERT(globalObject);
    125 
    126      RefPtr<File> file = File::Create(globalObject, blobImpl);
    127      MOZ_ASSERT(file);
    128 
    129      ofd->SetAsFile() = file;
    130    } else {
    131      MOZ_ASSERT(data.type() ==
    132                 FileSystemDirectoryListingResponseData::
    133                     TFileSystemDirectoryListingResponseDirectory);
    134      const FileSystemDirectoryListingResponseDirectory& d =
    135          data.get_FileSystemDirectoryListingResponseDirectory();
    136 
    137      nsCOMPtr<nsIFile> path;
    138      aRv = NS_NewLocalFile(d.directoryRealPath(), getter_AddRefs(path));
    139      if (NS_WARN_IF(aRv.Failed())) {
    140        return;
    141      }
    142 
    143      RefPtr<Directory> directory =
    144          Directory::Create(mFileSystem->GetParentObject(), path, mFileSystem);
    145      MOZ_ASSERT(directory);
    146 
    147      ofd->SetAsDirectory() = directory;
    148    }
    149  }
    150 }
    151 
    152 void GetDirectoryListingTaskChild::HandlerCallback() {
    153  mFileSystem->AssertIsOnOwningThread();
    154 
    155  if (mFileSystem->IsShutdown()) {
    156    mPromise = nullptr;
    157    return;
    158  }
    159 
    160  if (HasError()) {
    161    mPromise->MaybeReject(mErrorValue);
    162    mPromise = nullptr;
    163    return;
    164  }
    165 
    166  mPromise->MaybeResolve(mTargetData);
    167  mPromise = nullptr;
    168 }
    169 
    170 /**
    171 * GetDirectoryListingTaskParent
    172 */
    173 
    174 /* static */
    175 already_AddRefed<GetDirectoryListingTaskParent>
    176 GetDirectoryListingTaskParent::Create(
    177    FileSystemBase* aFileSystem,
    178    const FileSystemGetDirectoryListingParams& aParam,
    179    FileSystemRequestParent* aParent, ErrorResult& aRv) {
    180  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
    181  mozilla::ipc::AssertIsOnBackgroundThread();
    182  MOZ_ASSERT(aFileSystem);
    183 
    184  RefPtr<GetDirectoryListingTaskParent> task =
    185      new GetDirectoryListingTaskParent(aFileSystem, aParam, aParent);
    186 
    187  aRv = NS_NewLocalFile(aParam.realPath(), getter_AddRefs(task->mTargetPath));
    188  if (NS_WARN_IF(aRv.Failed())) {
    189    return nullptr;
    190  }
    191 
    192  return task.forget();
    193 }
    194 
    195 GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(
    196    FileSystemBase* aFileSystem,
    197    const FileSystemGetDirectoryListingParams& aParam,
    198    FileSystemRequestParent* aParent)
    199    : FileSystemTaskParentBase(aFileSystem, aParam, aParent),
    200      mDOMPath(aParam.domPath()),
    201      mFilters(aParam.filters()) {
    202  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
    203  mozilla::ipc::AssertIsOnBackgroundThread();
    204  MOZ_ASSERT(aFileSystem);
    205 }
    206 
    207 FileSystemResponseValue GetDirectoryListingTaskParent::GetSuccessRequestResult(
    208    ErrorResult& aRv) const {
    209  mozilla::ipc::AssertIsOnBackgroundThread();
    210 
    211  nsTArray<FileSystemDirectoryListingResponseData> inputs;
    212 
    213  for (unsigned i = 0; i < mTargetData.Length(); i++) {
    214    if (mTargetData[i].mType == FileOrDirectoryPath::eFilePath) {
    215      nsCOMPtr<nsIFile> path;
    216      nsresult rv = NS_NewLocalFile(mTargetData[i].mPath, getter_AddRefs(path));
    217      if (NS_WARN_IF(NS_FAILED(rv))) {
    218        continue;
    219      }
    220 
    221      nsAutoString fileName;
    222      path->GetLeafName(fileName);
    223 
    224      int64_t fileSize = 0;
    225      rv = path->GetFileSize(&fileSize);
    226      if (NS_WARN_IF(NS_FAILED(rv))) {
    227        continue;
    228      }
    229 
    230      FileSystemDirectoryListingResponseFile fileData;
    231      RefPtr<BlobImpl> blobImpl = new FileBlobImpl(
    232          fileName, mTargetData[i].mContentType, fileSize, path);
    233 
    234      nsAutoString filePath;
    235      filePath.Assign(mDOMPath);
    236 
    237      // This is specific for unix root filesystem.
    238      if (!mDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
    239        filePath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
    240      }
    241 
    242      nsAutoString name;
    243      blobImpl->GetName(name);
    244      filePath.Append(name);
    245      blobImpl->SetDOMPath(filePath);
    246 
    247      IPCBlob ipcBlob;
    248      rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
    249      if (NS_WARN_IF(NS_FAILED(rv))) {
    250        continue;
    251      }
    252 
    253      fileData.blob() = ipcBlob;
    254      inputs.AppendElement(fileData);
    255    } else {
    256      MOZ_ASSERT(mTargetData[i].mType == FileOrDirectoryPath::eDirectoryPath);
    257      FileSystemDirectoryListingResponseDirectory directoryData;
    258      directoryData.directoryRealPath() = mTargetData[i].mPath;
    259      inputs.AppendElement(directoryData);
    260    }
    261  }
    262 
    263  FileSystemDirectoryListingResponse response;
    264  response.data() = std::move(inputs);
    265  return response;
    266 }
    267 
    268 nsresult GetDirectoryListingTaskParent::IOWork() {
    269  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
    270  MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
    271 
    272  if (mFileSystem->IsShutdown()) {
    273    return NS_ERROR_FAILURE;
    274  }
    275 
    276  bool exists;
    277  nsresult rv = mTargetPath->Exists(&exists);
    278  if (NS_WARN_IF(NS_FAILED(rv))) {
    279    return rv;
    280  }
    281 
    282  if (!exists) {
    283    if (!mFileSystem->ShouldCreateDirectory()) {
    284      return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
    285    }
    286 
    287    rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
    288    if (NS_WARN_IF(NS_FAILED(rv))) {
    289      return rv;
    290    }
    291  }
    292 
    293  // Get isDirectory.
    294  bool isDir;
    295  rv = mTargetPath->IsDirectory(&isDir);
    296  if (NS_WARN_IF(NS_FAILED(rv))) {
    297    return rv;
    298  }
    299 
    300  if (!isDir) {
    301    return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
    302  }
    303 
    304  nsCOMPtr<nsIDirectoryEnumerator> entries;
    305  rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries));
    306  if (NS_WARN_IF(NS_FAILED(rv))) {
    307    return rv;
    308  }
    309 
    310  bool filterOutSensitive = false;
    311  {
    312    HTMLSplitOnSpacesTokenizer tokenizer(mFilters, ';');
    313    nsAutoString token;
    314    while (tokenizer.hasMoreTokens()) {
    315      token = tokenizer.nextToken();
    316      if (token.EqualsLiteral("filter-out-sensitive")) {
    317        filterOutSensitive = true;
    318      } else {
    319        MOZ_CRASH("Unrecognized filter");
    320      }
    321    }
    322  }
    323 
    324  for (;;) {
    325    nsCOMPtr<nsIFile> currFile;
    326    if (NS_WARN_IF(NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile)))) ||
    327        !currFile) {
    328      break;
    329    }
    330    bool isLink, isSpecial, isFile;
    331    if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
    332                   NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
    333        // Although we allow explicit individual selection of symlinks via the
    334        // file picker, we do not process symlinks in directory traversal.  Our
    335        // specific policy decision is documented at
    336        // https://bugzilla.mozilla.org/show_bug.cgi?id=1813299#c20
    337        isLink || isSpecial) {
    338      continue;
    339    }
    340    if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
    341                   NS_FAILED(currFile->IsDirectory(&isDir))) ||
    342        !(isFile || isDir)) {
    343      continue;
    344    }
    345 
    346    if (filterOutSensitive) {
    347      bool isHidden;
    348      if (NS_WARN_IF(NS_FAILED(currFile->IsHidden(&isHidden))) || isHidden) {
    349        continue;
    350      }
    351      nsAutoString leafName;
    352      if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
    353        continue;
    354      }
    355      if (leafName[0] == char16_t('.')) {
    356        continue;
    357      }
    358    }
    359 
    360    nsAutoString path;
    361    if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
    362      continue;
    363    }
    364 
    365    FileOrDirectoryPath element;
    366    element.mPath = path;
    367    element.mType = isDir ? FileOrDirectoryPath::eDirectoryPath
    368                          : FileOrDirectoryPath::eFilePath;
    369 
    370    if (!mTargetData.AppendElement(element, fallible)) {
    371      return NS_ERROR_OUT_OF_MEMORY;
    372    }
    373  }
    374  return NS_OK;
    375 }
    376 
    377 nsresult GetDirectoryListingTaskParent::MainThreadWork() {
    378  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
    379  MOZ_ASSERT(NS_IsMainThread(), "Only call on main-thread!");
    380 
    381  nsresult rv;
    382  nsCOMPtr<nsIMIMEService> mimeService =
    383      do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
    384  if (NS_WARN_IF(NS_FAILED(rv))) {
    385    return rv;
    386  }
    387 
    388  for (unsigned i = 0; i < mTargetData.Length(); i++) {
    389    if (mTargetData[i].mType != FileOrDirectoryPath::eFilePath) {
    390      continue;
    391    }
    392 
    393    nsCOMPtr<nsIFile> file;
    394    nsresult rv = NS_NewLocalFile(mTargetData[i].mPath, getter_AddRefs(file));
    395    if (NS_WARN_IF(NS_FAILED(rv))) {
    396      continue;
    397    }
    398 
    399    nsAutoCString mimeType;
    400    rv = mimeService->GetTypeFromFile(file, mimeType);
    401    if (NS_FAILED(rv)) {
    402      mimeType.Truncate();
    403    }
    404 
    405    AppendUTF8toUTF16(mimeType, mTargetData[i].mContentType);
    406  }
    407 
    408  return NS_OK;
    409 }
    410 
    411 bool GetDirectoryListingTaskParent::MainThreadNeeded() const {
    412  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
    413 
    414  // We need to go to the main-thread only if we have files in the list of
    415  // target.
    416  for (const FileOrDirectoryPath& data : mTargetData) {
    417    if (data.mType == FileOrDirectoryPath::eFilePath) {
    418      return true;
    419    }
    420  }
    421 
    422  return false;
    423 }
    424 
    425 nsresult GetDirectoryListingTaskParent::GetTargetPath(nsAString& aPath) const {
    426  return mTargetPath->GetPath(aPath);
    427 }
    428 
    429 }  // namespace mozilla::dom