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_drive.h"
6
7#include "base/command_line.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/chromeos/drive/drive_integration_service.h"
10#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
11#include "chrome/browser/chromeos/file_manager/file_tasks.h"
12#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
13#include "chrome/browser/chromeos/file_manager/url_util.h"
14#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
15#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
16#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
17#include "chrome/browser/chromeos/profiles/profile_helper.h"
18#include "chrome/browser/drive/drive_app_registry.h"
19#include "chrome/browser/drive/event_logger.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/profiles/profile_manager.h"
22#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23#include "chrome/browser/signin/signin_manager_factory.h"
24#include "chromeos/chromeos_switches.h"
25#include "components/signin/core/browser/profile_oauth2_token_service.h"
26#include "components/signin/core/browser/signin_manager.h"
27#include "content/public/browser/browser_thread.h"
28#include "google_apis/drive/auth_service.h"
29#include "storage/common/fileapi/file_system_info.h"
30#include "storage/common/fileapi/file_system_util.h"
31
32using content::BrowserThread;
33
34using chromeos::file_system_provider::EntryMetadata;
35using chromeos::file_system_provider::ProvidedFileSystemInterface;
36using chromeos::file_system_provider::util::FileSystemURLParser;
37using extensions::api::file_manager_private::EntryProperties;
38using file_manager::util::EntryDefinition;
39using file_manager::util::EntryDefinitionCallback;
40using file_manager::util::EntryDefinitionList;
41using file_manager::util::EntryDefinitionListCallback;
42using file_manager::util::FileDefinition;
43using file_manager::util::FileDefinitionList;
44
45namespace extensions {
46namespace {
47
48// List of connection types of drive.
49// Keep this in sync with the DriveConnectionType in common/js/util.js.
50const char kDriveConnectionTypeOffline[] = "offline";
51const char kDriveConnectionTypeMetered[] = "metered";
52const char kDriveConnectionTypeOnline[] = "online";
53
54// List of reasons of kDriveConnectionType*.
55// Keep this in sync with the DriveConnectionReason in common/js/util.js.
56const char kDriveConnectionReasonNotReady[] = "not_ready";
57const char kDriveConnectionReasonNoNetwork[] = "no_network";
58const char kDriveConnectionReasonNoService[] = "no_service";
59
60// Copies properties from |entry_proto| to |properties|. |shared_with_me| is
61// given from the running profile.
62void FillEntryPropertiesValueForDrive(const drive::ResourceEntry& entry_proto,
63                                      bool shared_with_me,
64                                      EntryProperties* properties) {
65  properties->shared_with_me.reset(new bool(shared_with_me));
66  properties->shared.reset(new bool(entry_proto.shared()));
67
68  const drive::PlatformFileInfoProto& file_info = entry_proto.file_info();
69  properties->file_size.reset(new double(file_info.size()));
70  properties->last_modified_time.reset(new double(
71      base::Time::FromInternalValue(file_info.last_modified()).ToJsTime()));
72
73  if (!entry_proto.has_file_specific_info())
74    return;
75
76  const drive::FileSpecificInfo& file_specific_info =
77      entry_proto.file_specific_info();
78
79  if (!entry_proto.resource_id().empty()) {
80    properties->thumbnail_url.reset(
81        new std::string("https://www.googledrive.com/thumb/" +
82                        entry_proto.resource_id() + "?width=500&height=500"));
83  }
84  if (file_specific_info.has_image_width()) {
85    properties->image_width.reset(
86        new int(file_specific_info.image_width()));
87  }
88  if (file_specific_info.has_image_height()) {
89    properties->image_height.reset(
90        new int(file_specific_info.image_height()));
91  }
92  if (file_specific_info.has_image_rotation()) {
93    properties->image_rotation.reset(
94        new int(file_specific_info.image_rotation()));
95  }
96  properties->is_hosted.reset(
97      new bool(file_specific_info.is_hosted_document()));
98  properties->content_mime_type.reset(
99      new std::string(file_specific_info.content_mime_type()));
100
101  properties->is_pinned.reset(
102      new bool(file_specific_info.cache_state().is_pinned()));
103  properties->is_present.reset(
104      new bool(file_specific_info.cache_state().is_present()));
105
106  if (file_specific_info.cache_state().is_present()) {
107    properties->is_available_offline.reset(new bool(true));
108  } else if (file_specific_info.is_hosted_document() &&
109             file_specific_info.has_document_extension()) {
110    const std::string file_extension = file_specific_info.document_extension();
111    // What's available offline? See the 'Web' column at:
112    // http://support.google.com/drive/answer/1628467
113    properties->is_available_offline.reset(
114        new bool(file_extension == ".gdoc" || file_extension == ".gdraw" ||
115                 file_extension == ".gsheet" || file_extension == ".gslides"));
116  } else {
117    properties->is_available_offline.reset(new bool(false));
118  }
119
120  properties->is_available_when_metered.reset(
121      new bool(file_specific_info.cache_state().is_present() ||
122               file_specific_info.is_hosted_document()));
123}
124
125// Creates entry definition list for (metadata) search result info list.
126template <class T>
127void ConvertSearchResultInfoListToEntryDefinitionList(
128    Profile* profile,
129    const std::string& extension_id,
130    const std::vector<T>& search_result_info_list,
131    const EntryDefinitionListCallback& callback) {
132  FileDefinitionList file_definition_list;
133
134  for (size_t i = 0; i < search_result_info_list.size(); ++i) {
135    FileDefinition file_definition;
136    file_definition.virtual_path =
137        file_manager::util::ConvertDrivePathToRelativeFileSystemPath(
138            profile, extension_id, search_result_info_list.at(i).path);
139    file_definition.is_directory = search_result_info_list.at(i).is_directory;
140    file_definition_list.push_back(file_definition);
141  }
142
143  file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
144      profile,
145      extension_id,
146      file_definition_list,  // Safe, since copied internally.
147      callback);
148}
149
150class SingleEntryPropertiesGetterForDrive {
151 public:
152  typedef base::Callback<void(scoped_ptr<EntryProperties> properties,
153                              base::File::Error error)> ResultCallback;
154
155  // Creates an instance and starts the process.
156  static void Start(const base::FilePath local_path,
157                    Profile* const profile,
158                    const ResultCallback& callback) {
159    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
160
161    SingleEntryPropertiesGetterForDrive* instance =
162        new SingleEntryPropertiesGetterForDrive(local_path, profile, callback);
163    instance->StartProcess();
164
165    // The instance will be destroyed by itself.
166  }
167
168  virtual ~SingleEntryPropertiesGetterForDrive() {}
169
170 private:
171  SingleEntryPropertiesGetterForDrive(const base::FilePath local_path,
172                                      Profile* const profile,
173                                      const ResultCallback& callback)
174      : callback_(callback),
175        local_path_(local_path),
176        running_profile_(profile),
177        properties_(new EntryProperties),
178        file_owner_profile_(NULL),
179        weak_ptr_factory_(this) {
180    DCHECK(!callback_.is_null());
181    DCHECK(profile);
182  }
183
184  void StartProcess() {
185    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186
187    file_path_ = drive::util::ExtractDrivePath(local_path_);
188    file_owner_profile_ = drive::util::ExtractProfileFromPath(local_path_);
189
190    if (!file_owner_profile_ ||
191        !g_browser_process->profile_manager()->IsValidProfile(
192            file_owner_profile_)) {
193      CompleteGetEntryProperties(drive::FILE_ERROR_FAILED);
194      return;
195    }
196
197    // Start getting the file info.
198    drive::FileSystemInterface* const file_system =
199        drive::util::GetFileSystemByProfile(file_owner_profile_);
200    if (!file_system) {
201      // |file_system| is NULL if Drive is disabled or not mounted.
202      CompleteGetEntryProperties(drive::FILE_ERROR_FAILED);
203      return;
204    }
205
206    file_system->GetResourceEntry(
207        file_path_,
208        base::Bind(&SingleEntryPropertiesGetterForDrive::OnGetFileInfo,
209                   weak_ptr_factory_.GetWeakPtr()));
210  }
211
212  void OnGetFileInfo(drive::FileError error,
213                     scoped_ptr<drive::ResourceEntry> entry) {
214    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215
216    if (error != drive::FILE_ERROR_OK) {
217      CompleteGetEntryProperties(error);
218      return;
219    }
220
221    DCHECK(entry);
222    owner_resource_entry_.swap(entry);
223
224    if (running_profile_->IsSameProfile(file_owner_profile_)) {
225      StartParseFileInfo(owner_resource_entry_->shared_with_me());
226      return;
227    }
228
229    // If the running profile does not own the file, obtain the shared_with_me
230    // flag from the running profile's value.
231    drive::FileSystemInterface* const file_system =
232        drive::util::GetFileSystemByProfile(running_profile_);
233    if (!file_system) {
234      CompleteGetEntryProperties(drive::FILE_ERROR_FAILED);
235      return;
236    }
237    file_system->GetPathFromResourceId(
238        owner_resource_entry_->resource_id(),
239        base::Bind(&SingleEntryPropertiesGetterForDrive::OnGetRunningPath,
240                   weak_ptr_factory_.GetWeakPtr()));
241  }
242
243  void OnGetRunningPath(drive::FileError error,
244                        const base::FilePath& file_path) {
245    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246
247    if (error != drive::FILE_ERROR_OK) {
248      // The running profile does not know the file.
249      StartParseFileInfo(false);
250      return;
251    }
252
253    drive::FileSystemInterface* const file_system =
254        drive::util::GetFileSystemByProfile(running_profile_);
255    if (!file_system) {
256      // The drive is disable for the running profile.
257      StartParseFileInfo(false);
258      return;
259    }
260
261    file_system->GetResourceEntry(
262        file_path,
263        base::Bind(&SingleEntryPropertiesGetterForDrive::OnGetShareInfo,
264                   weak_ptr_factory_.GetWeakPtr()));
265  }
266
267  void OnGetShareInfo(drive::FileError error,
268                      scoped_ptr<drive::ResourceEntry> entry) {
269    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
270
271    if (error != drive::FILE_ERROR_OK) {
272      CompleteGetEntryProperties(error);
273      return;
274    }
275
276    DCHECK(entry.get());
277    StartParseFileInfo(entry->shared_with_me());
278  }
279
280  void StartParseFileInfo(bool shared_with_me) {
281    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
282
283    FillEntryPropertiesValueForDrive(
284        *owner_resource_entry_, shared_with_me, properties_.get());
285
286    drive::FileSystemInterface* const file_system =
287        drive::util::GetFileSystemByProfile(file_owner_profile_);
288    drive::DriveAppRegistry* const app_registry =
289        drive::util::GetDriveAppRegistryByProfile(file_owner_profile_);
290    if (!file_system || !app_registry) {
291      // |file_system| or |app_registry| is NULL if Drive is disabled.
292      CompleteGetEntryProperties(drive::FILE_ERROR_FAILED);
293      return;
294    }
295
296    // The properties meaningful for directories are already filled in
297    // FillEntryPropertiesValueForDrive().
298    if (!owner_resource_entry_->has_file_specific_info()) {
299      CompleteGetEntryProperties(drive::FILE_ERROR_OK);
300      return;
301    }
302
303    const drive::FileSpecificInfo& file_specific_info =
304        owner_resource_entry_->file_specific_info();
305
306    // Get drive WebApps that can accept this file. We just need to extract the
307    // doc icon for the drive app, which is set as default.
308    std::vector<drive::DriveAppInfo> drive_apps;
309    app_registry->GetAppsForFile(file_path_.Extension(),
310                                 file_specific_info.content_mime_type(),
311                                 &drive_apps);
312    if (!drive_apps.empty()) {
313      std::string default_task_id =
314          file_manager::file_tasks::GetDefaultTaskIdFromPrefs(
315              *file_owner_profile_->GetPrefs(),
316              file_specific_info.content_mime_type(),
317              file_path_.Extension());
318      file_manager::file_tasks::TaskDescriptor default_task;
319      file_manager::file_tasks::ParseTaskID(default_task_id, &default_task);
320      DCHECK(default_task_id.empty() || !default_task.app_id.empty());
321      for (size_t i = 0; i < drive_apps.size(); ++i) {
322        const drive::DriveAppInfo& app_info = drive_apps[i];
323        if (default_task.app_id == app_info.app_id) {
324          // The drive app is set as default. Files.app should use the doc icon.
325          const GURL doc_icon = drive::util::FindPreferredIcon(
326              app_info.document_icons, drive::util::kPreferredIconSize);
327          properties_->custom_icon_url.reset(new std::string(doc_icon.spec()));
328        }
329      }
330    }
331
332    CompleteGetEntryProperties(drive::FILE_ERROR_OK);
333  }
334
335  void CompleteGetEntryProperties(drive::FileError error) {
336    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337    DCHECK(!callback_.is_null());
338
339    callback_.Run(properties_.Pass(), drive::FileErrorToBaseFileError(error));
340    BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
341  }
342
343  // Given parameters.
344  const ResultCallback callback_;
345  const base::FilePath local_path_;
346  Profile* const running_profile_;
347
348  // Values used in the process.
349  scoped_ptr<EntryProperties> properties_;
350  Profile* file_owner_profile_;
351  base::FilePath file_path_;
352  scoped_ptr<drive::ResourceEntry> owner_resource_entry_;
353
354  base::WeakPtrFactory<SingleEntryPropertiesGetterForDrive> weak_ptr_factory_;
355};  // class SingleEntryPropertiesGetterForDrive
356
357class SingleEntryPropertiesGetterForFileSystemProvider {
358 public:
359  typedef base::Callback<void(scoped_ptr<EntryProperties> properties,
360                              base::File::Error error)> ResultCallback;
361
362  // Creates an instance and starts the process.
363  static void Start(const storage::FileSystemURL file_system_url,
364                    const ResultCallback& callback) {
365    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
366
367    SingleEntryPropertiesGetterForFileSystemProvider* instance =
368        new SingleEntryPropertiesGetterForFileSystemProvider(file_system_url,
369                                                             callback);
370    instance->StartProcess();
371
372    // The instance will be destroyed by itself.
373  }
374
375  virtual ~SingleEntryPropertiesGetterForFileSystemProvider() {}
376
377 private:
378  SingleEntryPropertiesGetterForFileSystemProvider(
379      const storage::FileSystemURL& file_system_url,
380      const ResultCallback& callback)
381      : callback_(callback),
382        file_system_url_(file_system_url),
383        properties_(new EntryProperties),
384        weak_ptr_factory_(this) {
385    DCHECK(!callback_.is_null());
386  }
387
388  void StartProcess() {
389    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
390
391    FileSystemURLParser parser(file_system_url_);
392    if (!parser.Parse()) {
393      CompleteGetEntryProperties(base::File::FILE_ERROR_NOT_FOUND);
394      return;
395    }
396
397    parser.file_system()->GetMetadata(
398        parser.file_path(),
399        ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
400        base::Bind(&SingleEntryPropertiesGetterForFileSystemProvider::
401                       OnGetMetadataCompleted,
402                   weak_ptr_factory_.GetWeakPtr()));
403  }
404
405  void OnGetMetadataCompleted(scoped_ptr<EntryMetadata> metadata,
406                              base::File::Error result) {
407    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
408
409    if (result != base::File::FILE_OK) {
410      CompleteGetEntryProperties(result);
411      return;
412    }
413
414    properties_->file_size.reset(new double(metadata->size));
415    properties_->last_modified_time.reset(
416        new double(metadata->modification_time.ToJsTime()));
417
418    if (!metadata->thumbnail.empty())
419      properties_->thumbnail_url.reset(new std::string(metadata->thumbnail));
420
421    CompleteGetEntryProperties(base::File::FILE_OK);
422  }
423
424  void CompleteGetEntryProperties(base::File::Error result) {
425    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
426    DCHECK(!callback_.is_null());
427
428    callback_.Run(properties_.Pass(), result);
429    BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
430  }
431
432  // Given parameters.
433  const ResultCallback callback_;
434  const storage::FileSystemURL file_system_url_;
435
436  // Values used in the process.
437  scoped_ptr<EntryProperties> properties_;
438
439  base::WeakPtrFactory<SingleEntryPropertiesGetterForFileSystemProvider>
440      weak_ptr_factory_;
441};  // class SingleEntryPropertiesGetterForDrive
442
443}  // namespace
444
445FileManagerPrivateGetEntryPropertiesFunction::
446    FileManagerPrivateGetEntryPropertiesFunction()
447    : processed_count_(0) {
448}
449
450FileManagerPrivateGetEntryPropertiesFunction::
451    ~FileManagerPrivateGetEntryPropertiesFunction() {
452}
453
454bool FileManagerPrivateGetEntryPropertiesFunction::RunAsync() {
455  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
456
457  using api::file_manager_private::GetEntryProperties::Params;
458  const scoped_ptr<Params> params(Params::Create(*args_));
459  EXTENSION_FUNCTION_VALIDATE(params);
460
461  scoped_refptr<storage::FileSystemContext> file_system_context =
462      file_manager::util::GetFileSystemContextForRenderViewHost(
463          GetProfile(), render_view_host());
464
465  properties_list_.resize(params->file_urls.size());
466  for (size_t i = 0; i < params->file_urls.size(); i++) {
467    const GURL url = GURL(params->file_urls[i]);
468    const storage::FileSystemURL file_system_url =
469        file_system_context->CrackURL(url);
470    switch (file_system_url.type()) {
471      case storage::kFileSystemTypeDrive:
472        SingleEntryPropertiesGetterForDrive::Start(
473            file_system_url.path(),
474            GetProfile(),
475            base::Bind(&FileManagerPrivateGetEntryPropertiesFunction::
476                           CompleteGetEntryProperties,
477                       this,
478                       i));
479        break;
480      case storage::kFileSystemTypeProvided:
481        SingleEntryPropertiesGetterForFileSystemProvider::Start(
482            file_system_url,
483            base::Bind(&FileManagerPrivateGetEntryPropertiesFunction::
484                           CompleteGetEntryProperties,
485                       this,
486                       i));
487        break;
488      default:
489        LOG(ERROR) << "Not supported file system type.";
490        CompleteGetEntryProperties(i,
491                                   make_scoped_ptr(new EntryProperties),
492                                   base::File::FILE_ERROR_INVALID_OPERATION);
493    }
494  }
495
496  return true;
497}
498
499void FileManagerPrivateGetEntryPropertiesFunction::CompleteGetEntryProperties(
500    size_t index,
501    scoped_ptr<EntryProperties> properties,
502    base::File::Error error) {
503  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
504  DCHECK(0 <= processed_count_ && processed_count_ < properties_list_.size());
505
506  properties_list_[index] = make_linked_ptr(properties.release());
507
508  processed_count_++;
509  if (processed_count_ < properties_list_.size())
510    return;
511
512  results_ = extensions::api::file_manager_private::GetEntryProperties::
513      Results::Create(properties_list_);
514  SendResponse(true);
515}
516
517bool FileManagerPrivatePinDriveFileFunction::RunAsync() {
518  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
519
520  using extensions::api::file_manager_private::PinDriveFile::Params;
521  const scoped_ptr<Params> params(Params::Create(*args_));
522  EXTENSION_FUNCTION_VALIDATE(params);
523
524  drive::FileSystemInterface* const file_system =
525      drive::util::GetFileSystemByProfile(GetProfile());
526  if (!file_system)  // |file_system| is NULL if Drive is disabled.
527    return false;
528
529  const base::FilePath drive_path =
530      drive::util::ExtractDrivePath(file_manager::util::GetLocalPathFromURL(
531          render_view_host(), GetProfile(), GURL(params->file_url)));
532  if (params->pin) {
533    file_system->Pin(drive_path,
534                     base::Bind(&FileManagerPrivatePinDriveFileFunction::
535                                    OnPinStateSet, this));
536  } else {
537    file_system->Unpin(drive_path,
538                       base::Bind(&FileManagerPrivatePinDriveFileFunction::
539                                      OnPinStateSet, this));
540  }
541  return true;
542}
543
544void FileManagerPrivatePinDriveFileFunction::
545    OnPinStateSet(drive::FileError error) {
546  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
547
548  if (error == drive::FILE_ERROR_OK) {
549    SendResponse(true);
550  } else {
551    SetError(drive::FileErrorToString(error));
552    SendResponse(false);
553  }
554}
555
556bool FileManagerPrivateCancelFileTransfersFunction::RunAsync() {
557  using extensions::api::file_manager_private::CancelFileTransfers::Params;
558  const scoped_ptr<Params> params(Params::Create(*args_));
559  EXTENSION_FUNCTION_VALIDATE(params);
560
561  drive::DriveIntegrationService* integration_service =
562      drive::DriveIntegrationServiceFactory::FindForProfile(GetProfile());
563  if (!integration_service || !integration_service->IsMounted())
564    return false;
565
566  drive::JobListInterface* job_list = integration_service->job_list();
567  DCHECK(job_list);
568  std::vector<drive::JobInfo> jobs = job_list->GetJobInfoList();
569
570  // If file_urls are empty, cancel all jobs.
571  if (!params->file_urls.get()) {
572    for (size_t i = 0; i < jobs.size(); ++i) {
573      if (drive::IsActiveFileTransferJobInfo(jobs[i]))
574        job_list->CancelJob(jobs[i].job_id);
575    }
576  } else {
577    // Create the mapping from file path to job ID.
578    std::vector<std::string> file_urls(*params->file_urls.get());
579    typedef std::map<base::FilePath, std::vector<drive::JobID> > PathToIdMap;
580    PathToIdMap path_to_id_map;
581    for (size_t i = 0; i < jobs.size(); ++i) {
582      if (drive::IsActiveFileTransferJobInfo(jobs[i]))
583        path_to_id_map[jobs[i].file_path].push_back(jobs[i].job_id);
584    }
585
586    for (size_t i = 0; i < file_urls.size(); ++i) {
587      base::FilePath file_path = file_manager::util::GetLocalPathFromURL(
588          render_view_host(), GetProfile(), GURL(file_urls[i]));
589      if (file_path.empty())
590        continue;
591
592      file_path = drive::util::ExtractDrivePath(file_path);
593      DCHECK(file_path.empty());
594
595      // Cancel all the jobs for the file.
596      PathToIdMap::iterator it = path_to_id_map.find(file_path);
597      if (it != path_to_id_map.end()) {
598        for (size_t i = 0; i < it->second.size(); ++i)
599          job_list->CancelJob(it->second[i]);
600      }
601    }
602  }
603  SendResponse(true);
604  return true;
605}
606
607bool FileManagerPrivateSearchDriveFunction::RunAsync() {
608  using extensions::api::file_manager_private::SearchDrive::Params;
609  const scoped_ptr<Params> params(Params::Create(*args_));
610  EXTENSION_FUNCTION_VALIDATE(params);
611
612  drive::FileSystemInterface* const file_system =
613      drive::util::GetFileSystemByProfile(GetProfile());
614  if (!file_system) {
615    // |file_system| is NULL if Drive is disabled.
616    return false;
617  }
618
619  file_system->Search(
620      params->search_params.query, GURL(params->search_params.next_feed),
621      base::Bind(&FileManagerPrivateSearchDriveFunction::OnSearch, this));
622  return true;
623}
624
625void FileManagerPrivateSearchDriveFunction::OnSearch(
626    drive::FileError error,
627    const GURL& next_link,
628    scoped_ptr<SearchResultInfoList> results) {
629  if (error != drive::FILE_ERROR_OK) {
630    SendResponse(false);
631    return;
632  }
633
634  // Outlives the following conversion, since the pointer is bound to the
635  // callback.
636  DCHECK(results.get());
637  const SearchResultInfoList& results_ref = *results.get();
638
639  ConvertSearchResultInfoListToEntryDefinitionList(
640      GetProfile(),
641      extension_->id(),
642      results_ref,
643      base::Bind(&FileManagerPrivateSearchDriveFunction::OnEntryDefinitionList,
644                 this,
645                 next_link,
646                 base::Passed(&results)));
647}
648
649void FileManagerPrivateSearchDriveFunction::OnEntryDefinitionList(
650    const GURL& next_link,
651    scoped_ptr<SearchResultInfoList> search_result_info_list,
652    scoped_ptr<EntryDefinitionList> entry_definition_list) {
653  DCHECK_EQ(search_result_info_list->size(), entry_definition_list->size());
654  base::ListValue* entries = new base::ListValue();
655
656  // Convert Drive files to something File API stack can understand.
657  for (EntryDefinitionList::const_iterator it = entry_definition_list->begin();
658       it != entry_definition_list->end();
659       ++it) {
660    base::DictionaryValue* entry = new base::DictionaryValue();
661    entry->SetString("fileSystemName", it->file_system_name);
662    entry->SetString("fileSystemRoot", it->file_system_root_url);
663    entry->SetString("fileFullPath", "/" + it->full_path.AsUTF8Unsafe());
664    entry->SetBoolean("fileIsDirectory", it->is_directory);
665    entries->Append(entry);
666  }
667
668  base::DictionaryValue* result = new base::DictionaryValue();
669  result->Set("entries", entries);
670  result->SetString("nextFeed", next_link.spec());
671
672  SetResult(result);
673  SendResponse(true);
674}
675
676bool FileManagerPrivateSearchDriveMetadataFunction::RunAsync() {
677  using api::file_manager_private::SearchDriveMetadata::Params;
678  const scoped_ptr<Params> params(Params::Create(*args_));
679  EXTENSION_FUNCTION_VALIDATE(params);
680
681  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
682  if (logger) {
683    logger->Log(logging::LOG_INFO,
684                "%s[%d] called. (types: '%s', maxResults: '%d')",
685                name().c_str(),
686                request_id(),
687                api::file_manager_private::ToString(
688                    params->search_params.types).c_str(),
689                params->search_params.max_results);
690  }
691  set_log_on_completion(true);
692
693  drive::FileSystemInterface* const file_system =
694      drive::util::GetFileSystemByProfile(GetProfile());
695  if (!file_system) {
696    // |file_system| is NULL if Drive is disabled.
697    return false;
698  }
699
700  int options = -1;
701  switch (params->search_params.types) {
702    case api::file_manager_private::SEARCH_TYPE_EXCLUDE_DIRECTORIES:
703      options = drive::SEARCH_METADATA_EXCLUDE_DIRECTORIES;
704      break;
705    case api::file_manager_private::SEARCH_TYPE_SHARED_WITH_ME:
706      options = drive::SEARCH_METADATA_SHARED_WITH_ME;
707      break;
708    case api::file_manager_private::SEARCH_TYPE_OFFLINE:
709      options = drive::SEARCH_METADATA_OFFLINE;
710      break;
711    case api::file_manager_private::SEARCH_TYPE_ALL:
712      options = drive::SEARCH_METADATA_ALL;
713      break;
714    case api::file_manager_private::SEARCH_TYPE_NONE:
715      break;
716  }
717  DCHECK_NE(options, -1);
718
719  file_system->SearchMetadata(
720      params->search_params.query,
721      options,
722      params->search_params.max_results,
723      base::Bind(&FileManagerPrivateSearchDriveMetadataFunction::
724                     OnSearchMetadata, this));
725  return true;
726}
727
728void FileManagerPrivateSearchDriveMetadataFunction::OnSearchMetadata(
729    drive::FileError error,
730    scoped_ptr<drive::MetadataSearchResultVector> results) {
731  if (error != drive::FILE_ERROR_OK) {
732    SendResponse(false);
733    return;
734  }
735
736  // Outlives the following conversion, since the pointer is bound to the
737  // callback.
738  DCHECK(results.get());
739  const drive::MetadataSearchResultVector& results_ref = *results.get();
740
741  ConvertSearchResultInfoListToEntryDefinitionList(
742      GetProfile(),
743      extension_->id(),
744      results_ref,
745      base::Bind(
746          &FileManagerPrivateSearchDriveMetadataFunction::OnEntryDefinitionList,
747          this,
748          base::Passed(&results)));
749}
750
751void FileManagerPrivateSearchDriveMetadataFunction::OnEntryDefinitionList(
752    scoped_ptr<drive::MetadataSearchResultVector> search_result_info_list,
753    scoped_ptr<EntryDefinitionList> entry_definition_list) {
754  DCHECK_EQ(search_result_info_list->size(), entry_definition_list->size());
755  base::ListValue* results_list = new base::ListValue();
756
757  // Convert Drive files to something File API stack can understand.  See
758  // file_browser_handler_custom_bindings.cc and
759  // file_manager_private_custom_bindings.js for how this is magically
760  // converted to a FileEntry.
761  for (size_t i = 0; i < entry_definition_list->size(); ++i) {
762    base::DictionaryValue* result_dict = new base::DictionaryValue();
763
764    // FileEntry fields.
765    base::DictionaryValue* entry = new base::DictionaryValue();
766    entry->SetString(
767        "fileSystemName", entry_definition_list->at(i).file_system_name);
768    entry->SetString(
769        "fileSystemRoot", entry_definition_list->at(i).file_system_root_url);
770    entry->SetString(
771        "fileFullPath",
772        "/" + entry_definition_list->at(i).full_path.AsUTF8Unsafe());
773    entry->SetBoolean("fileIsDirectory",
774                      entry_definition_list->at(i).is_directory);
775
776    result_dict->Set("entry", entry);
777    result_dict->SetString(
778        "highlightedBaseName",
779        search_result_info_list->at(i).highlighted_base_name);
780    results_list->Append(result_dict);
781  }
782
783  SetResult(results_list);
784  SendResponse(true);
785}
786
787bool FileManagerPrivateGetDriveConnectionStateFunction::RunSync() {
788  api::file_manager_private::DriveConnectionState result;
789
790  switch (drive::util::GetDriveConnectionStatus(GetProfile())) {
791    case drive::util::DRIVE_DISCONNECTED_NOSERVICE:
792      result.type = kDriveConnectionTypeOffline;
793      result.reason.reset(new std::string(kDriveConnectionReasonNoService));
794      break;
795    case drive::util::DRIVE_DISCONNECTED_NONETWORK:
796      result.type = kDriveConnectionTypeOffline;
797      result.reason.reset(new std::string(kDriveConnectionReasonNoNetwork));
798      break;
799    case drive::util::DRIVE_DISCONNECTED_NOTREADY:
800      result.type = kDriveConnectionTypeOffline;
801      result.reason.reset(new std::string(kDriveConnectionReasonNotReady));
802      break;
803    case drive::util::DRIVE_CONNECTED_METERED:
804      result.type = kDriveConnectionTypeMetered;
805      break;
806    case drive::util::DRIVE_CONNECTED:
807      result.type = kDriveConnectionTypeOnline;
808      break;
809  }
810
811  results_ = api::file_manager_private::GetDriveConnectionState::Results::
812      Create(result);
813
814  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
815  if (logger)
816    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
817  return true;
818}
819
820bool FileManagerPrivateRequestAccessTokenFunction::RunAsync() {
821  using extensions::api::file_manager_private::RequestAccessToken::Params;
822  const scoped_ptr<Params> params(Params::Create(*args_));
823  EXTENSION_FUNCTION_VALIDATE(params);
824
825  drive::DriveServiceInterface* const drive_service =
826      drive::util::GetDriveServiceByProfile(GetProfile());
827
828  if (!drive_service) {
829    // DriveService is not available.
830    SetResult(new base::StringValue(""));
831    SendResponse(true);
832    return true;
833  }
834
835  // If refreshing is requested, then clear the token to refetch it.
836  if (params->refresh)
837    drive_service->ClearAccessToken();
838
839  // Retrieve the cached auth token (if available), otherwise the AuthService
840  // instance will try to refetch it.
841  drive_service->RequestAccessToken(
842      base::Bind(&FileManagerPrivateRequestAccessTokenFunction::
843                      OnAccessTokenFetched, this));
844  return true;
845}
846
847void FileManagerPrivateRequestAccessTokenFunction::OnAccessTokenFetched(
848    google_apis::GDataErrorCode code,
849    const std::string& access_token) {
850  SetResult(new base::StringValue(access_token));
851  SendResponse(true);
852}
853
854bool FileManagerPrivateGetShareUrlFunction::RunAsync() {
855  using extensions::api::file_manager_private::GetShareUrl::Params;
856  const scoped_ptr<Params> params(Params::Create(*args_));
857  EXTENSION_FUNCTION_VALIDATE(params);
858
859  const base::FilePath path = file_manager::util::GetLocalPathFromURL(
860      render_view_host(), GetProfile(), GURL(params->url));
861  DCHECK(drive::util::IsUnderDriveMountPoint(path));
862
863  const base::FilePath drive_path = drive::util::ExtractDrivePath(path);
864
865  drive::FileSystemInterface* const file_system =
866      drive::util::GetFileSystemByProfile(GetProfile());
867  if (!file_system) {
868    // |file_system| is NULL if Drive is disabled.
869    return false;
870  }
871
872  file_system->GetShareUrl(
873      drive_path,
874      GURL("chrome-extension://" + extension_id()),  // embed origin
875      base::Bind(&FileManagerPrivateGetShareUrlFunction::OnGetShareUrl, this));
876  return true;
877}
878
879void FileManagerPrivateGetShareUrlFunction::OnGetShareUrl(
880    drive::FileError error,
881    const GURL& share_url) {
882  if (error != drive::FILE_ERROR_OK) {
883    SetError("Share Url for this item is not available.");
884    SendResponse(false);
885    return;
886  }
887
888  SetResult(new base::StringValue(share_url.spec()));
889  SendResponse(true);
890}
891
892bool FileManagerPrivateRequestDriveShareFunction::RunAsync() {
893  using extensions::api::file_manager_private::RequestDriveShare::Params;
894  const scoped_ptr<Params> params(Params::Create(*args_));
895  EXTENSION_FUNCTION_VALIDATE(params);
896
897  const base::FilePath path = file_manager::util::GetLocalPathFromURL(
898      render_view_host(), GetProfile(), GURL(params->url));
899  const base::FilePath drive_path = drive::util::ExtractDrivePath(path);
900  Profile* const owner_profile = drive::util::ExtractProfileFromPath(path);
901
902  if (!owner_profile)
903    return false;
904
905  drive::FileSystemInterface* const owner_file_system =
906      drive::util::GetFileSystemByProfile(owner_profile);
907  if (!owner_file_system)
908    return false;
909
910  const user_manager::User* const user =
911      chromeos::ProfileHelper::Get()->GetUserByProfile(GetProfile());
912  if (!user || !user->is_logged_in())
913    return false;
914
915  google_apis::drive::PermissionRole role =
916      google_apis::drive::PERMISSION_ROLE_READER;
917  switch (params->share_type) {
918    case api::file_manager_private::DRIVE_SHARE_TYPE_NONE:
919      NOTREACHED();
920      return false;
921    case api::file_manager_private::DRIVE_SHARE_TYPE_CAN_EDIT:
922      role = google_apis::drive::PERMISSION_ROLE_WRITER;
923      break;
924    case api::file_manager_private::DRIVE_SHARE_TYPE_CAN_COMMENT:
925      role = google_apis::drive::PERMISSION_ROLE_COMMENTER;
926      break;
927    case api::file_manager_private::DRIVE_SHARE_TYPE_CAN_VIEW:
928      role = google_apis::drive::PERMISSION_ROLE_READER;
929      break;
930  }
931
932  // Share |drive_path| in |owner_file_system| to |user->email()|.
933  owner_file_system->AddPermission(
934      drive_path,
935      user->email(),
936      role,
937      base::Bind(&FileManagerPrivateRequestDriveShareFunction::OnAddPermission,
938                 this));
939  return true;
940}
941
942void FileManagerPrivateRequestDriveShareFunction::OnAddPermission(
943    drive::FileError error) {
944  SendResponse(error == drive::FILE_ERROR_OK);
945}
946
947FileManagerPrivateGetDownloadUrlFunction::
948    FileManagerPrivateGetDownloadUrlFunction() {
949}
950
951FileManagerPrivateGetDownloadUrlFunction::
952    ~FileManagerPrivateGetDownloadUrlFunction() {
953}
954
955bool FileManagerPrivateGetDownloadUrlFunction::RunAsync() {
956  using extensions::api::file_manager_private::GetShareUrl::Params;
957  const scoped_ptr<Params> params(Params::Create(*args_));
958  EXTENSION_FUNCTION_VALIDATE(params);
959
960  // Start getting the file info.
961  drive::FileSystemInterface* const file_system =
962      drive::util::GetFileSystemByProfile(GetProfile());
963  if (!file_system) {
964    // |file_system| is NULL if Drive is disabled or not mounted.
965    SetError("Drive is disabled or not mounted.");
966    SetResult(new base::StringValue(""));  // Intentionally returns a blank.
967    return false;
968  }
969
970  const base::FilePath path = file_manager::util::GetLocalPathFromURL(
971      render_view_host(), GetProfile(), GURL(params->url));
972  if (!drive::util::IsUnderDriveMountPoint(path)) {
973    SetError("The given file is not in Drive.");
974    SetResult(new base::StringValue(""));  // Intentionally returns a blank.
975    return false;
976  }
977  base::FilePath file_path = drive::util::ExtractDrivePath(path);
978
979  file_system->GetResourceEntry(
980      file_path,
981      base::Bind(&FileManagerPrivateGetDownloadUrlFunction::OnGetResourceEntry,
982                 this));
983  return true;
984}
985
986void FileManagerPrivateGetDownloadUrlFunction::OnGetResourceEntry(
987    drive::FileError error,
988    scoped_ptr<drive::ResourceEntry> entry) {
989  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
990
991  if (error != drive::FILE_ERROR_OK) {
992    SetError("Download Url for this item is not available.");
993    SetResult(new base::StringValue(""));  // Intentionally returns a blank.
994    SendResponse(false);
995    return;
996  }
997
998  download_url_ =
999      google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction +
1000      entry->resource_id();
1001
1002  ProfileOAuth2TokenService* oauth2_token_service =
1003      ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
1004  SigninManagerBase* signin_manager =
1005      SigninManagerFactory::GetForProfile(GetProfile());
1006  const std::string& account_id = signin_manager->GetAuthenticatedAccountId();
1007  std::vector<std::string> scopes;
1008  scopes.push_back("https://www.googleapis.com/auth/drive.readonly");
1009
1010  auth_service_.reset(
1011      new google_apis::AuthService(oauth2_token_service,
1012                                   account_id,
1013                                   GetProfile()->GetRequestContext(),
1014                                   scopes));
1015  auth_service_->StartAuthentication(base::Bind(
1016      &FileManagerPrivateGetDownloadUrlFunction::OnTokenFetched, this));
1017}
1018
1019void FileManagerPrivateGetDownloadUrlFunction::OnTokenFetched(
1020    google_apis::GDataErrorCode code,
1021    const std::string& access_token) {
1022  if (code != google_apis::HTTP_SUCCESS) {
1023    SetError("Not able to fetch the token.");
1024    SetResult(new base::StringValue(""));  // Intentionally returns a blank.
1025    SendResponse(false);
1026    return;
1027  }
1028
1029  const std::string url = download_url_ + "?access_token=" + access_token;
1030  SetResult(new base::StringValue(url));
1031
1032  SendResponse(true);
1033}
1034
1035}  // namespace extensions
1036