| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/url_request/url_request_file_dir_job.h" |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "net/base/directory_listing.h" |
| #include "net/base/io_buffer.h" |
| #include "net/url_request/url_request_status.h" |
| #include "url/gurl.h" |
| |
| #if defined(OS_POSIX) || defined(OS_FUCHSIA) |
| #include <sys/stat.h> |
| |
| #include "starboard/memory.h" |
| #include "starboard/types.h" |
| #endif |
| |
| namespace net { |
| |
| URLRequestFileDirJob::URLRequestFileDirJob(URLRequest* request, |
| NetworkDelegate* network_delegate, |
| const base::FilePath& dir_path) |
| : URLRequestJob(request, network_delegate), |
| lister_(dir_path, this), |
| dir_path_(dir_path), |
| canceled_(false), |
| list_complete_(false), |
| wrote_header_(false), |
| read_pending_(false), |
| read_buffer_length_(0), |
| weak_factory_(this) {} |
| |
| void URLRequestFileDirJob::StartAsync() { |
| base::PostTaskWithTraitsAndReplyWithResult( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::Bind(&base::MakeAbsoluteFilePath, dir_path_), |
| base::Bind(&URLRequestFileDirJob::DidMakeAbsolutePath, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void URLRequestFileDirJob::Start() { |
| // Start reading asynchronously so that all error reporting and data |
| // callbacks happen as they would for network requests. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::Bind(&URLRequestFileDirJob::StartAsync, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void URLRequestFileDirJob::Kill() { |
| if (canceled_) |
| return; |
| |
| canceled_ = true; |
| |
| if (!list_complete_) |
| lister_.Cancel(); |
| |
| URLRequestJob::Kill(); |
| |
| weak_factory_.InvalidateWeakPtrs(); |
| } |
| |
| int URLRequestFileDirJob::ReadRawData(IOBuffer* buf, int buf_size) { |
| int result = ReadBuffer(buf->data(), buf_size); |
| if (result == ERR_IO_PENDING) { |
| // We are waiting for more data |
| read_pending_ = true; |
| read_buffer_ = buf; |
| read_buffer_length_ = buf_size; |
| return ERR_IO_PENDING; |
| } |
| |
| return result; |
| } |
| |
| bool URLRequestFileDirJob::GetMimeType(std::string* mime_type) const { |
| *mime_type = "text/html"; |
| return true; |
| } |
| |
| bool URLRequestFileDirJob::GetCharset(std::string* charset) { |
| // All the filenames are converted to UTF-8 before being added. |
| *charset = "utf-8"; |
| return true; |
| } |
| |
| void URLRequestFileDirJob::OnListFile( |
| const DirectoryLister::DirectoryListerData& data) { |
| // We wait to write out the header until we get the first file, so that we |
| // can catch errors from DirectoryLister and show an error page. |
| if (!wrote_header_) { |
| wrote_header_ = true; |
| |
| #if defined(OS_WIN) |
| const base::string16& title = dir_path_.value(); |
| #elif defined(OS_POSIX) || defined(OS_FUCHSIA) || defined(STARBOARD) |
| // TODO(jungshik): Add SysNativeMBToUTF16 to sys_string_conversions. |
| // On Mac, need to add NFKC->NFC conversion either here or in file_path. |
| // On Linux, the file system encoding is not defined, but we assume that |
| // SysNativeMBToWide takes care of it at least for now. We can try something |
| // more sophisticated if necessary later. |
| const base::string16& title = base::WideToUTF16( |
| base::SysNativeMBToWide(dir_path_.value())); |
| #endif |
| data_.append(GetDirectoryListingHeader(title)); |
| |
| // If this isn't top level directory, add a link to the parent directory. |
| // To figure this out, first normalize |dir_path_| by stripping it of |
| // trailing separators. Then compare the resulting |stripped_dir_path| to |
| // its DirName(). For the top level directory, e.g. "/" or "c:\\", the |
| // normalized path is equal to its DirName(). |
| base::FilePath stripped_dir_path = dir_path_.StripTrailingSeparators(); |
| if (stripped_dir_path != stripped_dir_path.DirName()) { |
| data_.append(GetParentDirectoryLink()); |
| } |
| } |
| |
| // Skip the current and parent directory entries in the listing. |
| // GetParentDirectoryLink() takes care of them. |
| base::FilePath filename = data.info.GetName(); |
| if (filename.value() != base::FilePath::kCurrentDirectory && |
| filename.value() != base::FilePath::kParentDirectory) { |
| #if defined(OS_WIN) |
| std::string raw_bytes; // Empty on Windows means UTF-8 encoded name. |
| #elif defined(OS_POSIX) || defined(OS_FUCHSIA) || defined(STARBOARD) |
| // TODO(jungshik): The same issue as for the directory name. |
| const std::string& raw_bytes = filename.value(); |
| #endif |
| data_.append(GetDirectoryListingEntry( |
| filename.LossyDisplayName(), raw_bytes, data.info.IsDirectory(), |
| data.info.GetSize(), data.info.GetLastModifiedTime())); |
| } |
| |
| // TODO(darin): coalesce more? |
| CompleteRead(OK); |
| } |
| |
| void URLRequestFileDirJob::OnListDone(int error) { |
| DCHECK(!canceled_); |
| DCHECK_LE(error, OK); |
| |
| list_complete_ = true; |
| list_complete_result_ = static_cast<Error>(error); |
| CompleteRead(list_complete_result_); |
| } |
| |
| URLRequestFileDirJob::~URLRequestFileDirJob() = default; |
| |
| void URLRequestFileDirJob::DidMakeAbsolutePath( |
| const base::FilePath& absolute_path) { |
| if (network_delegate() && !network_delegate()->CanAccessFile( |
| *request(), dir_path_, absolute_path)) { |
| NotifyStartError(URLRequestStatus::FromError(ERR_ACCESS_DENIED)); |
| return; |
| } |
| |
| lister_.Start(); |
| NotifyHeadersComplete(); |
| } |
| |
| void URLRequestFileDirJob::CompleteRead(Error error) { |
| DCHECK_LE(error, OK); |
| DCHECK_NE(error, ERR_IO_PENDING); |
| |
| // Do nothing if there is no read pending. |
| if (!read_pending_) |
| return; |
| |
| int result = error; |
| if (error == OK) { |
| result = ReadBuffer(read_buffer_->data(), read_buffer_length_); |
| if (result >= 0) { |
| // We completed the read, so reset the read buffer. |
| read_buffer_ = nullptr; |
| read_buffer_length_ = 0; |
| } else { |
| NOTREACHED(); |
| // TODO: Better error code. |
| result = ERR_FAILED; |
| } |
| } |
| |
| read_pending_ = false; |
| ReadRawDataComplete(result); |
| } |
| |
| int URLRequestFileDirJob::ReadBuffer(char* buf, int buf_size) { |
| int count = std::min(buf_size, static_cast<int>(data_.size())); |
| if (count) { |
| memcpy(buf, &data_[0], count); |
| data_.erase(0, count); |
| return count; |
| } |
| if (list_complete_) { |
| // EOF |
| return list_complete_result_; |
| } |
| return ERR_IO_PENDING; |
| } |
| |
| } // namespace net |