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_misc.h"
6
7#include "ash/frame/frame_util.h"
8#include "base/files/file_path.h"
9#include "base/prefs/pref_service.h"
10#include "base/strings/stringprintf.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/chromeos/drive/file_system_util.h"
14#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
15#include "chrome/browser/chromeos/file_manager/app_installer.h"
16#include "chrome/browser/chromeos/file_manager/zip_file_creator.h"
17#include "chrome/browser/chromeos/profiles/profile_helper.h"
18#include "chrome/browser/chromeos/settings/cros_settings.h"
19#include "chrome/browser/devtools/devtools_window.h"
20#include "chrome/browser/drive/event_logger.h"
21#include "chrome/browser/extensions/devtools_util.h"
22#include "chrome/browser/lifetime/application_lifetime.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/profiles/profile_manager.h"
25#include "chrome/browser/profiles/profiles_state.h"
26#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
27#include "chrome/browser/signin/signin_manager_factory.h"
28#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
29#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
30#include "chrome/common/extensions/api/file_manager_private.h"
31#include "chrome/common/pref_names.h"
32#include "components/signin/core/browser/profile_oauth2_token_service.h"
33#include "components/signin/core/browser/signin_manager.h"
34#include "components/user_manager/user_manager.h"
35#include "content/public/browser/render_view_host.h"
36#include "content/public/browser/web_contents.h"
37#include "content/public/common/page_zoom.h"
38#include "extensions/browser/app_window/app_window.h"
39#include "extensions/browser/app_window/app_window_registry.h"
40#include "google_apis/drive/auth_service.h"
41#include "ui/base/webui/web_ui_util.h"
42#include "url/gurl.h"
43
44namespace extensions {
45
46namespace {
47const char kCWSScope[] = "https://www.googleapis.com/auth/chromewebstore";
48const char kGoogleCastApiExtensionId[] = "mafeflapfdfljijmlienjedomfjfmhpd";
49
50// Obtains the current app window.
51AppWindow* GetCurrentAppWindow(ChromeSyncExtensionFunction* function) {
52  AppWindowRegistry* const app_window_registry =
53      AppWindowRegistry::Get(function->GetProfile());
54  content::WebContents* const contents = function->GetAssociatedWebContents();
55  content::RenderViewHost* const render_view_host =
56      contents ? contents->GetRenderViewHost() : NULL;
57  return render_view_host ? app_window_registry->GetAppWindowForRenderViewHost(
58                                render_view_host)
59                          : NULL;
60}
61
62std::vector<linked_ptr<api::file_manager_private::ProfileInfo> >
63GetLoggedInProfileInfoList() {
64  DCHECK(user_manager::UserManager::IsInitialized());
65  const std::vector<Profile*>& profiles =
66      g_browser_process->profile_manager()->GetLoadedProfiles();
67  std::set<Profile*> original_profiles;
68  std::vector<linked_ptr<api::file_manager_private::ProfileInfo> >
69      result_profiles;
70
71  for (size_t i = 0; i < profiles.size(); ++i) {
72    // Filter the profile.
73    Profile* const profile = profiles[i]->GetOriginalProfile();
74    if (original_profiles.count(profile))
75      continue;
76    original_profiles.insert(profile);
77    const user_manager::User* const user =
78        chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
79    if (!user || !user->is_logged_in())
80      continue;
81
82    // Make a ProfileInfo.
83    linked_ptr<api::file_manager_private::ProfileInfo> profile_info(
84        new api::file_manager_private::ProfileInfo());
85    profile_info->profile_id = multi_user_util::GetUserIDFromProfile(profile);
86    profile_info->display_name = UTF16ToUTF8(user->GetDisplayName());
87    // TODO(hirono): Remove the property from the profile_info.
88    profile_info->is_current_profile = true;
89
90    result_profiles.push_back(profile_info);
91  }
92
93  return result_profiles;
94}
95} // namespace
96
97bool FileManagerPrivateLogoutUserForReauthenticationFunction::RunSync() {
98  user_manager::User* user =
99      chromeos::ProfileHelper::Get()->GetUserByProfile(GetProfile());
100  if (user) {
101    user_manager::UserManager::Get()->SaveUserOAuthStatus(
102        user->email(), user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
103  }
104
105  chrome::AttemptUserExit();
106  return true;
107}
108
109bool FileManagerPrivateGetPreferencesFunction::RunSync() {
110  api::file_manager_private::Preferences result;
111  const PrefService* const service = GetProfile()->GetPrefs();
112
113  result.drive_enabled = drive::util::IsDriveEnabledForProfile(GetProfile());
114  result.cellular_disabled =
115      service->GetBoolean(prefs::kDisableDriveOverCellular);
116  result.hosted_files_disabled =
117      service->GetBoolean(prefs::kDisableDriveHostedFiles);
118  result.use24hour_clock = service->GetBoolean(prefs::kUse24HourClock);
119  result.allow_redeem_offers = true;
120  if (!chromeos::CrosSettings::Get()->GetBoolean(
121          chromeos::kAllowRedeemChromeOsRegistrationOffers,
122          &result.allow_redeem_offers)) {
123    result.allow_redeem_offers = true;
124  }
125
126  SetResult(result.ToValue().release());
127
128  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
129  if (logger)
130    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
131  return true;
132}
133
134bool FileManagerPrivateSetPreferencesFunction::RunSync() {
135  using extensions::api::file_manager_private::SetPreferences::Params;
136  const scoped_ptr<Params> params(Params::Create(*args_));
137  EXTENSION_FUNCTION_VALIDATE(params);
138
139  PrefService* const service = GetProfile()->GetPrefs();
140
141  if (params->change_info.cellular_disabled)
142    service->SetBoolean(prefs::kDisableDriveOverCellular,
143                        *params->change_info.cellular_disabled);
144
145  if (params->change_info.hosted_files_disabled)
146    service->SetBoolean(prefs::kDisableDriveHostedFiles,
147                        *params->change_info.hosted_files_disabled);
148
149  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
150  if (logger)
151    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
152  return true;
153}
154
155FileManagerPrivateZipSelectionFunction::
156    FileManagerPrivateZipSelectionFunction() {}
157
158FileManagerPrivateZipSelectionFunction::
159    ~FileManagerPrivateZipSelectionFunction() {}
160
161bool FileManagerPrivateZipSelectionFunction::RunAsync() {
162  using extensions::api::file_manager_private::ZipSelection::Params;
163  const scoped_ptr<Params> params(Params::Create(*args_));
164  EXTENSION_FUNCTION_VALIDATE(params);
165
166  // First param is the source directory URL.
167  if (params->dir_url.empty())
168    return false;
169
170  base::FilePath src_dir = file_manager::util::GetLocalPathFromURL(
171      render_view_host(), GetProfile(), GURL(params->dir_url));
172  if (src_dir.empty())
173    return false;
174
175  // Second param is the list of selected file URLs.
176  if (params->selection_urls.empty())
177    return false;
178
179  std::vector<base::FilePath> files;
180  for (size_t i = 0; i < params->selection_urls.size(); ++i) {
181    base::FilePath path = file_manager::util::GetLocalPathFromURL(
182        render_view_host(), GetProfile(), GURL(params->selection_urls[i]));
183    if (path.empty())
184      return false;
185    files.push_back(path);
186  }
187
188  // Third param is the name of the output zip file.
189  if (params->dest_name.empty())
190    return false;
191
192  // Check if the dir path is under Drive mount point.
193  // TODO(hshi): support create zip file on Drive (crbug.com/158690).
194  if (drive::util::IsUnderDriveMountPoint(src_dir))
195    return false;
196
197  base::FilePath dest_file = src_dir.Append(params->dest_name);
198  std::vector<base::FilePath> src_relative_paths;
199  for (size_t i = 0; i != files.size(); ++i) {
200    const base::FilePath& file_path = files[i];
201
202    // Obtain the relative path of |file_path| under |src_dir|.
203    base::FilePath relative_path;
204    if (!src_dir.AppendRelativePath(file_path, &relative_path))
205      return false;
206    src_relative_paths.push_back(relative_path);
207  }
208
209  (new file_manager::ZipFileCreator(
210       base::Bind(&FileManagerPrivateZipSelectionFunction::OnZipDone, this),
211       src_dir,
212       src_relative_paths,
213       dest_file))->Start();
214  return true;
215}
216
217void FileManagerPrivateZipSelectionFunction::OnZipDone(bool success) {
218  SetResult(new base::FundamentalValue(success));
219  SendResponse(true);
220}
221
222bool FileManagerPrivateZoomFunction::RunSync() {
223  using extensions::api::file_manager_private::Zoom::Params;
224  const scoped_ptr<Params> params(Params::Create(*args_));
225  EXTENSION_FUNCTION_VALIDATE(params);
226
227  content::PageZoom zoom_type;
228  switch (params->operation) {
229    case api::file_manager_private::ZOOM_OPERATION_TYPE_IN:
230      zoom_type = content::PAGE_ZOOM_IN;
231      break;
232    case api::file_manager_private::ZOOM_OPERATION_TYPE_OUT:
233      zoom_type = content::PAGE_ZOOM_OUT;
234      break;
235    case api::file_manager_private::ZOOM_OPERATION_TYPE_RESET:
236      zoom_type = content::PAGE_ZOOM_RESET;
237      break;
238    default:
239      NOTREACHED();
240      return false;
241  }
242  render_view_host()->Zoom(zoom_type);
243  return true;
244}
245
246bool FileManagerPrivateInstallWebstoreItemFunction::RunAsync() {
247  using extensions::api::file_manager_private::InstallWebstoreItem::Params;
248  const scoped_ptr<Params> params(Params::Create(*args_));
249  EXTENSION_FUNCTION_VALIDATE(params);
250
251  if (params->item_id.empty())
252    return false;
253
254  const extensions::WebstoreStandaloneInstaller::Callback callback =
255      base::Bind(
256          &FileManagerPrivateInstallWebstoreItemFunction::OnInstallComplete,
257          this);
258
259  // Only GoogleCastAPI extension can use silent installation.
260  if (params->silent_installation &&
261      params->item_id != kGoogleCastApiExtensionId) {
262    SetError("Only whitelisted items can do silent installation.");
263    return false;
264  }
265
266  scoped_refptr<file_manager::AppInstaller> installer(
267      new file_manager::AppInstaller(GetAssociatedWebContents(),
268                                     params->item_id,
269                                     GetProfile(),
270                                     params->silent_installation,
271                                     callback));
272  // installer will be AddRef()'d in BeginInstall().
273  installer->BeginInstall();
274  return true;
275}
276
277void FileManagerPrivateInstallWebstoreItemFunction::OnInstallComplete(
278    bool success,
279    const std::string& error,
280    extensions::webstore_install::Result result) {
281  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
282  if (success) {
283    if (logger) {
284      logger->Log(logging::LOG_INFO,
285                  "App install succeeded. (item id: %s)",
286                  webstore_item_id_.c_str());
287    }
288  } else {
289    if (logger) {
290      logger->Log(logging::LOG_ERROR,
291                  "App install failed. (item id: %s, reason: %s)",
292                  webstore_item_id_.c_str(),
293                  error.c_str());
294    }
295    SetError(error);
296  }
297
298  SendResponse(success);
299}
300
301FileManagerPrivateRequestWebStoreAccessTokenFunction::
302    FileManagerPrivateRequestWebStoreAccessTokenFunction() {
303}
304
305FileManagerPrivateRequestWebStoreAccessTokenFunction::
306    ~FileManagerPrivateRequestWebStoreAccessTokenFunction() {
307}
308
309bool FileManagerPrivateRequestWebStoreAccessTokenFunction::RunAsync() {
310  std::vector<std::string> scopes;
311  scopes.push_back(kCWSScope);
312
313  ProfileOAuth2TokenService* oauth_service =
314      ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
315  net::URLRequestContextGetter* url_request_context_getter =
316      g_browser_process->system_request_context();
317
318  if (!oauth_service) {
319    drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
320    if (logger) {
321      logger->Log(logging::LOG_ERROR,
322                  "CWS OAuth token fetch failed. OAuth2TokenService can't "
323                  "be retrieved.");
324    }
325    SetResult(base::Value::CreateNullValue());
326    return false;
327  }
328
329  SigninManagerBase* signin_manager =
330      SigninManagerFactory::GetForProfile(GetProfile());
331  auth_service_.reset(new google_apis::AuthService(
332      oauth_service,
333      signin_manager->GetAuthenticatedAccountId(),
334      url_request_context_getter,
335      scopes));
336  auth_service_->StartAuthentication(base::Bind(
337      &FileManagerPrivateRequestWebStoreAccessTokenFunction::
338          OnAccessTokenFetched,
339      this));
340
341  return true;
342}
343
344void FileManagerPrivateRequestWebStoreAccessTokenFunction::OnAccessTokenFetched(
345    google_apis::GDataErrorCode code,
346    const std::string& access_token) {
347  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
348
349  if (code == google_apis::HTTP_SUCCESS) {
350    DCHECK(auth_service_->HasAccessToken());
351    DCHECK(access_token == auth_service_->access_token());
352    if (logger)
353      logger->Log(logging::LOG_INFO, "CWS OAuth token fetch succeeded.");
354    SetResult(new base::StringValue(access_token));
355    SendResponse(true);
356  } else {
357    if (logger) {
358      logger->Log(logging::LOG_ERROR,
359                  "CWS OAuth token fetch failed. (GDataErrorCode: %s)",
360                  google_apis::GDataErrorCodeToString(code).c_str());
361    }
362    SetResult(base::Value::CreateNullValue());
363    SendResponse(false);
364  }
365}
366
367bool FileManagerPrivateGetProfilesFunction::RunSync() {
368#if defined(USE_ATHENA)
369  // TODO(oshima): Figure out what to do.
370  return false;
371#endif
372
373  const std::vector<linked_ptr<api::file_manager_private::ProfileInfo> >&
374      profiles = GetLoggedInProfileInfoList();
375
376  // Obtains the display profile ID.
377  AppWindow* const app_window = GetCurrentAppWindow(this);
378  chrome::MultiUserWindowManager* const window_manager =
379      chrome::MultiUserWindowManager::GetInstance();
380  const std::string current_profile_id =
381      multi_user_util::GetUserIDFromProfile(GetProfile());
382  const std::string display_profile_id =
383      window_manager && app_window ? window_manager->GetUserPresentingWindow(
384                                         app_window->GetNativeWindow())
385                                   : "";
386
387  results_ = api::file_manager_private::GetProfiles::Results::Create(
388      profiles,
389      current_profile_id,
390      display_profile_id.empty() ? current_profile_id : display_profile_id);
391  return true;
392}
393
394bool FileManagerPrivateVisitDesktopFunction::RunSync() {
395  using api::file_manager_private::VisitDesktop::Params;
396  const scoped_ptr<Params> params(Params::Create(*args_));
397  const std::vector<linked_ptr<api::file_manager_private::ProfileInfo> >&
398      profiles = GetLoggedInProfileInfoList();
399
400  chrome::MultiUserWindowManager* const window_manager =
401      chrome::MultiUserWindowManager::GetInstance();
402  DCHECK(window_manager);
403
404  // Check if the target user is logged-in or not.
405  bool logged_in = false;
406  for (size_t i = 0; i < profiles.size(); ++i) {
407    if (profiles[i]->profile_id == params->profile_id) {
408      logged_in = true;
409      break;
410    }
411  }
412  if (!logged_in) {
413    SetError("The user is not logged-in now.");
414    return false;
415  }
416
417  // Look for the current app window.
418  AppWindow* const app_window = GetCurrentAppWindow(this);
419  if (!app_window) {
420    SetError("Target window is not found.");
421    return false;
422  }
423
424  // Move the window to the user's desktop.
425  window_manager->ShowWindowForUser(app_window->GetNativeWindow(),
426                                    params->profile_id);
427
428  // Check the result.
429  if (!window_manager->IsWindowOnDesktopOfUser(app_window->GetNativeWindow(),
430                                               params->profile_id)) {
431    SetError("The window cannot visit the desktop.");
432    return false;
433  }
434
435  return true;
436}
437
438bool FileManagerPrivateOpenInspectorFunction::RunSync() {
439  using extensions::api::file_manager_private::OpenInspector::Params;
440  const scoped_ptr<Params> params(Params::Create(*args_));
441  EXTENSION_FUNCTION_VALIDATE(params);
442
443  switch (params->type) {
444    case extensions::api::file_manager_private::INSPECTION_TYPE_NORMAL:
445      // Open inspector for foreground page.
446      DevToolsWindow::OpenDevToolsWindow(
447          content::WebContents::FromRenderViewHost(render_view_host()));
448      break;
449    case extensions::api::file_manager_private::INSPECTION_TYPE_CONSOLE:
450      // Open inspector for foreground page and bring focus to the console.
451      DevToolsWindow::OpenDevToolsWindow(
452          content::WebContents::FromRenderViewHost(render_view_host()),
453          DevToolsToggleAction::ShowConsole());
454      break;
455    case extensions::api::file_manager_private::INSPECTION_TYPE_ELEMENT:
456      // Open inspector for foreground page in inspect element mode.
457      DevToolsWindow::OpenDevToolsWindow(
458          content::WebContents::FromRenderViewHost(render_view_host()),
459          DevToolsToggleAction::Inspect());
460      break;
461    case extensions::api::file_manager_private::INSPECTION_TYPE_BACKGROUND:
462      // Open inspector for background page.
463      extensions::devtools_util::InspectBackgroundPage(extension(),
464                                                       GetProfile());
465      break;
466    default:
467      NOTREACHED();
468      SetError(
469          base::StringPrintf("Unexpected inspection type(%d) is specified.",
470                             static_cast<int>(params->type)));
471      return false;
472  }
473  return true;
474}
475
476}  // namespace extensions
477