1 // Copyright (c) 2012 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/file_manager/open_util.h"
6 
7 #include <memory>
8 #include <set>
9 #include <string>
10 #include <vector>
11 
12 #include "base/bind.h"
13 #include "base/check_op.h"
14 #include "base/files/file_path.h"
15 #include "base/metrics/user_metrics.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/chromeos/file_manager/app_id.h"
19 #include "chrome/browser/chromeos/file_manager/file_tasks.h"
20 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
21 #include "chrome/browser/chromeos/file_manager/path_util.h"
22 #include "chrome/browser/chromeos/file_manager/url_util.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "extensions/browser/api/file_handlers/directory_util.h"
26 #include "extensions/browser/api/file_handlers/mime_util.h"
27 #include "extensions/browser/entry_info.h"
28 #include "storage/browser/file_system/file_system_backend.h"
29 #include "storage/browser/file_system/file_system_context.h"
30 #include "storage/browser/file_system/file_system_operation_runner.h"
31 #include "storage/browser/file_system/file_system_url.h"
32 
33 using content::BrowserThread;
34 using storage::FileSystemURL;
35 
36 namespace file_manager {
37 namespace util {
38 namespace {
39 
40 bool shell_operations_allowed = true;
41 
IgnoreFileTaskExecuteResult(extensions::api::file_manager_private::TaskResult result,std::string failure_reason)42 void IgnoreFileTaskExecuteResult(
43     extensions::api::file_manager_private::TaskResult result,
44     std::string failure_reason) {}
45 
46 // Executes the |task| for the file specified by |url|.
ExecuteFileTaskForUrl(Profile * profile,const file_tasks::TaskDescriptor & task,const GURL & url)47 void ExecuteFileTaskForUrl(Profile* profile,
48                            const file_tasks::TaskDescriptor& task,
49                            const GURL& url) {
50   if (!shell_operations_allowed)
51     return;
52   storage::FileSystemContext* file_system_context =
53       GetFileSystemContextForExtensionId(profile, kFileManagerAppId);
54 
55   file_tasks::ExecuteFileTask(
56       profile,
57       GetFileManagerMainPageUrl(),  // Executing task on behalf of the Files
58                                     // app.
59       task, std::vector<FileSystemURL>(1, file_system_context->CrackURL(url)),
60       base::BindOnce(&IgnoreFileTaskExecuteResult));
61 }
62 
63 // Opens the file manager for the specified |url|. Used to implement
64 // internal handlers of special action IDs:
65 //
66 // "open" - Open the file manager for the given folder.
67 // "select" - Open the file manager for the given file. The folder containing
68 //            the file will be opened with the file selected.
OpenFileManagerWithInternalActionId(Profile * profile,const GURL & url,const std::string & action_id)69 void OpenFileManagerWithInternalActionId(Profile* profile,
70                                          const GURL& url,
71                                          const std::string& action_id) {
72   DCHECK(action_id == "open" || action_id == "select");
73   if (!shell_operations_allowed)
74     return;
75   base::RecordAction(base::UserMetricsAction("ShowFileBrowserFullTab"));
76 
77   file_tasks::TaskDescriptor task(
78       kFileManagerAppId, file_tasks::TASK_TYPE_FILE_HANDLER, action_id);
79   ExecuteFileTaskForUrl(profile, task, url);
80 }
81 
OpenFileMimeTypeAfterTasksListed(Profile * profile,const GURL & url,platform_util::OpenOperationCallback callback,std::unique_ptr<std::vector<file_tasks::FullTaskDescriptor>> tasks)82 void OpenFileMimeTypeAfterTasksListed(
83     Profile* profile,
84     const GURL& url,
85     platform_util::OpenOperationCallback callback,
86     std::unique_ptr<std::vector<file_tasks::FullTaskDescriptor>> tasks) {
87   // Select a default handler. If a default handler is not available, select
88   // the first non-generic file handler.
89   const file_tasks::FullTaskDescriptor* chosen_task = nullptr;
90   for (const auto& task : *tasks) {
91     if (!task.is_generic_file_handler()) {
92       if (task.is_default()) {
93         chosen_task = &task;
94         break;
95       }
96       if (!chosen_task)
97         chosen_task = &task;
98     }
99   }
100 
101   if (chosen_task != nullptr) {
102     if (shell_operations_allowed)
103       ExecuteFileTaskForUrl(profile, chosen_task->task_descriptor(), url);
104     std::move(callback).Run(platform_util::OPEN_SUCCEEDED);
105   } else {
106     std::move(callback).Run(
107         platform_util::OPEN_FAILED_NO_HANLDER_FOR_FILE_TYPE);
108   }
109 }
110 
111 // Opens the file with fetched MIME type and calls the callback.
OpenFileWithMimeType(Profile * profile,const base::FilePath & path,const GURL & url,platform_util::OpenOperationCallback callback,const std::string & mime_type)112 void OpenFileWithMimeType(Profile* profile,
113                           const base::FilePath& path,
114                           const GURL& url,
115                           platform_util::OpenOperationCallback callback,
116                           const std::string& mime_type) {
117   std::vector<extensions::EntryInfo> entries;
118   entries.emplace_back(path, mime_type, false);
119 
120   std::vector<GURL> file_urls;
121   file_urls.push_back(url);
122 
123   file_tasks::FindAllTypesOfTasks(
124       profile, entries, file_urls,
125       base::BindOnce(&OpenFileMimeTypeAfterTasksListed, profile, url,
126                      std::move(callback)));
127 }
128 
129 // Opens the file specified by |url| by finding and executing a file task for
130 // the file. Calls |callback| with the result.
OpenFile(Profile * profile,const base::FilePath & path,const GURL & url,platform_util::OpenOperationCallback callback)131 void OpenFile(Profile* profile,
132               const base::FilePath& path,
133               const GURL& url,
134               platform_util::OpenOperationCallback callback) {
135   extensions::app_file_handler_util::GetMimeTypeForLocalPath(
136       profile, path,
137       base::BindOnce(&OpenFileWithMimeType, profile, path, url,
138                      std::move(callback)));
139 }
140 
OpenItemWithMetadata(Profile * profile,const base::FilePath & file_path,const GURL & url,platform_util::OpenItemType expected_type,platform_util::OpenOperationCallback callback,base::File::Error error,const base::File::Info & file_info)141 void OpenItemWithMetadata(Profile* profile,
142                           const base::FilePath& file_path,
143                           const GURL& url,
144                           platform_util::OpenItemType expected_type,
145                           platform_util::OpenOperationCallback callback,
146                           base::File::Error error,
147                           const base::File::Info& file_info) {
148   DCHECK_CURRENTLY_ON(BrowserThread::UI);
149   if (error != base::File::FILE_OK) {
150     std::move(callback).Run(error == base::File::FILE_ERROR_NOT_FOUND
151                                 ? platform_util::OPEN_FAILED_PATH_NOT_FOUND
152                                 : platform_util::OPEN_FAILED_FILE_ERROR);
153     return;
154   }
155 
156   // Note that there exists a TOCTOU race between the time the metadata for
157   // |file_path| was determined and when it is opened based on the metadata.
158   if (expected_type == platform_util::OPEN_FOLDER && file_info.is_directory) {
159     OpenFileManagerWithInternalActionId(profile, url, "open");
160     std::move(callback).Run(platform_util::OPEN_SUCCEEDED);
161     return;
162   }
163 
164   if (expected_type == platform_util::OPEN_FILE && !file_info.is_directory) {
165     OpenFile(profile, file_path, url, std::move(callback));
166     return;
167   }
168 
169   std::move(callback).Run(platform_util::OPEN_FAILED_INVALID_TYPE);
170 }
171 
ShowItemInFolderWithMetadata(Profile * profile,const base::FilePath & file_path,const GURL & url,platform_util::OpenOperationCallback callback,base::File::Error error,const base::File::Info & file_info)172 void ShowItemInFolderWithMetadata(Profile* profile,
173                                   const base::FilePath& file_path,
174                                   const GURL& url,
175                                   platform_util::OpenOperationCallback callback,
176                                   base::File::Error error,
177                                   const base::File::Info& file_info) {
178   DCHECK_CURRENTLY_ON(BrowserThread::UI);
179   if (error != base::File::FILE_OK) {
180     std::move(callback).Run(error == base::File::FILE_ERROR_NOT_FOUND
181                                 ? platform_util::OPEN_FAILED_PATH_NOT_FOUND
182                                 : platform_util::OPEN_FAILED_FILE_ERROR);
183     return;
184   }
185 
186   // This action changes the selection so we do not reuse existing tabs.
187   OpenFileManagerWithInternalActionId(profile, url, "select");
188   std::move(callback).Run(platform_util::OPEN_SUCCEEDED);
189 }
190 
191 }  // namespace
192 
OpenItem(Profile * profile,const base::FilePath & file_path,platform_util::OpenItemType expected_type,platform_util::OpenOperationCallback callback)193 void OpenItem(Profile* profile,
194               const base::FilePath& file_path,
195               platform_util::OpenItemType expected_type,
196               platform_util::OpenOperationCallback callback) {
197   DCHECK_CURRENTLY_ON(BrowserThread::UI);
198 
199   // This is unfortunately necessary as file browser handlers operate on URLs.
200   GURL url;
201   if (!ConvertAbsoluteFilePathToFileSystemUrl(profile, file_path,
202                                               kFileManagerAppId, &url)) {
203     std::move(callback).Run(platform_util::OPEN_FAILED_PATH_NOT_FOUND);
204     return;
205   }
206 
207   GetMetadataForPath(
208       GetFileSystemContextForExtensionId(profile, kFileManagerAppId), file_path,
209       storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
210       base::BindOnce(&OpenItemWithMetadata, profile, file_path, url,
211                      expected_type, std::move(callback)));
212 }
213 
ShowItemInFolder(Profile * profile,const base::FilePath & file_path,platform_util::OpenOperationCallback callback)214 void ShowItemInFolder(Profile* profile,
215                       const base::FilePath& file_path,
216                       platform_util::OpenOperationCallback callback) {
217   DCHECK_CURRENTLY_ON(BrowserThread::UI);
218 
219   GURL url;
220   if (!ConvertAbsoluteFilePathToFileSystemUrl(profile, file_path,
221                                               kFileManagerAppId, &url)) {
222     std::move(callback).Run(platform_util::OPEN_FAILED_PATH_NOT_FOUND);
223     return;
224   }
225 
226   GetMetadataForPath(
227       GetFileSystemContextForExtensionId(profile, kFileManagerAppId), file_path,
228       storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
229       base::BindOnce(&ShowItemInFolderWithMetadata, profile, file_path, url,
230                      std::move(callback)));
231 }
232 
DisableShellOperationsForTesting()233 void DisableShellOperationsForTesting() {
234   shell_operations_allowed = false;
235 }
236 
237 }  // namespace util
238 }  // namespace file_manager
239