Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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 "chrome/browser/plugin_download_helper.h"
      6 
      7 #if defined(OS_WIN)
      8 #include <windows.h>
      9 
     10 #include "base/file_util.h"
     11 #include "chrome/browser/net/url_request_tracking.h"
     12 #include "net/base/io_buffer.h"
     13 
     14 PluginDownloadUrlHelper::PluginDownloadUrlHelper(
     15     const std::string& download_url,
     16     int source_child_unique_id,
     17     gfx::NativeWindow caller_window,
     18     PluginDownloadUrlHelper::DownloadDelegate* delegate)
     19     : download_file_request_(NULL),
     20       download_file_buffer_(new net::IOBuffer(kDownloadFileBufferSize)),
     21       download_file_caller_window_(caller_window),
     22       download_url_(download_url),
     23       download_source_child_unique_id_(source_child_unique_id),
     24       delegate_(delegate) {
     25   memset(download_file_buffer_->data(), 0, kDownloadFileBufferSize);
     26   download_file_.reset(new net::FileStream());
     27 }
     28 
     29 PluginDownloadUrlHelper::~PluginDownloadUrlHelper() {
     30   if (download_file_request_) {
     31     delete download_file_request_;
     32     download_file_request_ = NULL;
     33   }
     34 }
     35 
     36 void PluginDownloadUrlHelper::InitiateDownload(
     37     net::URLRequestContext* request_context) {
     38   download_file_request_ = new net::URLRequest(GURL(download_url_), this);
     39   chrome_browser_net::SetOriginPIDForRequest(
     40       download_source_child_unique_id_, download_file_request_);
     41   download_file_request_->set_context(request_context);
     42   download_file_request_->Start();
     43 }
     44 
     45 void PluginDownloadUrlHelper::OnAuthRequired(
     46     net::URLRequest* request,
     47     net::AuthChallengeInfo* auth_info) {
     48   net::URLRequest::Delegate::OnAuthRequired(request, auth_info);
     49   DownloadCompletedHelper(false);
     50 }
     51 
     52 void PluginDownloadUrlHelper::OnSSLCertificateError(
     53     net::URLRequest* request,
     54     int cert_error,
     55     net::X509Certificate* cert) {
     56   net::URLRequest::Delegate::OnSSLCertificateError(request, cert_error, cert);
     57   DownloadCompletedHelper(false);
     58 }
     59 
     60 void PluginDownloadUrlHelper::OnResponseStarted(net::URLRequest* request) {
     61   if (!download_file_->IsOpen()) {
     62     // This is safe because once the temp file has been safely created, an
     63     // attacker can't drop a symlink etc into place.
     64     file_util::CreateTemporaryFile(&download_file_path_);
     65     download_file_->Open(download_file_path_,
     66                          base::PLATFORM_FILE_CREATE_ALWAYS |
     67                          base::PLATFORM_FILE_READ | base::PLATFORM_FILE_WRITE);
     68     if (!download_file_->IsOpen()) {
     69       NOTREACHED();
     70       OnDownloadCompleted(request);
     71       return;
     72     }
     73   }
     74   if (!request->status().is_success()) {
     75     OnDownloadCompleted(request);
     76   } else {
     77     // Initiate a read.
     78     int bytes_read = 0;
     79     if (!request->Read(download_file_buffer_, kDownloadFileBufferSize,
     80                        &bytes_read)) {
     81       // If the error is not an IO pending, then we're done
     82       // reading.
     83       if (!request->status().is_io_pending()) {
     84         OnDownloadCompleted(request);
     85       }
     86     } else if (bytes_read == 0) {
     87       OnDownloadCompleted(request);
     88     } else {
     89       OnReadCompleted(request, bytes_read);
     90     }
     91   }
     92 }
     93 
     94 void PluginDownloadUrlHelper::OnReadCompleted(net::URLRequest* request,
     95                                               int bytes_read) {
     96   DCHECK(download_file_->IsOpen());
     97 
     98   if (bytes_read == 0) {
     99     OnDownloadCompleted(request);
    100     return;
    101   }
    102 
    103   int request_bytes_read = bytes_read;
    104 
    105   while (request->status().is_success()) {
    106     int bytes_written = download_file_->Write(download_file_buffer_->data(),
    107         request_bytes_read, NULL);
    108     DCHECK((bytes_written < 0) || (bytes_written == request_bytes_read));
    109 
    110     if ((bytes_written < 0) || (bytes_written != request_bytes_read)) {
    111       DownloadCompletedHelper(false);
    112       break;
    113     }
    114 
    115     // Start reading
    116     request_bytes_read = 0;
    117     if (!request->Read(download_file_buffer_, kDownloadFileBufferSize,
    118                        &request_bytes_read)) {
    119       if (!request->status().is_io_pending()) {
    120         // If the error is not an IO pending, then we're done
    121         // reading.
    122         OnDownloadCompleted(request);
    123       }
    124       break;
    125     } else if (request_bytes_read == 0) {
    126       OnDownloadCompleted(request);
    127       break;
    128     }
    129   }
    130 }
    131 
    132 void PluginDownloadUrlHelper::OnDownloadCompleted(net::URLRequest* request) {
    133   bool success = true;
    134   if (!request->status().is_success()) {
    135     success = false;
    136   } else if (!download_file_->IsOpen()) {
    137     success = false;
    138   }
    139 
    140   DownloadCompletedHelper(success);
    141 }
    142 
    143 void PluginDownloadUrlHelper::DownloadCompletedHelper(bool success) {
    144   if (download_file_->IsOpen()) {
    145       download_file_.reset();
    146   }
    147 
    148   if (success) {
    149     FilePath new_download_file_path =
    150       download_file_path_.DirName().AppendASCII(
    151           download_file_request_->url().ExtractFileName());
    152 
    153     file_util::Delete(new_download_file_path, false);
    154 
    155     if (!file_util::ReplaceFileW(download_file_path_,
    156                                  new_download_file_path)) {
    157       DLOG(ERROR) << "Failed to rename file:"
    158                   << download_file_path_.value()
    159                   << " to file:"
    160                   << new_download_file_path.value();
    161     } else {
    162       download_file_path_ = new_download_file_path;
    163     }
    164   }
    165 
    166   if (delegate_) {
    167     delegate_->OnDownloadCompleted(download_file_path_, success);
    168   } else {
    169     std::wstring path = download_file_path_.value();
    170     COPYDATASTRUCT download_file_data = {0};
    171     download_file_data.cbData =
    172         static_cast<unsigned long>((path.length() + 1) * sizeof(wchar_t));
    173     download_file_data.lpData = const_cast<wchar_t *>(path.c_str());
    174     download_file_data.dwData = success;
    175 
    176     if (::IsWindow(download_file_caller_window_)) {
    177       ::SendMessage(download_file_caller_window_, WM_COPYDATA, NULL,
    178                     reinterpret_cast<LPARAM>(&download_file_data));
    179     }
    180   }
    181 
    182   // Don't access any members after this.
    183   delete this;
    184 }
    185 
    186 #endif  // OS_WIN
    187