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 "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
6
7#include <string>
8
9#include "base/files/file_path.h"
10#include "base/message_loop/message_loop.h"
11#include "chrome/browser/chromeos/drive/drive.pb.h"
12#include "chrome/browser/chromeos/drive/drive_integration_service.h"
13#include "chrome/browser/chromeos/drive/file_errors.h"
14#include "chrome/browser/chromeos/drive/file_system_interface.h"
15#include "chrome/browser/chromeos/drive/file_system_util.h"
16#include "chrome/browser/chromeos/file_manager/app_id.h"
17#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
18#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
19#include "chrome/browser/chromeos/file_manager/path_util.h"
20#include "chrome/browser/chromeos/file_manager/snapshot_manager.h"
21#include "chrome/browser/chromeos/file_manager/volume_manager.h"
22#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/common/extensions/api/file_manager_private.h"
25#include "content/public/browser/child_process_security_policy.h"
26#include "storage/browser/fileapi/file_system_context.h"
27#include "storage/browser/fileapi/file_system_url.h"
28#include "ui/shell_dialogs/selected_file_info.h"
29
30namespace file_manager_private = extensions::api::file_manager_private;
31
32namespace file_manager {
33namespace util {
34namespace {
35
36// The struct is used for GetSelectedFileInfo().
37struct GetSelectedFileInfoParams {
38  GetSelectedFileInfoLocalPathOption local_path_option;
39  GetSelectedFileInfoCallback callback;
40  std::vector<base::FilePath> file_paths;
41  std::vector<ui::SelectedFileInfo> selected_files;
42};
43
44// The callback type for GetFileNativeLocalPathFor{Opening,Saving}. It receives
45// the resolved local path when successful, and receives empty path for failure.
46typedef base::Callback<void(const base::FilePath&)> LocalPathCallback;
47
48// Converts a callback from Drive file system to LocalPathCallback.
49void OnDriveGetFile(const base::FilePath& path,
50                    const LocalPathCallback& callback,
51                    drive::FileError error,
52                    const base::FilePath& local_file_path,
53                    scoped_ptr<drive::ResourceEntry> entry) {
54  if (error != drive::FILE_ERROR_OK)
55    DLOG(ERROR) << "Failed to get " << path.value() << " with: " << error;
56  callback.Run(local_file_path);
57}
58
59// Gets a resolved local file path of a non native |path| for file opening.
60void GetFileNativeLocalPathForOpening(Profile* profile,
61                                      const base::FilePath& path,
62                                      const LocalPathCallback& callback) {
63  if (drive::util::IsUnderDriveMountPoint(path)) {
64    drive::FileSystemInterface* file_system =
65        drive::util::GetFileSystemByProfile(profile);
66    if (!file_system) {
67      DLOG(ERROR) << "Drive file selected while disabled: " << path.value();
68      callback.Run(base::FilePath());
69      return;
70    }
71    file_system->GetFile(drive::util::ExtractDrivePath(path),
72                         base::Bind(&OnDriveGetFile, path, callback));
73    return;
74  }
75
76  VolumeManager::Get(profile)->snapshot_manager()->CreateManagedSnapshot(
77      path, callback);
78}
79
80// Gets a resolved local file path of a non native |path| for file saving.
81void GetFileNativeLocalPathForSaving(Profile* profile,
82                                     const base::FilePath& path,
83                                     const LocalPathCallback& callback) {
84  if (drive::util::IsUnderDriveMountPoint(path)) {
85    drive::FileSystemInterface* file_system =
86        drive::util::GetFileSystemByProfile(profile);
87    if (!file_system) {
88      DLOG(ERROR) << "Drive file selected while disabled: " << path.value();
89      callback.Run(base::FilePath());
90      return;
91    }
92    file_system->GetFileForSaving(drive::util::ExtractDrivePath(path),
93                                  base::Bind(&OnDriveGetFile, path, callback));
94    return;
95  }
96
97  // TODO(kinaba): For now, the only writable non-local volume is Drive.
98  NOTREACHED();
99  callback.Run(base::FilePath());
100}
101
102// Forward declarations of helper functions for GetSelectedFileInfo().
103void ContinueGetSelectedFileInfo(Profile* profile,
104                                 scoped_ptr<GetSelectedFileInfoParams> params,
105                                 const base::FilePath& local_file_path);
106
107// Part of GetSelectedFileInfo().
108void GetSelectedFileInfoInternal(Profile* profile,
109                                 scoped_ptr<GetSelectedFileInfoParams> params) {
110  DCHECK(profile);
111
112  for (size_t i = params->selected_files.size();
113       i < params->file_paths.size(); ++i) {
114    const base::FilePath& file_path = params->file_paths[i];
115
116    if (file_manager::util::IsUnderNonNativeLocalPath(profile, file_path)) {
117      // When the caller of the select file dialog wants local file paths, and
118      // the selected path does not point to a native local path (e.g., Drive,
119      // MTP, or provided file system), we should resolve the path.
120      switch (params->local_path_option) {
121        case NO_LOCAL_PATH_RESOLUTION:
122          break;  // No special handling needed.
123        case NEED_LOCAL_PATH_FOR_OPENING:
124          GetFileNativeLocalPathForOpening(
125              profile,
126              file_path,
127              base::Bind(&ContinueGetSelectedFileInfo,
128                         profile,
129                         base::Passed(&params)));
130          return;  // Remaining work is done in ContinueGetSelectedFileInfo.
131        case NEED_LOCAL_PATH_FOR_SAVING:
132          GetFileNativeLocalPathForSaving(
133              profile,
134              file_path,
135              base::Bind(&ContinueGetSelectedFileInfo,
136                         profile,
137                         base::Passed(&params)));
138          return;  // Remaining work is done in ContinueGetSelectedFileInfo.
139      }
140    }
141    params->selected_files.push_back(
142        ui::SelectedFileInfo(file_path, base::FilePath()));
143  }
144  params->callback.Run(params->selected_files);
145}
146
147// Part of GetSelectedFileInfo().
148void ContinueGetSelectedFileInfo(Profile* profile,
149                                 scoped_ptr<GetSelectedFileInfoParams> params,
150                                 const base::FilePath& local_path) {
151  if (local_path.empty()) {
152    params->callback.Run(std::vector<ui::SelectedFileInfo>());
153    return;
154  }
155  const int index = params->selected_files.size();
156  const base::FilePath& file_path = params->file_paths[index];
157  params->selected_files.push_back(ui::SelectedFileInfo(file_path, local_path));
158  GetSelectedFileInfoInternal(profile, params.Pass());
159}
160
161}  // namespace
162
163void VolumeInfoToVolumeMetadata(
164    Profile* profile,
165    const VolumeInfo& volume_info,
166    file_manager_private::VolumeMetadata* volume_metadata) {
167  DCHECK(volume_metadata);
168
169  volume_metadata->volume_id = volume_info.volume_id;
170
171  // TODO(kinaba): fill appropriate information once multi-profile support is
172  // implemented.
173  volume_metadata->profile.display_name = profile->GetProfileName();
174  volume_metadata->profile.is_current_profile = true;
175
176  if (!volume_info.source_path.empty()) {
177    volume_metadata->source_path.reset(
178        new std::string(volume_info.source_path.AsUTF8Unsafe()));
179  }
180
181  if (volume_info.type == VOLUME_TYPE_PROVIDED) {
182    volume_metadata->extension_id.reset(
183        new std::string(volume_info.extension_id));
184
185    volume_metadata->file_system_id.reset(
186        new std::string(volume_info.file_system_id));
187  }
188
189  volume_metadata->volume_label.reset(
190      new std::string(volume_info.volume_label));
191
192  switch (volume_info.type) {
193    case VOLUME_TYPE_GOOGLE_DRIVE:
194      volume_metadata->volume_type =
195          file_manager_private::VOLUME_TYPE_DRIVE;
196      break;
197    case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
198      volume_metadata->volume_type =
199          file_manager_private::VOLUME_TYPE_DOWNLOADS;
200      break;
201    case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
202      volume_metadata->volume_type =
203          file_manager_private::VOLUME_TYPE_REMOVABLE;
204      break;
205    case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
206      volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_ARCHIVE;
207      break;
208    case VOLUME_TYPE_CLOUD_DEVICE:
209      volume_metadata->volume_type =
210          file_manager_private::VOLUME_TYPE_CLOUD_DEVICE;
211      break;
212    case VOLUME_TYPE_PROVIDED:
213      volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_PROVIDED;
214      break;
215    case VOLUME_TYPE_MTP:
216      volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_MTP;
217      break;
218    case VOLUME_TYPE_TESTING:
219      volume_metadata->volume_type =
220          file_manager_private::VOLUME_TYPE_TESTING;
221      break;
222    case NUM_VOLUME_TYPE:
223      NOTREACHED();
224      break;
225  }
226
227  // Fill device_type iff the volume is removable partition.
228  if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
229    switch (volume_info.device_type) {
230      case chromeos::DEVICE_TYPE_UNKNOWN:
231        volume_metadata->device_type =
232            file_manager_private::DEVICE_TYPE_UNKNOWN;
233        break;
234      case chromeos::DEVICE_TYPE_USB:
235        volume_metadata->device_type = file_manager_private::DEVICE_TYPE_USB;
236        break;
237      case chromeos::DEVICE_TYPE_SD:
238        volume_metadata->device_type = file_manager_private::DEVICE_TYPE_SD;
239        break;
240      case chromeos::DEVICE_TYPE_OPTICAL_DISC:
241      case chromeos::DEVICE_TYPE_DVD:
242        volume_metadata->device_type =
243            file_manager_private::DEVICE_TYPE_OPTICAL;
244        break;
245      case chromeos::DEVICE_TYPE_MOBILE:
246        volume_metadata->device_type = file_manager_private::DEVICE_TYPE_MOBILE;
247        break;
248    }
249    volume_metadata->device_path.reset(
250        new std::string(volume_info.system_path_prefix.AsUTF8Unsafe()));
251    volume_metadata->is_parent_device.reset(
252        new bool(volume_info.is_parent));
253  } else {
254    volume_metadata->device_type =
255        file_manager_private::DEVICE_TYPE_NONE;
256  }
257
258  volume_metadata->is_read_only = volume_info.is_read_only;
259
260  switch (volume_info.mount_condition) {
261    case chromeos::disks::MOUNT_CONDITION_NONE:
262      volume_metadata->mount_condition =
263          file_manager_private::MOUNT_CONDITION_NONE;
264      break;
265    case chromeos::disks::MOUNT_CONDITION_UNKNOWN_FILESYSTEM:
266      volume_metadata->mount_condition =
267          file_manager_private::MOUNT_CONDITION_UNKNOWN;
268      break;
269    case chromeos::disks::MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM:
270      volume_metadata->mount_condition =
271          file_manager_private::MOUNT_CONDITION_UNSUPPORTED;
272      break;
273  }
274}
275
276base::FilePath GetLocalPathFromURL(content::RenderViewHost* render_view_host,
277                                   Profile* profile,
278                                   const GURL& url) {
279  DCHECK(render_view_host);
280  DCHECK(profile);
281
282  scoped_refptr<storage::FileSystemContext> file_system_context =
283      util::GetFileSystemContextForRenderViewHost(profile, render_view_host);
284
285  const storage::FileSystemURL filesystem_url(
286      file_system_context->CrackURL(url));
287  base::FilePath path;
288  if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
289    return base::FilePath();
290  return filesystem_url.path();
291}
292
293void GetSelectedFileInfo(content::RenderViewHost* render_view_host,
294                         Profile* profile,
295                         const std::vector<GURL>& file_urls,
296                         GetSelectedFileInfoLocalPathOption local_path_option,
297                         GetSelectedFileInfoCallback callback) {
298  DCHECK(render_view_host);
299  DCHECK(profile);
300
301  scoped_ptr<GetSelectedFileInfoParams> params(new GetSelectedFileInfoParams);
302  params->local_path_option = local_path_option;
303  params->callback = callback;
304
305  for (size_t i = 0; i < file_urls.size(); ++i) {
306    const GURL& file_url = file_urls[i];
307    const base::FilePath path = GetLocalPathFromURL(
308        render_view_host, profile, file_url);
309    if (!path.empty()) {
310      DVLOG(1) << "Selected: file path: " << path.value();
311      params->file_paths.push_back(path);
312    }
313  }
314
315  base::MessageLoop::current()->PostTask(
316      FROM_HERE,
317      base::Bind(&GetSelectedFileInfoInternal, profile, base::Passed(&params)));
318}
319
320void SetupProfileFileAccessPermissions(int render_view_process_id,
321                                       Profile* profile) {
322  const base::FilePath paths[] = {
323    drive::util::GetDriveMountPointPath(profile),
324    util::GetDownloadsFolderForProfile(profile),
325  };
326  for (size_t i = 0; i < arraysize(paths); ++i) {
327    content::ChildProcessSecurityPolicy::GetInstance(
328        )->GrantCreateReadWriteFile(render_view_process_id, paths[i]);
329  }
330}
331
332drive::EventLogger* GetLogger(Profile* profile) {
333  drive::DriveIntegrationService* service =
334      drive::DriveIntegrationServiceFactory::FindForProfile(profile);
335  return service ? service->event_logger() : NULL;
336}
337
338}  // namespace util
339}  // namespace file_manager
340