1// Copyright 2013 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 "base/files/file_util.h"
6#include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
7#include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
8#include "chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h"
9#include "content/public/browser/browser_thread.h"
10#include "net/url_request/url_fetcher.h"
11
12namespace extensions {
13namespace image_writer {
14
15using content::BrowserThread;
16
17WriteFromUrlOperation::WriteFromUrlOperation(
18    base::WeakPtr<OperationManager> manager,
19    const ExtensionId& extension_id,
20    net::URLRequestContextGetter* request_context,
21    GURL url,
22    const std::string& hash,
23    const std::string& device_path)
24    : Operation(manager, extension_id, device_path),
25      request_context_(request_context),
26      url_(url),
27      hash_(hash),
28      download_continuation_() {}
29
30WriteFromUrlOperation::~WriteFromUrlOperation() {
31}
32
33void WriteFromUrlOperation::StartImpl() {
34  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
35
36  GetDownloadTarget(base::Bind(
37      &WriteFromUrlOperation::Download,
38      this,
39      base::Bind(
40          &WriteFromUrlOperation::VerifyDownload,
41          this,
42          base::Bind(
43              &WriteFromUrlOperation::Unzip,
44              this,
45              base::Bind(&WriteFromUrlOperation::Write,
46                         this,
47                         base::Bind(&WriteFromUrlOperation::VerifyWrite,
48                                    this,
49                                    base::Bind(&WriteFromUrlOperation::Finish,
50                                               this)))))));
51}
52
53void WriteFromUrlOperation::GetDownloadTarget(
54    const base::Closure& continuation) {
55  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
56  if (IsCancelled()) {
57    return;
58  }
59
60  if (url_.ExtractFileName() == "") {
61    if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &image_path_)) {
62      Error(error::kTempFileError);
63      return;
64    }
65  } else {
66    base::FilePath file_name =
67        base::FilePath::FromUTF8Unsafe(url_.ExtractFileName());
68    image_path_ = temp_dir_.path().Append(file_name);
69  }
70
71  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
72}
73
74void WriteFromUrlOperation::Download(const base::Closure& continuation) {
75  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
76
77  if (IsCancelled()) {
78    return;
79  }
80
81  download_continuation_ = continuation;
82
83  SetStage(image_writer_api::STAGE_DOWNLOAD);
84
85  // Store the URL fetcher on this object so that it is destroyed before this
86  // object is.
87  url_fetcher_.reset(net::URLFetcher::Create(url_, net::URLFetcher::GET, this));
88
89  url_fetcher_->SetRequestContext(request_context_);
90  url_fetcher_->SaveResponseToFileAtPath(
91      image_path_,
92      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
93
94  AddCleanUpFunction(
95      base::Bind(&WriteFromUrlOperation::DestroyUrlFetcher, this));
96
97  url_fetcher_->Start();
98}
99
100void WriteFromUrlOperation::DestroyUrlFetcher() { url_fetcher_.reset(); }
101
102void WriteFromUrlOperation::OnURLFetchUploadProgress(
103    const net::URLFetcher* source,
104    int64 current,
105    int64 total) {
106  // No-op
107}
108
109void WriteFromUrlOperation::OnURLFetchDownloadProgress(
110    const net::URLFetcher* source,
111    int64 current,
112    int64 total) {
113  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
114
115  if (IsCancelled()) {
116    url_fetcher_.reset(NULL);
117  }
118
119  int progress = (kProgressComplete * current) / total;
120
121  SetProgress(progress);
122}
123
124void WriteFromUrlOperation::OnURLFetchComplete(const net::URLFetcher* source) {
125  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
126
127  if (source->GetStatus().is_success() && source->GetResponseCode() == 200) {
128    SetProgress(kProgressComplete);
129
130    download_continuation_.Run();
131
132    // Remove the reference to ourselves in this closure.
133    download_continuation_ = base::Closure();
134  } else {
135    Error(error::kDownloadInterrupted);
136  }
137}
138
139void WriteFromUrlOperation::VerifyDownload(const base::Closure& continuation) {
140  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
141
142  if (IsCancelled()) {
143    return;
144  }
145
146  // Skip verify if no hash.
147  if (hash_.empty()) {
148    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
149    return;
150  }
151
152  SetStage(image_writer_api::STAGE_VERIFYDOWNLOAD);
153
154  GetMD5SumOfFile(
155      image_path_,
156      0,
157      0,
158      kProgressComplete,
159      base::Bind(
160          &WriteFromUrlOperation::VerifyDownloadCompare, this, continuation));
161}
162
163void WriteFromUrlOperation::VerifyDownloadCompare(
164    const base::Closure& continuation,
165    const std::string& download_hash) {
166  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
167  if (download_hash != hash_) {
168    Error(error::kDownloadHashError);
169    return;
170  }
171
172  BrowserThread::PostTask(
173      BrowserThread::FILE,
174      FROM_HERE,
175      base::Bind(
176          &WriteFromUrlOperation::VerifyDownloadComplete, this, continuation));
177}
178
179void WriteFromUrlOperation::VerifyDownloadComplete(
180    const base::Closure& continuation) {
181  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
182  if (IsCancelled()) {
183    return;
184  }
185
186  SetProgress(kProgressComplete);
187  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
188}
189
190} // namespace image_writer
191} // namespace extensions
192