1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code, released
16 : * March 31, 1998.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Mike Shaver <shaver@mozilla.org>
25 : * Christopher Blizzard <blizzard@mozilla.org>
26 : * Jason Eager <jce2@po.cwru.edu>
27 : * Stuart Parmenter <pavlov@netscape.com>
28 : * Brendan Eich <brendan@mozilla.org>
29 : * Pete Collins <petejc@mozdev.org>
30 : * Paul Ashford <arougthopher@lizardland.net>
31 : * Fredrik Holmqvist <thesuckiestemail@yahoo.se>
32 : * Josh Aas <josh@mozilla.com>
33 : *
34 : * Alternatively, the contents of this file may be used under the terms of
35 : * either of the GNU General Public License Version 2 or later (the "GPL"),
36 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
37 : * in which case the provisions of the GPL or the LGPL are applicable instead
38 : * of those above. If you wish to allow use of your version of this file only
39 : * under the terms of either the GPL or the LGPL, and not to allow others to
40 : * use your version of this file under the terms of the MPL, indicate your
41 : * decision by deleting the provisions above and replace them with the notice
42 : * and other provisions required by the GPL or the LGPL. If you do not delete
43 : * the provisions above, a recipient may use your version of this file under
44 : * the terms of any one of the MPL, the GPL or the LGPL.
45 : *
46 : * ***** END LICENSE BLOCK ***** */
47 :
48 : /**
49 : * Implementation of nsIFile for "unixy" systems.
50 : */
51 :
52 : #include "mozilla/Util.h"
53 :
54 : #include <sys/types.h>
55 : #include <sys/stat.h>
56 : #include <unistd.h>
57 : #include <fcntl.h>
58 : #include <errno.h>
59 : #include <utime.h>
60 : #include <dirent.h>
61 : #include <ctype.h>
62 : #include <locale.h>
63 : #if defined(VMS)
64 : #include <fabdef.h>
65 : #endif
66 :
67 : #if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
68 : #define USE_LINUX_QUOTACTL
69 : #include <sys/quota.h>
70 : #endif
71 :
72 : #if (MOZ_PLATFORM_MAEMO == 6)
73 : #include <QUrl>
74 : #include <QString>
75 : #if (MOZ_ENABLE_CONTENTACTION)
76 : #include <contentaction/contentaction.h>
77 : #endif
78 : #endif
79 :
80 : #include "nsDirectoryServiceDefs.h"
81 : #include "nsCRT.h"
82 : #include "nsCOMPtr.h"
83 : #include "nsMemory.h"
84 : #include "nsIFile.h"
85 : #include "nsString.h"
86 : #include "nsReadableUtils.h"
87 : #include "nsLocalFile.h"
88 : #include "nsIComponentManager.h"
89 : #include "nsXPIDLString.h"
90 : #include "prproces.h"
91 : #include "nsIDirectoryEnumerator.h"
92 : #include "nsISimpleEnumerator.h"
93 : #include "private/pprio.h"
94 :
95 : #ifdef MOZ_WIDGET_GTK2
96 : #include "nsIGIOService.h"
97 : #include "nsIGnomeVFSService.h"
98 : #endif
99 :
100 : #ifdef MOZ_WIDGET_COCOA
101 : #include <Carbon/Carbon.h>
102 : #include "CocoaFileUtils.h"
103 : #include "prmem.h"
104 : #include "plbase64.h"
105 :
106 : static nsresult MacErrorMapper(OSErr inErr);
107 : #endif
108 :
109 : #if (MOZ_PLATFORM_MAEMO == 5)
110 : #include <glib.h>
111 : #include <hildon-uri.h>
112 : #include <hildon-mime.h>
113 : #include <libosso.h>
114 : #endif
115 :
116 : #ifdef MOZ_WIDGET_ANDROID
117 : #include "AndroidBridge.h"
118 : #include "nsIMIMEService.h"
119 : #include <linux/magic.h>
120 : #endif
121 :
122 : #include "nsNativeCharsetUtils.h"
123 : #include "nsTraceRefcntImpl.h"
124 : #include "nsHashKeys.h"
125 :
126 : using namespace mozilla;
127 :
128 : #define ENSURE_STAT_CACHE() \
129 : PR_BEGIN_MACRO \
130 : if (!FillStatCache()) \
131 : return NSRESULT_FOR_ERRNO(); \
132 : PR_END_MACRO
133 :
134 : #define CHECK_mPath() \
135 : PR_BEGIN_MACRO \
136 : if (mPath.IsEmpty()) \
137 : return NS_ERROR_NOT_INITIALIZED; \
138 : PR_END_MACRO
139 :
140 : /* directory enumerator */
141 : class
142 : nsDirEnumeratorUnix : public nsISimpleEnumerator,
143 : public nsIDirectoryEnumerator
144 : {
145 : public:
146 : nsDirEnumeratorUnix();
147 :
148 : // nsISupports interface
149 : NS_DECL_ISUPPORTS
150 :
151 : // nsISimpleEnumerator interface
152 : NS_DECL_NSISIMPLEENUMERATOR
153 :
154 : // nsIDirectoryEnumerator interface
155 : NS_DECL_NSIDIRECTORYENUMERATOR
156 :
157 : NS_IMETHOD Init(nsLocalFile *parent, bool ignored);
158 :
159 : private:
160 : ~nsDirEnumeratorUnix();
161 :
162 : protected:
163 : NS_IMETHOD GetNextEntry();
164 :
165 : DIR *mDir;
166 : struct dirent *mEntry;
167 : nsCString mParentPath;
168 : };
169 :
170 12472 : nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
171 : mDir(nsnull),
172 12472 : mEntry(nsnull)
173 : {
174 12472 : }
175 :
176 24944 : nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
177 : {
178 12472 : Close();
179 12472 : }
180 :
181 233448 : NS_IMPL_ISUPPORTS2(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
182 :
183 : NS_IMETHODIMP
184 12472 : nsDirEnumeratorUnix::Init(nsLocalFile *parent, bool resolveSymlinks /*ignored*/)
185 : {
186 24944 : nsCAutoString dirPath;
187 24944 : if (NS_FAILED(parent->GetNativePath(dirPath)) ||
188 12472 : dirPath.IsEmpty()) {
189 0 : return NS_ERROR_FILE_INVALID_PATH;
190 : }
191 :
192 12472 : if (NS_FAILED(parent->GetNativePath(mParentPath)))
193 0 : return NS_ERROR_FAILURE;
194 :
195 12472 : mDir = opendir(dirPath.get());
196 12472 : if (!mDir)
197 1412 : return NSRESULT_FOR_ERRNO();
198 11060 : return GetNextEntry();
199 : }
200 :
201 : NS_IMETHODIMP
202 19949 : nsDirEnumeratorUnix::HasMoreElements(bool *result)
203 : {
204 19949 : *result = mDir && mEntry;
205 19949 : if (!*result)
206 5298 : Close();
207 19949 : return NS_OK;
208 : }
209 :
210 : NS_IMETHODIMP
211 14561 : nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
212 : {
213 29122 : nsCOMPtr<nsIFile> file;
214 14561 : nsresult rv = GetNextFile(getter_AddRefs(file));
215 14561 : if (NS_FAILED(rv))
216 0 : return rv;
217 14561 : NS_IF_ADDREF(*_retval = file);
218 14561 : return NS_OK;
219 : }
220 :
221 : NS_IMETHODIMP
222 56167 : nsDirEnumeratorUnix::GetNextEntry()
223 : {
224 134412 : do {
225 56167 : errno = 0;
226 56167 : mEntry = readdir(mDir);
227 :
228 : // end of dir or error
229 56167 : if (!mEntry)
230 11013 : return NSRESULT_FOR_ERRNO();
231 :
232 : // keep going past "." and ".."
233 45154 : } while (mEntry->d_name[0] == '.' &&
234 22047 : (mEntry->d_name[1] == '\0' || // .\0
235 11034 : (mEntry->d_name[1] == '.' &&
236 11023 : mEntry->d_name[2] == '\0'))); // ..\0
237 23118 : return NS_OK;
238 : }
239 :
240 : NS_IMETHODIMP
241 28787 : nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
242 : {
243 : nsresult rv;
244 28787 : if (!mDir || !mEntry) {
245 5716 : *_retval = nsnull;
246 5716 : return NS_OK;
247 : }
248 :
249 46142 : nsCOMPtr<nsILocalFile> file = new nsLocalFile();
250 23071 : if (!file)
251 0 : return NS_ERROR_OUT_OF_MEMORY;
252 :
253 92284 : if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
254 69213 : NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
255 0 : return rv;
256 :
257 23071 : *_retval = file;
258 23071 : NS_ADDREF(*_retval);
259 23071 : return GetNextEntry();
260 : }
261 :
262 : NS_IMETHODIMP
263 23881 : nsDirEnumeratorUnix::Close()
264 : {
265 23881 : if (mDir) {
266 11060 : closedir(mDir);
267 11060 : mDir = nsnull;
268 : }
269 23881 : return NS_OK;
270 : }
271 :
272 561310 : nsLocalFile::nsLocalFile()
273 : {
274 561310 : }
275 :
276 236238 : nsLocalFile::nsLocalFile(const nsLocalFile& other)
277 236238 : : mPath(other.mPath)
278 : {
279 236238 : }
280 :
281 : #ifdef MOZ_WIDGET_COCOA
282 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
283 : nsILocalFileMac,
284 : nsILocalFile,
285 : nsIFile,
286 : nsIHashable)
287 : #else
288 15897806 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
289 : nsILocalFile,
290 : nsIFile,
291 : nsIHashable)
292 : #endif
293 :
294 : nsresult
295 8229 : nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
296 : const nsIID &aIID,
297 : void **aInstancePtr)
298 : {
299 8229 : NS_ENSURE_ARG_POINTER(aInstancePtr);
300 8229 : NS_ENSURE_NO_AGGREGATION(outer);
301 :
302 8229 : *aInstancePtr = nsnull;
303 :
304 16458 : nsCOMPtr<nsIFile> inst = new nsLocalFile();
305 8229 : if (!inst)
306 0 : return NS_ERROR_OUT_OF_MEMORY;
307 8229 : return inst->QueryInterface(aIID, aInstancePtr);
308 : }
309 :
310 : bool
311 145472 : nsLocalFile::FillStatCache() {
312 145472 : if (STAT(mPath.get(), &mCachedStat) == -1) {
313 : // try lstat it may be a symlink
314 5701 : if (LSTAT(mPath.get(), &mCachedStat) == -1) {
315 5680 : return false;
316 : }
317 : }
318 139792 : return true;
319 : }
320 :
321 : NS_IMETHODIMP
322 236238 : nsLocalFile::Clone(nsIFile **file)
323 : {
324 : // Just copy-construct ourselves
325 236238 : *file = new nsLocalFile(*this);
326 236238 : if (!*file)
327 0 : return NS_ERROR_OUT_OF_MEMORY;
328 :
329 236238 : NS_ADDREF(*file);
330 :
331 236238 : return NS_OK;
332 : }
333 :
334 : NS_IMETHODIMP
335 561311 : nsLocalFile::InitWithNativePath(const nsACString &filePath)
336 : {
337 561311 : if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
338 272 : nsCOMPtr<nsIFile> homeDir;
339 272 : nsCAutoString homePath;
340 272 : if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
341 : getter_AddRefs(homeDir))) ||
342 136 : NS_FAILED(homeDir->GetNativePath(homePath))) {
343 0 : return NS_ERROR_FAILURE;
344 : }
345 :
346 272 : mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
347 : } else {
348 561175 : if (filePath.IsEmpty() || filePath.First() != '/')
349 30 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
350 561145 : mPath = filePath;
351 : }
352 :
353 : // trim off trailing slashes
354 561281 : ssize_t len = mPath.Length();
355 1122629 : while ((len > 1) && (mPath[len - 1] == '/'))
356 67 : --len;
357 561281 : mPath.SetLength(len);
358 :
359 561281 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 528 : nsLocalFile::CreateAllAncestors(PRUint32 permissions)
364 : {
365 : // <jband> I promise to play nice
366 528 : char *buffer = mPath.BeginWriting(),
367 528 : *slashp = buffer;
368 :
369 : #ifdef DEBUG_NSIFILE
370 : fprintf(stderr, "nsIFile: before: %s\n", buffer);
371 : #endif
372 :
373 3245 : while ((slashp = strchr(slashp + 1, '/'))) {
374 : /*
375 : * Sequences of '/' are equivalent to a single '/'.
376 : */
377 2189 : if (slashp[1] == '/')
378 0 : continue;
379 :
380 : /*
381 : * If the path has a trailing slash, don't make the last component,
382 : * because we'll get EEXIST in Create when we try to build the final
383 : * component again, and it's easier to condition the logic here than
384 : * there.
385 : */
386 2189 : if (slashp[1] == '\0')
387 0 : break;
388 :
389 : /* Temporarily NUL-terminate here */
390 2189 : *slashp = '\0';
391 : #ifdef DEBUG_NSIFILE
392 : fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
393 : #endif
394 2189 : int mkdir_result = mkdir(buffer, permissions);
395 2189 : int mkdir_errno = errno;
396 2189 : if (mkdir_result == -1) {
397 : /*
398 : * Always set |errno| to EEXIST if the dir already exists
399 : * (we have to do this here since the errno value is not consistent
400 : * in all cases - various reasons like different platform,
401 : * automounter-controlled dir, etc. can affect it (see bug 125489
402 : * for details)).
403 : */
404 1603 : if (access(buffer, F_OK) == 0) {
405 1603 : mkdir_errno = EEXIST;
406 : }
407 : }
408 :
409 : /* Put the / back before we (maybe) return */
410 2189 : *slashp = '/';
411 :
412 : /*
413 : * We could get EEXIST for an existing file -- not directory --
414 : * with the name of one of our ancestors, but that's OK: we'll get
415 : * ENOTDIR when we try to make the next component in the path,
416 : * either here on back in Create, and error out appropriately.
417 : */
418 2189 : if (mkdir_result == -1 && mkdir_errno != EEXIST)
419 0 : return nsresultForErrno(mkdir_errno);
420 : }
421 :
422 : #ifdef DEBUG_NSIFILE
423 : fprintf(stderr, "nsIFile: after: %s\n", buffer);
424 : #endif
425 :
426 528 : return NS_OK;
427 : }
428 :
429 : NS_IMETHODIMP
430 353718 : nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
431 : {
432 353718 : *_retval = PR_Open(mPath.get(), flags, mode);
433 353718 : if (! *_retval)
434 767 : return NS_ErrorAccordingToNSPR();
435 :
436 352951 : if (flags & DELETE_ON_CLOSE) {
437 1 : PR_Delete(mPath.get());
438 : }
439 :
440 : #if defined(LINUX) && !defined(ANDROID)
441 352951 : if (flags & OS_READAHEAD) {
442 0 : readahead(PR_FileDesc2NativeHandle(*_retval), 0, 0);
443 : }
444 : #endif
445 352951 : return NS_OK;
446 : }
447 :
448 : NS_IMETHODIMP
449 0 : nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
450 : {
451 0 : *_retval = fopen(mPath.get(), mode);
452 0 : if (! *_retval)
453 0 : return NS_ERROR_FAILURE;
454 :
455 0 : return NS_OK;
456 : }
457 :
458 : static int
459 3724 : do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
460 : {
461 3724 : *_retval = PR_Open(path, flags, mode);
462 3724 : return *_retval ? 0 : -1;
463 : }
464 :
465 : static int
466 8430 : do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
467 : {
468 8430 : *_retval = nsnull;
469 8430 : return mkdir(path, mode);
470 : }
471 :
472 : nsresult
473 11626 : nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags,
474 : PRUint32 permissions, PRFileDesc **_retval)
475 : {
476 11626 : if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
477 0 : return NS_ERROR_FILE_UNKNOWN_TYPE;
478 :
479 : int result;
480 : int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) =
481 11626 : (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
482 :
483 11626 : result = createFunc(mPath.get(), flags, permissions, _retval);
484 11626 : if (result == -1 && errno == ENOENT) {
485 : /*
486 : * If we failed because of missing ancestor components, try to create
487 : * them and then retry the original creation.
488 : *
489 : * Ancestor directories get the same permissions as the file we're
490 : * creating, with the X bit set for each of (user,group,other) with
491 : * an R bit in the original permissions. If you want to do anything
492 : * fancy like setgid or sticky bits, do it by hand.
493 : */
494 528 : int dirperm = permissions;
495 528 : if (permissions & S_IRUSR)
496 528 : dirperm |= S_IXUSR;
497 528 : if (permissions & S_IRGRP)
498 528 : dirperm |= S_IXGRP;
499 528 : if (permissions & S_IROTH)
500 528 : dirperm |= S_IXOTH;
501 :
502 : #ifdef DEBUG_NSIFILE
503 : fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
504 : dirperm);
505 : #endif
506 :
507 528 : if (NS_FAILED(CreateAllAncestors(dirperm)))
508 0 : return NS_ERROR_FAILURE;
509 :
510 : #ifdef DEBUG_NSIFILE
511 : fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
512 : #endif
513 528 : result = createFunc(mPath.get(), flags, permissions, _retval);
514 : }
515 11626 : return NSRESULT_FOR_RETURN(result);
516 : }
517 :
518 : NS_IMETHODIMP
519 11122 : nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
520 : {
521 11122 : PRFileDesc *junk = nsnull;
522 : nsresult rv = CreateAndKeepOpen(type,
523 : PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
524 : PR_EXCL,
525 : permissions,
526 11122 : &junk);
527 11122 : if (junk)
528 1621 : PR_Close(junk);
529 11122 : return rv;
530 : }
531 :
532 : NS_IMETHODIMP
533 85976 : nsLocalFile::AppendNative(const nsACString &fragment)
534 : {
535 85976 : if (fragment.IsEmpty())
536 75 : return NS_OK;
537 :
538 : // only one component of path can be appended
539 85901 : nsACString::const_iterator begin, end;
540 85901 : if (FindCharInReadable('/', fragment.BeginReading(begin),
541 85901 : fragment.EndReading(end)))
542 1 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
543 :
544 85900 : return AppendRelativeNativePath(fragment);
545 : }
546 :
547 : NS_IMETHODIMP
548 545410 : nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
549 : {
550 545410 : if (fragment.IsEmpty())
551 0 : return NS_OK;
552 :
553 : // No leading '/'
554 545410 : if (fragment.First() == '/')
555 0 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
556 :
557 545410 : if (mPath.EqualsLiteral("/"))
558 0 : mPath.Append(fragment);
559 : else
560 545410 : mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
561 :
562 545410 : return NS_OK;
563 : }
564 :
565 : NS_IMETHODIMP
566 1412 : nsLocalFile::Normalize()
567 : {
568 1412 : char resolved_path[PATH_MAX] = "";
569 1412 : char *resolved_path_ptr = nsnull;
570 :
571 1412 : resolved_path_ptr = realpath(mPath.get(), resolved_path);
572 :
573 : // if there is an error, the return is null.
574 1412 : if (!resolved_path_ptr)
575 485 : return NSRESULT_FOR_ERRNO();
576 :
577 927 : mPath = resolved_path;
578 927 : return NS_OK;
579 : }
580 :
581 : void
582 72529 : nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
583 : nsACString::const_iterator &end)
584 : {
585 : // XXX perhaps we should cache this??
586 :
587 72529 : mPath.BeginReading(begin);
588 72529 : mPath.EndReading(end);
589 :
590 72529 : nsACString::const_iterator it = end;
591 72529 : nsACString::const_iterator stop = begin;
592 72529 : --stop;
593 72529 : while (--it != stop) {
594 1265888 : if (*it == '/') {
595 72529 : begin = ++it;
596 72529 : return;
597 : }
598 : }
599 : // else, the entire path is the leaf name (which means this
600 : // isn't an absolute path... unexpected??)
601 : }
602 :
603 : NS_IMETHODIMP
604 68338 : nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
605 : {
606 68338 : nsACString::const_iterator begin, end;
607 68338 : LocateNativeLeafName(begin, end);
608 68338 : aLeafName = Substring(begin, end);
609 68338 : return NS_OK;
610 : }
611 :
612 : NS_IMETHODIMP
613 3007 : nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
614 : {
615 3007 : nsACString::const_iterator begin, end;
616 3007 : LocateNativeLeafName(begin, end);
617 3007 : mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
618 3007 : return NS_OK;
619 : }
620 :
621 : NS_IMETHODIMP
622 240002 : nsLocalFile::GetNativePath(nsACString &_retval)
623 : {
624 240002 : _retval = mPath;
625 240002 : return NS_OK;
626 : }
627 :
628 : nsresult
629 2598 : nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
630 : const nsACString &newName,
631 : nsACString &_retval)
632 : {
633 : nsresult rv;
634 5196 : nsCOMPtr<nsIFile> oldParent;
635 :
636 2598 : if (!newParent) {
637 828 : if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
638 0 : return rv;
639 828 : newParent = oldParent.get();
640 : } else {
641 : // check to see if our target directory exists
642 : bool targetExists;
643 1770 : if (NS_FAILED(rv = newParent->Exists(&targetExists)))
644 0 : return rv;
645 :
646 1770 : if (!targetExists) {
647 : // XXX create the new directory with some permissions
648 167 : rv = newParent->Create(DIRECTORY_TYPE, 0755);
649 167 : if (NS_FAILED(rv))
650 0 : return rv;
651 : } else {
652 : // make sure that the target is actually a directory
653 : bool targetIsDirectory;
654 1603 : if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
655 0 : return rv;
656 1603 : if (!targetIsDirectory)
657 0 : return NS_ERROR_FILE_DESTINATION_NOT_DIR;
658 : }
659 : }
660 :
661 2598 : nsACString::const_iterator nameBegin, nameEnd;
662 2598 : if (!newName.IsEmpty()) {
663 1487 : newName.BeginReading(nameBegin);
664 1487 : newName.EndReading(nameEnd);
665 : }
666 : else
667 1111 : LocateNativeLeafName(nameBegin, nameEnd);
668 :
669 5196 : nsCAutoString dirName;
670 2598 : if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
671 0 : return rv;
672 :
673 : _retval = dirName
674 5196 : + NS_LITERAL_CSTRING("/")
675 7794 : + Substring(nameBegin, nameEnd);
676 2598 : return NS_OK;
677 : }
678 :
679 : nsresult
680 6 : nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
681 : {
682 : nsresult rv;
683 : /*
684 : * dirCheck is used for various boolean test results such as from Equals,
685 : * Exists, isDir, etc.
686 : */
687 : bool dirCheck, isSymlink;
688 : PRUint32 oldPerms;
689 :
690 6 : if (NS_FAILED(rv = IsDirectory(&dirCheck)))
691 0 : return rv;
692 6 : if (!dirCheck)
693 0 : return CopyToNative(newParent, EmptyCString());
694 :
695 6 : if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
696 0 : return rv;
697 6 : if (dirCheck) {
698 : // can't copy dir to itself
699 0 : return NS_ERROR_INVALID_ARG;
700 : }
701 :
702 6 : if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
703 0 : return rv;
704 : // get the dirs old permissions
705 6 : if (NS_FAILED(rv = GetPermissions(&oldPerms)))
706 0 : return rv;
707 6 : if (!dirCheck) {
708 6 : if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
709 0 : return rv;
710 : } else { // dir exists lets try to use leaf
711 0 : nsCAutoString leafName;
712 0 : if (NS_FAILED(rv = GetNativeLeafName(leafName)))
713 0 : return rv;
714 0 : if (NS_FAILED(rv = newParent->AppendNative(leafName)))
715 0 : return rv;
716 0 : if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
717 0 : return rv;
718 0 : if (dirCheck)
719 0 : return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
720 0 : if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
721 0 : return rv;
722 : }
723 :
724 12 : nsCOMPtr<nsISimpleEnumerator> dirIterator;
725 6 : if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
726 0 : return rv;
727 :
728 6 : bool hasMore = false;
729 24 : while (dirIterator->HasMoreElements(&hasMore), hasMore) {
730 24 : nsCOMPtr<nsIFile> entry;
731 12 : rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
732 12 : if (NS_FAILED(rv))
733 0 : continue;
734 12 : if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
735 0 : return rv;
736 12 : if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
737 0 : return rv;
738 12 : if (dirCheck && !isSymlink) {
739 8 : nsCOMPtr<nsIFile> destClone;
740 4 : rv = newParent->Clone(getter_AddRefs(destClone));
741 4 : if (NS_SUCCEEDED(rv)) {
742 8 : nsCOMPtr<nsILocalFile> newDir(do_QueryInterface(destClone));
743 4 : if (NS_FAILED(rv = entry->CopyToNative(newDir, EmptyCString()))) {
744 : #ifdef DEBUG
745 : nsresult rv2;
746 0 : nsCAutoString pathName;
747 0 : if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
748 0 : return rv2;
749 0 : printf("Operation not supported: %s\n", pathName.get());
750 : #endif
751 0 : if (rv == NS_ERROR_OUT_OF_MEMORY)
752 0 : return rv;
753 0 : continue;
754 : }
755 4 : }
756 : } else {
757 8 : if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
758 : #ifdef DEBUG
759 : nsresult rv2;
760 0 : nsCAutoString pathName;
761 0 : if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
762 0 : return rv2;
763 0 : printf("Operation not supported: %s\n", pathName.get());
764 : #endif
765 0 : if (rv == NS_ERROR_OUT_OF_MEMORY)
766 0 : return rv;
767 0 : continue;
768 : }
769 : }
770 : }
771 6 : return NS_OK;
772 : }
773 :
774 : NS_IMETHODIMP
775 510 : nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
776 : {
777 : nsresult rv;
778 : // check to make sure that this has been initialized properly
779 510 : CHECK_mPath();
780 :
781 : // we copy the parent here so 'newParent' remains immutable
782 1020 : nsCOMPtr <nsIFile> workParent;
783 510 : if (newParent) {
784 506 : if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
785 0 : return rv;
786 : } else {
787 4 : if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
788 0 : return rv;
789 : }
790 :
791 : // check to see if we are a directory or if we are a file
792 : bool isDirectory;
793 510 : if (NS_FAILED(rv = IsDirectory(&isDirectory)))
794 0 : return rv;
795 :
796 1020 : nsCAutoString newPathName;
797 510 : if (isDirectory) {
798 6 : if (!newName.IsEmpty()) {
799 2 : if (NS_FAILED(rv = workParent->AppendNative(newName)))
800 0 : return rv;
801 : } else {
802 4 : if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
803 0 : return rv;
804 4 : if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
805 0 : return rv;
806 : }
807 6 : if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
808 0 : return rv;
809 : } else {
810 504 : rv = GetNativeTargetPathName(workParent, newName, newPathName);
811 504 : if (NS_FAILED(rv))
812 0 : return rv;
813 :
814 : #ifdef DEBUG_blizzard
815 : printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
816 : #endif
817 :
818 : // actually create the file.
819 504 : nsLocalFile *newFile = new nsLocalFile();
820 504 : if (!newFile)
821 0 : return NS_ERROR_OUT_OF_MEMORY;
822 :
823 1008 : nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
824 :
825 504 : rv = newFile->InitWithNativePath(newPathName);
826 504 : if (NS_FAILED(rv))
827 0 : return rv;
828 :
829 : // get the old permissions
830 : PRUint32 myPerms;
831 504 : GetPermissions(&myPerms);
832 :
833 : // Create the new file with the old file's permissions, even if write
834 : // permission is missing. We can't create with write permission and
835 : // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
836 : // But we can write to a read-only file on all Unix filesystems if we
837 : // open it successfully for writing.
838 :
839 : PRFileDesc *newFD;
840 : rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
841 : PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
842 : myPerms,
843 504 : &newFD);
844 504 : if (NS_FAILED(rv))
845 0 : return rv;
846 :
847 : // open the old file, too
848 : bool specialFile;
849 504 : if (NS_FAILED(rv = IsSpecial(&specialFile))) {
850 0 : PR_Close(newFD);
851 0 : return rv;
852 : }
853 504 : if (specialFile) {
854 : #ifdef DEBUG
855 0 : printf("Operation not supported: %s\n", mPath.get());
856 : #endif
857 : // make sure to clean up properly
858 0 : PR_Close(newFD);
859 0 : return NS_OK;
860 : }
861 :
862 : PRFileDesc *oldFD;
863 504 : rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
864 504 : if (NS_FAILED(rv)) {
865 : // make sure to clean up properly
866 0 : PR_Close(newFD);
867 0 : return rv;
868 : }
869 :
870 : #ifdef DEBUG_blizzard
871 : PRInt32 totalRead = 0;
872 : PRInt32 totalWritten = 0;
873 : #endif
874 : char buf[BUFSIZ];
875 : PRInt32 bytesRead;
876 :
877 2794 : while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
878 : #ifdef DEBUG_blizzard
879 : totalRead += bytesRead;
880 : #endif
881 :
882 : // PR_Write promises never to do a short write
883 1786 : PRInt32 bytesWritten = PR_Write(newFD, buf, bytesRead);
884 1786 : if (bytesWritten < 0) {
885 0 : bytesRead = -1;
886 0 : break;
887 : }
888 1786 : NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
889 :
890 : #ifdef DEBUG_blizzard
891 : totalWritten += bytesWritten;
892 : #endif
893 : }
894 :
895 : #ifdef DEBUG_blizzard
896 : printf("read %d bytes, wrote %d bytes\n",
897 : totalRead, totalWritten);
898 : #endif
899 :
900 : // close the files
901 504 : PR_Close(newFD);
902 504 : PR_Close(oldFD);
903 :
904 : // check for read (or write) error after cleaning up
905 504 : if (bytesRead < 0)
906 0 : return NS_ERROR_OUT_OF_MEMORY;
907 : }
908 510 : return rv;
909 : }
910 :
911 : NS_IMETHODIMP
912 30 : nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
913 : {
914 30 : return CopyToNative(newParent, newName);
915 : }
916 :
917 : NS_IMETHODIMP
918 2094 : nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
919 : {
920 : nsresult rv;
921 :
922 : // check to make sure that this has been initialized properly
923 2094 : CHECK_mPath();
924 :
925 : // check to make sure that we have a new parent
926 4188 : nsCAutoString newPathName;
927 2094 : rv = GetNativeTargetPathName(newParent, newName, newPathName);
928 2094 : if (NS_FAILED(rv))
929 0 : return rv;
930 :
931 : // try for atomic rename, falling back to copy/delete
932 2094 : if (rename(mPath.get(), newPathName.get()) < 0) {
933 : #ifdef VMS
934 : if (errno == EXDEV || errno == ENXIO) {
935 : #else
936 0 : if (errno == EXDEV) {
937 : #endif
938 0 : rv = CopyToNative(newParent, newName);
939 0 : if (NS_SUCCEEDED(rv))
940 0 : rv = Remove(true);
941 : } else {
942 0 : rv = NSRESULT_FOR_ERRNO();
943 : }
944 : }
945 :
946 2094 : if (NS_SUCCEEDED(rv)) {
947 : // Adjust this
948 2094 : mPath = newPathName;
949 : }
950 2094 : return rv;
951 : }
952 :
953 : NS_IMETHODIMP
954 8211 : nsLocalFile::Remove(bool recursive)
955 : {
956 8211 : CHECK_mPath();
957 8211 : ENSURE_STAT_CACHE();
958 :
959 : bool isSymLink;
960 :
961 6704 : nsresult rv = IsSymlink(&isSymLink);
962 6704 : if (NS_FAILED(rv))
963 0 : return rv;
964 :
965 6704 : if (isSymLink || !S_ISDIR(mCachedStat.st_mode))
966 3272 : return NSRESULT_FOR_RETURN(unlink(mPath.get()));
967 :
968 3432 : if (recursive) {
969 1452 : nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
970 1452 : if (!dir)
971 0 : return NS_ERROR_OUT_OF_MEMORY;
972 :
973 2904 : nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
974 :
975 1452 : rv = dir->Init(this, false);
976 1452 : if (NS_FAILED(rv))
977 0 : return rv;
978 :
979 : bool more;
980 4150 : while (dir->HasMoreElements(&more), more) {
981 2492 : nsCOMPtr<nsISupports> item;
982 1246 : rv = dir->GetNext(getter_AddRefs(item));
983 1246 : if (NS_FAILED(rv))
984 0 : return NS_ERROR_FAILURE;
985 :
986 2492 : nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
987 1246 : if (NS_FAILED(rv))
988 0 : return NS_ERROR_FAILURE;
989 1246 : rv = file->Remove(recursive);
990 :
991 : #ifdef ANDROID
992 : // See bug 580434 - Bionic gives us just deleted files
993 : if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
994 : continue;
995 : #endif
996 1246 : if (NS_FAILED(rv))
997 0 : return rv;
998 : }
999 : }
1000 :
1001 3432 : return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
1002 : }
1003 :
1004 : NS_IMETHODIMP
1005 10585 : nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
1006 : {
1007 10585 : CHECK_mPath();
1008 10585 : NS_ENSURE_ARG(aLastModTime);
1009 :
1010 : PRFileInfo64 info;
1011 10585 : if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
1012 0 : return NSRESULT_FOR_ERRNO();
1013 10585 : PRInt64 modTime = PRInt64(info.modifyTime);
1014 10585 : if (modTime == 0)
1015 0 : *aLastModTime = 0;
1016 : else
1017 10585 : *aLastModTime = modTime / PRInt64(PR_USEC_PER_MSEC);
1018 :
1019 10585 : return NS_OK;
1020 : }
1021 :
1022 : NS_IMETHODIMP
1023 1348 : nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
1024 : {
1025 1348 : CHECK_mPath();
1026 :
1027 : int result;
1028 1348 : if (aLastModTime != 0) {
1029 1348 : ENSURE_STAT_CACHE();
1030 : struct utimbuf ut;
1031 1348 : ut.actime = mCachedStat.st_atime;
1032 :
1033 : // convert milliseconds to seconds since the unix epoch
1034 1348 : ut.modtime = (time_t)(PRFloat64(aLastModTime) / PR_MSEC_PER_SEC);
1035 1348 : result = utime(mPath.get(), &ut);
1036 : } else {
1037 0 : result = utime(mPath.get(), nsnull);
1038 : }
1039 1348 : return NSRESULT_FOR_RETURN(result);
1040 : }
1041 :
1042 : NS_IMETHODIMP
1043 0 : nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
1044 : {
1045 0 : CHECK_mPath();
1046 0 : NS_ENSURE_ARG(aLastModTimeOfLink);
1047 :
1048 : struct STAT sbuf;
1049 0 : if (LSTAT(mPath.get(), &sbuf) == -1)
1050 0 : return NSRESULT_FOR_ERRNO();
1051 0 : *aLastModTimeOfLink = PRInt64(sbuf.st_mtime) * PRInt64(PR_MSEC_PER_SEC);
1052 :
1053 0 : return NS_OK;
1054 : }
1055 :
1056 : /*
1057 : * utime(2) may or may not dereference symlinks, joy.
1058 : */
1059 : NS_IMETHODIMP
1060 0 : nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
1061 : {
1062 0 : return SetLastModifiedTime(aLastModTimeOfLink);
1063 : }
1064 :
1065 : /*
1066 : * Only send back permissions bits: maybe we want to send back the whole
1067 : * mode_t to permit checks against other file types?
1068 : */
1069 :
1070 : #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
1071 :
1072 : NS_IMETHODIMP
1073 2561 : nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1074 : {
1075 2561 : NS_ENSURE_ARG(aPermissions);
1076 2561 : ENSURE_STAT_CACHE();
1077 2561 : *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
1078 2561 : return NS_OK;
1079 : }
1080 :
1081 : NS_IMETHODIMP
1082 0 : nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
1083 : {
1084 0 : CHECK_mPath();
1085 0 : NS_ENSURE_ARG(aPermissionsOfLink);
1086 :
1087 : struct STAT sbuf;
1088 0 : if (LSTAT(mPath.get(), &sbuf) == -1)
1089 0 : return NSRESULT_FOR_ERRNO();
1090 0 : *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1091 0 : return NS_OK;
1092 : }
1093 :
1094 : NS_IMETHODIMP
1095 3196 : nsLocalFile::SetPermissions(PRUint32 aPermissions)
1096 : {
1097 3196 : CHECK_mPath();
1098 :
1099 : /*
1100 : * Race condition here: we should use fchmod instead, there's no way to
1101 : * guarantee the name still refers to the same file.
1102 : */
1103 3196 : if (chmod(mPath.get(), aPermissions) >= 0)
1104 3196 : return NS_OK;
1105 : #if defined(ANDROID) && defined(STATFS)
1106 : // For the time being, this is restricted for use by Android, but we
1107 : // will figure out what to do for all platforms in bug 638503
1108 : struct STATFS sfs;
1109 : if (STATFS(mPath.get(), &sfs) < 0)
1110 : return NSRESULT_FOR_ERRNO();
1111 :
1112 : // if this is a FAT file system we can't set file permissions
1113 : if (sfs.f_type == MSDOS_SUPER_MAGIC )
1114 : return NS_OK;
1115 : #endif
1116 0 : return NSRESULT_FOR_ERRNO();
1117 : }
1118 :
1119 : NS_IMETHODIMP
1120 0 : nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1121 : {
1122 : // There isn't a consistent mechanism for doing this on UNIX platforms. We
1123 : // might want to carefully implement this in the future though.
1124 0 : return NS_ERROR_NOT_IMPLEMENTED;
1125 : }
1126 :
1127 : NS_IMETHODIMP
1128 83843 : nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1129 : {
1130 83843 : NS_ENSURE_ARG_POINTER(aFileSize);
1131 83843 : *aFileSize = 0;
1132 83843 : ENSURE_STAT_CACHE();
1133 :
1134 : #if defined(VMS)
1135 : /* Only two record formats can report correct file content size */
1136 : if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
1137 : (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
1138 : return NS_ERROR_FAILURE;
1139 : }
1140 : #endif
1141 :
1142 83672 : if (!S_ISDIR(mCachedStat.st_mode)) {
1143 83616 : *aFileSize = (PRInt64)mCachedStat.st_size;
1144 : }
1145 83672 : return NS_OK;
1146 : }
1147 :
1148 : NS_IMETHODIMP
1149 0 : nsLocalFile::SetFileSize(PRInt64 aFileSize)
1150 : {
1151 0 : CHECK_mPath();
1152 :
1153 : #if defined(ANDROID)
1154 : /* no truncate on bionic */
1155 : int fd = open(mPath.get(), O_WRONLY);
1156 : if (fd == -1)
1157 : return NSRESULT_FOR_ERRNO();
1158 :
1159 : int ret = ftruncate(fd, (off_t)aFileSize);
1160 : close(fd);
1161 :
1162 : if (ret == -1)
1163 : return NSRESULT_FOR_ERRNO();
1164 : #elif defined(HAVE_TRUNCATE64)
1165 0 : if (truncate64(mPath.get(), (off64_t)aFileSize) == -1)
1166 0 : return NSRESULT_FOR_ERRNO();
1167 : #else
1168 : off_t size = (off_t)aFileSize;
1169 : if (truncate(mPath.get(), size) == -1)
1170 : return NSRESULT_FOR_ERRNO();
1171 : #endif
1172 0 : return NS_OK;
1173 : }
1174 :
1175 : NS_IMETHODIMP
1176 0 : nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1177 : {
1178 0 : CHECK_mPath();
1179 0 : NS_ENSURE_ARG(aFileSize);
1180 :
1181 : struct STAT sbuf;
1182 0 : if (LSTAT(mPath.get(), &sbuf) == -1)
1183 0 : return NSRESULT_FOR_ERRNO();
1184 :
1185 0 : *aFileSize = (PRInt64)sbuf.st_size;
1186 0 : return NS_OK;
1187 : }
1188 :
1189 : #if defined(USE_LINUX_QUOTACTL)
1190 : /*
1191 : * Searches /proc/self/mountinfo for given device (Major:Minor),
1192 : * returns exported name from /dev
1193 : *
1194 : * Fails when /proc/self/mountinfo or diven device don't exist.
1195 : */
1196 : static bool
1197 : GetDeviceName(int deviceMajor, int deviceMinor, nsACString &deviceName)
1198 : {
1199 : bool ret = false;
1200 :
1201 : const int kMountInfoLineLength = 200;
1202 : const int kMountInfoDevPosition = 6;
1203 :
1204 : char mountinfo_line[kMountInfoLineLength];
1205 : char device_num[kMountInfoLineLength];
1206 :
1207 : snprintf(device_num,kMountInfoLineLength,"%d:%d", deviceMajor, deviceMinor);
1208 :
1209 : FILE *f = fopen("/proc/self/mountinfo","rt");
1210 : if(!f)
1211 : return ret;
1212 :
1213 : // Expects /proc/self/mountinfo in format:
1214 : // 'ID ID major:minor root mountpoint flags - type devicename flags'
1215 : while(fgets(mountinfo_line,kMountInfoLineLength,f)) {
1216 : char *p_dev = strstr(mountinfo_line,device_num);
1217 :
1218 : int i;
1219 : for(i = 0; i < kMountInfoDevPosition && p_dev != NULL; i++) {
1220 : p_dev = strchr(p_dev,' ');
1221 : if(p_dev)
1222 : p_dev++;
1223 : }
1224 :
1225 : if(p_dev) {
1226 : char *p_dev_end = strchr(p_dev,' ');
1227 : if(p_dev_end) {
1228 : *p_dev_end = '\0';
1229 : deviceName.Assign(p_dev);
1230 : ret = true;
1231 : break;
1232 : }
1233 : }
1234 : }
1235 :
1236 : fclose(f);
1237 : return ret;
1238 : }
1239 : #endif
1240 :
1241 : NS_IMETHODIMP
1242 840 : nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1243 : {
1244 840 : NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1245 :
1246 : // These systems have the operations necessary to check disk space.
1247 :
1248 : #ifdef STATFS
1249 :
1250 : // check to make sure that mPath is properly initialized
1251 840 : CHECK_mPath();
1252 :
1253 : struct STATFS fs_buf;
1254 :
1255 : /*
1256 : * Members of the STATFS struct that you should know about:
1257 : * f_bsize = block size on disk.
1258 : * f_bavail = number of free blocks available to a non-superuser.
1259 : * f_bfree = number of total free blocks in file system.
1260 : */
1261 :
1262 840 : if (STATFS(mPath.get(), &fs_buf) < 0) {
1263 : // The call to STATFS failed.
1264 : #ifdef DEBUG
1265 0 : printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
1266 : #endif
1267 0 : return NS_ERROR_FAILURE;
1268 : }
1269 : #ifdef DEBUG_DISK_SPACE
1270 : printf("DiskSpaceAvailable: %d bytes\n",
1271 : fs_buf.f_bsize * (fs_buf.f_bavail - 1));
1272 : #endif
1273 :
1274 : /*
1275 : * The number of bytes free == The number of free blocks available to
1276 : * a non-superuser, minus one as a fudge factor, multiplied by the size
1277 : * of the aforementioned blocks.
1278 : */
1279 : #if defined(SOLARIS) || defined(XP_MACOSX)
1280 : /* On Solaris and Mac, unit is f_frsize. */
1281 : *aDiskSpaceAvailable = (PRInt64)fs_buf.f_frsize * (fs_buf.f_bavail - 1);
1282 : #else
1283 840 : *aDiskSpaceAvailable = (PRInt64)fs_buf.f_bsize * (fs_buf.f_bavail - 1);
1284 : #endif /* SOLARIS */
1285 :
1286 : #if defined(USE_LINUX_QUOTACTL)
1287 :
1288 : if(!FillStatCache()) {
1289 : // Return available size from statfs
1290 : return NS_OK;
1291 : }
1292 :
1293 : nsCString deviceName;
1294 : if(!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev), deviceName)) {
1295 : return NS_OK;
1296 : }
1297 :
1298 : struct dqblk dq;
1299 : if(!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(), (caddr_t)&dq)) {
1300 : PRInt64 QuotaSpaceAvailable = PRInt64(fs_buf.f_bsize * dq.dqb_bhardlimit);
1301 : if(QuotaSpaceAvailable < *aDiskSpaceAvailable) {
1302 : *aDiskSpaceAvailable = QuotaSpaceAvailable;
1303 : }
1304 : }
1305 : #endif
1306 :
1307 840 : return NS_OK;
1308 :
1309 : #else
1310 : /*
1311 : * This platform doesn't have statfs or statvfs. I'm sure that there's
1312 : * a way to check for free disk space on platforms that don't have statfs
1313 : * (I'm SURE they have df, for example).
1314 : *
1315 : * Until we figure out how to do that, lets be honest and say that this
1316 : * command isn't implemented properly for these platforms yet.
1317 : */
1318 : #ifdef DEBUG
1319 : printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
1320 : #endif
1321 : return NS_ERROR_NOT_IMPLEMENTED;
1322 :
1323 : #endif /* STATFS */
1324 :
1325 : }
1326 :
1327 : NS_IMETHODIMP
1328 459870 : nsLocalFile::GetParent(nsIFile **aParent)
1329 : {
1330 459870 : CHECK_mPath();
1331 459870 : NS_ENSURE_ARG_POINTER(aParent);
1332 459870 : *aParent = nsnull;
1333 :
1334 : // if '/' we are at the top of the volume, return null
1335 459870 : if (mPath.Equals("/"))
1336 1 : return NS_OK;
1337 :
1338 : // <brendan, after jband> I promise to play nice
1339 459869 : char *buffer = mPath.BeginWriting(),
1340 459869 : *slashp = buffer;
1341 :
1342 : // find the last significant slash in buffer
1343 459869 : slashp = strrchr(buffer, '/');
1344 459869 : NS_ASSERTION(slashp, "non-canonical path?");
1345 459869 : if (!slashp)
1346 0 : return NS_ERROR_FILE_INVALID_PATH;
1347 :
1348 : // for the case where we are at '/'
1349 459869 : if (slashp == buffer)
1350 1 : slashp++;
1351 :
1352 : // temporarily terminate buffer at the last significant slash
1353 459869 : char c = *slashp;
1354 459869 : *slashp = '\0';
1355 :
1356 919738 : nsCOMPtr<nsILocalFile> localFile;
1357 459869 : nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
1358 919738 : getter_AddRefs(localFile));
1359 :
1360 : // make buffer whole again
1361 459869 : *slashp = c;
1362 :
1363 459869 : if (NS_SUCCEEDED(rv) && localFile)
1364 459869 : rv = CallQueryInterface(localFile, aParent);
1365 459869 : return rv;
1366 : }
1367 :
1368 : /*
1369 : * The results of Exists, isWritable and isReadable are not cached.
1370 : */
1371 :
1372 :
1373 : NS_IMETHODIMP
1374 47794 : nsLocalFile::Exists(bool *_retval)
1375 : {
1376 47794 : CHECK_mPath();
1377 47794 : NS_ENSURE_ARG_POINTER(_retval);
1378 :
1379 47794 : *_retval = (access(mPath.get(), F_OK) == 0);
1380 47794 : return NS_OK;
1381 : }
1382 :
1383 :
1384 : NS_IMETHODIMP
1385 74 : nsLocalFile::IsWritable(bool *_retval)
1386 : {
1387 74 : CHECK_mPath();
1388 74 : NS_ENSURE_ARG_POINTER(_retval);
1389 :
1390 74 : *_retval = (access(mPath.get(), W_OK) == 0);
1391 74 : if (*_retval || errno == EACCES)
1392 74 : return NS_OK;
1393 0 : return NSRESULT_FOR_ERRNO();
1394 : }
1395 :
1396 : NS_IMETHODIMP
1397 6 : nsLocalFile::IsReadable(bool *_retval)
1398 : {
1399 6 : CHECK_mPath();
1400 6 : NS_ENSURE_ARG_POINTER(_retval);
1401 :
1402 6 : *_retval = (access(mPath.get(), R_OK) == 0);
1403 6 : if (*_retval || errno == EACCES)
1404 6 : return NS_OK;
1405 0 : return NSRESULT_FOR_ERRNO();
1406 : }
1407 :
1408 : NS_IMETHODIMP
1409 6 : nsLocalFile::IsExecutable(bool *_retval)
1410 : {
1411 6 : CHECK_mPath();
1412 6 : NS_ENSURE_ARG_POINTER(_retval);
1413 :
1414 : // Check extension (bug 663899). On certain platforms, the file
1415 : // extension may cause the OS to treat it as executable regardless of
1416 : // the execute bit, such as .jar on Mac OS X. We borrow the code from
1417 : // nsLocalFileWin, slightly modified.
1418 :
1419 : // Don't be fooled by symlinks.
1420 : bool symLink;
1421 6 : nsresult rv = IsSymlink(&symLink);
1422 6 : if (NS_FAILED(rv))
1423 0 : return rv;
1424 :
1425 12 : nsAutoString path;
1426 6 : if (symLink)
1427 0 : GetTarget(path);
1428 : else
1429 6 : GetPath(path);
1430 :
1431 6 : PRInt32 dotIdx = path.RFindChar(PRUnichar('.'));
1432 6 : if (dotIdx != kNotFound) {
1433 : // Convert extension to lower case.
1434 6 : PRUnichar *p = path.BeginWriting();
1435 24 : for(p += dotIdx + 1; *p; p++)
1436 18 : *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
1437 :
1438 : // Search for any of the set of executable extensions.
1439 : static const char * const executableExts[] = {
1440 : "air", // Adobe AIR installer
1441 : "jar"}; // java application bundle
1442 12 : nsDependentSubstring ext = Substring(path, dotIdx + 1);
1443 18 : for (size_t i = 0; i < ArrayLength(executableExts); i++) {
1444 12 : if (ext.EqualsASCII(executableExts[i])) {
1445 : // Found a match. Set result and quit.
1446 0 : *_retval = true;
1447 0 : return NS_OK;
1448 : }
1449 : }
1450 : }
1451 :
1452 : // On OS X, then query Launch Services.
1453 : #ifdef MOZ_WIDGET_COCOA
1454 : // Certain Mac applications, such as Classic applications, which
1455 : // run under Rosetta, might not have the +x mode bit but are still
1456 : // considered to be executable by Launch Services (bug 646748).
1457 : CFURLRef url;
1458 : if (NS_FAILED(GetCFURL(&url))) {
1459 : return NS_ERROR_FAILURE;
1460 : }
1461 :
1462 : LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
1463 : LSItemInfoRecord theInfo;
1464 : OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
1465 : ::CFRelease(url);
1466 : if (result == noErr) {
1467 : if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
1468 : *_retval = true;
1469 : return NS_OK;
1470 : }
1471 : }
1472 : #endif
1473 :
1474 : // Then check the execute bit.
1475 6 : *_retval = (access(mPath.get(), X_OK) == 0);
1476 : #ifdef SOLARIS
1477 : // On Solaris, access will always return 0 for root user, however
1478 : // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
1479 : // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
1480 : if (*_retval) {
1481 : struct STAT buf;
1482 :
1483 : *_retval = (STAT(mPath.get(), &buf) == 0);
1484 : if (*_retval || errno == EACCES) {
1485 : *_retval = *_retval &&
1486 : (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
1487 : return NS_OK;
1488 : }
1489 :
1490 : return NSRESULT_FOR_ERRNO();
1491 : }
1492 : #endif
1493 6 : if (*_retval || errno == EACCES)
1494 6 : return NS_OK;
1495 0 : return NSRESULT_FOR_ERRNO();
1496 : }
1497 :
1498 : NS_IMETHODIMP
1499 28199 : nsLocalFile::IsDirectory(bool *_retval)
1500 : {
1501 28199 : NS_ENSURE_ARG_POINTER(_retval);
1502 28199 : *_retval = false;
1503 28199 : ENSURE_STAT_CACHE();
1504 27035 : *_retval = S_ISDIR(mCachedStat.st_mode);
1505 27035 : return NS_OK;
1506 : }
1507 :
1508 : NS_IMETHODIMP
1509 20806 : nsLocalFile::IsFile(bool *_retval)
1510 : {
1511 20806 : NS_ENSURE_ARG_POINTER(_retval);
1512 20806 : *_retval = false;
1513 20806 : ENSURE_STAT_CACHE();
1514 17968 : *_retval = S_ISREG(mCachedStat.st_mode);
1515 17968 : return NS_OK;
1516 : }
1517 :
1518 : NS_IMETHODIMP
1519 73 : nsLocalFile::IsHidden(bool *_retval)
1520 : {
1521 73 : NS_ENSURE_ARG_POINTER(_retval);
1522 73 : nsACString::const_iterator begin, end;
1523 73 : LocateNativeLeafName(begin, end);
1524 73 : *_retval = (*begin == '.');
1525 73 : return NS_OK;
1526 : }
1527 :
1528 : NS_IMETHODIMP
1529 59476 : nsLocalFile::IsSymlink(bool *_retval)
1530 : {
1531 59476 : NS_ENSURE_ARG_POINTER(_retval);
1532 59476 : CHECK_mPath();
1533 :
1534 : struct STAT symStat;
1535 59476 : if (LSTAT(mPath.get(), &symStat) == -1)
1536 54 : return NSRESULT_FOR_ERRNO();
1537 59422 : *_retval=S_ISLNK(symStat.st_mode);
1538 59422 : return NS_OK;
1539 : }
1540 :
1541 : NS_IMETHODIMP
1542 504 : nsLocalFile::IsSpecial(bool *_retval)
1543 : {
1544 504 : NS_ENSURE_ARG_POINTER(_retval);
1545 504 : ENSURE_STAT_CACHE();
1546 : *_retval = S_ISCHR(mCachedStat.st_mode) ||
1547 : S_ISBLK(mCachedStat.st_mode) ||
1548 : #ifdef S_ISSOCK
1549 : S_ISSOCK(mCachedStat.st_mode) ||
1550 : #endif
1551 504 : S_ISFIFO(mCachedStat.st_mode);
1552 :
1553 504 : return NS_OK;
1554 : }
1555 :
1556 : NS_IMETHODIMP
1557 5476 : nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
1558 : {
1559 5476 : NS_ENSURE_ARG(inFile);
1560 5476 : NS_ENSURE_ARG_POINTER(_retval);
1561 5476 : *_retval = false;
1562 :
1563 10952 : nsCAutoString inPath;
1564 5476 : nsresult rv = inFile->GetNativePath(inPath);
1565 5476 : if (NS_FAILED(rv))
1566 0 : return rv;
1567 :
1568 : // We don't need to worry about "/foo/" vs. "/foo" here
1569 : // because trailing slashes are stripped on init.
1570 5476 : *_retval = !strcmp(inPath.get(), mPath.get());
1571 5476 : return NS_OK;
1572 : }
1573 :
1574 : NS_IMETHODIMP
1575 0 : nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
1576 : {
1577 0 : CHECK_mPath();
1578 0 : NS_ENSURE_ARG(inFile);
1579 0 : NS_ENSURE_ARG_POINTER(_retval);
1580 :
1581 0 : nsCAutoString inPath;
1582 : nsresult rv;
1583 :
1584 0 : if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1585 0 : return rv;
1586 :
1587 0 : *_retval = false;
1588 :
1589 0 : ssize_t len = mPath.Length();
1590 0 : if (strncmp(mPath.get(), inPath.get(), len) == 0) {
1591 : // Now make sure that the |inFile|'s path has a separator at len,
1592 : // which implies that it has more components after len.
1593 0 : if (inPath[len] == '/')
1594 0 : *_retval = true;
1595 : }
1596 :
1597 0 : return NS_OK;
1598 : }
1599 :
1600 : NS_IMETHODIMP
1601 332 : nsLocalFile::GetNativeTarget(nsACString &_retval)
1602 : {
1603 332 : CHECK_mPath();
1604 332 : _retval.Truncate();
1605 :
1606 : struct STAT symStat;
1607 332 : if (LSTAT(mPath.get(), &symStat) == -1)
1608 0 : return NSRESULT_FOR_ERRNO();
1609 :
1610 332 : if (!S_ISLNK(symStat.st_mode))
1611 0 : return NS_ERROR_FILE_INVALID_PATH;
1612 :
1613 332 : PRInt32 size = (PRInt32)symStat.st_size;
1614 332 : char *target = (char *)nsMemory::Alloc(size + 1);
1615 332 : if (!target)
1616 0 : return NS_ERROR_OUT_OF_MEMORY;
1617 :
1618 332 : if (readlink(mPath.get(), target, (size_t)size) < 0) {
1619 0 : nsMemory::Free(target);
1620 0 : return NSRESULT_FOR_ERRNO();
1621 : }
1622 332 : target[size] = '\0';
1623 :
1624 332 : nsresult rv = NS_OK;
1625 664 : nsCOMPtr<nsIFile> self(this);
1626 332 : PRInt32 maxLinks = 40;
1627 88 : while (true) {
1628 420 : if (maxLinks-- == 0) {
1629 2 : rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
1630 2 : break;
1631 : }
1632 :
1633 418 : if (target[0] != '/') {
1634 112 : nsCOMPtr<nsIFile> parent;
1635 56 : if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent))))
1636 : break;
1637 112 : nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
1638 56 : if (NS_FAILED(rv))
1639 : break;
1640 56 : if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
1641 : break;
1642 56 : if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
1643 : break;
1644 112 : self = parent;
1645 : } else {
1646 362 : _retval = target;
1647 : }
1648 :
1649 836 : const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
1650 :
1651 : // Any failure in testing the current target we'll just interpret
1652 : // as having reached our destiny.
1653 418 : if (LSTAT(flatRetval.get(), &symStat) == -1)
1654 : break;
1655 :
1656 : // And of course we're done if it isn't a symlink.
1657 414 : if (!S_ISLNK(symStat.st_mode))
1658 : break;
1659 :
1660 88 : PRInt32 newSize = (PRInt32)symStat.st_size;
1661 88 : if (newSize > size) {
1662 8 : char *newTarget = (char *)nsMemory::Realloc(target, newSize + 1);
1663 8 : if (!newTarget) {
1664 0 : rv = NS_ERROR_OUT_OF_MEMORY;
1665 : break;
1666 : }
1667 8 : target = newTarget;
1668 8 : size = newSize;
1669 : }
1670 :
1671 88 : PRInt32 linkLen = readlink(flatRetval.get(), target, size);
1672 88 : if (linkLen == -1) {
1673 0 : rv = NSRESULT_FOR_ERRNO();
1674 : break;
1675 : }
1676 506 : target[linkLen] = '\0';
1677 : }
1678 :
1679 332 : nsMemory::Free(target);
1680 :
1681 332 : if (NS_FAILED(rv))
1682 2 : _retval.Truncate();
1683 332 : return rv;
1684 : }
1685 :
1686 : NS_IMETHODIMP
1687 0 : nsLocalFile::GetFollowLinks(bool *aFollowLinks)
1688 : {
1689 0 : *aFollowLinks = true;
1690 0 : return NS_OK;
1691 : }
1692 :
1693 : NS_IMETHODIMP
1694 533185 : nsLocalFile::SetFollowLinks(bool aFollowLinks)
1695 : {
1696 533185 : return NS_OK;
1697 : }
1698 :
1699 : NS_IMETHODIMP
1700 11020 : nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
1701 : {
1702 11020 : nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
1703 11020 : if (!dir)
1704 0 : return NS_ERROR_OUT_OF_MEMORY;
1705 :
1706 11020 : NS_ADDREF(dir);
1707 11020 : nsresult rv = dir->Init(this, false);
1708 11020 : if (NS_FAILED(rv)) {
1709 1412 : *entries = nsnull;
1710 1412 : NS_RELEASE(dir);
1711 : } else {
1712 9608 : *entries = dir; // transfer reference
1713 : }
1714 :
1715 11020 : return rv;
1716 : }
1717 :
1718 : NS_IMETHODIMP
1719 5680 : nsLocalFile::Load(PRLibrary **_retval)
1720 : {
1721 5680 : CHECK_mPath();
1722 5680 : NS_ENSURE_ARG_POINTER(_retval);
1723 :
1724 : #ifdef NS_BUILD_REFCNT_LOGGING
1725 5680 : nsTraceRefcntImpl::SetActivityIsLegal(false);
1726 : #endif
1727 :
1728 5680 : *_retval = PR_LoadLibrary(mPath.get());
1729 :
1730 : #ifdef NS_BUILD_REFCNT_LOGGING
1731 5680 : nsTraceRefcntImpl::SetActivityIsLegal(true);
1732 : #endif
1733 :
1734 5680 : if (!*_retval)
1735 0 : return NS_ERROR_FAILURE;
1736 5680 : return NS_OK;
1737 : }
1738 :
1739 : NS_IMETHODIMP
1740 4939 : nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
1741 : {
1742 4939 : return GetNativePath(aPersistentDescriptor);
1743 : }
1744 :
1745 : NS_IMETHODIMP
1746 3131 : nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
1747 : {
1748 : #ifdef MOZ_WIDGET_COCOA
1749 : if (aPersistentDescriptor.IsEmpty())
1750 : return NS_ERROR_INVALID_ARG;
1751 :
1752 : // Support pathnames as user-supplied descriptors if they begin with '/'
1753 : // or '~'. These characters do not collide with the base64 set used for
1754 : // encoding alias records.
1755 : char first = aPersistentDescriptor.First();
1756 : if (first == '/' || first == '~')
1757 : return InitWithNativePath(aPersistentDescriptor);
1758 :
1759 : PRUint32 dataSize = aPersistentDescriptor.Length();
1760 : char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
1761 : if (!decodedData) {
1762 : NS_ERROR("SetPersistentDescriptor was given bad data");
1763 : return NS_ERROR_FAILURE;
1764 : }
1765 :
1766 : // Cast to an alias record and resolve.
1767 : AliasRecord aliasHeader = *(AliasPtr)decodedData;
1768 : PRInt32 aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
1769 : if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data
1770 : PR_Free(decodedData);
1771 : return NS_ERROR_FAILURE;
1772 : }
1773 :
1774 : nsresult rv = NS_OK;
1775 :
1776 : // Move the now-decoded data into the Handle.
1777 : // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
1778 : Handle newHandle = nsnull;
1779 : if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
1780 : rv = NS_ERROR_OUT_OF_MEMORY;
1781 : PR_Free(decodedData);
1782 : if (NS_FAILED(rv))
1783 : return rv;
1784 :
1785 : Boolean changed;
1786 : FSRef resolvedFSRef;
1787 : OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed);
1788 :
1789 : rv = MacErrorMapper(err);
1790 : DisposeHandle(newHandle);
1791 : if (NS_FAILED(rv))
1792 : return rv;
1793 :
1794 : return InitWithFSRef(&resolvedFSRef);
1795 : #else
1796 3131 : return InitWithNativePath(aPersistentDescriptor);
1797 : #endif
1798 : }
1799 :
1800 : NS_IMETHODIMP
1801 0 : nsLocalFile::Reveal()
1802 : {
1803 : #ifdef MOZ_WIDGET_GTK2
1804 0 : nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
1805 0 : nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1806 0 : if (!giovfs && !gnomevfs)
1807 0 : return NS_ERROR_FAILURE;
1808 :
1809 : bool isDirectory;
1810 0 : if (NS_FAILED(IsDirectory(&isDirectory)))
1811 0 : return NS_ERROR_FAILURE;
1812 :
1813 0 : if (isDirectory) {
1814 0 : if (giovfs)
1815 0 : return giovfs->ShowURIForInput(mPath);
1816 : else
1817 : /* Fallback to GnomeVFS */
1818 0 : return gnomevfs->ShowURIForInput(mPath);
1819 : } else {
1820 0 : nsCOMPtr<nsIFile> parentDir;
1821 0 : nsCAutoString dirPath;
1822 0 : if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
1823 0 : return NS_ERROR_FAILURE;
1824 0 : if (NS_FAILED(parentDir->GetNativePath(dirPath)))
1825 0 : return NS_ERROR_FAILURE;
1826 :
1827 0 : if (giovfs)
1828 0 : return giovfs->ShowURIForInput(dirPath);
1829 : else
1830 0 : return gnomevfs->ShowURIForInput(dirPath);
1831 : }
1832 : #elif defined(MOZ_WIDGET_COCOA)
1833 : CFURLRef url;
1834 : if (NS_SUCCEEDED(GetCFURL(&url))) {
1835 : nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
1836 : ::CFRelease(url);
1837 : return rv;
1838 : }
1839 : return NS_ERROR_FAILURE;
1840 : #else
1841 : return NS_ERROR_FAILURE;
1842 : #endif
1843 : }
1844 :
1845 : NS_IMETHODIMP
1846 0 : nsLocalFile::Launch()
1847 : {
1848 : #ifdef MOZ_WIDGET_GTK2
1849 : #if (MOZ_PLATFORM_MAEMO==5)
1850 : const PRInt32 kHILDON_SUCCESS = 1;
1851 : DBusError err;
1852 : dbus_error_init(&err);
1853 :
1854 : DBusConnection *connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
1855 : if (dbus_error_is_set(&err)) {
1856 : dbus_error_free(&err);
1857 : return NS_ERROR_FAILURE;
1858 : }
1859 :
1860 : if (nsnull == connection)
1861 : return NS_ERROR_FAILURE;
1862 :
1863 : if (hildon_mime_open_file(connection, mPath.get()) != kHILDON_SUCCESS)
1864 : return NS_ERROR_FAILURE;
1865 : return NS_OK;
1866 : #else
1867 0 : nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
1868 0 : nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1869 0 : if (giovfs) {
1870 0 : return giovfs->ShowURIForInput(mPath);
1871 0 : } else if (gnomevfs) {
1872 : /* GnomeVFS fallback */
1873 0 : return gnomevfs->ShowURIForInput(mPath);
1874 : }
1875 :
1876 0 : return NS_ERROR_FAILURE;
1877 : #endif
1878 : #elif defined(MOZ_ENABLE_CONTENTACTION)
1879 : QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
1880 : ContentAction::Action action =
1881 : ContentAction::Action::defaultActionForFile(uri);
1882 :
1883 : if (action.isValid()) {
1884 : action.trigger();
1885 : return NS_OK;
1886 : }
1887 :
1888 : return NS_ERROR_FAILURE;
1889 : #elif defined(MOZ_WIDGET_ANDROID)
1890 : // Try to get a mimetype, if this fails just use the file uri alone
1891 : nsresult rv;
1892 : nsCAutoString type;
1893 : nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
1894 : if (NS_SUCCEEDED(rv))
1895 : rv = mimeService->GetTypeFromFile(this, type);
1896 :
1897 : nsDependentCString fileUri = NS_LITERAL_CSTRING("file://");
1898 : fileUri.Append(mPath);
1899 : mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge();
1900 : return bridge->OpenUriExternal(fileUri, type) ? NS_OK : NS_ERROR_FAILURE;
1901 : #elif defined(MOZ_WIDGET_COCOA)
1902 : CFURLRef url;
1903 : if (NS_SUCCEEDED(GetCFURL(&url))) {
1904 : nsresult rv = CocoaFileUtils::OpenURL(url);
1905 : ::CFRelease(url);
1906 : return rv;
1907 : }
1908 : return NS_ERROR_FAILURE;
1909 : #else
1910 : return NS_ERROR_FAILURE;
1911 : #endif
1912 : }
1913 :
1914 : nsresult
1915 529439 : NS_NewNativeLocalFile(const nsACString &path, bool followSymlinks, nsILocalFile **result)
1916 : {
1917 529439 : nsLocalFile *file = new nsLocalFile();
1918 529439 : if (!file)
1919 0 : return NS_ERROR_OUT_OF_MEMORY;
1920 529439 : NS_ADDREF(file);
1921 :
1922 529439 : file->SetFollowLinks(followSymlinks);
1923 :
1924 529439 : if (!path.IsEmpty()) {
1925 467630 : nsresult rv = file->InitWithNativePath(path);
1926 467630 : if (NS_FAILED(rv)) {
1927 2 : NS_RELEASE(file);
1928 2 : return rv;
1929 : }
1930 : }
1931 529437 : *result = file;
1932 529437 : return NS_OK;
1933 : }
1934 :
1935 : //-----------------------------------------------------------------------------
1936 : // unicode support
1937 : //-----------------------------------------------------------------------------
1938 :
1939 : #define SET_UCS(func, ucsArg) \
1940 : { \
1941 : nsCAutoString buf; \
1942 : nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1943 : if (NS_FAILED(rv)) \
1944 : return rv; \
1945 : return (func)(buf); \
1946 : }
1947 :
1948 : #define GET_UCS(func, ucsArg) \
1949 : { \
1950 : nsCAutoString buf; \
1951 : nsresult rv = (func)(buf); \
1952 : if (NS_FAILED(rv)) return rv; \
1953 : return NS_CopyNativeToUnicode(buf, ucsArg); \
1954 : }
1955 :
1956 : #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
1957 : { \
1958 : nsCAutoString buf; \
1959 : nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1960 : if (NS_FAILED(rv)) \
1961 : return rv; \
1962 : return (func)(opaqueArg, buf); \
1963 : }
1964 :
1965 : // Unicode interface Wrapper
1966 : nsresult
1967 3829 : nsLocalFile::InitWithPath(const nsAString &filePath)
1968 : {
1969 3829 : SET_UCS(InitWithNativePath, filePath);
1970 : }
1971 : nsresult
1972 37189 : nsLocalFile::Append(const nsAString &node)
1973 : {
1974 37189 : SET_UCS(AppendNative, node);
1975 : }
1976 : nsresult
1977 2631 : nsLocalFile::AppendRelativePath(const nsAString &node)
1978 : {
1979 2631 : SET_UCS(AppendRelativeNativePath, node);
1980 : }
1981 : nsresult
1982 12091 : nsLocalFile::GetLeafName(nsAString &aLeafName)
1983 : {
1984 12091 : GET_UCS(GetNativeLeafName, aLeafName);
1985 : }
1986 : nsresult
1987 1135 : nsLocalFile::SetLeafName(const nsAString &aLeafName)
1988 : {
1989 1135 : SET_UCS(SetNativeLeafName, aLeafName);
1990 : }
1991 : nsresult
1992 22723 : nsLocalFile::GetPath(nsAString &_retval)
1993 : {
1994 22723 : return NS_CopyNativeToUnicode(mPath, _retval);
1995 : }
1996 : nsresult
1997 466 : nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1998 : {
1999 466 : SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
2000 : }
2001 : nsresult
2002 30 : nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
2003 : {
2004 30 : SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
2005 : }
2006 : nsresult
2007 1855 : nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
2008 : {
2009 1855 : SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
2010 : }
2011 : nsresult
2012 26 : nsLocalFile::GetTarget(nsAString &_retval)
2013 : {
2014 26 : GET_UCS(GetNativeTarget, _retval);
2015 : }
2016 :
2017 : // nsIHashable
2018 :
2019 : NS_IMETHODIMP
2020 0 : nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
2021 : {
2022 0 : nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
2023 0 : if (!otherFile) {
2024 0 : *aResult = false;
2025 0 : return NS_OK;
2026 : }
2027 :
2028 0 : return Equals(otherFile, aResult);
2029 : }
2030 :
2031 : NS_IMETHODIMP
2032 11359 : nsLocalFile::GetHashCode(PRUint32 *aResult)
2033 : {
2034 11359 : *aResult = HashString(mPath);
2035 11359 : return NS_OK;
2036 : }
2037 :
2038 : nsresult
2039 8 : NS_NewLocalFile(const nsAString &path, bool followLinks, nsILocalFile* *result)
2040 : {
2041 16 : nsCAutoString buf;
2042 8 : nsresult rv = NS_CopyUnicodeToNative(path, buf);
2043 8 : if (NS_FAILED(rv))
2044 0 : return rv;
2045 8 : return NS_NewNativeLocalFile(buf, followLinks, result);
2046 : }
2047 :
2048 : //-----------------------------------------------------------------------------
2049 : // global init/shutdown
2050 : //-----------------------------------------------------------------------------
2051 :
2052 : void
2053 1419 : nsLocalFile::GlobalInit()
2054 : {
2055 1419 : }
2056 :
2057 : void
2058 1419 : nsLocalFile::GlobalShutdown()
2059 : {
2060 1419 : }
2061 :
2062 : // nsILocalFileMac
2063 :
2064 : #ifdef MOZ_WIDGET_COCOA
2065 :
2066 : static nsresult MacErrorMapper(OSErr inErr)
2067 : {
2068 : nsresult outErr;
2069 :
2070 : switch (inErr)
2071 : {
2072 : case noErr:
2073 : outErr = NS_OK;
2074 : break;
2075 :
2076 : case fnfErr:
2077 : case afpObjectNotFound:
2078 : case afpDirNotFound:
2079 : outErr = NS_ERROR_FILE_NOT_FOUND;
2080 : break;
2081 :
2082 : case dupFNErr:
2083 : case afpObjectExists:
2084 : outErr = NS_ERROR_FILE_ALREADY_EXISTS;
2085 : break;
2086 :
2087 : case dskFulErr:
2088 : case afpDiskFull:
2089 : outErr = NS_ERROR_FILE_DISK_FULL;
2090 : break;
2091 :
2092 : case fLckdErr:
2093 : case afpVolLocked:
2094 : outErr = NS_ERROR_FILE_IS_LOCKED;
2095 : break;
2096 :
2097 : case afpAccessDenied:
2098 : outErr = NS_ERROR_FILE_ACCESS_DENIED;
2099 : break;
2100 :
2101 : case afpDirNotEmpty:
2102 : outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
2103 : break;
2104 :
2105 : // Can't find good map for some
2106 : case bdNamErr:
2107 : outErr = NS_ERROR_FAILURE;
2108 : break;
2109 :
2110 : default:
2111 : outErr = NS_ERROR_FAILURE;
2112 : break;
2113 : }
2114 :
2115 : return outErr;
2116 : }
2117 :
2118 : static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
2119 : {
2120 : // first see if the conversion would succeed and find the length of the result
2121 : CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
2122 : CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2123 : kCFStringEncodingUTF8, 0, false,
2124 : NULL, 0, &usedBufLen);
2125 : if (charsConverted == inStrLen) {
2126 : // all characters converted, do the actual conversion
2127 : aOutStr.SetLength(usedBufLen);
2128 : if (aOutStr.Length() != (unsigned int)usedBufLen)
2129 : return NS_ERROR_OUT_OF_MEMORY;
2130 : UInt8 *buffer = (UInt8*)aOutStr.BeginWriting();
2131 : ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
2132 : 0, false, buffer, usedBufLen, &usedBufLen);
2133 : return NS_OK;
2134 : }
2135 :
2136 : return NS_ERROR_FAILURE;
2137 : }
2138 :
2139 : NS_IMETHODIMP
2140 : nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
2141 : {
2142 : UInt8 path[PATH_MAX];
2143 : if (::CFURLGetFileSystemRepresentation(aCFURL, false, path, PATH_MAX)) {
2144 : nsDependentCString nativePath((char*)path);
2145 : return InitWithNativePath(nativePath);
2146 : }
2147 :
2148 : return NS_ERROR_FAILURE;
2149 : }
2150 :
2151 : NS_IMETHODIMP
2152 : nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
2153 : {
2154 : NS_ENSURE_ARG(aFSRef);
2155 :
2156 : CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
2157 : if (newURLRef) {
2158 : nsresult rv = InitWithCFURL(newURLRef);
2159 : ::CFRelease(newURLRef);
2160 : return rv;
2161 : }
2162 :
2163 : return NS_ERROR_FAILURE;
2164 : }
2165 :
2166 : NS_IMETHODIMP
2167 : nsLocalFile::GetCFURL(CFURLRef *_retval)
2168 : {
2169 : CHECK_mPath();
2170 :
2171 : bool isDir;
2172 : IsDirectory(&isDir);
2173 : *_retval = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
2174 : (UInt8*)mPath.get(),
2175 : mPath.Length(),
2176 : isDir);
2177 :
2178 : return (*_retval ? NS_OK : NS_ERROR_FAILURE);
2179 : }
2180 :
2181 : NS_IMETHODIMP
2182 : nsLocalFile::GetFSRef(FSRef *_retval)
2183 : {
2184 : NS_ENSURE_ARG_POINTER(_retval);
2185 :
2186 : nsresult rv = NS_ERROR_FAILURE;
2187 :
2188 : CFURLRef url = NULL;
2189 : if (NS_SUCCEEDED(GetCFURL(&url))) {
2190 : if (::CFURLGetFSRef(url, _retval)) {
2191 : rv = NS_OK;
2192 : }
2193 : ::CFRelease(url);
2194 : }
2195 :
2196 : return rv;
2197 : }
2198 :
2199 : NS_IMETHODIMP
2200 : nsLocalFile::GetFSSpec(FSSpec *_retval)
2201 : {
2202 : NS_ENSURE_ARG_POINTER(_retval);
2203 :
2204 : FSRef fsRef;
2205 : nsresult rv = GetFSRef(&fsRef);
2206 : if (NS_SUCCEEDED(rv)) {
2207 : OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nsnull, nsnull, _retval, nsnull);
2208 : return MacErrorMapper(err);
2209 : }
2210 :
2211 : return rv;
2212 : }
2213 :
2214 : NS_IMETHODIMP
2215 : nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork)
2216 : {
2217 : NS_ENSURE_ARG_POINTER(aFileSizeWithResFork);
2218 :
2219 : FSRef fsRef;
2220 : nsresult rv = GetFSRef(&fsRef);
2221 : if (NS_FAILED(rv))
2222 : return rv;
2223 :
2224 : FSCatalogInfo catalogInfo;
2225 : OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
2226 : &catalogInfo, nsnull, nsnull, nsnull);
2227 : if (err != noErr)
2228 : return MacErrorMapper(err);
2229 :
2230 : *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
2231 : return NS_OK;
2232 : }
2233 :
2234 : NS_IMETHODIMP
2235 : nsLocalFile::GetFileType(OSType *aFileType)
2236 : {
2237 : CFURLRef url;
2238 : if (NS_SUCCEEDED(GetCFURL(&url))) {
2239 : nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
2240 : ::CFRelease(url);
2241 : return rv;
2242 : }
2243 : return NS_ERROR_FAILURE;
2244 : }
2245 :
2246 : NS_IMETHODIMP
2247 : nsLocalFile::SetFileType(OSType aFileType)
2248 : {
2249 : CFURLRef url;
2250 : if (NS_SUCCEEDED(GetCFURL(&url))) {
2251 : nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
2252 : ::CFRelease(url);
2253 : return rv;
2254 : }
2255 : return NS_ERROR_FAILURE;
2256 : }
2257 :
2258 : NS_IMETHODIMP
2259 : nsLocalFile::GetFileCreator(OSType *aFileCreator)
2260 : {
2261 : CFURLRef url;
2262 : if (NS_SUCCEEDED(GetCFURL(&url))) {
2263 : nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
2264 : ::CFRelease(url);
2265 : return rv;
2266 : }
2267 : return NS_ERROR_FAILURE;
2268 : }
2269 :
2270 : NS_IMETHODIMP
2271 : nsLocalFile::SetFileCreator(OSType aFileCreator)
2272 : {
2273 : CFURLRef url;
2274 : if (NS_SUCCEEDED(GetCFURL(&url))) {
2275 : nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
2276 : ::CFRelease(url);
2277 : return rv;
2278 : }
2279 : return NS_ERROR_FAILURE;
2280 : }
2281 :
2282 : NS_IMETHODIMP
2283 : nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, bool aLaunchInBackground)
2284 : {
2285 : bool isExecutable;
2286 : nsresult rv = IsExecutable(&isExecutable);
2287 : if (NS_FAILED(rv))
2288 : return rv;
2289 : if (!isExecutable)
2290 : return NS_ERROR_FILE_EXECUTION_FAILED;
2291 :
2292 : FSRef appFSRef, docFSRef;
2293 : rv = GetFSRef(&appFSRef);
2294 : if (NS_FAILED(rv))
2295 : return rv;
2296 :
2297 : if (aDocToLoad) {
2298 : nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
2299 : rv = macDoc->GetFSRef(&docFSRef);
2300 : if (NS_FAILED(rv))
2301 : return rv;
2302 : }
2303 :
2304 : LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
2305 : LSLaunchFSRefSpec thelaunchSpec;
2306 :
2307 : if (aLaunchInBackground)
2308 : theLaunchFlags |= kLSLaunchDontSwitch;
2309 : memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2310 :
2311 : thelaunchSpec.appRef = &appFSRef;
2312 : if (aDocToLoad) {
2313 : thelaunchSpec.numDocs = 1;
2314 : thelaunchSpec.itemRefs = &docFSRef;
2315 : }
2316 : thelaunchSpec.launchFlags = theLaunchFlags;
2317 :
2318 : OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
2319 : if (err != noErr)
2320 : return MacErrorMapper(err);
2321 :
2322 : return NS_OK;
2323 : }
2324 :
2325 : NS_IMETHODIMP
2326 : nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, bool aLaunchInBackground)
2327 : {
2328 : FSRef docFSRef;
2329 : nsresult rv = GetFSRef(&docFSRef);
2330 : if (NS_FAILED(rv))
2331 : return rv;
2332 :
2333 : if (!aAppToOpenWith) {
2334 : OSErr err = ::LSOpenFSRef(&docFSRef, NULL);
2335 : return MacErrorMapper(err);
2336 : }
2337 :
2338 : nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
2339 : if (!appFileMac)
2340 : return rv;
2341 :
2342 : bool isExecutable;
2343 : rv = appFileMac->IsExecutable(&isExecutable);
2344 : if (NS_FAILED(rv))
2345 : return rv;
2346 : if (!isExecutable)
2347 : return NS_ERROR_FILE_EXECUTION_FAILED;
2348 :
2349 : FSRef appFSRef;
2350 : rv = appFileMac->GetFSRef(&appFSRef);
2351 : if (NS_FAILED(rv))
2352 : return rv;
2353 :
2354 : LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
2355 : LSLaunchFSRefSpec thelaunchSpec;
2356 :
2357 : if (aLaunchInBackground)
2358 : theLaunchFlags |= kLSLaunchDontSwitch;
2359 : memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2360 :
2361 : thelaunchSpec.appRef = &appFSRef;
2362 : thelaunchSpec.numDocs = 1;
2363 : thelaunchSpec.itemRefs = &docFSRef;
2364 : thelaunchSpec.launchFlags = theLaunchFlags;
2365 :
2366 : OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
2367 : if (err != noErr)
2368 : return MacErrorMapper(err);
2369 :
2370 : return NS_OK;
2371 : }
2372 :
2373 : NS_IMETHODIMP
2374 : nsLocalFile::IsPackage(bool *_retval)
2375 : {
2376 : NS_ENSURE_ARG(_retval);
2377 : *_retval = false;
2378 :
2379 : CFURLRef url;
2380 : nsresult rv = GetCFURL(&url);
2381 : if (NS_FAILED(rv))
2382 : return rv;
2383 :
2384 : LSItemInfoRecord info;
2385 : OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
2386 :
2387 : ::CFRelease(url);
2388 :
2389 : if (status != noErr) {
2390 : return NS_ERROR_FAILURE;
2391 : }
2392 :
2393 : *_retval = !!(info.flags & kLSItemInfoIsPackage);
2394 :
2395 : return NS_OK;
2396 : }
2397 :
2398 : NS_IMETHODIMP
2399 : nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
2400 : {
2401 : bool isPackage = false;
2402 : nsresult rv = IsPackage(&isPackage);
2403 : if (NS_FAILED(rv) || !isPackage)
2404 : return NS_ERROR_FAILURE;
2405 :
2406 : nsAutoString name;
2407 : rv = GetLeafName(name);
2408 : if (NS_FAILED(rv))
2409 : return rv;
2410 :
2411 : PRInt32 length = name.Length();
2412 : if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
2413 : // 4 characters in ".app"
2414 : outBundleName = Substring(name, 0, length - 4);
2415 : }
2416 : else {
2417 : outBundleName = name;
2418 : }
2419 :
2420 : return NS_OK;
2421 : }
2422 :
2423 : NS_IMETHODIMP
2424 : nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
2425 : {
2426 : nsresult rv = NS_ERROR_FAILURE;
2427 :
2428 : CFURLRef urlRef;
2429 : if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
2430 : CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef);
2431 : if (bundle) {
2432 : CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
2433 : if (bundleIdentifier)
2434 : rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
2435 : ::CFRelease(bundle);
2436 : }
2437 : ::CFRelease(urlRef);
2438 : }
2439 :
2440 : return rv;
2441 : }
2442 :
2443 : NS_IMETHODIMP
2444 : nsLocalFile::GetBundleContentsLastModifiedTime(PRInt64 *aLastModTime)
2445 : {
2446 : CHECK_mPath();
2447 : NS_ENSURE_ARG_POINTER(aLastModTime);
2448 :
2449 : bool isPackage = false;
2450 : nsresult rv = IsPackage(&isPackage);
2451 : if (NS_FAILED(rv) || !isPackage) {
2452 : return GetLastModifiedTime(aLastModTime);
2453 : }
2454 :
2455 : nsCAutoString infoPlistPath(mPath);
2456 : infoPlistPath.AppendLiteral("/Contents/Info.plist");
2457 : PRFileInfo64 info;
2458 : if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
2459 : return GetLastModifiedTime(aLastModTime);
2460 : }
2461 : PRInt64 modTime = PRInt64(info.modifyTime);
2462 : if (modTime == 0) {
2463 : *aLastModTime = 0;
2464 : } else {
2465 : *aLastModTime = modTime / PRInt64(PR_USEC_PER_MSEC);
2466 : }
2467 :
2468 : return NS_OK;
2469 : }
2470 :
2471 : NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile)
2472 : {
2473 : NS_ENSURE_ARG(aFile);
2474 :
2475 : nsCAutoString nativePath;
2476 : nsresult rv = aFile->GetNativePath(nativePath);
2477 : if (NS_FAILED(rv))
2478 : return rv;
2479 :
2480 : return InitWithNativePath(nativePath);
2481 : }
2482 :
2483 : nsresult
2484 : NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, nsILocalFileMac** result)
2485 : {
2486 : nsLocalFile* file = new nsLocalFile();
2487 : if (file == nsnull)
2488 : return NS_ERROR_OUT_OF_MEMORY;
2489 : NS_ADDREF(file);
2490 :
2491 : file->SetFollowLinks(aFollowLinks);
2492 :
2493 : nsresult rv = file->InitWithFSRef(aFSRef);
2494 : if (NS_FAILED(rv)) {
2495 : NS_RELEASE(file);
2496 : return rv;
2497 : }
2498 : *result = file;
2499 : return NS_OK;
2500 : }
2501 :
2502 : nsresult
2503 : NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, nsILocalFileMac** result)
2504 : {
2505 : nsLocalFile* file = new nsLocalFile();
2506 : if (!file)
2507 : return NS_ERROR_OUT_OF_MEMORY;
2508 : NS_ADDREF(file);
2509 :
2510 : file->SetFollowLinks(aFollowLinks);
2511 :
2512 : nsresult rv = file->InitWithCFURL(aURL);
2513 : if (NS_FAILED(rv)) {
2514 : NS_RELEASE(file);
2515 : return rv;
2516 : }
2517 : *result = file;
2518 : return NS_OK;
2519 : }
2520 :
2521 : #endif
|