chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_

#include <optional>
#include <string>

#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/volume.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/ash/file_system_provider/provider_interface.h"
#include "chrome/browser/platform_util.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"

class Profile;

namespace ash::cloud_upload {

// The state of the user's OneDrive account. Matches the enum in ODFS.
enum class OdfsAccountState {
  kNormal = 0,
  kReauthenticationRequired = 1,
  kFrozenAccount = 2,
};

struct ODFSMetadata {
  // TODO(b/330786891): Remove reauthentication_required and make the
  // account_state non-optional once no longer needed for backwards
  // compatibility with ODFS.
  bool reauthentication_required = false;
  std::optional<OdfsAccountState> account_state;
  std::string user_email;
};

struct ODFSEntryMetadata {
  ODFSEntryMetadata();
  ODFSEntryMetadata(const ODFSEntryMetadata&);
  ~ODFSEntryMetadata();
  std::optional<std::string> url;
};

typedef base::OnceCallback<void(
    base::expected<ODFSMetadata, base::File::Error> metadata_or_error)>
    GetODFSMetadataCallback;

typedef base::OnceCallback<void(
    base::expected<ODFSEntryMetadata, base::File::Error> metadata)>
    GetODFSEntryMetadataCallback;

// Type of the source location from which a given file is being uploaded.
enum class SourceType {
  LOCAL = 0,
  READ_ONLY = 1,
  CLOUD = 2,
  kMaxValue = CLOUD,
};

// Type of upload of a file to the Cloud.
enum class UploadType {
  kCopy = 0,
  kMove = 1,
  kMaxValue = kMove,
};

// List of UMA enum values for the cloud provider used when opening a file. The
// enum values must be kept in sync with CloudProvider in
// tools/metrics/histograms/metadata/file/enums.xml.
enum class CloudProvider {
  kNone = 0,
  kUnknown = 1,
  kGoogleDrive = 2,
  kOneDrive = 3,
  kMaxValue = kOneDrive,
};

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class OfficeFilesTransferRequired {
  kNotRequired = 0,
  kMove = 1,
  kCopy = 2,
  kMaxValue = kCopy,
};

// List of UMA enum values for Office File Handler task results for Drive. The
// enum values must be kept in sync with OfficeDriveOpenErrors in
// tools/metrics/histograms/metadata/file/enums.xml.
enum class OfficeDriveOpenErrors {
  kOffline = 0,
  kDriveFsInterface = 1,
  kTimeout = 2,
  kNoMetadata = 3,
  kInvalidAlternateUrl = 4,
  kDriveAlternateUrl = 5,
  kUnexpectedAlternateUrl = 6,
  kSuccess = 7,
  kDriveDisabled = 8,
  kNoDriveService = 9,
  kDriveAuthenticationNotReady = 10,
  kMeteredConnection = 11,
  kEmptyAlternateUrl = 12,
  kWaitingForUpload = 13,
  kDisableDrivePreferenceSet = 14,
  kDriveDisabledForAccountType = 15,
  kCannotGetRelativePath = 16,
  kMaxValue = kCannotGetRelativePath,
};

// List of UMA enum values for opening Office files from OneDrive, with the
// MS365 PWA. The enum values must be kept in sync with OfficeOneDriveOpenErrors
// in tools/metrics/histograms/metadata/file/enums.xml.
enum class OfficeOneDriveOpenErrors {
  kSuccess = 0,
  kOffline = 1,
  kNoProfile = 2,
  kNoFileSystemURL = 3,
  kInvalidFileSystemURL = 4,
  kGetActionsGenericError = 5,
  kGetActionsReauthRequired = 6,
  kGetActionsInvalidUrl = 7,
  kGetActionsNoUrl = 8,
  kGetActionsAccessDenied = 9,
  kGetActionsNoEmail = 10,
  kConversionToODFSUrlError = 11,
  kEmailsDoNotMatch = 12,
  kAndroidOneDriveUnsupportedLocation = 13,
  kAndroidOneDriveInvalidUrl = 14,
  kMaxValue = kAndroidOneDriveInvalidUrl,
};

// Records the source volume that an office file is opened from. The values up
// to 12 must be kept in sync with file_manager::VolumeType.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class OfficeFilesSourceVolume {
  kGoogleDrive = 0,
  kDownloadsDirectory = 1,
  kRemovableDiskPartition = 2,
  kMountedArchiveFile = 3,
  kProvided = 4,  // File system provided by FileSystemProvider API.
  kMtp = 5,
  kMediaView = 6,
  kCrostini = 7,
  kAndriodFiles = 8,
  kDocumentsProvider = 9,
  kSmb = 10,
  kSystemInternal = 11,  // Internal volume never exposed to users.
  kGuestOS = 12,         // Guest OS volumes (Crostini, Bruschetta, etc)
  kUnknown = 100,
  kMicrosoftOneDrive = 101,
  kAndroidOneDriveDocumentsProvider = 102,
  kMaxValue = kAndroidOneDriveDocumentsProvider,
};

// List of UMA enum value for Web Drive Office task results. The enum values
// must be kept in sync with OfficeTaskResult in
// tools/metrics/histograms/metadata/file/enums.xml.
enum class OfficeTaskResult {
  kFallbackQuickOffice = 0,
  kFallbackOther = 1,
  kOpened = 2,
  kMoved = 3,
  kCancelledAtConfirmation = 4,
  kFailedToUpload = 5,
  kFailedToOpen = 6,
  kCopied = 7,
  kCancelledAtFallback = 8,
  kCancelledAtSetup = 9,
  kLocalFileTask = 10,
  kFileAlreadyBeingUploaded = 11,
  kCannotGetFallbackChoice = 12,
  kCannotShowSetupDialog = 13,
  kCannotShowMoveConfirmation = 14,
  kNoFilesToOpen = 15,
  kOkAtFallback = 16,
  kOkAtFallbackAfterOpen = 17,
  kFallbackQuickOfficeAfterOpen = 18,
  kCancelledAtFallbackAfterOpen = 19,
  kCannotGetFallbackChoiceAfterOpen = 20,
  kFileAlreadyBeingOpened = 21,
  kMaxValue = kFileAlreadyBeingOpened,
};

// The result of the "Upload to cloud" workflow for Office files.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// The enum values must be kept in sync with OfficeFilesUploadResult in
// tools/metrics/histograms/metadata/file/enums.xml.
enum class OfficeFilesUploadResult {
  kSuccess = 0,
  kOtherError = 1,
  kFileSystemNotFound = 2,
  kMoveOperationCancelled = 3,
  kMoveOperationError = 4,
  kMoveOperationNeedPassword = 5,
  kCopyOperationCancelled = 6,
  kCopyOperationError = 7,
  kCopyOperationNeedPassword = 8,
  kPinningFailedDiskFull = 9,
  kCloudAccessDenied = 10,
  kCloudMetadataError = 11,
  kCloudQuotaFull = 12,
  kCloudError = 13,
  kNoConnection = 14,
  kDestinationUrlError = 15,
  kInvalidURL = 16,
  kCloudReauthRequired = 17,
  kInvalidAlternateUrl = 18,
  kUnexpectedAlternateUrlHost = 19,
  kSyncError = 20,
  kSyncCancelledAndDeleted = 21,
  kSyncCancelledAndTrashed = 22,
  kUploadNotStartedReauthenticationRequired = 23,
  kSuccessAfterReauth = 24,
  kFileNotAnOfficeFile = 25,
  kMaxValue = kFileNotAnOfficeFile,
};

constexpr char kGoogleDriveTaskResultMetricName[] =
    "FileBrowser.OfficeFiles.TaskResult.Drive";
constexpr char kGoogleDriveTaskResultMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.TaskResult.GoogleDrive.MetricState";

constexpr char kOneDriveTaskResultMetricName[] =
    "FileBrowser.OfficeFiles.TaskResult.OneDrive";
constexpr char kOneDriveTaskResultMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.TaskResult.OneDrive.MetricState";

constexpr char kGoogleDriveUploadResultMetricName[] =
    "FileBrowser.OfficeFiles.Open.UploadResult.GoogleDrive";
constexpr char kGoogleDriveUploadResultMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Open.UploadResult.GoogleDrive.MetricState";

constexpr char kOneDriveUploadResultMetricName[] =
    "FileBrowser.OfficeFiles.Open.UploadResult.OneDrive";
constexpr char kOneDriveUploadResultMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Open.UploadResult.OneDrive.MetricState";

constexpr char kGoogleDriveMoveErrorMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Move";
constexpr char kGoogleDriveMoveErrorMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Move.MetricState";

constexpr char kGoogleDriveCopyErrorMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Copy";
constexpr char kGoogleDriveCopyErrorMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Copy.MetricState";

constexpr char kOneDriveMoveErrorMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Move";
constexpr char kOneDriveMoveErrorMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Move.MetricState";

constexpr char kOneDriveCopyErrorMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Copy";
constexpr char kOneDriveCopyErrorMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Copy.MetricState";

constexpr char kDriveOpenSourceVolumeMetric[] =
    "FileBrowser.OfficeFiles.Open.SourceVolume.GoogleDrive";
constexpr char kDriveOpenSourceVolumeMetricStateMetric[] =
    "FileBrowser.OfficeFiles.Open.SourceVolume.GoogleDrive.MetricState";

constexpr char kOneDriveOpenSourceVolumeMetric[] =
    "FileBrowser.OfficeFiles.Open.SourceVolume.MicrosoftOneDrive";
constexpr char kOneDriveOpenSourceVolumeMetricStateMetric[] =
    "FileBrowser.OfficeFiles.Open.SourceVolume.OneDrive.MetricState";

constexpr char kNumberOfFilesToOpenWithGoogleDriveMetric[] =
    "FileBrowser.OfficeFiles.Open.NumberOfFiles.GoogleDrive";

constexpr char kNumberOfFilesToOpenWithOneDriveMetric[] =
    "FileBrowser.OfficeFiles.Open.NumberOfFiles.OneDrive";

constexpr char kOpenInitialCloudProviderMetric[] =
    "FileBrowser.OfficeFiles.Open.CloudProvider";

constexpr char kDriveTransferRequiredMetric[] =
    "FileBrowser.OfficeFiles.Open.TransferRequired.GoogleDrive";
constexpr char kDriveTransferRequiredMetricStateMetric[] =
    "FileBrowser.OfficeFiles.Open.TransferRequired.GoogleDrive.MetricState";

constexpr char kOneDriveTransferRequiredMetric[] =
    "FileBrowser.OfficeFiles.Open.TransferRequired.OneDrive";
constexpr char kOneDriveTransferRequiredMetricStateMetric[] =
    "FileBrowser.OfficeFiles.Open.TransferRequired.OneDrive.MetricState";

constexpr char kDriveErrorMetricName[] = "FileBrowser.OfficeFiles.Errors.Drive";
constexpr char kDriveErrorMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Errors.GoogleDrive.MetricState";

constexpr char kOneDriveErrorMetricName[] =
    "FileBrowser.OfficeFiles.Errors.OneDrive";
constexpr char kOneDriveErrorMetricStateMetricName[] =
    "FileBrowser.OfficeFiles.Errors.OneDrive.MetricState";

// Query actions for this path to get ODFS Metadata.
const char kODFSMetadataQueryPath[] = "/";

// Custom action ids passed from ODFS.
const char kOneDriveUrlActionId[] = "HIDDEN_ONEDRIVE_URL";
const char kUserEmailActionId[] = "HIDDEN_ONEDRIVE_USER_EMAIL";
// TODO(b/330786891): Remove this once it's no longer needed for backwards
// compatibility with ODFS.
const char kReauthenticationRequiredId[] =
    "HIDDEN_ONEDRIVE_REAUTHENTICATION_REQUIRED";
const char kAccountStateId[] = "HIDDEN_ONEDRIVE_ACCOUNT_STATE";

// Get generic error message for uploading office files.
std::string GetGenericErrorMessage();
// Get Microsoft authentication error message for uploading office files.
std::string GetReauthenticationRequiredMessage();
// Get error message for when the file is not a valid document.
std::string GetNotAValidDocumentErrorMessage();
// Get message for when a file is already being opened.
std::string GetAlreadyBeingOpenedMessage();
// Get title for when a file is already being opened.
std::string GetAlreadyBeingOpenedTitle();

// Converts an absolute FilePath into a filesystem URL.
storage::FileSystemURL FilePathToFileSystemURL(
    Profile* profile,
    scoped_refptr<storage::FileSystemContext> file_system_context,
    base::FilePath file_path);

// Creates a directory from a filesystem URL. The callback is called without
// error if the directory already exists.
void CreateDirectoryOnIOThread(
    scoped_refptr<storage::FileSystemContext> file_system_context,
    storage::FileSystemURL destination_folder_url,
    base::OnceCallback<void(base::File::Error)> complete_callback);

// Converts the `volume_type` to the equivalent `OfficeFilesSourceVolume`.
OfficeFilesSourceVolume VolumeTypeToSourceVolume(
    ::file_manager::VolumeType volume_type);

// Returns the type of the source location from which the file is getting
// uploaded (see SourceType values).
SourceType GetSourceType(Profile* profile,
                         const storage::FileSystemURL& source_path);

// Returns the upload type (move or copy) for the upload flow based on the
// source path of the file to upload.
UploadType GetUploadType(Profile* profile,
                         const storage::FileSystemURL& source_path);

// Request ODFS be mounted. If there is an existing mount, ODFS will unmount
// that one after authentication of the new mount.
void RequestODFSMount(Profile* profile,
                      file_system_provider::RequestMountCallback callback);

// Get information of the currently provided ODFS. Expect there to be exactly
// one ODFS.
std::optional<file_system_provider::ProvidedFileSystemInfo> GetODFSInfo(
    Profile* profile);

// Get currently provided ODFS, or null if not mounted.
file_system_provider::ProvidedFileSystemInterface* GetODFS(Profile* profile);

// Get Fusebox path of ODFS mount, or empty if not mounted.
base::FilePath GetODFSFuseboxMount(Profile* profile);

bool IsODFSMounted(Profile* profile);
bool IsODFSInstalled(Profile* profile);
bool IsOfficeWebAppInstalled(Profile* profile);

// Returns true if the IsMicrosoftOfficeOneDriveIntegrationAllowed() returns
// true and if the ODFS extension is installed. It returns false otherwise.
bool IsMicrosoftOfficeOneDriveIntegrationAllowedAndOdfsInstalled(
    Profile* profile);

// Returns true if url refers to an entry on any current mount provided by the
// ODFS file system provider.
bool UrlIsOnODFS(const storage::FileSystemURL& url);

// Get ODFS metadata as actions by doing a special GetActions request (for the
// root directory) and return the actions to |OnODFSMetadataActions| which will
// be converted to |ODFSMetadata| and passed to |callback|.
void GetODFSMetadata(
    file_system_provider::ProvidedFileSystemInterface* file_system,
    GetODFSMetadataCallback callback);

// Get ODFS-specific file metadata as actions by doing a GetActions request for
// this path and post-processing the list of actions into a struct.
void GetODFSEntryMetadata(
    file_system_provider::ProvidedFileSystemInterface* file_system,
    const base::FilePath& path,
    GetODFSEntryMetadataCallback callback);

bool PathIsOnDriveFS(Profile* profile, const base::FilePath& file_path);

// Get the first task error that is not `base::File::Error::FILE_OK`.
std::optional<base::File::Error> GetFirstTaskError(
    const ::file_manager::io_task::ProgressStatus& status);

// Use the most recent Files app window to calculate where the popup auth window
// for OneDrive OAuth should display on the screen.
std::optional<gfx::Rect> CalculateAuthWindowBounds(Profile* profile);

}  // namespace ash::cloud_upload

#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_