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
11 #include "nsReadableUtils.h"
12 #include "nsPrintfCString.h"
14 #include "nsNativeCharsetUtils.h"
15 #include "nsUTF8Utils.h"
17 #include "nsLocalFileCommon.h"
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.
31 const char* const sExecutableExts
[] = {
33 ".accda", // MS Access database
34 ".accdb", // MS Access database
35 ".accde", // MS Access database
36 ".accdr", // MS Access database
38 ".ade", // access project extension
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
48 ".atloc", // Appletalk Location
51 ".cer", // Signed certificate file
58 ".diagcab", // Windows archive
60 ".fileloc", // Apple finder internet location data file
61 ".ftploc", // Apple FTP Location
62 ".fxp", // FoxPro compiled app
65 ".inetloc", // Apple finder internet location data file
69 ".jar", // java application bundle
75 ".library-ms", // Windows Library Files
77 ".mad", // Access Module Shortcut
79 ".mag", // Access Diagram Shortcut
80 ".mam", // Access Macro Shortcut
81 ".maq", // Access Query Shortcut
82 ".mar", // Access Report Shortcut
83 ".mas", // Access Stored Procedure
84 ".mat", // Access Table Shortcut
85 ".mau", // Media Attachment Unit
86 ".mav", // Access View Shortcut
87 ".maw", // Access Data Access Page
88 ".mda", // Access Add-in, MDA Access 2 Workgroup
91 ".mdt", // Access Add-in Data
92 ".mdw", // Access Workgroup Information
93 ".mdz", // Access Wizard Template
95 ".msh", // Microsoft Shell
96 ".msh1", // Microsoft Shell
97 ".msh1xml", // Microsoft Shell
98 ".msh2", // Microsoft Shell
99 ".msh2xml", // Microsoft Shell
100 ".mshxml", // Microsoft Shell
106 ".ops", // Office Profile Settings
109 ".plg", // Developer Studio Build Log
110 ".prf", // windows system file
114 ".scf", // Windows explorer command
117 ".settingcontent-ms",
128 ".vsmacros", // Visual Studio .NET Binary-based Macro Project
138 ".webloc", // MacOS website location file
143 ".xll", // MS Excel dynamic link library
148 #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
150 nsLocalFile::InitWithFile(nsIFile
* aFile
) {
151 if (NS_WARN_IF(!aFile
)) {
152 return NS_ERROR_INVALID_ARG
;
156 aFile
->GetNativePath(path
);
157 if (path
.IsEmpty()) {
158 return NS_ERROR_INVALID_ARG
;
160 return InitWithNativePath(path
);
164 #define kMaxFilenameLength 255
165 #define kMaxExtensionLength 100
166 #define kMaxSequenceNumberLength 5 // "-9999"
167 // requirement: kMaxExtensionLength <
168 // kMaxFilenameLength - kMaxSequenceNumberLength
171 nsLocalFile::CreateUnique(uint32_t aType
, uint32_t aAttributes
) {
176 nsAutoString pathName
, leafName
, rootName
, suffix
;
177 rv
= GetPath(pathName
);
179 nsAutoCString pathName
, leafName
, rootName
, suffix
;
180 rv
= GetNativePath(pathName
);
186 auto FailedBecauseExists
= [&](nsresult aRv
) {
187 if (aRv
== NS_ERROR_FILE_ACCESS_DENIED
) {
189 return NS_SUCCEEDED(Exists(&exists
)) && exists
;
191 return aRv
== NS_ERROR_FILE_ALREADY_EXISTS
;
195 (pathName
.Length() + kMaxSequenceNumberLength
> kMaxFilenameLength
);
197 rv
= Create(aType
, aAttributes
);
198 if (!FailedBecauseExists(rv
)) {
204 rv
= GetLeafName(leafName
);
209 const int32_t lastDot
= leafName
.RFindChar(char16_t('.'));
211 rv
= GetNativeLeafName(leafName
);
216 const int32_t lastDot
= leafName
.RFindChar('.');
219 if (lastDot
== kNotFound
) {
222 suffix
= Substring(leafName
, lastDot
); // include '.'
223 rootName
= Substring(leafName
, 0, lastDot
); // strip suffix and dot
227 int32_t maxRootLength
=
228 (kMaxFilenameLength
- (pathName
.Length() - leafName
.Length()) -
229 suffix
.Length() - kMaxSequenceNumberLength
);
231 // We cannot create an item inside a directory whose name is too long.
232 // Also, ensure that at least one character remains after we truncate
233 // the root name, as we don't want to end up with an empty leaf name.
234 if (maxRootLength
< 2) {
235 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
239 // ensure that we don't cut the name in mid-UTF16-character
240 rootName
.SetLength(NS_IS_LOW_SURROGATE(rootName
[maxRootLength
])
243 SetLeafName(rootName
+ suffix
);
245 if (NS_IsNativeUTF8()) {
246 // ensure that we don't cut the name in mid-UTF8-character
247 // (assume the name is valid UTF8 to begin with)
248 while (UTF8traits::isInSeq(rootName
[maxRootLength
])) {
252 // Another check to avoid ending up with an empty leaf name.
253 if (maxRootLength
== 0 && suffix
.IsEmpty()) {
254 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
258 rootName
.SetLength(maxRootLength
);
259 SetNativeLeafName(rootName
+ suffix
);
261 nsresult rvCreate
= Create(aType
, aAttributes
);
262 if (!FailedBecauseExists(rvCreate
)) {
267 for (int indx
= 1; indx
< 10000; ++indx
) {
268 // start with "Picture-1.jpg" after "Picture.jpg" exists
270 SetLeafName(rootName
+
271 NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx
)) + suffix
);
273 SetNativeLeafName(rootName
+ nsPrintfCString("-%d", indx
) + suffix
);
275 rv
= Create(aType
, aAttributes
);
276 if (NS_SUCCEEDED(rv
) || !FailedBecauseExists(rv
)) {
281 // The disk is full, sort of
282 return NS_ERROR_FILE_TOO_BIG
;
286 static const char16_t kPathSeparatorChar
= '\\';
287 #elif defined(XP_UNIX)
288 static const char16_t kPathSeparatorChar
= '/';
290 # error Need to define file path separator for your platform
293 static void SplitPath(char16_t
* aPath
, nsTArray
<char16_t
*>& aNodeArray
) {
298 if (*aPath
== kPathSeparatorChar
) {
301 aNodeArray
.AppendElement(aPath
);
303 for (char16_t
* cp
= aPath
; *cp
!= 0; ++cp
) {
304 if (*cp
== kPathSeparatorChar
) {
309 aNodeArray
.AppendElement(cp
);
315 nsLocalFile::GetRelativeDescriptor(nsIFile
* aFromFile
, nsACString
& aResult
) {
316 if (NS_WARN_IF(!aFromFile
)) {
317 return NS_ERROR_INVALID_ARG
;
321 // aResult will be UTF-8 encoded
327 nsAutoString thisPath
, fromPath
;
328 AutoTArray
<char16_t
*, 32> thisNodes
;
329 AutoTArray
<char16_t
*, 32> fromNodes
;
331 rv
= GetPath(thisPath
);
335 rv
= aFromFile
->GetPath(fromPath
);
340 // get raw pointer to mutable string buffer
341 char16_t
* thisPathPtr
= thisPath
.BeginWriting();
342 char16_t
* fromPathPtr
= fromPath
.BeginWriting();
344 SplitPath(thisPathPtr
, thisNodes
);
345 SplitPath(fromPathPtr
, fromNodes
);
349 nodeIndex
< thisNodes
.Length() && nodeIndex
< fromNodes
.Length();
352 if (_wcsicmp(char16ptr_t(thisNodes
[nodeIndex
]),
353 char16ptr_t(fromNodes
[nodeIndex
]))) {
357 if (nsCRT::strcmp(thisNodes
[nodeIndex
], fromNodes
[nodeIndex
])) {
363 size_t branchIndex
= nodeIndex
;
364 for (nodeIndex
= branchIndex
; nodeIndex
< fromNodes
.Length(); ++nodeIndex
) {
365 aResult
.AppendLiteral("../");
367 StringJoinAppend(aResult
, "/"_ns
, mozilla::Span
{thisNodes
}.From(branchIndex
),
368 [](nsACString
& dest
, const auto& thisNode
) {
369 // XXX(Bug 1682869) We wouldn't need to reconstruct a
370 // nsDependentString here if SplitPath already returned
371 // nsDependentString. In fact, it seems SplitPath might be
372 // replaced by ParseString?
373 AppendUTF16toUTF8(nsDependentString
{thisNode
}, dest
);
380 nsLocalFile::SetRelativeDescriptor(nsIFile
* aFromFile
,
381 const nsACString
& aRelativeDesc
) {
382 constexpr auto kParentDirStr
= "../"_ns
;
384 nsCOMPtr
<nsIFile
> targetFile
;
385 nsresult rv
= aFromFile
->Clone(getter_AddRefs(targetFile
));
391 // aRelativeDesc is UTF-8 encoded
394 nsCString::const_iterator strBegin
, strEnd
;
395 aRelativeDesc
.BeginReading(strBegin
);
396 aRelativeDesc
.EndReading(strEnd
);
398 nsCString::const_iterator
nodeBegin(strBegin
), nodeEnd(strEnd
);
399 nsCString::const_iterator
pos(strBegin
);
401 nsCOMPtr
<nsIFile
> parentDir
;
402 while (FindInReadable(kParentDirStr
, nodeBegin
, nodeEnd
)) {
403 rv
= targetFile
->GetParent(getter_AddRefs(parentDir
));
408 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
410 targetFile
= parentDir
;
417 nodeBegin
= nodeEnd
= pos
;
418 while (nodeEnd
!= strEnd
) {
419 FindCharInReadable('/', nodeEnd
, strEnd
);
420 targetFile
->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin
, nodeEnd
)));
421 if (nodeEnd
!= strEnd
) { // If there's more left in the string, inc over
422 // the '/' nodeEnd is on.
428 return InitWithFile(targetFile
);
432 nsLocalFile::GetRelativePath(nsIFile
* aFromFile
, nsACString
& aResult
) {
433 return GetRelativeDescriptor(aFromFile
, aResult
);
437 nsLocalFile::SetRelativePath(nsIFile
* aFromFile
,
438 const nsACString
& aRelativePath
) {
439 return SetRelativeDescriptor(aFromFile
, aRelativePath
);