no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / xpcom / io / nsLocalFileCommon.cpp
blob7e9d7151c1d5ad011ca0e422d9d22c822050e0f5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsLocalFile.h" // includes platform-specific headers
9 #include "nsString.h"
10 #include "nsCOMPtr.h"
11 #include "nsReadableUtils.h"
12 #include "nsPrintfCString.h"
13 #include "nsCRT.h"
14 #include "nsNativeCharsetUtils.h"
15 #include "nsUTF8Utils.h"
16 #include "nsArray.h"
17 #include "nsLocalFileCommon.h"
19 #ifdef XP_WIN
20 # include <string.h>
21 #endif
23 // Extensions that should be considered 'executable', ie will not allow users
24 // to open immediately without first saving to disk, and potentially provoke
25 // other warnings. PLEASE read the longer comment in
26 // toolkit/components/reputationservice/ApplicationReputation.cpp
27 // before modifying this list!
28 // If you update this list, make sure to update the length of sExecutableExts
29 // in nsLocalFileCommmon.h.
30 /* static */
31 const char* const sExecutableExts[] = {
32 // clang-format off
33 ".accda", // MS Access database
34 ".accdb", // MS Access database
35 ".accde", // MS Access database
36 ".accdr", // MS Access database
37 ".ad",
38 ".ade", // access project extension
39 ".adp",
40 ".afploc", // Apple Filing Protocol Location
41 ".air", // Adobe AIR installer
42 ".app", // executable application
43 ".application", // from bug 348763
44 ".appref-ms", // ClickOnce link
45 ".appx",
46 ".appxbundle",
47 ".asp",
48 ".atloc", // Appletalk Location
49 ".bas",
50 ".bat",
51 ".cer", // Signed certificate file
52 ".chm",
53 ".cmd",
54 ".com",
55 ".cpl",
56 ".crt",
57 ".der",
58 ".diagcab", // Windows archive
59 ".exe",
60 ".fileloc", // Apple finder internet location data file
61 ".ftploc", // Apple FTP Location
62 ".fxp", // FoxPro compiled app
63 ".hlp",
64 ".hta",
65 ".inetloc", // Apple finder internet location data file
66 ".inf",
67 ".ins",
68 ".isp",
69 ".jar", // java application bundle
70 #ifndef MOZ_ESR
71 ".jnlp",
72 #endif
73 ".js",
74 ".jse",
75 ".lnk",
76 ".mad", // Access Module Shortcut
77 ".maf", // Access
78 ".mag", // Access Diagram Shortcut
79 ".mam", // Access Macro Shortcut
80 ".maq", // Access Query Shortcut
81 ".mar", // Access Report Shortcut
82 ".mas", // Access Stored Procedure
83 ".mat", // Access Table Shortcut
84 ".mau", // Media Attachment Unit
85 ".mav", // Access View Shortcut
86 ".maw", // Access Data Access Page
87 ".mda", // Access Add-in, MDA Access 2 Workgroup
88 ".mdb",
89 ".mde",
90 ".mdt", // Access Add-in Data
91 ".mdw", // Access Workgroup Information
92 ".mdz", // Access Wizard Template
93 ".msc",
94 ".msh", // Microsoft Shell
95 ".msh1", // Microsoft Shell
96 ".msh1xml", // Microsoft Shell
97 ".msh2", // Microsoft Shell
98 ".msh2xml", // Microsoft Shell
99 ".mshxml", // Microsoft Shell
100 ".msi",
101 ".msix",
102 ".msixbundle",
103 ".msp",
104 ".mst",
105 ".ops", // Office Profile Settings
106 ".pcd",
107 ".pif",
108 ".plg", // Developer Studio Build Log
109 ".prf", // windows system file
110 ".prg",
111 ".pst",
112 ".reg",
113 ".scf", // Windows explorer command
114 ".scr",
115 ".sct",
116 ".settingcontent-ms",
117 ".shb",
118 ".shs",
119 ".url",
120 ".vb",
121 ".vbe",
122 ".vbs",
123 ".vdx",
124 ".vsd",
125 ".vsdm",
126 ".vsdx",
127 ".vsmacros", // Visual Studio .NET Binary-based Macro Project
128 ".vss",
129 ".vssm",
130 ".vssx",
131 ".vst",
132 ".vstm",
133 ".vstx",
134 ".vsw",
135 ".vsx",
136 ".vtx",
137 ".webloc", // MacOS website location file
138 ".ws",
139 ".wsc",
140 ".wsf",
141 ".wsh",
142 ".xll", // MS Excel dynamic link library
143 ".xrm-ms"
144 // clang-format on
147 #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
148 NS_IMETHODIMP
149 nsLocalFile::InitWithFile(nsIFile* aFile) {
150 if (NS_WARN_IF(!aFile)) {
151 return NS_ERROR_INVALID_ARG;
154 nsAutoCString path;
155 aFile->GetNativePath(path);
156 if (path.IsEmpty()) {
157 return NS_ERROR_INVALID_ARG;
159 return InitWithNativePath(path);
161 #endif
163 #define kMaxFilenameLength 255
164 #define kMaxExtensionLength 100
165 #define kMaxSequenceNumberLength 5 // "-9999"
166 // requirement: kMaxExtensionLength <
167 // kMaxFilenameLength - kMaxSequenceNumberLength
169 NS_IMETHODIMP
170 nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes) {
171 nsresult rv;
172 bool longName;
174 #ifdef XP_WIN
175 nsAutoString pathName, leafName, rootName, suffix;
176 rv = GetPath(pathName);
177 #else
178 nsAutoCString pathName, leafName, rootName, suffix;
179 rv = GetNativePath(pathName);
180 #endif
181 if (NS_FAILED(rv)) {
182 return rv;
185 auto FailedBecauseExists = [&](nsresult aRv) {
186 if (aRv == NS_ERROR_FILE_ACCESS_DENIED) {
187 bool exists;
188 return NS_SUCCEEDED(Exists(&exists)) && exists;
190 return aRv == NS_ERROR_FILE_ALREADY_EXISTS;
193 longName =
194 (pathName.Length() + kMaxSequenceNumberLength > kMaxFilenameLength);
195 if (!longName) {
196 rv = Create(aType, aAttributes);
197 if (!FailedBecauseExists(rv)) {
198 return rv;
202 #ifdef XP_WIN
203 rv = GetLeafName(leafName);
204 if (NS_FAILED(rv)) {
205 return rv;
208 const int32_t lastDot = leafName.RFindChar(char16_t('.'));
209 #else
210 rv = GetNativeLeafName(leafName);
211 if (NS_FAILED(rv)) {
212 return rv;
215 const int32_t lastDot = leafName.RFindChar('.');
216 #endif
218 if (lastDot == kNotFound) {
219 rootName = leafName;
220 } else {
221 suffix = Substring(leafName, lastDot); // include '.'
222 rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
225 if (longName) {
226 int32_t maxRootLength =
227 (kMaxFilenameLength - (pathName.Length() - leafName.Length()) -
228 suffix.Length() - kMaxSequenceNumberLength);
230 // We cannot create an item inside a directory whose name is too long.
231 // Also, ensure that at least one character remains after we truncate
232 // the root name, as we don't want to end up with an empty leaf name.
233 if (maxRootLength < 2) {
234 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
237 #ifdef XP_WIN
238 // ensure that we don't cut the name in mid-UTF16-character
239 rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength])
240 ? maxRootLength - 1
241 : maxRootLength);
242 SetLeafName(rootName + suffix);
243 #else
244 if (NS_IsNativeUTF8()) {
245 // ensure that we don't cut the name in mid-UTF8-character
246 // (assume the name is valid UTF8 to begin with)
247 while (UTF8traits::isInSeq(rootName[maxRootLength])) {
248 --maxRootLength;
251 // Another check to avoid ending up with an empty leaf name.
252 if (maxRootLength == 0 && suffix.IsEmpty()) {
253 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
257 rootName.SetLength(maxRootLength);
258 SetNativeLeafName(rootName + suffix);
259 #endif
260 nsresult rvCreate = Create(aType, aAttributes);
261 if (!FailedBecauseExists(rvCreate)) {
262 return rvCreate;
266 for (int indx = 1; indx < 10000; ++indx) {
267 // start with "Picture-1.jpg" after "Picture.jpg" exists
268 #ifdef XP_WIN
269 SetLeafName(rootName +
270 NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) + suffix);
271 #else
272 SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
273 #endif
274 rv = Create(aType, aAttributes);
275 if (NS_SUCCEEDED(rv) || !FailedBecauseExists(rv)) {
276 return rv;
280 // The disk is full, sort of
281 return NS_ERROR_FILE_TOO_BIG;
284 #if defined(XP_WIN)
285 static const char16_t kPathSeparatorChar = '\\';
286 #elif defined(XP_UNIX)
287 static const char16_t kPathSeparatorChar = '/';
288 #else
289 # error Need to define file path separator for your platform
290 #endif
292 static void SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray) {
293 if (*aPath == 0) {
294 return;
297 if (*aPath == kPathSeparatorChar) {
298 aPath++;
300 aNodeArray.AppendElement(aPath);
302 for (char16_t* cp = aPath; *cp != 0; ++cp) {
303 if (*cp == kPathSeparatorChar) {
304 *cp++ = 0;
305 if (*cp == 0) {
306 break;
308 aNodeArray.AppendElement(cp);
313 NS_IMETHODIMP
314 nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult) {
315 if (NS_WARN_IF(!aFromFile)) {
316 return NS_ERROR_INVALID_ARG;
320 // aResult will be UTF-8 encoded
323 nsresult rv;
324 aResult.Truncate(0);
326 nsAutoString thisPath, fromPath;
327 AutoTArray<char16_t*, 32> thisNodes;
328 AutoTArray<char16_t*, 32> fromNodes;
330 rv = GetPath(thisPath);
331 if (NS_FAILED(rv)) {
332 return rv;
334 rv = aFromFile->GetPath(fromPath);
335 if (NS_FAILED(rv)) {
336 return rv;
339 // get raw pointer to mutable string buffer
340 char16_t* thisPathPtr = thisPath.BeginWriting();
341 char16_t* fromPathPtr = fromPath.BeginWriting();
343 SplitPath(thisPathPtr, thisNodes);
344 SplitPath(fromPathPtr, fromNodes);
346 size_t nodeIndex;
347 for (nodeIndex = 0;
348 nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
349 ++nodeIndex) {
350 #ifdef XP_WIN
351 if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
352 char16ptr_t(fromNodes[nodeIndex]))) {
353 break;
355 #else
356 if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
357 break;
359 #endif
362 size_t branchIndex = nodeIndex;
363 for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
364 aResult.AppendLiteral("../");
366 StringJoinAppend(aResult, "/"_ns, mozilla::Span{thisNodes}.From(branchIndex),
367 [](nsACString& dest, const auto& thisNode) {
368 // XXX(Bug 1682869) We wouldn't need to reconstruct a
369 // nsDependentString here if SplitPath already returned
370 // nsDependentString. In fact, it seems SplitPath might be
371 // replaced by ParseString?
372 AppendUTF16toUTF8(nsDependentString{thisNode}, dest);
375 return NS_OK;
378 NS_IMETHODIMP
379 nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
380 const nsACString& aRelativeDesc) {
381 constexpr auto kParentDirStr = "../"_ns;
383 nsCOMPtr<nsIFile> targetFile;
384 nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
385 if (NS_FAILED(rv)) {
386 return rv;
390 // aRelativeDesc is UTF-8 encoded
393 nsCString::const_iterator strBegin, strEnd;
394 aRelativeDesc.BeginReading(strBegin);
395 aRelativeDesc.EndReading(strEnd);
397 nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
398 nsCString::const_iterator pos(strBegin);
400 nsCOMPtr<nsIFile> parentDir;
401 while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
402 rv = targetFile->GetParent(getter_AddRefs(parentDir));
403 if (NS_FAILED(rv)) {
404 return rv;
406 if (!parentDir) {
407 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
409 targetFile = parentDir;
411 nodeBegin = nodeEnd;
412 pos = nodeEnd;
413 nodeEnd = strEnd;
416 nodeBegin = nodeEnd = pos;
417 while (nodeEnd != strEnd) {
418 FindCharInReadable('/', nodeEnd, strEnd);
419 targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
420 if (nodeEnd != strEnd) { // If there's more left in the string, inc over
421 // the '/' nodeEnd is on.
422 ++nodeEnd;
424 nodeBegin = nodeEnd;
427 return InitWithFile(targetFile);
430 NS_IMETHODIMP
431 nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult) {
432 return GetRelativeDescriptor(aFromFile, aResult);
435 NS_IMETHODIMP
436 nsLocalFile::SetRelativePath(nsIFile* aFromFile,
437 const nsACString& aRelativePath) {
438 return SetRelativeDescriptor(aFromFile, aRelativePath);