1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef WEBRTC_BASE_FILEUTILS_H_
12#define WEBRTC_BASE_FILEUTILS_H_
13
14#include <string>
15
16#if !defined(WEBRTC_WIN)
17#include <dirent.h>
18#include <stdio.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <unistd.h>
22#endif
23
24#include "webrtc/base/basictypes.h"
25#include "webrtc/base/common.h"
26#include "webrtc/base/platform_file.h"
27#include "webrtc/base/scoped_ptr.h"
28
29namespace rtc {
30
31class FileStream;
32class Pathname;
33
34//////////////////////////
35// Directory Iterator   //
36//////////////////////////
37
38// A DirectoryIterator is created with a given directory. It originally points
39// to the first file in the directory, and can be advanecd with Next(). This
40// allows you to get information about each file.
41
42class DirectoryIterator {
43  friend class Filesystem;
44 public:
45  // Constructor
46  DirectoryIterator();
47  // Destructor
48  virtual ~DirectoryIterator();
49
50  // Starts traversing a directory
51  // dir is the directory to traverse
52  // returns true if the directory exists and is valid
53  // The iterator will point to the first entry in the directory
54  virtual bool Iterate(const Pathname &path);
55
56  // Advances to the next file
57  // returns true if there were more files in the directory.
58  virtual bool Next();
59
60  // returns true if the file currently pointed to is a directory
61  virtual bool IsDirectory() const;
62
63  // returns the name of the file currently pointed to
64  virtual std::string Name() const;
65
66  // returns the size of the file currently pointed to
67  virtual size_t FileSize() const;
68
69  // returns true if the file is older than seconds
70  virtual bool OlderThan(int seconds) const;
71
72  // checks whether current file is a special directory file "." or ".."
73  bool IsDots() const {
74    std::string filename(Name());
75    return (filename.compare(".") == 0) || (filename.compare("..") == 0);
76  }
77
78 private:
79  std::string directory_;
80#if defined(WEBRTC_WIN)
81  WIN32_FIND_DATA data_;
82  HANDLE handle_;
83#else
84  DIR *dir_;
85  struct dirent *dirent_;
86  struct stat stat_;
87#endif
88};
89
90enum FileTimeType { FTT_CREATED, FTT_MODIFIED, FTT_ACCESSED };
91
92class FilesystemInterface {
93 public:
94  virtual ~FilesystemInterface() {}
95
96  // Returns a DirectoryIterator for a given pathname.
97  // TODO: Do fancy abstracted stuff
98  virtual DirectoryIterator *IterateDirectory() {
99    return new DirectoryIterator();
100  }
101
102  // Opens a file. Returns an open StreamInterface if function succeeds.
103  // Otherwise, returns NULL.
104  // TODO: Add an error param to indicate failure reason, similar to
105  // FileStream::Open
106  virtual FileStream *OpenFile(const Pathname &filename,
107                               const std::string &mode) = 0;
108
109  // Atomically creates an empty file accessible only to the current user if one
110  // does not already exist at the given path, otherwise fails. This is the only
111  // secure way to create a file in a shared temp directory (e.g., C:\Temp on
112  // Windows or /tmp on Linux).
113  // Note that if it is essential that a file be successfully created then the
114  // app must generate random names and retry on failure, or else it will be
115  // vulnerable to a trivial DoS.
116  virtual bool CreatePrivateFile(const Pathname &filename) = 0;
117
118  // This will attempt to delete the path located at filename.
119  // It ASSERTS and returns false if the path points to a folder or a
120  // non-existent file.
121  virtual bool DeleteFile(const Pathname &filename) = 0;
122
123  // This will attempt to delete the empty folder located at 'folder'
124  // It ASSERTS and returns false if the path points to a file or a non-existent
125  // folder. It fails normally if the folder is not empty or can otherwise
126  // not be deleted.
127  virtual bool DeleteEmptyFolder(const Pathname &folder) = 0;
128
129  // This will call IterateDirectory, to get a directory iterator, and then
130  // call DeleteFolderAndContents and DeleteFile on every path contained in this
131  // folder. If the folder is empty, this returns true.
132  virtual bool DeleteFolderContents(const Pathname &folder);
133
134  // This deletes the contents of a folder, recursively, and then deletes
135  // the folder itself.
136  virtual bool DeleteFolderAndContents(const Pathname &folder) {
137    return DeleteFolderContents(folder) && DeleteEmptyFolder(folder);
138  }
139
140  // This will delete whatever is located at path, be it a file or a folder.
141  // If it is a folder, it will delete it recursively by calling
142  // DeleteFolderAndContents
143  bool DeleteFileOrFolder(const Pathname &path) {
144    if (IsFolder(path))
145      return DeleteFolderAndContents(path);
146    else
147      return DeleteFile(path);
148  }
149
150  // Creates a directory. This will call itself recursively to create /foo/bar
151  // even if /foo does not exist. Returns true if the function succeeds.
152  virtual bool CreateFolder(const Pathname &pathname) = 0;
153
154  // This moves a file from old_path to new_path, where "old_path" is a
155  // plain file. This ASSERTs and returns false if old_path points to a
156  // directory, and returns true if the function succeeds.
157  // If the new path is on a different volume than the old path, this function
158  // will attempt to copy and, if that succeeds, delete the old path.
159  virtual bool MoveFolder(const Pathname &old_path,
160                          const Pathname &new_path) = 0;
161
162  // This moves a directory from old_path to new_path, where "old_path" is a
163  // directory. This ASSERTs and returns false if old_path points to a plain
164  // file, and returns true if the function succeeds.
165  // If the new path is on a different volume, this function will attempt to
166  // copy and if that succeeds, delete the old path.
167  virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0;
168
169  // This attempts to move whatever is located at old_path to new_path,
170  // be it a file or folder.
171  bool MoveFileOrFolder(const Pathname &old_path, const Pathname &new_path) {
172    if (IsFile(old_path)) {
173      return MoveFile(old_path, new_path);
174    } else {
175      return MoveFolder(old_path, new_path);
176    }
177  }
178
179  // This copies a file from old_path to new_path. This method ASSERTs and
180  // returns false if old_path is a folder, and returns true if the copy
181  // succeeds.
182  virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path) = 0;
183
184  // This copies a folder from old_path to new_path.
185  bool CopyFolder(const Pathname &old_path, const Pathname &new_path);
186
187  bool CopyFileOrFolder(const Pathname &old_path, const Pathname &new_path) {
188    if (IsFile(old_path))
189      return CopyFile(old_path, new_path);
190    else
191      return CopyFolder(old_path, new_path);
192  }
193
194  // Returns true if pathname refers to a directory
195  virtual bool IsFolder(const Pathname& pathname) = 0;
196
197  // Returns true if pathname refers to a file
198  virtual bool IsFile(const Pathname& pathname) = 0;
199
200  // Returns true if pathname refers to no filesystem object, every parent
201  // directory either exists, or is also absent.
202  virtual bool IsAbsent(const Pathname& pathname) = 0;
203
204  // Returns true if pathname represents a temporary location on the system.
205  virtual bool IsTemporaryPath(const Pathname& pathname) = 0;
206
207  // A folder appropriate for storing temporary files (Contents are
208  // automatically deleted when the program exits)
209  virtual bool GetTemporaryFolder(Pathname &path, bool create,
210                                  const std::string *append) = 0;
211
212  virtual std::string TempFilename(const Pathname &dir,
213                                   const std::string &prefix) = 0;
214
215  // Determines the size of the file indicated by path.
216  virtual bool GetFileSize(const Pathname& path, size_t* size) = 0;
217
218  // Determines a timestamp associated with the file indicated by path.
219  virtual bool GetFileTime(const Pathname& path, FileTimeType which,
220                           time_t* time) = 0;
221
222  // Returns the path to the running application.
223  // Note: This is not guaranteed to work on all platforms.  Be aware of the
224  // limitations before using it, and robustly handle failure.
225  virtual bool GetAppPathname(Pathname* path) = 0;
226
227  // Get a folder that is unique to the current application, which is suitable
228  // for sharing data between executions of the app.  If the per_user arg is
229  // true, the folder is also specific to the current user.
230  virtual bool GetAppDataFolder(Pathname* path, bool per_user) = 0;
231
232  // Get a temporary folder that is unique to the current user and application.
233  // TODO: Re-evaluate the goals of this function.  We probably just need any
234  // directory that won't collide with another existing directory, and which
235  // will be cleaned up when the program exits.
236  virtual bool GetAppTempFolder(Pathname* path) = 0;
237
238  // Delete the contents of the folder returned by GetAppTempFolder
239  bool CleanAppTempFolder();
240
241  virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) = 0;
242
243  // Returns the absolute path of the current directory.
244  virtual Pathname GetCurrentDirectory() = 0;
245
246  // Note: These might go into some shared config section later, but they're
247  // used by some methods in this interface, so we're leaving them here for now.
248  void SetOrganizationName(const std::string& organization) {
249    organization_name_ = organization;
250  }
251  void GetOrganizationName(std::string* organization) {
252    ASSERT(NULL != organization);
253    *organization = organization_name_;
254  }
255  void SetApplicationName(const std::string& application) {
256    application_name_ = application;
257  }
258  void GetApplicationName(std::string* application) {
259    ASSERT(NULL != application);
260    *application = application_name_;
261  }
262
263 protected:
264  std::string organization_name_;
265  std::string application_name_;
266};
267
268class Filesystem {
269 public:
270  static FilesystemInterface *default_filesystem() {
271    ASSERT(default_filesystem_ != NULL);
272    return default_filesystem_;
273  }
274
275  static void set_default_filesystem(FilesystemInterface *filesystem) {
276    default_filesystem_ = filesystem;
277  }
278
279  static FilesystemInterface *swap_default_filesystem(
280      FilesystemInterface *filesystem) {
281    FilesystemInterface *cur = default_filesystem_;
282    default_filesystem_ = filesystem;
283    return cur;
284  }
285
286  static DirectoryIterator *IterateDirectory() {
287    return EnsureDefaultFilesystem()->IterateDirectory();
288  }
289
290  static bool CreateFolder(const Pathname &pathname) {
291    return EnsureDefaultFilesystem()->CreateFolder(pathname);
292  }
293
294  static FileStream *OpenFile(const Pathname &filename,
295                              const std::string &mode) {
296    return EnsureDefaultFilesystem()->OpenFile(filename, mode);
297  }
298
299  static bool CreatePrivateFile(const Pathname &filename) {
300    return EnsureDefaultFilesystem()->CreatePrivateFile(filename);
301  }
302
303  static bool DeleteFile(const Pathname &filename) {
304    return EnsureDefaultFilesystem()->DeleteFile(filename);
305  }
306
307  static bool DeleteEmptyFolder(const Pathname &folder) {
308    return EnsureDefaultFilesystem()->DeleteEmptyFolder(folder);
309  }
310
311  static bool DeleteFolderContents(const Pathname &folder) {
312    return EnsureDefaultFilesystem()->DeleteFolderContents(folder);
313  }
314
315  static bool DeleteFolderAndContents(const Pathname &folder) {
316    return EnsureDefaultFilesystem()->DeleteFolderAndContents(folder);
317  }
318
319  static bool MoveFolder(const Pathname &old_path, const Pathname &new_path) {
320    return EnsureDefaultFilesystem()->MoveFolder(old_path, new_path);
321  }
322
323  static bool MoveFile(const Pathname &old_path, const Pathname &new_path) {
324    return EnsureDefaultFilesystem()->MoveFile(old_path, new_path);
325  }
326
327  static bool CopyFolder(const Pathname &old_path, const Pathname &new_path) {
328    return EnsureDefaultFilesystem()->CopyFolder(old_path, new_path);
329  }
330
331  static bool CopyFile(const Pathname &old_path, const Pathname &new_path) {
332    return EnsureDefaultFilesystem()->CopyFile(old_path, new_path);
333  }
334
335  static bool IsFolder(const Pathname& pathname) {
336    return EnsureDefaultFilesystem()->IsFolder(pathname);
337  }
338
339  static bool IsFile(const Pathname &pathname) {
340    return EnsureDefaultFilesystem()->IsFile(pathname);
341  }
342
343  static bool IsAbsent(const Pathname &pathname) {
344    return EnsureDefaultFilesystem()->IsAbsent(pathname);
345  }
346
347  static bool IsTemporaryPath(const Pathname& pathname) {
348    return EnsureDefaultFilesystem()->IsTemporaryPath(pathname);
349  }
350
351  static bool GetTemporaryFolder(Pathname &path, bool create,
352                                 const std::string *append) {
353    return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append);
354  }
355
356  static std::string TempFilename(const Pathname &dir,
357                                  const std::string &prefix) {
358    return EnsureDefaultFilesystem()->TempFilename(dir, prefix);
359  }
360
361  static bool GetFileSize(const Pathname& path, size_t* size) {
362    return EnsureDefaultFilesystem()->GetFileSize(path, size);
363  }
364
365  static bool GetFileTime(const Pathname& path, FileTimeType which,
366                          time_t* time) {
367    return EnsureDefaultFilesystem()->GetFileTime(path, which, time);
368  }
369
370  static bool GetAppPathname(Pathname* path) {
371    return EnsureDefaultFilesystem()->GetAppPathname(path);
372  }
373
374  static bool GetAppDataFolder(Pathname* path, bool per_user) {
375    return EnsureDefaultFilesystem()->GetAppDataFolder(path, per_user);
376  }
377
378  static bool GetAppTempFolder(Pathname* path) {
379    return EnsureDefaultFilesystem()->GetAppTempFolder(path);
380  }
381
382  static bool CleanAppTempFolder() {
383    return EnsureDefaultFilesystem()->CleanAppTempFolder();
384  }
385
386  static bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
387    return EnsureDefaultFilesystem()->GetDiskFreeSpace(path, freebytes);
388  }
389
390  // Definition has to be in the .cc file due to returning forward-declared
391  // Pathname by value.
392  static Pathname GetCurrentDirectory();
393
394  static void SetOrganizationName(const std::string& organization) {
395    EnsureDefaultFilesystem()->SetOrganizationName(organization);
396  }
397
398  static void GetOrganizationName(std::string* organization) {
399    EnsureDefaultFilesystem()->GetOrganizationName(organization);
400  }
401
402  static void SetApplicationName(const std::string& application) {
403    EnsureDefaultFilesystem()->SetApplicationName(application);
404  }
405
406  static void GetApplicationName(std::string* application) {
407    EnsureDefaultFilesystem()->GetApplicationName(application);
408  }
409
410 private:
411  static FilesystemInterface* default_filesystem_;
412
413  static FilesystemInterface *EnsureDefaultFilesystem();
414  DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem);
415};
416
417class FilesystemScope{
418 public:
419  explicit FilesystemScope(FilesystemInterface *new_fs) {
420    old_fs_ = Filesystem::swap_default_filesystem(new_fs);
421  }
422  ~FilesystemScope() {
423    Filesystem::set_default_filesystem(old_fs_);
424  }
425 private:
426  FilesystemInterface* old_fs_;
427  DISALLOW_IMPLICIT_CONSTRUCTORS(FilesystemScope);
428};
429
430// Generates a unique filename based on the input path.  If no path component
431// is specified, it uses the temporary directory.  If a filename is provided,
432// up to 100 variations of form basename-N.extension are tried.  When
433// create_empty is true, an empty file of this name is created (which
434// decreases the chance of a temporary filename collision with another
435// process).
436bool CreateUniqueFile(Pathname& path, bool create_empty);
437
438}  // namespace rtc
439
440#endif  // WEBRTC_BASE_FILEUTILS_H_
441