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#include <assert.h>
12
13#if defined(WEBRTC_WIN)
14// TODO(grunell): Remove io.h includes when Chromium has started
15// to use AEC in each source. http://crbug.com/264611.
16#include <io.h>
17#include "webrtc/base/win32.h"
18#endif
19
20#include "webrtc/base/pathutils.h"
21#include "webrtc/base/fileutils.h"
22#include "webrtc/base/stringutils.h"
23#include "webrtc/base/stream.h"
24
25#if defined(WEBRTC_WIN)
26#include "webrtc/base/win32filesystem.h"
27#else
28#include "webrtc/base/unixfilesystem.h"
29#endif
30
31#if !defined(WEBRTC_WIN)
32#define MAX_PATH 260
33#endif
34
35namespace rtc {
36
37//////////////////////////
38// Directory Iterator   //
39//////////////////////////
40
41// A DirectoryIterator is created with a given directory. It originally points
42// to the first file in the directory, and can be advanecd with Next(). This
43// allows you to get information about each file.
44
45  // Constructor
46DirectoryIterator::DirectoryIterator()
47#ifdef WEBRTC_WIN
48    : handle_(INVALID_HANDLE_VALUE) {
49#else
50    : dir_(NULL), dirent_(NULL) {
51#endif
52}
53
54  // Destructor
55DirectoryIterator::~DirectoryIterator() {
56#if defined(WEBRTC_WIN)
57  if (handle_ != INVALID_HANDLE_VALUE)
58    ::FindClose(handle_);
59#else
60  if (dir_)
61    closedir(dir_);
62#endif
63}
64
65  // Starts traversing a directory.
66  // dir is the directory to traverse
67  // returns true if the directory exists and is valid
68bool DirectoryIterator::Iterate(const Pathname &dir) {
69  directory_ = dir.pathname();
70#if defined(WEBRTC_WIN)
71  if (handle_ != INVALID_HANDLE_VALUE)
72    ::FindClose(handle_);
73  std::string d = dir.pathname() + '*';
74  handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_);
75  if (handle_ == INVALID_HANDLE_VALUE)
76    return false;
77#else
78  if (dir_ != NULL)
79    closedir(dir_);
80  dir_ = ::opendir(directory_.c_str());
81  if (dir_ == NULL)
82    return false;
83  dirent_ = readdir(dir_);
84  if (dirent_ == NULL)
85    return false;
86
87  if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0)
88    return false;
89#endif
90  return true;
91}
92
93  // Advances to the next file
94  // returns true if there were more files in the directory.
95bool DirectoryIterator::Next() {
96#if defined(WEBRTC_WIN)
97  return ::FindNextFile(handle_, &data_) == TRUE;
98#else
99  dirent_ = ::readdir(dir_);
100  if (dirent_ == NULL)
101    return false;
102
103  return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0;
104#endif
105}
106
107  // returns true if the file currently pointed to is a directory
108bool DirectoryIterator::IsDirectory() const {
109#if defined(WEBRTC_WIN)
110  return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE;
111#else
112  return S_ISDIR(stat_.st_mode);
113#endif
114}
115
116  // returns the name of the file currently pointed to
117std::string DirectoryIterator::Name() const {
118#if defined(WEBRTC_WIN)
119  return ToUtf8(data_.cFileName);
120#else
121  assert(dirent_ != NULL);
122  return dirent_->d_name;
123#endif
124}
125
126  // returns the size of the file currently pointed to
127size_t DirectoryIterator::FileSize() const {
128#if !defined(WEBRTC_WIN)
129  return stat_.st_size;
130#else
131  return data_.nFileSizeLow;
132#endif
133}
134
135  // returns the last modified time of this file
136time_t DirectoryIterator::FileModifyTime() const {
137#if defined(WEBRTC_WIN)
138  time_t val;
139  FileTimeToUnixTime(data_.ftLastWriteTime, &val);
140  return val;
141#else
142  return stat_.st_mtime;
143#endif
144}
145
146FilesystemInterface* Filesystem::default_filesystem_ = NULL;
147
148FilesystemInterface *Filesystem::EnsureDefaultFilesystem() {
149  if (!default_filesystem_) {
150#if defined(WEBRTC_WIN)
151    default_filesystem_ = new Win32Filesystem();
152#else
153    default_filesystem_ = new UnixFilesystem();
154#endif
155  }
156  return default_filesystem_;
157}
158
159bool FilesystemInterface::CopyFolder(const Pathname &old_path,
160                                     const Pathname &new_path) {
161  bool success = true;
162  VERIFY(IsFolder(old_path));
163  Pathname new_dir;
164  new_dir.SetFolder(new_path.pathname());
165  Pathname old_dir;
166  old_dir.SetFolder(old_path.pathname());
167  if (!CreateFolder(new_dir))
168    return false;
169  DirectoryIterator *di = IterateDirectory();
170  if (!di)
171    return false;
172  if (di->Iterate(old_dir.pathname())) {
173    do {
174      if (di->Name() == "." || di->Name() == "..")
175        continue;
176      Pathname source;
177      Pathname dest;
178      source.SetFolder(old_dir.pathname());
179      dest.SetFolder(new_path.pathname());
180      source.SetFilename(di->Name());
181      dest.SetFilename(di->Name());
182      if (!CopyFileOrFolder(source, dest))
183        success = false;
184    } while (di->Next());
185  }
186  delete di;
187  return success;
188}
189
190bool FilesystemInterface::DeleteFolderContents(const Pathname &folder) {
191  bool success = true;
192  VERIFY(IsFolder(folder));
193  DirectoryIterator *di = IterateDirectory();
194  if (!di)
195    return false;
196  if (di->Iterate(folder)) {
197    do {
198      if (di->Name() == "." || di->Name() == "..")
199        continue;
200      Pathname subdir;
201      subdir.SetFolder(folder.pathname());
202      if (di->IsDirectory()) {
203        subdir.AppendFolder(di->Name());
204        if (!DeleteFolderAndContents(subdir)) {
205          success = false;
206        }
207      } else {
208        subdir.SetFilename(di->Name());
209        if (!DeleteFile(subdir)) {
210          success = false;
211        }
212      }
213    } while (di->Next());
214  }
215  delete di;
216  return success;
217}
218
219bool FilesystemInterface::CleanAppTempFolder() {
220  Pathname path;
221  if (!GetAppTempFolder(&path))
222    return false;
223  if (IsAbsent(path))
224    return true;
225  if (!IsTemporaryPath(path)) {
226    ASSERT(false);
227    return false;
228  }
229  return DeleteFolderContents(path);
230}
231
232Pathname Filesystem::GetCurrentDirectory() {
233  return EnsureDefaultFilesystem()->GetCurrentDirectory();
234}
235
236bool CreateUniqueFile(Pathname& path, bool create_empty) {
237  LOG(LS_INFO) << "Path " << path.pathname() << std::endl;
238  // If no folder is supplied, use the temporary folder
239  if (path.folder().empty()) {
240    Pathname temporary_path;
241    if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) {
242      printf("Get temp failed\n");
243      return false;
244    }
245    path.SetFolder(temporary_path.pathname());
246  }
247
248  // If no filename is supplied, use a temporary name
249  if (path.filename().empty()) {
250    std::string folder(path.folder());
251    std::string filename = Filesystem::TempFilename(folder, "gt");
252    path.SetPathname(filename);
253    if (!create_empty) {
254      Filesystem::DeleteFile(path.pathname());
255    }
256    return true;
257  }
258
259  // Otherwise, create a unique name based on the given filename
260  // foo.txt -> foo-N.txt
261  const std::string basename = path.basename();
262  const size_t MAX_VERSION = 100;
263  size_t version = 0;
264  while (version < MAX_VERSION) {
265    std::string pathname = path.pathname();
266
267    if (!Filesystem::IsFile(pathname)) {
268      if (create_empty) {
269        FileStream* fs = Filesystem::OpenFile(pathname, "w");
270        delete fs;
271      }
272      return true;
273    }
274    version += 1;
275    char version_base[MAX_PATH];
276    sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u",
277             basename.c_str(), version);
278    path.SetBasename(version_base);
279  }
280  return true;
281}
282
283// Taken from Chromium's base/platform_file_*.cc.
284// TODO(grunell): Remove when Chromium has started to use AEC in each source.
285// http://crbug.com/264611.
286FILE* FdopenPlatformFileForWriting(PlatformFile file) {
287#if defined(WEBRTC_WIN)
288  if (file == kInvalidPlatformFileValue)
289    return NULL;
290  int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0);
291  if (fd < 0)
292    return NULL;
293  return _fdopen(fd, "w");
294#else
295  return fdopen(file, "w");
296#endif
297}
298
299bool ClosePlatformFile(PlatformFile file) {
300#if defined(WEBRTC_WIN)
301  return CloseHandle(file) != 0;
302#else
303  return close(file);
304#endif
305}
306
307}  // namespace rtc
308