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