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 "webkit/common/fileapi/file_system_util.h"
6
7#include <algorithm>
8
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/strings/string_util.h"
12#include "base/strings/sys_string_conversions.h"
13#include "base/strings/utf_string_conversions.h"
14#include "url/gurl.h"
15#include "webkit/common/database/database_identifier.h"
16
17namespace fileapi {
18
19const char kPersistentDir[] = "/persistent";
20const char kTemporaryDir[] = "/temporary";
21const char kIsolatedDir[] = "/isolated";
22const char kExternalDir[] = "/external";
23const char kTestDir[] = "/test";
24
25const base::FilePath::CharType VirtualPath::kRoot[] = FILE_PATH_LITERAL("/");
26const base::FilePath::CharType VirtualPath::kSeparator = FILE_PATH_LITERAL('/');
27
28// TODO(ericu): Consider removing support for '\', even on Windows, if possible.
29// There's a lot of test code that will need reworking, and we may have trouble
30// with base::FilePath elsewhere [e.g. DirName and other methods may also need
31// replacement].
32base::FilePath VirtualPath::BaseName(const base::FilePath& virtual_path) {
33  base::FilePath::StringType path = virtual_path.value();
34
35  // Keep everything after the final separator, but if the pathname is only
36  // one character and it's a separator, leave it alone.
37  while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
38    path.resize(path.size() - 1);
39  base::FilePath::StringType::size_type last_separator =
40      path.find_last_of(base::FilePath::kSeparators);
41  if (last_separator != base::FilePath::StringType::npos &&
42      last_separator < path.size() - 1)
43    path.erase(0, last_separator + 1);
44
45  return base::FilePath(path);
46}
47
48base::FilePath VirtualPath::DirName(const base::FilePath& virtual_path) {
49  typedef base::FilePath::StringType StringType;
50  StringType  path = virtual_path.value();
51
52  // The logic below is taken from that of base::FilePath::DirName, except
53  // that this version never cares about '//' or drive-letters even on win32.
54
55  // Strip trailing separators.
56  while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
57    path.resize(path.size() - 1);
58
59  StringType::size_type last_separator =
60      path.find_last_of(base::FilePath::kSeparators);
61  if (last_separator == StringType::npos) {
62    // path_ is in the current directory.
63    return base::FilePath(base::FilePath::kCurrentDirectory);
64  }
65  if (last_separator == 0) {
66    // path_ is in the root directory.
67    return base::FilePath(path.substr(0, 1));
68  }
69  // path_ is somewhere else, trim the basename.
70  path.resize(last_separator);
71
72  // Strip trailing separators.
73  while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1]))
74    path.resize(path.size() - 1);
75
76  if (path.empty())
77    return base::FilePath(base::FilePath::kCurrentDirectory);
78
79  return base::FilePath(path);
80}
81
82void VirtualPath::GetComponents(
83    const base::FilePath& path,
84    std::vector<base::FilePath::StringType>* components) {
85  typedef base::FilePath::StringType StringType;
86
87  DCHECK(components);
88  if (!components)
89    return;
90  components->clear();
91  if (path.value().empty())
92    return;
93
94  StringType::size_type begin = 0, end = 0;
95  while (begin < path.value().length() && end != StringType::npos) {
96    end = path.value().find_first_of(base::FilePath::kSeparators, begin);
97    StringType component = path.value().substr(
98        begin, end == StringType::npos ? StringType::npos : end - begin);
99    if (!component.empty() && component != base::FilePath::kCurrentDirectory)
100      components->push_back(component);
101    begin = end + 1;
102  }
103}
104
105void VirtualPath::GetComponentsUTF8Unsafe(
106    const base::FilePath& path,
107    std::vector<std::string>* components) {
108  DCHECK(components);
109  if (!components)
110    return;
111  components->clear();
112
113  std::vector<base::FilePath::StringType> stringtype_components;
114  VirtualPath::GetComponents(path, &stringtype_components);
115  std::vector<base::FilePath::StringType>::const_iterator it;
116  for (it = stringtype_components.begin(); it != stringtype_components.end();
117       ++it) {
118    components->push_back(base::FilePath(*it).AsUTF8Unsafe());
119  }
120}
121
122base::FilePath::StringType VirtualPath::GetNormalizedFilePath(
123    const base::FilePath& path) {
124  base::FilePath::StringType normalized_path = path.value();
125  const size_t num_separators = base::FilePath::StringType(
126      base::FilePath::kSeparators).length();
127  for (size_t i = 0; i < num_separators; ++i) {
128    std::replace(normalized_path.begin(), normalized_path.end(),
129                 base::FilePath::kSeparators[i], kSeparator);
130  }
131
132  return (IsAbsolute(normalized_path)) ?
133      normalized_path : base::FilePath::StringType(kRoot) + normalized_path;
134}
135
136bool VirtualPath::IsAbsolute(const base::FilePath::StringType& path) {
137  return path.find(kRoot) == 0;
138}
139
140bool VirtualPath::IsRootPath(const base::FilePath& path) {
141  std::vector<base::FilePath::StringType> components;
142  VirtualPath::GetComponents(path, &components);
143  return (path.empty() || components.empty() ||
144          (components.size() == 1 &&
145           components[0] == VirtualPath::kRoot));
146}
147
148GURL GetFileSystemRootURI(const GURL& origin_url, FileSystemType type) {
149  // origin_url is based on a security origin, so http://foo.com or file:///
150  // instead of the corresponding filesystem URL.
151  DCHECK(!origin_url.SchemeIsFileSystem());
152
153  std::string url = "filesystem:" + origin_url.GetWithEmptyPath().spec();
154  switch (type) {
155  case kFileSystemTypeTemporary:
156    url += (kTemporaryDir + 1);  // We don't want the leading slash.
157    return GURL(url + "/");
158  case kFileSystemTypePersistent:
159    url += (kPersistentDir + 1);  // We don't want the leading slash.
160    return GURL(url + "/");
161  case kFileSystemTypeExternal:
162    url += (kExternalDir + 1);  // We don't want the leading slash.
163    return GURL(url + "/");
164  case kFileSystemTypeIsolated:
165    url += (kIsolatedDir + 1);  // We don't want the leading slash.
166    return GURL(url + "/");
167  case kFileSystemTypeTest:
168    url += (kTestDir + 1);  // We don't want the leading slash.
169    return GURL(url + "/");
170  // Internal types are always pointed via isolated or external URLs.
171  default:
172    NOTREACHED();
173  }
174  NOTREACHED();
175  return GURL();
176}
177
178std::string GetFileSystemName(const GURL& origin_url, FileSystemType type) {
179  std::string origin_identifier =
180      webkit_database::GetIdentifierFromOrigin(origin_url);
181  std::string type_string = GetFileSystemTypeString(type);
182  DCHECK(!type_string.empty());
183  return origin_identifier + ":" + type_string;
184}
185
186FileSystemType QuotaStorageTypeToFileSystemType(
187    quota::StorageType storage_type) {
188  switch (storage_type) {
189    case quota::kStorageTypeTemporary:
190      return kFileSystemTypeTemporary;
191    case quota::kStorageTypePersistent:
192      return kFileSystemTypePersistent;
193    case quota::kStorageTypeSyncable:
194      return kFileSystemTypeSyncable;
195    case quota::kStorageTypeUnknown:
196      return kFileSystemTypeUnknown;
197  }
198  return kFileSystemTypeUnknown;
199}
200
201quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) {
202  switch (type) {
203    case kFileSystemTypeTemporary:
204      return quota::kStorageTypeTemporary;
205    case kFileSystemTypePersistent:
206      return quota::kStorageTypePersistent;
207    case kFileSystemTypeSyncable:
208    case kFileSystemTypeSyncableForInternalSync:
209      return quota::kStorageTypeSyncable;
210    default:
211      return quota::kStorageTypeUnknown;
212  }
213}
214
215std::string GetFileSystemTypeString(FileSystemType type) {
216  switch (type) {
217    case kFileSystemTypeTemporary:
218      return "Temporary";
219    case kFileSystemTypePersistent:
220      return "Persistent";
221    case kFileSystemTypeIsolated:
222      return "Isolated";
223    case kFileSystemTypeExternal:
224      return "External";
225    case kFileSystemTypeTest:
226      return "Test";
227    case kFileSystemTypeNativeLocal:
228      return "NativeLocal";
229    case kFileSystemTypeRestrictedNativeLocal:
230      return "RestrictedNativeLocal";
231    case kFileSystemTypeDragged:
232      return "Dragged";
233    case kFileSystemTypeNativeMedia:
234      return "NativeMedia";
235    case kFileSystemTypeDeviceMedia:
236      return "DeviceMedia";
237    case kFileSystemTypePicasa:
238      return "Picasa";
239    case kFileSystemTypeItunes:
240      return "Itunes";
241    case kFileSystemTypeDrive:
242      return "Drive";
243    case kFileSystemTypeSyncable:
244    case kFileSystemTypeSyncableForInternalSync:
245      return "Syncable";
246    case kFileSystemTypeNativeForPlatformApp:
247      return "NativeForPlatformApp";
248    case kFileSystemTypeForTransientFile:
249      return "TransientFile";
250    case kFileSystemInternalTypeEnumStart:
251    case kFileSystemInternalTypeEnumEnd:
252      NOTREACHED();
253      // Fall through.
254    case kFileSystemTypeUnknown:
255      return "Unknown";
256  }
257  NOTREACHED();
258  return std::string();
259}
260
261std::string FilePathToString(const base::FilePath& file_path) {
262#if defined(OS_WIN)
263  return UTF16ToUTF8(file_path.value());
264#elif defined(OS_POSIX)
265  return file_path.value();
266#endif
267}
268
269base::FilePath StringToFilePath(const std::string& file_path_string) {
270#if defined(OS_WIN)
271  return base::FilePath(UTF8ToUTF16(file_path_string));
272#elif defined(OS_POSIX)
273  return base::FilePath(file_path_string);
274#endif
275}
276
277WebKit::WebFileError PlatformFileErrorToWebFileError(
278    base::PlatformFileError error_code) {
279  switch (error_code) {
280    case base::PLATFORM_FILE_ERROR_NOT_FOUND:
281      return WebKit::WebFileErrorNotFound;
282    case base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
283    case base::PLATFORM_FILE_ERROR_EXISTS:
284    case base::PLATFORM_FILE_ERROR_NOT_EMPTY:
285      return WebKit::WebFileErrorInvalidModification;
286    case base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
287    case base::PLATFORM_FILE_ERROR_NOT_A_FILE:
288      return WebKit::WebFileErrorTypeMismatch;
289    case base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
290      return WebKit::WebFileErrorNoModificationAllowed;
291    case base::PLATFORM_FILE_ERROR_FAILED:
292      return WebKit::WebFileErrorInvalidState;
293    case base::PLATFORM_FILE_ERROR_ABORT:
294      return WebKit::WebFileErrorAbort;
295    case base::PLATFORM_FILE_ERROR_SECURITY:
296      return WebKit::WebFileErrorSecurity;
297    case base::PLATFORM_FILE_ERROR_NO_SPACE:
298      return WebKit::WebFileErrorQuotaExceeded;
299    default:
300      return WebKit::WebFileErrorInvalidModification;
301  }
302}
303
304bool GetFileSystemPublicType(
305    const std::string type_string,
306    WebKit::WebFileSystemType* type
307) {
308  DCHECK(type);
309  if (type_string == "Temporary") {
310    *type = WebKit::WebFileSystemTypeTemporary;
311    return true;
312  }
313  if (type_string == "Persistent") {
314    *type = WebKit::WebFileSystemTypePersistent;
315    return true;
316  }
317  if (type_string == "Isolated") {
318    *type = WebKit::WebFileSystemTypeIsolated;
319    return true;
320  }
321  if (type_string == "External") {
322    *type = WebKit::WebFileSystemTypeExternal;
323    return true;
324  }
325  NOTREACHED();
326  return false;
327}
328
329std::string GetIsolatedFileSystemName(const GURL& origin_url,
330                                      const std::string& filesystem_id) {
331  std::string name(fileapi::GetFileSystemName(origin_url,
332      fileapi::kFileSystemTypeIsolated));
333  name.append("_");
334  name.append(filesystem_id);
335  return name;
336}
337
338bool CrackIsolatedFileSystemName(const std::string& filesystem_name,
339                                 std::string* filesystem_id) {
340  DCHECK(filesystem_id);
341
342  // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}.
343  std::string start_token(":");
344  start_token = start_token.append(
345      GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_");
346  // WebKit uses different case in its constant for isolated file system
347  // names, so we do a case insensitive compare by converting both strings
348  // to uppercase.
349  // TODO(benwells): Remove this when WebKit uses the same constant.
350  start_token = StringToUpperASCII(start_token);
351  std::string filesystem_name_upper = StringToUpperASCII(filesystem_name);
352  size_t pos = filesystem_name_upper.find(start_token);
353  if (pos == std::string::npos)
354    return false;
355  if (pos == 0)
356    return false;
357
358  *filesystem_id = filesystem_name.substr(pos + start_token.length(),
359                                          std::string::npos);
360  if (filesystem_id->empty())
361    return false;
362
363  return true;
364}
365
366std::string GetIsolatedFileSystemRootURIString(
367    const GURL& origin_url,
368    const std::string& filesystem_id,
369    const std::string& optional_root_name) {
370  std::string root = GetFileSystemRootURI(origin_url,
371                                          kFileSystemTypeIsolated).spec();
372  if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent())
373    return std::string();
374  root.append(filesystem_id);
375  root.append("/");
376  if (!optional_root_name.empty()) {
377    if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent())
378      return std::string();
379    root.append(optional_root_name);
380    root.append("/");
381  }
382  return root;
383}
384
385std::string GetExternalFileSystemRootURIString(
386    const GURL& origin_url,
387    const std::string& mount_name) {
388  std::string root = GetFileSystemRootURI(origin_url,
389                                          kFileSystemTypeExternal).spec();
390  if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent())
391    return std::string();
392  root.append(mount_name);
393  root.append("/");
394  return root;
395}
396
397}  // namespace fileapi
398