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 23 namespace net { 24 25 URLRequestFileDirJob::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 39 void URLRequestFileDirJob::StartAsync() { 40 lister_.Start(); 41 42 NotifyHeadersComplete(); 43 } 44 45 void 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 54 void 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 68 bool 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 87 bool URLRequestFileDirJob::GetMimeType(std::string* mime_type) const { 88 *mime_type = "text/html"; 89 return true; 90 } 91 92 bool 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 98 void 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 136 void 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 147 URLRequestFileDirJob::~URLRequestFileDirJob() {} 148 149 void 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 169 bool 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