| // 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 |