1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "net/url_request/url_request_file_dir_job.h" 6 7#include "base/bind.h" 8#include "base/compiler_specific.h" 9#include "base/message_loop/message_loop.h" 10#include "base/strings/sys_string_conversions.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/time/time.h" 13#include "net/base/io_buffer.h" 14#include "net/base/net_errors.h" 15#include "net/base/net_util.h" 16#include "net/url_request/url_request_status.h" 17#include "url/gurl.h" 18 19#if defined(OS_POSIX) 20#include <sys/stat.h> 21#endif 22 23namespace net { 24 25URLRequestFileDirJob::URLRequestFileDirJob(URLRequest* request, 26 NetworkDelegate* network_delegate, 27 const base::FilePath& dir_path) 28 : URLRequestJob(request, network_delegate), 29 lister_(dir_path, this), 30 dir_path_(dir_path), 31 canceled_(false), 32 list_complete_(false), 33 wrote_header_(false), 34 read_pending_(false), 35 read_buffer_length_(0), 36 weak_factory_(this) { 37} 38 39void URLRequestFileDirJob::StartAsync() { 40 lister_.Start(); 41 42 NotifyHeadersComplete(); 43} 44 45void URLRequestFileDirJob::Start() { 46 // Start reading asynchronously so that all error reporting and data 47 // callbacks happen as they would for network requests. 48 base::MessageLoop::current()->PostTask( 49 FROM_HERE, 50 base::Bind(&URLRequestFileDirJob::StartAsync, 51 weak_factory_.GetWeakPtr())); 52} 53 54void URLRequestFileDirJob::Kill() { 55 if (canceled_) 56 return; 57 58 canceled_ = true; 59 60 if (!list_complete_) 61 lister_.Cancel(); 62 63 URLRequestJob::Kill(); 64 65 weak_factory_.InvalidateWeakPtrs(); 66} 67 68bool URLRequestFileDirJob::ReadRawData(IOBuffer* buf, int buf_size, 69 int* bytes_read) { 70 DCHECK(bytes_read); 71 *bytes_read = 0; 72 73 if (is_done()) 74 return true; 75 76 if (FillReadBuffer(buf->data(), buf_size, bytes_read)) 77 return true; 78 79 // We are waiting for more data 80 read_pending_ = true; 81 read_buffer_ = buf; 82 read_buffer_length_ = buf_size; 83 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 84 return false; 85} 86 87bool URLRequestFileDirJob::GetMimeType(std::string* mime_type) const { 88 *mime_type = "text/html"; 89 return true; 90} 91 92bool URLRequestFileDirJob::GetCharset(std::string* charset) { 93 // All the filenames are converted to UTF-8 before being added. 94 *charset = "utf-8"; 95 return true; 96} 97 98void URLRequestFileDirJob::OnListFile( 99 const DirectoryLister::DirectoryListerData& data) { 100 // We wait to write out the header until we get the first file, so that we 101 // can catch errors from DirectoryLister and show an error page. 102 if (!wrote_header_) { 103#if defined(OS_WIN) 104 const base::string16& title = dir_path_.value(); 105#elif defined(OS_POSIX) 106 // TODO(jungshik): Add SysNativeMBToUTF16 to sys_string_conversions. 107 // On Mac, need to add NFKC->NFC conversion either here or in file_path. 108 // On Linux, the file system encoding is not defined, but we assume that 109 // SysNativeMBToWide takes care of it at least for now. We can try something 110 // more sophisticated if necessary later. 111 const base::string16& title = base::WideToUTF16( 112 base::SysNativeMBToWide(dir_path_.value())); 113#endif 114 data_.append(GetDirectoryListingHeader(title)); 115 wrote_header_ = true; 116 } 117 118#if defined(OS_WIN) 119 std::string raw_bytes; // Empty on Windows means UTF-8 encoded name. 120#elif defined(OS_POSIX) 121 // TOOD(jungshik): The same issue as for the directory name. 122 base::FilePath filename = data.info.GetName(); 123 const std::string& raw_bytes = filename.value(); 124#endif 125 data_.append(GetDirectoryListingEntry( 126 data.info.GetName().LossyDisplayName(), 127 raw_bytes, 128 data.info.IsDirectory(), 129 data.info.GetSize(), 130 data.info.GetLastModifiedTime())); 131 132 // TODO(darin): coalesce more? 133 CompleteRead(); 134} 135 136void URLRequestFileDirJob::OnListDone(int error) { 137 DCHECK(!canceled_); 138 if (error != OK) { 139 read_pending_ = false; 140 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, error)); 141 } else { 142 list_complete_ = true; 143 CompleteRead(); 144 } 145} 146 147URLRequestFileDirJob::~URLRequestFileDirJob() {} 148 149void URLRequestFileDirJob::CompleteRead() { 150 if (read_pending_) { 151 int bytes_read; 152 if (FillReadBuffer(read_buffer_->data(), read_buffer_length_, 153 &bytes_read)) { 154 // We completed the read, so reset the read buffer. 155 read_pending_ = false; 156 read_buffer_ = NULL; 157 read_buffer_length_ = 0; 158 159 SetStatus(URLRequestStatus()); 160 NotifyReadComplete(bytes_read); 161 } else { 162 NOTREACHED(); 163 // TODO: Better error code. 164 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0)); 165 } 166 } 167} 168 169bool URLRequestFileDirJob::FillReadBuffer(char* buf, int buf_size, 170 int* bytes_read) { 171 DCHECK(bytes_read); 172 173 *bytes_read = 0; 174 175 int count = std::min(buf_size, static_cast<int>(data_.size())); 176 if (count) { 177 memcpy(buf, &data_[0], count); 178 data_.erase(0, count); 179 *bytes_read = count; 180 return true; 181 } else if (list_complete_) { 182 // EOF 183 return true; 184 } 185 return false; 186} 187 188} // namespace net 189