1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org Code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 #include "nsNetUtil.h"
41 #include "nsIIOService.h"
42 #include "nsIStandardURL.h"
44 #include "nsIComponentManager.h"
45 #include "nsIServiceManager.h"
46 #include "nsIZipReader.h"
47 #include "nsReadableUtils.h"
48 #include "nsAutoPtr.h"
50 #include "nsIObjectInputStream.h"
51 #include "nsIObjectOutputStream.h"
52 #include "nsIProgrammingLanguage.h"
54 static NS_DEFINE_CID(kJARURICID
, NS_JARURI_CID
);
56 ////////////////////////////////////////////////////////////////////////////////
66 // XXX Why is this threadsafe?
67 NS_IMPL_THREADSAFE_ADDREF(nsJARURI
)
68 NS_IMPL_THREADSAFE_RELEASE(nsJARURI
)
69 NS_INTERFACE_MAP_BEGIN(nsJARURI
)
70 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIJARURI
)
71 NS_INTERFACE_MAP_ENTRY(nsIURI
)
72 NS_INTERFACE_MAP_ENTRY(nsIURL
)
73 NS_INTERFACE_MAP_ENTRY(nsIJARURI
)
74 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
75 NS_INTERFACE_MAP_ENTRY(nsIClassInfo
)
76 NS_INTERFACE_MAP_ENTRY(nsINestedURI
)
77 // see nsJARURI::Equals
78 if (aIID
.Equals(NS_GET_IID(nsJARURI
)))
79 foundInterface
= reinterpret_cast<nsISupports
*>(this);
84 nsJARURI::Init(const char *charsetHint
)
86 mCharsetHint
= charsetHint
;
90 #define NS_JAR_SCHEME NS_LITERAL_CSTRING("jar:")
91 #define NS_JAR_DELIMITER NS_LITERAL_CSTRING("!/")
92 #define NS_BOGUS_ENTRY_SCHEME NS_LITERAL_CSTRING("x:///")
94 // FormatSpec takes the entry spec (including the "x:///" at the
95 // beginning) and gives us a full JAR spec.
97 nsJARURI::FormatSpec(const nsACString
&entrySpec
, nsACString
&result
,
98 PRBool aIncludeScheme
)
100 // The entrySpec MUST start with "x:///"
101 NS_ASSERTION(StringBeginsWith(entrySpec
, NS_BOGUS_ENTRY_SCHEME
),
104 nsCAutoString fileSpec
;
105 nsresult rv
= mJARFile
->GetSpec(fileSpec
);
106 if (NS_FAILED(rv
)) return rv
;
109 result
= NS_JAR_SCHEME
;
113 result
.Append(fileSpec
+ NS_JAR_DELIMITER
+
114 Substring(entrySpec
, 5, entrySpec
.Length() - 5));
119 nsJARURI::CreateEntryURL(const nsACString
& entryFilename
,
125 nsCOMPtr
<nsIStandardURL
> stdURL(do_CreateInstance(NS_STANDARDURL_CONTRACTID
));
127 return NS_ERROR_OUT_OF_MEMORY
;
130 // Flatten the concatenation, just in case. See bug 128288
131 nsCAutoString
spec(NS_BOGUS_ENTRY_SCHEME
+ entryFilename
);
132 nsresult rv
= stdURL
->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY
, -1,
133 spec
, charset
, nsnull
);
138 return CallQueryInterface(stdURL
, url
);
141 ////////////////////////////////////////////////////////////////////////////////
142 // nsISerializable methods:
145 nsJARURI::Read(nsIObjectInputStream
* aInputStream
)
149 rv
= aInputStream
->ReadObject(PR_TRUE
, getter_AddRefs(mJARFile
));
150 NS_ENSURE_SUCCESS(rv
, rv
);
152 rv
= aInputStream
->ReadObject(PR_TRUE
, getter_AddRefs(mJAREntry
));
153 NS_ENSURE_SUCCESS(rv
, rv
);
155 rv
= aInputStream
->ReadCString(mCharsetHint
);
160 nsJARURI::Write(nsIObjectOutputStream
* aOutputStream
)
164 rv
= aOutputStream
->WriteCompoundObject(mJARFile
, NS_GET_IID(nsIURI
),
166 NS_ENSURE_SUCCESS(rv
, rv
);
168 rv
= aOutputStream
->WriteCompoundObject(mJAREntry
, NS_GET_IID(nsIURL
),
170 NS_ENSURE_SUCCESS(rv
, rv
);
172 rv
= aOutputStream
->WriteStringZ(mCharsetHint
.get());
176 ////////////////////////////////////////////////////////////////////////////////
177 // nsIClassInfo methods:
180 nsJARURI::GetInterfaces(PRUint32
*count
, nsIID
* **array
)
188 nsJARURI::GetHelperForLanguage(PRUint32 language
, nsISupports
**_retval
)
195 nsJARURI::GetContractID(char * *aContractID
)
197 *aContractID
= nsnull
;
202 nsJARURI::GetClassDescription(char * *aClassDescription
)
204 *aClassDescription
= nsnull
;
209 nsJARURI::GetClassID(nsCID
* *aClassID
)
211 *aClassID
= (nsCID
*) nsMemory::Alloc(sizeof(nsCID
));
213 return NS_ERROR_OUT_OF_MEMORY
;
214 return GetClassIDNoAlloc(*aClassID
);
218 nsJARURI::GetImplementationLanguage(PRUint32
*aImplementationLanguage
)
220 *aImplementationLanguage
= nsIProgrammingLanguage::CPLUSPLUS
;
225 nsJARURI::GetFlags(PRUint32
*aFlags
)
227 // XXX We implement THREADSAFE addref/release, but probably shouldn't.
228 *aFlags
= nsIClassInfo::MAIN_THREAD_ONLY
;
233 nsJARURI::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
)
235 *aClassIDNoAlloc
= kJARURICID
;
239 ////////////////////////////////////////////////////////////////////////////////
243 nsJARURI::GetSpec(nsACString
&aSpec
)
245 nsCAutoString entrySpec
;
246 mJAREntry
->GetSpec(entrySpec
);
247 return FormatSpec(entrySpec
, aSpec
);
251 nsJARURI::SetSpec(const nsACString
& aSpec
)
253 return SetSpecWithBase(aSpec
, nsnull
);
257 nsJARURI::SetSpecWithBase(const nsACString
&aSpec
, nsIURI
* aBaseURL
)
261 nsCOMPtr
<nsIIOService
> ioServ(do_GetIOService(&rv
));
262 NS_ENSURE_SUCCESS(rv
, rv
);
264 nsCAutoString scheme
;
265 rv
= ioServ
->ExtractScheme(aSpec
, scheme
);
267 // not an absolute URI
269 return NS_ERROR_MALFORMED_URI
;
271 nsRefPtr
<nsJARURI
> otherJAR
;
272 aBaseURL
->QueryInterface(NS_GET_IID(nsJARURI
), getter_AddRefs(otherJAR
));
273 NS_ENSURE_TRUE(otherJAR
, NS_NOINTERFACE
);
275 mJARFile
= otherJAR
->mJARFile
;
277 nsCOMPtr
<nsIStandardURL
> entry(do_CreateInstance(NS_STANDARDURL_CONTRACTID
));
279 return NS_ERROR_OUT_OF_MEMORY
;
281 rv
= entry
->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY
, -1,
282 aSpec
, mCharsetHint
.get(), otherJAR
->mJAREntry
);
286 mJAREntry
= do_QueryInterface(entry
);
288 return NS_NOINTERFACE
;
293 NS_ENSURE_TRUE(scheme
.EqualsLiteral("jar"), NS_ERROR_MALFORMED_URI
);
295 nsACString::const_iterator begin
, end
;
296 aSpec
.BeginReading(begin
);
297 aSpec
.EndReading(end
);
299 while (begin
!= end
&& *begin
!= ':')
302 ++begin
; // now we're past the "jar:"
304 // Search backward from the end for the "!/" delimiter. Remember, jar URLs
306 // jar:jar:http://www.foo.com/bar.jar!/a.jar!/b.html
307 // This gets the b.html document from out of the a.jar file, that's
308 // contained within the bar.jar file.
309 // Also, the outermost "inner" URI may be a relative URI:
310 // jar:../relative.jar!/a.html
312 nsACString::const_iterator
delim_begin (begin
),
315 if (!RFindInReadable(NS_JAR_DELIMITER
, delim_begin
, delim_end
))
316 return NS_ERROR_MALFORMED_URI
;
318 rv
= ioServ
->NewURI(Substring(begin
, delim_begin
), mCharsetHint
.get(),
319 aBaseURL
, getter_AddRefs(mJARFile
));
320 if (NS_FAILED(rv
)) return rv
;
322 NS_TryToSetImmutable(mJARFile
);
324 // skip over any extra '/' chars
325 while (*delim_end
== '/')
328 return SetJAREntry(Substring(delim_end
, end
));
332 nsJARURI::GetPrePath(nsACString
&prePath
)
334 prePath
= NS_JAR_SCHEME
;
339 nsJARURI::GetScheme(nsACString
&aScheme
)
346 nsJARURI::SetScheme(const nsACString
&aScheme
)
348 // doesn't make sense to set the scheme of a jar: URL
349 return NS_ERROR_FAILURE
;
353 nsJARURI::GetUserPass(nsACString
&aUserPass
)
355 return NS_ERROR_FAILURE
;
359 nsJARURI::SetUserPass(const nsACString
&aUserPass
)
361 return NS_ERROR_FAILURE
;
365 nsJARURI::GetUsername(nsACString
&aUsername
)
367 return NS_ERROR_FAILURE
;
371 nsJARURI::SetUsername(const nsACString
&aUsername
)
373 return NS_ERROR_FAILURE
;
377 nsJARURI::GetPassword(nsACString
&aPassword
)
379 return NS_ERROR_FAILURE
;
383 nsJARURI::SetPassword(const nsACString
&aPassword
)
385 return NS_ERROR_FAILURE
;
389 nsJARURI::GetHostPort(nsACString
&aHostPort
)
391 return NS_ERROR_FAILURE
;
395 nsJARURI::SetHostPort(const nsACString
&aHostPort
)
397 return NS_ERROR_FAILURE
;
401 nsJARURI::GetHost(nsACString
&aHost
)
403 return NS_ERROR_FAILURE
;
407 nsJARURI::SetHost(const nsACString
&aHost
)
409 return NS_ERROR_FAILURE
;
413 nsJARURI::GetPort(PRInt32
*aPort
)
415 return NS_ERROR_FAILURE
;
419 nsJARURI::SetPort(PRInt32 aPort
)
421 return NS_ERROR_FAILURE
;
425 nsJARURI::GetPath(nsACString
&aPath
)
427 nsCAutoString entrySpec
;
428 mJAREntry
->GetSpec(entrySpec
);
429 return FormatSpec(entrySpec
, aPath
, PR_FALSE
);
433 nsJARURI::SetPath(const nsACString
&aPath
)
435 return NS_ERROR_FAILURE
;
439 nsJARURI::GetAsciiSpec(nsACString
&aSpec
)
441 // XXX Shouldn't this like... make sure it returns ASCII or something?
442 return GetSpec(aSpec
);
446 nsJARURI::GetAsciiHost(nsACString
&aHost
)
448 return NS_ERROR_FAILURE
;
452 nsJARURI::GetOriginCharset(nsACString
&aOriginCharset
)
454 aOriginCharset
= mCharsetHint
;
459 nsJARURI::Equals(nsIURI
*other
, PRBool
*result
)
466 return NS_OK
; // not equal
468 nsRefPtr
<nsJARURI
> otherJAR
;
469 other
->QueryInterface(NS_GET_IID(nsJARURI
), getter_AddRefs(otherJAR
));
471 return NS_OK
; // not equal
474 rv
= mJARFile
->Equals(otherJAR
->mJARFile
, &equal
);
475 if (NS_FAILED(rv
) || !equal
) {
476 return rv
; // not equal
479 rv
= mJAREntry
->Equals(otherJAR
->mJAREntry
, result
);
484 nsJARURI::SchemeIs(const char *i_Scheme
, PRBool
*o_Equals
)
486 NS_ENSURE_ARG_POINTER(o_Equals
);
487 if (!i_Scheme
) return NS_ERROR_INVALID_ARG
;
489 if (*i_Scheme
== 'j' || *i_Scheme
== 'J') {
490 *o_Equals
= PL_strcasecmp("jar", i_Scheme
) ? PR_FALSE
: PR_TRUE
;
492 *o_Equals
= PR_FALSE
;
498 nsJARURI::Clone(nsIURI
**result
)
502 nsCOMPtr
<nsIJARURI
> uri
;
503 rv
= CloneWithJARFile(mJARFile
, getter_AddRefs(uri
));
504 if (NS_FAILED(rv
)) return rv
;
506 return CallQueryInterface(uri
, result
);
510 nsJARURI::Resolve(const nsACString
&relativePath
, nsACString
&result
)
514 nsCOMPtr
<nsIIOService
> ioServ(do_GetIOService(&rv
));
518 nsCAutoString scheme
;
519 rv
= ioServ
->ExtractScheme(relativePath
, scheme
);
520 if (NS_SUCCEEDED(rv
)) {
521 // then aSpec is absolute
522 result
= relativePath
;
526 nsCAutoString resolvedPath
;
527 mJAREntry
->Resolve(relativePath
, resolvedPath
);
529 return FormatSpec(resolvedPath
, result
);
532 ////////////////////////////////////////////////////////////////////////////////
536 nsJARURI::GetFilePath(nsACString
& filePath
)
538 return mJAREntry
->GetFilePath(filePath
);
542 nsJARURI::SetFilePath(const nsACString
& filePath
)
544 return mJAREntry
->SetFilePath(filePath
);
548 nsJARURI::GetParam(nsACString
& param
)
555 nsJARURI::SetParam(const nsACString
& param
)
557 return NS_ERROR_NOT_AVAILABLE
;
561 nsJARURI::GetQuery(nsACString
& query
)
563 return mJAREntry
->GetQuery(query
);
567 nsJARURI::SetQuery(const nsACString
& query
)
569 return mJAREntry
->SetQuery(query
);
573 nsJARURI::GetRef(nsACString
& ref
)
575 return mJAREntry
->GetRef(ref
);
579 nsJARURI::SetRef(const nsACString
& ref
)
581 return mJAREntry
->SetRef(ref
);
585 nsJARURI::GetDirectory(nsACString
& directory
)
587 return mJAREntry
->GetDirectory(directory
);
591 nsJARURI::SetDirectory(const nsACString
& directory
)
593 return mJAREntry
->SetDirectory(directory
);
597 nsJARURI::GetFileName(nsACString
& fileName
)
599 return mJAREntry
->GetFileName(fileName
);
603 nsJARURI::SetFileName(const nsACString
& fileName
)
605 return mJAREntry
->SetFileName(fileName
);
609 nsJARURI::GetFileBaseName(nsACString
& fileBaseName
)
611 return mJAREntry
->GetFileBaseName(fileBaseName
);
615 nsJARURI::SetFileBaseName(const nsACString
& fileBaseName
)
617 return mJAREntry
->SetFileBaseName(fileBaseName
);
621 nsJARURI::GetFileExtension(nsACString
& fileExtension
)
623 return mJAREntry
->GetFileExtension(fileExtension
);
627 nsJARURI::SetFileExtension(const nsACString
& fileExtension
)
629 return mJAREntry
->SetFileExtension(fileExtension
);
633 nsJARURI::GetCommonBaseSpec(nsIURI
* uriToCompare
, nsACString
& commonSpec
)
635 commonSpec
.Truncate();
637 NS_ENSURE_ARG_POINTER(uriToCompare
);
639 commonSpec
.Truncate();
640 nsCOMPtr
<nsIJARURI
> otherJARURI(do_QueryInterface(uriToCompare
));
646 nsCOMPtr
<nsIURI
> otherJARFile
;
647 nsresult rv
= otherJARURI
->GetJARFile(getter_AddRefs(otherJARFile
));
648 if (NS_FAILED(rv
)) return rv
;
651 rv
= mJARFile
->Equals(otherJARFile
, &equal
);
652 if (NS_FAILED(rv
)) return rv
;
655 // See what the JAR file URIs have in common
656 nsCOMPtr
<nsIURL
> ourJARFileURL(do_QueryInterface(mJARFile
));
657 if (!ourJARFileURL
) {
658 // Not a URL, so nothing in common
661 nsCAutoString common
;
662 rv
= ourJARFileURL
->GetCommonBaseSpec(otherJARFile
, common
);
663 if (NS_FAILED(rv
)) return rv
;
665 commonSpec
= NS_JAR_SCHEME
+ common
;
670 // At this point we have the same JAR file. Compare the JAREntrys
671 nsCAutoString otherEntry
;
672 rv
= otherJARURI
->GetJAREntry(otherEntry
);
673 if (NS_FAILED(rv
)) return rv
;
675 nsCAutoString otherCharset
;
676 rv
= uriToCompare
->GetOriginCharset(otherCharset
);
677 if (NS_FAILED(rv
)) return rv
;
679 nsCOMPtr
<nsIURL
> url
;
680 rv
= CreateEntryURL(otherEntry
, otherCharset
.get(), getter_AddRefs(url
));
681 if (NS_FAILED(rv
)) return rv
;
683 nsCAutoString common
;
684 rv
= mJAREntry
->GetCommonBaseSpec(url
, common
);
685 if (NS_FAILED(rv
)) return rv
;
687 rv
= FormatSpec(common
, commonSpec
);
692 nsJARURI::GetRelativeSpec(nsIURI
* uriToCompare
, nsACString
& relativeSpec
)
694 GetSpec(relativeSpec
);
696 NS_ENSURE_ARG_POINTER(uriToCompare
);
698 nsCOMPtr
<nsIJARURI
> otherJARURI(do_QueryInterface(uriToCompare
));
704 nsCOMPtr
<nsIURI
> otherJARFile
;
705 nsresult rv
= otherJARURI
->GetJARFile(getter_AddRefs(otherJARFile
));
706 if (NS_FAILED(rv
)) return rv
;
709 rv
= mJARFile
->Equals(otherJARFile
, &equal
);
710 if (NS_FAILED(rv
)) return rv
;
713 // We live in different JAR files. Nothing in common.
717 // Same JAR file. Compare the JAREntrys
718 nsCAutoString otherEntry
;
719 rv
= otherJARURI
->GetJAREntry(otherEntry
);
720 if (NS_FAILED(rv
)) return rv
;
722 nsCAutoString otherCharset
;
723 rv
= uriToCompare
->GetOriginCharset(otherCharset
);
724 if (NS_FAILED(rv
)) return rv
;
726 nsCOMPtr
<nsIURL
> url
;
727 rv
= CreateEntryURL(otherEntry
, otherCharset
.get(), getter_AddRefs(url
));
728 if (NS_FAILED(rv
)) return rv
;
730 nsCAutoString relativeEntrySpec
;
731 rv
= mJAREntry
->GetRelativeSpec(url
, relativeEntrySpec
);
732 if (NS_FAILED(rv
)) return rv
;
734 if (!StringBeginsWith(relativeEntrySpec
, NS_BOGUS_ENTRY_SCHEME
)) {
735 // An actual relative spec!
736 relativeSpec
= relativeEntrySpec
;
741 ////////////////////////////////////////////////////////////////////////////////
742 // nsIJARURI methods:
745 nsJARURI::GetJARFile(nsIURI
* *jarFile
)
747 return GetInnerURI(jarFile
);
751 nsJARURI::GetJAREntry(nsACString
&entryPath
)
753 nsCAutoString filePath
;
754 mJAREntry
->GetFilePath(filePath
);
755 NS_ASSERTION(filePath
.Length() > 0, "path should never be empty!");
756 // Trim off the leading '/'
757 entryPath
= Substring(filePath
, 1, filePath
.Length() - 1);
762 nsJARURI::SetJAREntry(const nsACString
&entryPath
)
764 return CreateEntryURL(entryPath
, mCharsetHint
.get(),
765 getter_AddRefs(mJAREntry
));
769 nsJARURI::CloneWithJARFile(nsIURI
*jarFile
, nsIJARURI
**result
)
772 return NS_ERROR_INVALID_ARG
;
777 nsCOMPtr
<nsIURI
> newJARFile
;
778 rv
= jarFile
->Clone(getter_AddRefs(newJARFile
));
779 if (NS_FAILED(rv
)) return rv
;
781 NS_TryToSetImmutable(newJARFile
);
783 nsCOMPtr
<nsIURI
> newJAREntryURI
;
784 rv
= mJAREntry
->Clone(getter_AddRefs(newJAREntryURI
));
785 if (NS_FAILED(rv
)) return rv
;
787 nsCOMPtr
<nsIURL
> newJAREntry(do_QueryInterface(newJAREntryURI
));
788 NS_ASSERTION(newJAREntry
, "This had better QI to nsIURL!");
790 nsJARURI
* uri
= new nsJARURI();
793 uri
->mJARFile
= newJARFile
;
794 uri
->mJAREntry
= newJAREntry
;
798 rv
= NS_ERROR_OUT_OF_MEMORY
;
804 ////////////////////////////////////////////////////////////////////////////////
807 nsJARURI::GetInnerURI(nsIURI
**uri
)
809 return NS_EnsureSafeToReturn(mJARFile
, uri
);
813 nsJARURI::GetInnermostURI(nsIURI
** uri
)
815 return NS_ImplGetInnermostURI(this, uri
);