~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/googletalk/libjingle/talk/base/win32filesystem.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
#include "talk/base/win32filesystem.h"
29
 
 
30
 
#include "talk/base/win32.h"
31
 
#include <shellapi.h>
32
 
#include <shlobj.h>
33
 
#include <tchar.h>
34
 
 
35
 
#include "talk/base/fileutils.h"
36
 
#include "talk/base/pathutils.h"
37
 
#include "talk/base/scoped_ptr.h"
38
 
#include "talk/base/stream.h"
39
 
#include "talk/base/stringutils.h"
40
 
 
41
 
// In several places in this file, we test the integrity level of the process
42
 
// before calling GetLongPathName. We do this because calling GetLongPathName
43
 
// when running under protected mode IE (a low integrity process) can result in
44
 
// a virtualized path being returned, which is wrong if you only plan to read.
45
 
// TODO: Waiting to hear back from IE team on whether this is the
46
 
// best approach; IEIsProtectedModeProcess is another possible solution.
47
 
 
48
 
namespace talk_base {
49
 
 
50
 
bool Win32Filesystem::CreateFolder(const Pathname &pathname) {
51
 
  if (pathname.pathname().empty() || !pathname.filename().empty())
52
 
    return false;
53
 
 
54
 
  std::wstring path16;
55
 
  if (!Utf8ToWindowsFilename(pathname.pathname(), &path16))
56
 
    return false;
57
 
 
58
 
  DWORD res = ::GetFileAttributes(path16.c_str());
59
 
  if (res != INVALID_FILE_ATTRIBUTES) {
60
 
    // Something exists at this location, check if it is a directory
61
 
    return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
62
 
  } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
63
 
              && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
64
 
    // Unexpected error
65
 
    return false;
66
 
  }
67
 
 
68
 
  // Directory doesn't exist, look up one directory level
69
 
  if (!pathname.parent_folder().empty()) {
70
 
    Pathname parent(pathname);
71
 
    parent.SetFolder(pathname.parent_folder());
72
 
    if (!CreateFolder(parent)) {
73
 
      return false;
74
 
    }
75
 
  }
76
 
 
77
 
  return (::CreateDirectory(path16.c_str(), NULL) != 0);
78
 
}
79
 
 
80
 
FileStream *Win32Filesystem::OpenFile(const Pathname &filename,
81
 
                                      const std::string &mode) {
82
 
  FileStream *fs = new FileStream();
83
 
  if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) {
84
 
    delete fs;
85
 
    fs = NULL;
86
 
  }
87
 
  return fs;
88
 
}
89
 
 
90
 
bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) {
91
 
  // To make the file private to the current user, we first must construct a
92
 
  // SECURITY_DESCRIPTOR specifying an ACL. This code is mostly based upon
93
 
  // http://msdn.microsoft.com/en-us/library/ms707085%28VS.85%29.aspx
94
 
 
95
 
  // Get the current process token.
96
 
  HANDLE process_token = INVALID_HANDLE_VALUE;
97
 
  if (!::OpenProcessToken(::GetCurrentProcess(),
98
 
                          TOKEN_QUERY,
99
 
                          &process_token)) {
100
 
    LOG_ERR(LS_ERROR) << "OpenProcessToken() failed";
101
 
    return false;
102
 
  }
103
 
 
104
 
  // Get the size of its TOKEN_USER structure. Return value is not checked
105
 
  // because we expect it to fail.
106
 
  DWORD token_user_size = 0;
107
 
  (void)::GetTokenInformation(process_token,
108
 
                              TokenUser,
109
 
                              NULL,
110
 
                              0,
111
 
                              &token_user_size);
112
 
 
113
 
  // Get the TOKEN_USER structure.
114
 
  scoped_array<char> token_user_bytes(new char[token_user_size]);
115
 
  PTOKEN_USER token_user = reinterpret_cast<PTOKEN_USER>(
116
 
      token_user_bytes.get());
117
 
  memset(token_user, 0, token_user_size);
118
 
  BOOL success = ::GetTokenInformation(process_token,
119
 
                                       TokenUser,
120
 
                                       token_user,
121
 
                                       token_user_size,
122
 
                                       &token_user_size);
123
 
  // We're now done with this.
124
 
  ::CloseHandle(process_token);
125
 
  if (!success) {
126
 
    LOG_ERR(LS_ERROR) << "GetTokenInformation() failed";
127
 
    return false;
128
 
  }
129
 
 
130
 
  if (!IsValidSid(token_user->User.Sid)) {
131
 
    LOG_ERR(LS_ERROR) << "Current process has invalid user SID";
132
 
    return false;
133
 
  }
134
 
 
135
 
  // Compute size needed for an ACL that allows access to just this user.
136
 
  int acl_size = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) +
137
 
      GetLengthSid(token_user->User.Sid);
138
 
 
139
 
  // Allocate it.
140
 
  scoped_array<char> acl_bytes(new char[acl_size]);
141
 
  PACL acl = reinterpret_cast<PACL>(acl_bytes.get());
142
 
  memset(acl, 0, acl_size);
143
 
  if (!::InitializeAcl(acl, acl_size, ACL_REVISION)) {
144
 
    LOG_ERR(LS_ERROR) << "InitializeAcl() failed";
145
 
    return false;
146
 
  }
147
 
 
148
 
  // Allow access to only the current user.
149
 
  if (!::AddAccessAllowedAce(acl,
150
 
                             ACL_REVISION,
151
 
                             GENERIC_READ | GENERIC_WRITE | STANDARD_RIGHTS_ALL,
152
 
                             token_user->User.Sid)) {
153
 
    LOG_ERR(LS_ERROR) << "AddAccessAllowedAce() failed";
154
 
    return false;
155
 
  }
156
 
 
157
 
  // Now make the security descriptor.
158
 
  SECURITY_DESCRIPTOR security_descriptor;
159
 
  if (!::InitializeSecurityDescriptor(&security_descriptor,
160
 
                                      SECURITY_DESCRIPTOR_REVISION)) {
161
 
    LOG_ERR(LS_ERROR) << "InitializeSecurityDescriptor() failed";
162
 
    return false;
163
 
  }
164
 
 
165
 
  // Put the ACL in it.
166
 
  if (!::SetSecurityDescriptorDacl(&security_descriptor,
167
 
                                   TRUE,
168
 
                                   acl,
169
 
                                   FALSE)) {
170
 
    LOG_ERR(LS_ERROR) << "SetSecurityDescriptorDacl() failed";
171
 
    return false;
172
 
  }
173
 
 
174
 
  // Finally create the file.
175
 
  SECURITY_ATTRIBUTES security_attributes;
176
 
  security_attributes.nLength = sizeof(security_attributes);
177
 
  security_attributes.lpSecurityDescriptor = &security_descriptor;
178
 
  security_attributes.bInheritHandle = FALSE;
179
 
  HANDLE handle = ::CreateFile(
180
 
      ToUtf16(filename.pathname()).c_str(),
181
 
      GENERIC_READ | GENERIC_WRITE,
182
 
      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
183
 
      &security_attributes,
184
 
      CREATE_NEW,
185
 
      0,
186
 
      NULL);
187
 
  if (INVALID_HANDLE_VALUE == handle) {
188
 
    LOG_ERR(LS_ERROR) << "CreateFile() failed";
189
 
    return false;
190
 
  }
191
 
  if (!::CloseHandle(handle)) {
192
 
    LOG_ERR(LS_ERROR) << "CloseFile() failed";
193
 
    // Continue.
194
 
  }
195
 
  return true;
196
 
}
197
 
 
198
 
bool Win32Filesystem::DeleteFile(const Pathname &filename) {
199
 
  LOG(LS_INFO) << "Deleting file " << filename.pathname();
200
 
  if (!IsFile(filename)) {
201
 
    ASSERT(IsFile(filename));
202
 
    return false;
203
 
  }
204
 
  return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0;
205
 
}
206
 
 
207
 
bool Win32Filesystem::DeleteEmptyFolder(const Pathname &folder) {
208
 
  LOG(LS_INFO) << "Deleting folder " << folder.pathname();
209
 
 
210
 
  std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1);
211
 
  return ::RemoveDirectory(ToUtf16(no_slash).c_str()) != 0;
212
 
}
213
 
 
214
 
bool Win32Filesystem::GetTemporaryFolder(Pathname &pathname, bool create,
215
 
                                         const std::string *append) {
216
 
  wchar_t buffer[MAX_PATH + 1];
217
 
  if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
218
 
    return false;
219
 
  if (!IsCurrentProcessLowIntegrity() &&
220
 
      !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
221
 
    return false;
222
 
  size_t len = strlen(buffer);
223
 
  if ((len > 0) && (buffer[len-1] != '\\')) {
224
 
    len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, L"\\");
225
 
  }
226
 
  if (len >= ARRAY_SIZE(buffer) - 1)
227
 
    return false;
228
 
  pathname.clear();
229
 
  pathname.SetFolder(ToUtf8(buffer));
230
 
  if (append != NULL) {
231
 
    ASSERT(!append->empty());
232
 
    pathname.AppendFolder(*append);
233
 
  }
234
 
  return !create || CreateFolder(pathname);
235
 
}
236
 
 
237
 
std::string Win32Filesystem::TempFilename(const Pathname &dir,
238
 
                                          const std::string &prefix) {
239
 
  wchar_t filename[MAX_PATH];
240
 
  if (::GetTempFileName(ToUtf16(dir.pathname()).c_str(),
241
 
                        ToUtf16(prefix).c_str(), 0, filename) != 0)
242
 
    return ToUtf8(filename);
243
 
  ASSERT(false);
244
 
  return "";
245
 
}
246
 
 
247
 
bool Win32Filesystem::MoveFile(const Pathname &old_path,
248
 
                               const Pathname &new_path) {
249
 
  if (!IsFile(old_path)) {
250
 
    ASSERT(IsFile(old_path));
251
 
    return false;
252
 
  }
253
 
  LOG(LS_INFO) << "Moving " << old_path.pathname()
254
 
               << " to " << new_path.pathname();
255
 
  return ::MoveFile(ToUtf16(old_path.pathname()).c_str(),
256
 
                    ToUtf16(new_path.pathname()).c_str()) != 0;
257
 
}
258
 
 
259
 
bool Win32Filesystem::MoveFolder(const Pathname &old_path,
260
 
                                 const Pathname &new_path) {
261
 
  if (!IsFolder(old_path)) {
262
 
    ASSERT(IsFolder(old_path));
263
 
    return false;
264
 
  }
265
 
  LOG(LS_INFO) << "Moving " << old_path.pathname()
266
 
               << " to " << new_path.pathname();
267
 
  if (::MoveFile(ToUtf16(old_path.pathname()).c_str(),
268
 
               ToUtf16(new_path.pathname()).c_str()) == 0) {
269
 
    if (::GetLastError() != ERROR_NOT_SAME_DEVICE) {
270
 
      LOG_GLE(LS_ERROR) << "Failed to move file";
271
 
      return false;
272
 
    }
273
 
    if (!CopyFolder(old_path, new_path))
274
 
      return false;
275
 
    if (!DeleteFolderAndContents(old_path))
276
 
      return false;
277
 
  }
278
 
  return true;
279
 
}
280
 
 
281
 
bool Win32Filesystem::IsFolder(const Pathname &path) {
282
 
  WIN32_FILE_ATTRIBUTE_DATA data = {0};
283
 
  if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
284
 
                                 GetFileExInfoStandard, &data))
285
 
    return false;
286
 
  return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
287
 
      FILE_ATTRIBUTE_DIRECTORY;
288
 
}
289
 
 
290
 
bool Win32Filesystem::IsFile(const Pathname &path) {
291
 
  WIN32_FILE_ATTRIBUTE_DATA data = {0};
292
 
  if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
293
 
                                 GetFileExInfoStandard, &data))
294
 
    return false;
295
 
  return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
296
 
}
297
 
 
298
 
bool Win32Filesystem::IsAbsent(const Pathname& path) {
299
 
  WIN32_FILE_ATTRIBUTE_DATA data = {0};
300
 
  if (0 != ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
301
 
                                 GetFileExInfoStandard, &data))
302
 
    return false;
303
 
  DWORD err = ::GetLastError();
304
 
  return (ERROR_FILE_NOT_FOUND == err || ERROR_PATH_NOT_FOUND == err);
305
 
}
306
 
 
307
 
bool Win32Filesystem::CopyFile(const Pathname &old_path,
308
 
                               const Pathname &new_path) {
309
 
  return ::CopyFile(ToUtf16(old_path.pathname()).c_str(),
310
 
                    ToUtf16(new_path.pathname()).c_str(), TRUE) != 0;
311
 
}
312
 
 
313
 
bool Win32Filesystem::IsTemporaryPath(const Pathname& pathname) {
314
 
  TCHAR buffer[MAX_PATH + 1];
315
 
  if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
316
 
    return false;
317
 
  if (!IsCurrentProcessLowIntegrity() &&
318
 
      !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
319
 
    return false;
320
 
  return (::strnicmp(ToUtf16(pathname.pathname()).c_str(),
321
 
                     buffer, strlen(buffer)) == 0);
322
 
}
323
 
 
324
 
bool Win32Filesystem::GetFileSize(const Pathname &pathname, size_t *size) {
325
 
  WIN32_FILE_ATTRIBUTE_DATA data = {0};
326
 
  if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(),
327
 
                            GetFileExInfoStandard, &data) == 0)
328
 
  return false;
329
 
  *size = data.nFileSizeLow;
330
 
  return true;
331
 
}
332
 
 
333
 
bool Win32Filesystem::GetFileTime(const Pathname& path, FileTimeType which,
334
 
                                  time_t* time) {
335
 
  WIN32_FILE_ATTRIBUTE_DATA data = {0};
336
 
  if (::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
337
 
                            GetFileExInfoStandard, &data) == 0)
338
 
    return false;
339
 
  switch (which) {
340
 
  case FTT_CREATED:
341
 
    FileTimeToUnixTime(data.ftCreationTime, time);
342
 
    break;
343
 
  case FTT_MODIFIED:
344
 
    FileTimeToUnixTime(data.ftLastWriteTime, time);
345
 
    break;
346
 
  case FTT_ACCESSED:
347
 
    FileTimeToUnixTime(data.ftLastAccessTime, time);
348
 
    break;
349
 
  default:
350
 
    return false;
351
 
  }
352
 
  return true;
353
 
}
354
 
 
355
 
bool Win32Filesystem::GetAppPathname(Pathname* path) {
356
 
  TCHAR buffer[MAX_PATH + 1];
357
 
  if (0 == ::GetModuleFileName(NULL, buffer, ARRAY_SIZE(buffer)))
358
 
    return false;
359
 
  path->SetPathname(ToUtf8(buffer));
360
 
  return true;
361
 
}
362
 
 
363
 
bool Win32Filesystem::GetAppDataFolder(Pathname* path, bool per_user) {
364
 
  ASSERT(!organization_name_.empty());
365
 
  ASSERT(!application_name_.empty());
366
 
  TCHAR buffer[MAX_PATH + 1];
367
 
  int csidl = per_user ? CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA;
368
 
  if (!::SHGetSpecialFolderPath(NULL, buffer, csidl, TRUE))
369
 
    return false;
370
 
  if (!IsCurrentProcessLowIntegrity() &&
371
 
      !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
372
 
    return false;
373
 
  size_t len = strcatn(buffer, ARRAY_SIZE(buffer), __T("\\"));
374
 
  len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
375
 
                 ToUtf16(organization_name_).c_str());
376
 
  if ((len > 0) && (buffer[len-1] != __T('\\'))) {
377
 
    len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\"));
378
 
  }
379
 
  len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
380
 
                 ToUtf16(application_name_).c_str());
381
 
  if ((len > 0) && (buffer[len-1] != __T('\\'))) {
382
 
    len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\"));
383
 
  }
384
 
  if (len >= ARRAY_SIZE(buffer) - 1)
385
 
    return false;
386
 
  path->clear();
387
 
  path->SetFolder(ToUtf8(buffer));
388
 
  return CreateFolder(*path);
389
 
}
390
 
 
391
 
bool Win32Filesystem::GetAppTempFolder(Pathname* path) {
392
 
  if (!GetAppPathname(path))
393
 
    return false;
394
 
  std::string filename(path->filename());
395
 
  return GetTemporaryFolder(*path, true, &filename);
396
 
}
397
 
 
398
 
bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
399
 
  if (!freebytes) {
400
 
    return false;
401
 
  }
402
 
  char drive[4];
403
 
  std::wstring drive16;
404
 
  const wchar_t* target_drive = NULL;
405
 
  if (path.GetDrive(drive, sizeof(drive))) {
406
 
    drive16 = ToUtf16(drive);
407
 
    target_drive = drive16.c_str();
408
 
  } else if (path.folder().substr(0, 2) == "\\\\") {
409
 
    // UNC path, fail.
410
 
    // TODO: Handle UNC paths.
411
 
    return false;
412
 
  } else {
413
 
    // The path is probably relative.  GetDriveType and GetDiskFreeSpaceEx
414
 
    // use the current drive if NULL is passed as the drive name.
415
 
    // TODO: Add method to Pathname to determine if the path is relative.
416
 
    // TODO: Add method to Pathname to convert a path to absolute.
417
 
  }
418
 
  UINT driveType = ::GetDriveType(target_drive);
419
 
  if ( (driveType & DRIVE_REMOTE) || (driveType & DRIVE_UNKNOWN) ) {
420
 
    LOG(LS_VERBOSE) << " remove or unknown drive " << drive;
421
 
    return false;
422
 
  }
423
 
 
424
 
  int64 totalNumberOfBytes;  // receives the number of bytes on disk
425
 
  int64 totalNumberOfFreeBytes;  // receives the free bytes on disk
426
 
  // make sure things won't change in 64 bit machine
427
 
  // TODO replace with compile time assert
428
 
  ASSERT(sizeof(ULARGE_INTEGER) == sizeof(uint64));  //NOLINT
429
 
  if (::GetDiskFreeSpaceEx(target_drive,
430
 
                           (PULARGE_INTEGER)freebytes,
431
 
                           (PULARGE_INTEGER)&totalNumberOfBytes,
432
 
                           (PULARGE_INTEGER)&totalNumberOfFreeBytes)) {
433
 
    return true;
434
 
  } else {
435
 
    LOG(LS_VERBOSE) << " GetDiskFreeSpaceEx returns error ";
436
 
    return false;
437
 
  }
438
 
}
439
 
 
440
 
Pathname Win32Filesystem::GetCurrentDirectory() {
441
 
  Pathname cwd;
442
 
  int path_len = 0;
443
 
  scoped_array<wchar_t> path;
444
 
  do {
445
 
    int needed = ::GetCurrentDirectory(path_len, path.get());
446
 
    if (needed == 0) {
447
 
      // Error.
448
 
      LOG_GLE(LS_ERROR) << "::GetCurrentDirectory() failed";
449
 
      return cwd;  // returns empty pathname
450
 
    }
451
 
    if (needed <= path_len) {
452
 
      // It wrote successfully.
453
 
      break;
454
 
    }
455
 
    // Else need to re-alloc for "needed".
456
 
    path.reset(new wchar_t[needed]);
457
 
    path_len = needed;
458
 
  } while (true);
459
 
  cwd.SetFolder(ToUtf8(path.get()));
460
 
  return cwd;
461
 
}
462
 
 
463
 
// TODO: Consider overriding DeleteFolderAndContents for speed and potentially
464
 
// better OS integration (recycle bin?)
465
 
/*
466
 
  std::wstring temp_path16 = ToUtf16(temp_path.pathname());
467
 
  temp_path16.append(1, '*');
468
 
  temp_path16.append(1, '\0');
469
 
 
470
 
  SHFILEOPSTRUCT file_op = { 0 };
471
 
  file_op.wFunc = FO_DELETE;
472
 
  file_op.pFrom = temp_path16.c_str();
473
 
  file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
474
 
  return (0 == SHFileOperation(&file_op));
475
 
*/
476
 
 
477
 
}  // namespace talk_base