/* * libjingle * Copyright 2004--2006, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TALK_BASE_FILEUTILS_H_ #define TALK_BASE_FILEUTILS_H_ #include #ifdef WIN32 #include "talk/base/win32.h" #else #include #include #include #include #include #endif #include "talk/base/basictypes.h" #include "talk/base/common.h" #include "talk/base/scoped_ptr.h" namespace talk_base { class FileStream; class Pathname; ////////////////////////// // Directory Iterator // ////////////////////////// // A DirectoryIterator is created with a given directory. It originally points // to the first file in the directory, and can be advanecd with Next(). This // allows you to get information about each file. class DirectoryIterator { friend class Filesystem; public: // Constructor DirectoryIterator(); // Destructor virtual ~DirectoryIterator(); // Starts traversing a directory // dir is the directory to traverse // returns true if the directory exists and is valid // The iterator will point to the first entry in the directory virtual bool Iterate(const Pathname &path); // Advances to the next file // returns true if there were more files in the directory. virtual bool Next(); // returns true if the file currently pointed to is a directory virtual bool IsDirectory() const; // returns the name of the file currently pointed to virtual std::string Name() const; // returns the size of the file currently pointed to virtual size_t FileSize() const; // returns the last modified time of the file currently pointed to virtual time_t FileModifyTime() const; // checks whether current file is a special directory file "." or ".." bool IsDots() const { std::string filename(Name()); return (filename.compare(".") == 0) || (filename.compare("..") == 0); } private: std::string directory_; #ifdef WIN32 WIN32_FIND_DATA data_; HANDLE handle_; #else DIR *dir_; struct dirent *dirent_; struct stat stat_; #endif }; enum FileTimeType { FTT_CREATED, FTT_MODIFIED, FTT_ACCESSED }; class FilesystemInterface { public: virtual ~FilesystemInterface() {} // Returns a DirectoryIterator for a given pathname. // TODO: Do fancy abstracted stuff virtual DirectoryIterator *IterateDirectory() { return new DirectoryIterator(); } // Opens a file. Returns an open StreamInterface if function succeeds. // Otherwise, returns NULL. // TODO: Add an error param to indicate failure reason, similar to // FileStream::Open virtual FileStream *OpenFile(const Pathname &filename, const std::string &mode) = 0; // Atomically creates an empty file accessible only to the current user if one // does not already exist at the given path, otherwise fails. This is the only // secure way to create a file in a shared temp directory (e.g., C:\Temp on // Windows or /tmp on Linux). // Note that if it is essential that a file be successfully created then the // app must generate random names and retry on failure, or else it will be // vulnerable to a trivial DoS. virtual bool CreatePrivateFile(const Pathname &filename) = 0; // This will attempt to delete the path located at filename. // It ASSERTS and returns false if the path points to a folder or a // non-existent file. virtual bool DeleteFile(const Pathname &filename) = 0; // This will attempt to delete the empty folder located at 'folder' // It ASSERTS and returns false if the path points to a file or a non-existent // folder. It fails normally if the folder is not empty or can otherwise // not be deleted. virtual bool DeleteEmptyFolder(const Pathname &folder) = 0; // This will call IterateDirectory, to get a directory iterator, and then // call DeleteFolderAndContents and DeleteFile on every path contained in this // folder. If the folder is empty, this returns true. virtual bool DeleteFolderContents(const Pathname &folder); // This deletes the contents of a folder, recursively, and then deletes // the folder itself. virtual bool DeleteFolderAndContents(const Pathname &folder) { return DeleteFolderContents(folder) && DeleteEmptyFolder(folder); } // This will delete whatever is located at path, be it a file or a folder. // If it is a folder, it will delete it recursively by calling // DeleteFolderAndContents bool DeleteFileOrFolder(const Pathname &path) { if (IsFolder(path)) return DeleteFolderAndContents(path); else return DeleteFile(path); } // Creates a directory. This will call itself recursively to create /foo/bar // even if /foo does not exist. Returns true if the function succeeds. virtual bool CreateFolder(const Pathname &pathname) = 0; // This moves a file from old_path to new_path, where "old_path" is a // plain file. This ASSERTs and returns false if old_path points to a // directory, and returns true if the function succeeds. // If the new path is on a different volume than the old path, this function // will attempt to copy and, if that succeeds, delete the old path. virtual bool MoveFolder(const Pathname &old_path, const Pathname &new_path) = 0; // This moves a directory from old_path to new_path, where "old_path" is a // directory. This ASSERTs and returns false if old_path points to a plain // file, and returns true if the function succeeds. // If the new path is on a different volume, this function will attempt to // copy and if that succeeds, delete the old path. virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0; // This attempts to move whatever is located at old_path to new_path, // be it a file or folder. bool MoveFileOrFolder(const Pathname &old_path, const Pathname &new_path) { if (IsFile(old_path)) { return MoveFile(old_path, new_path); } else { return MoveFolder(old_path, new_path); } } // This copies a file from old_path to new_path. This method ASSERTs and // returns false if old_path is a folder, and returns true if the copy // succeeds. virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path) = 0; // This copies a folder from old_path to new_path. bool CopyFolder(const Pathname &old_path, const Pathname &new_path); bool CopyFileOrFolder(const Pathname &old_path, const Pathname &new_path) { if (IsFile(old_path)) return CopyFile(old_path, new_path); else return CopyFolder(old_path, new_path); } // Returns true if pathname refers to a directory virtual bool IsFolder(const Pathname& pathname) = 0; // Returns true if pathname refers to a file virtual bool IsFile(const Pathname& pathname) = 0; // Returns true if pathname refers to no filesystem object, every parent // directory either exists, or is also absent. virtual bool IsAbsent(const Pathname& pathname) = 0; // Returns true if pathname represents a temporary location on the system. virtual bool IsTemporaryPath(const Pathname& pathname) = 0; // A folder appropriate for storing temporary files (Contents are // automatically deleted when the program exits) virtual bool GetTemporaryFolder(Pathname &path, bool create, const std::string *append) = 0; virtual std::string TempFilename(const Pathname &dir, const std::string &prefix) = 0; // Determines the size of the file indicated by path. virtual bool GetFileSize(const Pathname& path, size_t* size) = 0; // Determines a timestamp associated with the file indicated by path. virtual bool GetFileTime(const Pathname& path, FileTimeType which, time_t* time) = 0; // Returns the path to the running application. // Note: This is not guaranteed to work on all platforms. Be aware of the // limitations before using it, and robustly handle failure. virtual bool GetAppPathname(Pathname* path) = 0; // Get a folder that is unique to the current application, which is suitable // for sharing data between executions of the app. If the per_user arg is // true, the folder is also specific to the current user. virtual bool GetAppDataFolder(Pathname* path, bool per_user) = 0; // Get a temporary folder that is unique to the current user and application. // TODO: Re-evaluate the goals of this function. We probably just need any // directory that won't collide with another existing directory, and which // will be cleaned up when the program exits. virtual bool GetAppTempFolder(Pathname* path) = 0; // Delete the contents of the folder returned by GetAppTempFolder bool CleanAppTempFolder(); virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) = 0; // Returns the absolute path of the current directory. virtual Pathname GetCurrentDirectory() = 0; // Note: These might go into some shared config section later, but they're // used by some methods in this interface, so we're leaving them here for now. void SetOrganizationName(const std::string& organization) { organization_name_ = organization; } void GetOrganizationName(std::string* organization) { ASSERT(NULL != organization); *organization = organization_name_; } void SetApplicationName(const std::string& application) { application_name_ = application; } void GetApplicationName(std::string* application) { ASSERT(NULL != application); *application = application_name_; } protected: std::string organization_name_; std::string application_name_; }; class Filesystem { public: static FilesystemInterface *default_filesystem() { ASSERT(default_filesystem_ != NULL); return default_filesystem_; } static void set_default_filesystem(FilesystemInterface *filesystem) { default_filesystem_ = filesystem; } static FilesystemInterface *swap_default_filesystem( FilesystemInterface *filesystem) { FilesystemInterface *cur = default_filesystem_; default_filesystem_ = filesystem; return cur; } static DirectoryIterator *IterateDirectory() { return EnsureDefaultFilesystem()->IterateDirectory(); } static bool CreateFolder(const Pathname &pathname) { return EnsureDefaultFilesystem()->CreateFolder(pathname); } static FileStream *OpenFile(const Pathname &filename, const std::string &mode) { return EnsureDefaultFilesystem()->OpenFile(filename, mode); } static bool CreatePrivateFile(const Pathname &filename) { return EnsureDefaultFilesystem()->CreatePrivateFile(filename); } static bool DeleteFile(const Pathname &filename) { return EnsureDefaultFilesystem()->DeleteFile(filename); } static bool DeleteEmptyFolder(const Pathname &folder) { return EnsureDefaultFilesystem()->DeleteEmptyFolder(folder); } static bool DeleteFolderContents(const Pathname &folder) { return EnsureDefaultFilesystem()->DeleteFolderContents(folder); } static bool DeleteFolderAndContents(const Pathname &folder) { return EnsureDefaultFilesystem()->DeleteFolderAndContents(folder); } static bool MoveFolder(const Pathname &old_path, const Pathname &new_path) { return EnsureDefaultFilesystem()->MoveFolder(old_path, new_path); } static bool MoveFile(const Pathname &old_path, const Pathname &new_path) { return EnsureDefaultFilesystem()->MoveFile(old_path, new_path); } static bool CopyFolder(const Pathname &old_path, const Pathname &new_path) { return EnsureDefaultFilesystem()->CopyFolder(old_path, new_path); } static bool CopyFile(const Pathname &old_path, const Pathname &new_path) { return EnsureDefaultFilesystem()->CopyFile(old_path, new_path); } static bool IsFolder(const Pathname& pathname) { return EnsureDefaultFilesystem()->IsFolder(pathname); } static bool IsFile(const Pathname &pathname) { return EnsureDefaultFilesystem()->IsFile(pathname); } static bool IsAbsent(const Pathname &pathname) { return EnsureDefaultFilesystem()->IsAbsent(pathname); } static bool IsTemporaryPath(const Pathname& pathname) { return EnsureDefaultFilesystem()->IsTemporaryPath(pathname); } static bool GetTemporaryFolder(Pathname &path, bool create, const std::string *append) { return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append); } static std::string TempFilename(const Pathname &dir, const std::string &prefix) { return EnsureDefaultFilesystem()->TempFilename(dir, prefix); } static bool GetFileSize(const Pathname& path, size_t* size) { return EnsureDefaultFilesystem()->GetFileSize(path, size); } static bool GetFileTime(const Pathname& path, FileTimeType which, time_t* time) { return EnsureDefaultFilesystem()->GetFileTime(path, which, time); } static bool GetAppPathname(Pathname* path) { return EnsureDefaultFilesystem()->GetAppPathname(path); } static bool GetAppDataFolder(Pathname* path, bool per_user) { return EnsureDefaultFilesystem()->GetAppDataFolder(path, per_user); } static bool GetAppTempFolder(Pathname* path) { return EnsureDefaultFilesystem()->GetAppTempFolder(path); } static bool CleanAppTempFolder() { return EnsureDefaultFilesystem()->CleanAppTempFolder(); } static bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { return EnsureDefaultFilesystem()->GetDiskFreeSpace(path, freebytes); } // Definition has to be in the .cc file due to returning forward-declared // Pathname by value. static Pathname GetCurrentDirectory(); static void SetOrganizationName(const std::string& organization) { EnsureDefaultFilesystem()->SetOrganizationName(organization); } static void GetOrganizationName(std::string* organization) { EnsureDefaultFilesystem()->GetOrganizationName(organization); } static void SetApplicationName(const std::string& application) { EnsureDefaultFilesystem()->SetApplicationName(application); } static void GetApplicationName(std::string* application) { EnsureDefaultFilesystem()->GetApplicationName(application); } private: static FilesystemInterface* default_filesystem_; static FilesystemInterface *EnsureDefaultFilesystem(); DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem); }; class FilesystemScope{ public: explicit FilesystemScope(FilesystemInterface *new_fs) { old_fs_ = Filesystem::swap_default_filesystem(new_fs); } ~FilesystemScope() { Filesystem::set_default_filesystem(old_fs_); } private: FilesystemInterface* old_fs_; DISALLOW_IMPLICIT_CONSTRUCTORS(FilesystemScope); }; // Generates a unique filename based on the input path. If no path component // is specified, it uses the temporary directory. If a filename is provided, // up to 100 variations of form basename-N.extension are tried. When // create_empty is true, an empty file of this name is created (which // decreases the chance of a temporary filename collision with another // process). bool CreateUniqueFile(Pathname& path, bool create_empty); // Taken from Chromium's base/platform_file.h. // Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile. // Use fclose instead. // TODO(grunell): Remove when Chromium has started to use AEC in each source. // http://crbug.com/264611. #if defined(WIN32) typedef HANDLE PlatformFile; const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; #elif defined(POSIX) typedef int PlatformFile; const PlatformFile kInvalidPlatformFileValue = -1; #else #error Unsupported platform #endif FILE* FdopenPlatformFileForWriting(PlatformFile file); bool ClosePlatformFile(PlatformFile file); } // namespace talk_base #endif // TALK_BASE_FILEUTILS_H_