// Copyright (c) 2011 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 "chrome/browser/plugin_download_helper.h"
#if defined(OS_WIN)
#include <windows.h>
#include "base/file_util.h"
#include "chrome/browser/net/url_request_tracking.h"
#include "net/base/io_buffer.h"
PluginDownloadUrlHelper::PluginDownloadUrlHelper(
const std::string& download_url,
int source_child_unique_id,
gfx::NativeWindow caller_window,
PluginDownloadUrlHelper::DownloadDelegate* delegate)
: download_file_request_(NULL),
download_file_buffer_(new net::IOBuffer(kDownloadFileBufferSize)),
download_file_caller_window_(caller_window),
download_url_(download_url),
download_source_child_unique_id_(source_child_unique_id),
delegate_(delegate) {
memset(download_file_buffer_->data(), 0, kDownloadFileBufferSize);
download_file_.reset(new net::FileStream());
}
PluginDownloadUrlHelper::~PluginDownloadUrlHelper() {
if (download_file_request_) {
delete download_file_request_;
download_file_request_ = NULL;
}
}
void PluginDownloadUrlHelper::InitiateDownload(
net::URLRequestContext* request_context) {
download_file_request_ = new net::URLRequest(GURL(download_url_), this);
chrome_browser_net::SetOriginPIDForRequest(
download_source_child_unique_id_, download_file_request_);
download_file_request_->set_context(request_context);
download_file_request_->Start();
}
void PluginDownloadUrlHelper::OnAuthRequired(
net::URLRequest* request,
net::AuthChallengeInfo* auth_info) {
net::URLRequest::Delegate::OnAuthRequired(request, auth_info);
DownloadCompletedHelper(false);
}
void PluginDownloadUrlHelper::OnSSLCertificateError(
net::URLRequest* request,
int cert_error,
net::X509Certificate* cert) {
net::URLRequest::Delegate::OnSSLCertificateError(request, cert_error, cert);
DownloadCompletedHelper(false);
}
void PluginDownloadUrlHelper::OnResponseStarted(net::URLRequest* request) {
if (!download_file_->IsOpen()) {
// This is safe because once the temp file has been safely created, an
// attacker can't drop a symlink etc into place.
file_util::CreateTemporaryFile(&download_file_path_);
download_file_->Open(download_file_path_,
base::PLATFORM_FILE_CREATE_ALWAYS |
base::PLATFORM_FILE_READ | base::PLATFORM_FILE_WRITE);
if (!download_file_->IsOpen()) {
NOTREACHED();
OnDownloadCompleted(request);
return;
}
}
if (!request->status().is_success()) {
OnDownloadCompleted(request);
} else {
// Initiate a read.
int bytes_read = 0;
if (!request->Read(download_file_buffer_, kDownloadFileBufferSize,
&bytes_read)) {
// If the error is not an IO pending, then we're done
// reading.
if (!request->status().is_io_pending()) {
OnDownloadCompleted(request);
}
} else if (bytes_read == 0) {
OnDownloadCompleted(request);
} else {
OnReadCompleted(request, bytes_read);
}
}
}
void PluginDownloadUrlHelper::OnReadCompleted(net::URLRequest* request,
int bytes_read) {
DCHECK(download_file_->IsOpen());
if (bytes_read == 0) {
OnDownloadCompleted(request);
return;
}
int request_bytes_read = bytes_read;
while (request->status().is_success()) {
int bytes_written = download_file_->Write(download_file_buffer_->data(),
request_bytes_read, NULL);
DCHECK((bytes_written < 0) || (bytes_written == request_bytes_read));
if ((bytes_written < 0) || (bytes_written != request_bytes_read)) {
DownloadCompletedHelper(false);
break;
}
// Start reading
request_bytes_read = 0;
if (!request->Read(download_file_buffer_, kDownloadFileBufferSize,
&request_bytes_read)) {
if (!request->status().is_io_pending()) {
// If the error is not an IO pending, then we're done
// reading.
OnDownloadCompleted(request);
}
break;
} else if (request_bytes_read == 0) {
OnDownloadCompleted(request);
break;
}
}
}
void PluginDownloadUrlHelper::OnDownloadCompleted(net::URLRequest* request) {
bool success = true;
if (!request->status().is_success()) {
success = false;
} else if (!download_file_->IsOpen()) {
success = false;
}
DownloadCompletedHelper(success);
}
void PluginDownloadUrlHelper::DownloadCompletedHelper(bool success) {
if (download_file_->IsOpen()) {
download_file_.reset();
}
if (success) {
FilePath new_download_file_path =
download_file_path_.DirName().AppendASCII(
download_file_request_->url().ExtractFileName());
file_util::Delete(new_download_file_path, false);
if (!file_util::ReplaceFileW(download_file_path_,
new_download_file_path)) {
DLOG(ERROR) << "Failed to rename file:"
<< download_file_path_.value()
<< " to file:"
<< new_download_file_path.value();
} else {
download_file_path_ = new_download_file_path;
}
}
if (delegate_) {
delegate_->OnDownloadCompleted(download_file_path_, success);
} else {
std::wstring path = download_file_path_.value();
COPYDATASTRUCT download_file_data = {0};
download_file_data.cbData =
static_cast<unsigned long>((path.length() + 1) * sizeof(wchar_t));
download_file_data.lpData = const_cast<wchar_t *>(path.c_str());
download_file_data.dwData = success;
if (::IsWindow(download_file_caller_window_)) {
::SendMessage(download_file_caller_window_, WM_COPYDATA, NULL,
reinterpret_cast<LPARAM>(&download_file_data));
}
}
// Don't access any members after this.
delete this;
}
#endif // OS_WIN