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