Home | History | Annotate | Download | only in url_request
      1 // Copyright (c) 2010 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/compiler_specific.h"
      8 #include "base/file_util.h"
      9 #include "base/message_loop.h"
     10 #include "base/sys_string_conversions.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "base/time.h"
     13 #include "googleurl/src/gurl.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/net_util.h"
     16 #include "net/url_request/url_request.h"
     17 
     18 #if defined(OS_POSIX)
     19 #include <sys/stat.h>
     20 #endif
     21 
     22 namespace net {
     23 
     24 URLRequestFileDirJob::URLRequestFileDirJob(URLRequest* request,
     25                                            const FilePath& dir_path)
     26     : URLRequestJob(request),
     27       dir_path_(dir_path),
     28       canceled_(false),
     29       list_complete_(false),
     30       wrote_header_(false),
     31       read_pending_(false),
     32       read_buffer_length_(0),
     33       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
     34 }
     35 
     36 void URLRequestFileDirJob::StartAsync() {
     37   DCHECK(!lister_);
     38 
     39   // TODO(willchan): This is stupid.  We should tell |lister_| not to call us
     40   // back.  Fix this stupidity.
     41 
     42   // AddRef so that *this* cannot be destroyed while the lister_
     43   // is trying to feed us data.
     44 
     45   AddRef();
     46   lister_ = new DirectoryLister(dir_path_, this);
     47   lister_->Start();
     48 
     49   NotifyHeadersComplete();
     50 }
     51 
     52 void URLRequestFileDirJob::Start() {
     53   // Start reading asynchronously so that all error reporting and data
     54   // callbacks happen as they would for network requests.
     55   MessageLoop::current()->PostTask(
     56       FROM_HERE,
     57       method_factory_.NewRunnableMethod(
     58           &URLRequestFileDirJob::StartAsync));
     59 }
     60 
     61 void URLRequestFileDirJob::Kill() {
     62   if (canceled_)
     63     return;
     64 
     65   canceled_ = true;
     66 
     67   // Don't call CloseLister or dispatch an error to the URLRequest because
     68   // we want OnListDone to be called to also write the error to the output
     69   // stream. OnListDone will notify the URLRequest at this time.
     70   if (lister_)
     71     lister_->Cancel();
     72 
     73   URLRequestJob::Kill();
     74 
     75   method_factory_.RevokeAll();
     76 }
     77 
     78 bool URLRequestFileDirJob::ReadRawData(IOBuffer* buf, int buf_size,
     79                                        int *bytes_read) {
     80   DCHECK(bytes_read);
     81   *bytes_read = 0;
     82 
     83   if (is_done())
     84     return true;
     85 
     86   if (FillReadBuffer(buf->data(), buf_size, bytes_read))
     87     return true;
     88 
     89   // We are waiting for more data
     90   read_pending_ = true;
     91   read_buffer_ = buf;
     92   read_buffer_length_ = buf_size;
     93   SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
     94   return false;
     95 }
     96 
     97 bool URLRequestFileDirJob::GetMimeType(std::string* mime_type) const {
     98   *mime_type = "text/html";
     99   return true;
    100 }
    101 
    102 bool URLRequestFileDirJob::GetCharset(std::string* charset) {
    103   // All the filenames are converted to UTF-8 before being added.
    104   *charset = "utf-8";
    105   return true;
    106 }
    107 
    108 void URLRequestFileDirJob::OnListFile(
    109     const DirectoryLister::DirectoryListerData& data) {
    110   // We wait to write out the header until we get the first file, so that we
    111   // can catch errors from DirectoryLister and show an error page.
    112   if (!wrote_header_) {
    113 #if defined(OS_WIN)
    114     const string16& title = dir_path_.value();
    115 #elif defined(OS_POSIX)
    116     // TODO(jungshik): Add SysNativeMBToUTF16 to sys_string_conversions.
    117     // On Mac, need to add NFKC->NFC conversion either here or in file_path.
    118     // On Linux, the file system encoding is not defined, but we assume that
    119     // SysNativeMBToWide takes care of it at least for now. We can try something
    120     // more sophisticated if necessary later.
    121     const string16& title = WideToUTF16(
    122         base::SysNativeMBToWide(dir_path_.value()));
    123 #endif
    124     data_.append(GetDirectoryListingHeader(title));
    125     wrote_header_ = true;
    126   }
    127 
    128 #if defined(OS_WIN)
    129   int64 size = (static_cast<unsigned __int64>(data.info.nFileSizeHigh) << 32) |
    130       data.info.nFileSizeLow;
    131 
    132   // Note that we should not convert ftLastWriteTime to the local time because
    133   // ICU's datetime formatting APIs expect time in UTC and take into account
    134   // the timezone before formatting.
    135   data_.append(GetDirectoryListingEntry(
    136       data.info.cFileName, std::string(),
    137       (data.info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false,
    138       size,
    139       base::Time::FromFileTime(data.info.ftLastWriteTime)));
    140 
    141 #elif defined(OS_POSIX)
    142   // TOOD(jungshik): The same issue as for the directory name.
    143   data_.append(GetDirectoryListingEntry(
    144       WideToUTF16(base::SysNativeMBToWide(data.info.filename)),
    145       data.info.filename,
    146       S_ISDIR(data.info.stat.st_mode),
    147       data.info.stat.st_size,
    148       base::Time::FromTimeT(data.info.stat.st_mtime)));
    149 #endif
    150 
    151   // TODO(darin): coalesce more?
    152   CompleteRead();
    153 }
    154 
    155 void URLRequestFileDirJob::OnListDone(int error) {
    156   CloseLister();
    157 
    158   if (canceled_) {
    159     read_pending_ = false;
    160     // No need for NotifyCanceled() since canceled_ is set inside Kill().
    161   } else if (error) {
    162     read_pending_ = false;
    163     NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, error));
    164   } else {
    165     list_complete_ = true;
    166     CompleteRead();
    167   }
    168 
    169   Release();  // The Lister is finished; may delete *this*
    170 }
    171 
    172 URLRequestFileDirJob::~URLRequestFileDirJob() {
    173   DCHECK(read_pending_ == false);
    174   DCHECK(lister_ == NULL);
    175 }
    176 
    177 void URLRequestFileDirJob::CloseLister() {
    178   if (lister_) {
    179     lister_->Cancel();
    180     lister_->set_delegate(NULL);
    181     lister_ = NULL;
    182   }
    183 }
    184 
    185 void URLRequestFileDirJob::CompleteRead() {
    186   if (read_pending_) {
    187     int bytes_read;
    188     if (FillReadBuffer(read_buffer_->data(), read_buffer_length_,
    189                        &bytes_read)) {
    190       // We completed the read, so reset the read buffer.
    191       read_pending_ = false;
    192       read_buffer_ = NULL;
    193       read_buffer_length_ = 0;
    194 
    195       SetStatus(URLRequestStatus());
    196       NotifyReadComplete(bytes_read);
    197     } else {
    198       NOTREACHED();
    199       // TODO: Better error code.
    200       NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0));
    201     }
    202   }
    203 }
    204 
    205 bool URLRequestFileDirJob::FillReadBuffer(char *buf, int buf_size,
    206                                           int *bytes_read) {
    207   DCHECK(bytes_read);
    208 
    209   *bytes_read = 0;
    210 
    211   int count = std::min(buf_size, static_cast<int>(data_.size()));
    212   if (count) {
    213     memcpy(buf, &data_[0], count);
    214     data_.erase(0, count);
    215     *bytes_read = count;
    216     return true;
    217   } else if (list_complete_) {
    218     // EOF
    219     return true;
    220   }
    221   return false;
    222 }
    223 
    224 }  // namespace net
    225