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/drive/file_system/create_file_operation.h"
6
7#include <string>
8
9#include "base/files/file_util.h"
10#include "chrome/browser/chromeos/drive/drive.pb.h"
11#include "chrome/browser/chromeos/drive/file_change.h"
12#include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
13#include "chrome/browser/chromeos/drive/resource_metadata.h"
14#include "content/public/browser/browser_thread.h"
15#include "net/base/mime_util.h"
16
17using content::BrowserThread;
18
19namespace drive {
20namespace file_system {
21
22namespace {
23
24const char kMimeTypeOctetStream[] = "application/octet-stream";
25
26// Updates local state.
27FileError UpdateLocalState(internal::ResourceMetadata* metadata,
28                           const base::FilePath& file_path,
29                           const std::string& mime_type_in,
30                           ResourceEntry* entry) {
31  DCHECK(metadata);
32
33  FileError error = metadata->GetResourceEntryByPath(file_path, entry);
34  if (error == FILE_ERROR_OK)
35    return FILE_ERROR_EXISTS;
36
37  if (error != FILE_ERROR_NOT_FOUND)
38    return error;
39
40  // If parent path is not a directory, it is an error.
41  ResourceEntry parent;
42  if (metadata->GetResourceEntryByPath(
43          file_path.DirName(), &parent) != FILE_ERROR_OK ||
44      !parent.file_info().is_directory())
45    return FILE_ERROR_NOT_A_DIRECTORY;
46
47  // If mime_type is not set or "application/octet-stream", guess from the
48  // |file_path|. If it is still unsure, use octet-stream by default.
49  std::string mime_type = mime_type_in;
50  if ((mime_type.empty() || mime_type == kMimeTypeOctetStream) &&
51      !net::GetMimeTypeFromFile(file_path, &mime_type))
52    mime_type = kMimeTypeOctetStream;
53
54  // Add the entry to the local resource metadata.
55  const base::Time now = base::Time::Now();
56  entry->mutable_file_info()->set_last_modified(now.ToInternalValue());
57  entry->mutable_file_info()->set_last_accessed(now.ToInternalValue());
58  entry->set_title(file_path.BaseName().AsUTF8Unsafe());
59  entry->set_parent_local_id(parent.local_id());
60  entry->set_metadata_edit_state(ResourceEntry::DIRTY);
61  entry->set_modification_date(base::Time::Now().ToInternalValue());
62  entry->mutable_file_specific_info()->set_content_mime_type(mime_type);
63
64  std::string local_id;
65  error = metadata->AddEntry(*entry, &local_id);
66  entry->set_local_id(local_id);
67  return error;
68}
69
70}  // namespace
71
72CreateFileOperation::CreateFileOperation(
73    base::SequencedTaskRunner* blocking_task_runner,
74    OperationDelegate* delegate,
75    internal::ResourceMetadata* metadata)
76    : blocking_task_runner_(blocking_task_runner),
77      delegate_(delegate),
78      metadata_(metadata),
79      weak_ptr_factory_(this) {
80  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
81}
82
83CreateFileOperation::~CreateFileOperation() {
84  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
85}
86
87void CreateFileOperation::CreateFile(const base::FilePath& file_path,
88                                     bool is_exclusive,
89                                     const std::string& mime_type,
90                                     const FileOperationCallback& callback) {
91  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92  DCHECK(!callback.is_null());
93
94  ResourceEntry* entry = new ResourceEntry;
95  base::PostTaskAndReplyWithResult(
96      blocking_task_runner_.get(),
97      FROM_HERE,
98      base::Bind(&UpdateLocalState,
99                 metadata_,
100                 file_path,
101                 mime_type,
102                 entry),
103      base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
104                 weak_ptr_factory_.GetWeakPtr(),
105                 callback,
106                 file_path,
107                 is_exclusive,
108                 base::Owned(entry)));
109}
110
111void CreateFileOperation::CreateFileAfterUpdateLocalState(
112    const FileOperationCallback& callback,
113    const base::FilePath& file_path,
114    bool is_exclusive,
115    ResourceEntry* entry,
116    FileError error) {
117  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118  DCHECK(!callback.is_null());
119
120  if (error == FILE_ERROR_EXISTS) {
121    // Error if an exclusive mode is requested, or the entry is not a file.
122    error = (is_exclusive ||
123             entry->file_info().is_directory() ||
124             entry->file_specific_info().is_hosted_document()) ?
125        FILE_ERROR_EXISTS : FILE_ERROR_OK;
126  } else if (error == FILE_ERROR_OK) {
127    DCHECK(!entry->file_info().is_directory());
128
129    // Notify delegate if the file was newly created.
130    FileChange changed_file;
131    changed_file.Update(
132        file_path, FileChange::FILE_TYPE_FILE, FileChange::ADD_OR_UPDATE);
133    delegate_->OnFileChangedByOperation(changed_file);
134    delegate_->OnEntryUpdatedByOperation(entry->local_id());
135  }
136  callback.Run(error);
137}
138
139}  // namespace file_system
140}  // namespace drive
141