1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Mozilla Communicator client code, released
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Daniel Veditz <dveditz@netscape.com>
24 * Dave Townsend <dtownsend@oxymoronical.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
45 #include "nsISupports.h"
46 #include "nsIServiceManager.h"
49 #include "nsIFileURL.h"
52 #include "nsITransport.h"
53 #include "nsIOutputStream.h"
54 #include "nsNetUtil.h"
55 #include "nsIInputStream.h"
56 #include "nsIFileStreams.h"
57 #include "nsIStreamListener.h"
58 #include "nsICryptoHash.h"
60 #include "nsIExtensionManager.h"
61 #include "nsSoftwareUpdateIIDs.h"
63 #include "nsIStringEnumerator.h"
64 #include "nsXPITriggerInfo.h"
65 #include "nsXPInstallManager.h"
66 #include "nsInstallTrigger.h"
67 #include "nsIWindowWatcher.h"
68 #include "nsIAuthPrompt.h"
69 #include "nsIWindowMediator.h"
70 #include "nsIDOMWindowInternal.h"
71 #include "nsDirectoryService.h"
72 #include "nsDirectoryServiceDefs.h"
73 #include "nsAppDirectoryServiceDefs.h"
75 #include "nsReadableUtils.h"
76 #include "nsIPromptService.h"
77 #include "nsIScriptGlobalObject.h"
79 #include "nsISupportsPrimitives.h"
80 #include "nsIObserverService.h"
82 #include "nsISSLStatusProvider.h"
83 #include "nsISSLStatus.h"
84 #include "nsIX509Cert.h"
86 #include "nsIPrefService.h"
87 #include "nsIPrefBranch.h"
89 #include "CertReader.h"
91 #include "nsEmbedCID.h"
93 #define PREF_XPINSTALL_ENABLED "xpinstall.enabled"
94 #define PREF_XPINSTALL_CONFIRM_DLG "xpinstall.dialog.confirm"
95 #define PREF_XPINSTALL_STATUS_DLG_SKIN "xpinstall.dialog.progress.skin"
96 #define PREF_XPINSTALL_STATUS_DLG_CHROME "xpinstall.dialog.progress.chrome"
97 #define PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN "xpinstall.dialog.progress.type.skin"
98 #define PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME "xpinstall.dialog.progress.type.chrome"
100 static NS_DEFINE_IID(kZipReaderCID
, NS_ZIPREADER_CID
);
103 nsXPInstallManager::nsXPInstallManager()
104 : mTriggers(0), mItem(0), mNextItem(0), mChromeType(NOT_CHROME
),
105 mContentLength(0), mDialogOpen(PR_FALSE
), mCancelled(PR_FALSE
),
106 mNeedsShutdown(PR_FALSE
), mFromChrome(PR_FALSE
)
108 // we need to own ourself because we have a longer
109 // lifetime than the scriptlet that created us.
114 nsXPInstallManager::~nsXPInstallManager()
116 NS_ASSERTION(!mTriggers
, "Shutdown not called, triggers still alive");
120 NS_INTERFACE_MAP_BEGIN(nsXPInstallManager
)
121 NS_INTERFACE_MAP_ENTRY(nsIXPIDialogService
)
122 NS_INTERFACE_MAP_ENTRY(nsIXPInstallManager
)
123 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
124 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
125 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
126 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
127 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
128 NS_INTERFACE_MAP_ENTRY(nsPICertNotification
)
129 NS_INTERFACE_MAP_ENTRY(nsIBadCertListener2
)
130 NS_INTERFACE_MAP_ENTRY(nsISSLErrorListener
)
131 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
132 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
133 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsISupportsWeakReference
)
136 NS_IMPL_ADDREF(nsXPInstallManager
)
137 NS_IMPL_RELEASE(nsXPInstallManager
)
140 nsXPInstallManager::InitManagerFromChrome(const PRUnichar
**aURLs
,
142 nsIXPIProgressDialog
* aListener
)
144 return InitManagerWithHashes(aURLs
, nsnull
, aURLCount
, aListener
);
148 nsXPInstallManager::InitManagerWithHashes(const PRUnichar
**aURLs
,
149 const char **aHashes
,
151 nsIXPIProgressDialog
* aListener
)
153 // If Software Installation is not enabled, we don't want to proceed with
155 PRBool xpinstallEnabled
= PR_TRUE
;
156 nsCOMPtr
<nsIPrefBranch
> pref(do_GetService(NS_PREFSERVICE_CONTRACTID
));
158 pref
->GetBoolPref(PREF_XPINSTALL_ENABLED
, &xpinstallEnabled
);
160 if (!xpinstallEnabled
)
163 mTriggers
= new nsXPITriggerInfo();
165 return NS_ERROR_OUT_OF_MEMORY
;
167 mNeedsShutdown
= PR_TRUE
;
169 for (PRUint32 i
= 0; i
< aURLCount
; ++i
)
171 nsXPITriggerItem
* item
= new nsXPITriggerItem(0, aURLs
[i
], nsnull
,
172 aHashes
? aHashes
[i
] : nsnull
);
175 delete mTriggers
; // nsXPITriggerInfo frees any alloc'ed nsXPITriggerItems
178 return NS_ERROR_OUT_OF_MEMORY
;
180 mTriggers
->Add(item
);
183 mFromChrome
= PR_TRUE
;
185 nsresult rv
= Observe(aListener
, XPI_PROGRESS_TOPIC
, NS_LITERAL_STRING("open").get());
192 nsXPInstallManager::InitManagerWithInstallInfo(nsIXPIInstallInfo
* aInstallInfo
)
194 nsXPITriggerInfo
* triggers
;
195 nsresult rv
= aInstallInfo
->GetTriggerInfo(&triggers
);
196 NS_ENSURE_SUCCESS(rv
, rv
);
198 nsCOMPtr
<nsIDOMWindowInternal
> win
;
199 rv
= aInstallInfo
->GetOriginatingWindow(getter_AddRefs(win
));
200 if (NS_SUCCEEDED(rv
))
203 rv
= aInstallInfo
->GetChromeType(&type
);
204 if (NS_SUCCEEDED(rv
))
206 // Passing ownership onto InitManager which will free when necessary
207 aInstallInfo
->SetTriggerInfo(nsnull
);
208 return InitManager(win
, triggers
, type
);
217 nsXPInstallManager::InitManager(nsIDOMWindowInternal
* aParentWindow
, nsXPITriggerInfo
* aTriggers
, PRUint32 aChromeType
)
219 if ( !aTriggers
|| aTriggers
->Size() == 0 )
221 NS_WARNING("XPInstallManager called with no trigger info!");
224 return NS_ERROR_INVALID_POINTER
;
229 mNeedsShutdown
= PR_TRUE
;
230 mTriggers
= aTriggers
;
231 mChromeType
= aChromeType
;
233 mParentWindow
= aParentWindow
;
235 // Start downloading initial chunks looking for signatures,
236 mOutstandingCertLoads
= mTriggers
->Size();
238 nsXPITriggerItem
*item
= mTriggers
->Get(--mOutstandingCertLoads
);
240 nsCOMPtr
<nsIURI
> uri
;
241 NS_NewURI(getter_AddRefs(uri
), NS_ConvertUTF16toUTF8(item
->mURL
));
242 nsCOMPtr
<nsIStreamListener
> listener
= new CertReader(uri
, nsnull
, this);
244 rv
= NS_OpenURI(listener
, nsnull
, uri
);
246 rv
= NS_ERROR_OUT_OF_MEMORY
;
256 nsXPInstallManager::InitManagerInternal()
259 PRBool OKtoInstall
= PR_FALSE
; // initialize to secure state
261 //-----------------------------------------------------
262 // *** Do not return early after this point ***
264 // We have to clean up the triggers in case of error
265 //-----------------------------------------------------
267 // --- use embedding dialogs if any registered
268 nsCOMPtr
<nsIXPIDialogService
> dlgSvc(do_CreateInstance(NS_XPIDIALOGSERVICE_CONTRACTID
));
270 dlgSvc
= this; // provide our own dialogs
272 // --- prepare dialog params
273 PRUint32 numTriggers
= mTriggers
->Size();
274 PRUint32 numStrings
= 4 * numTriggers
;
275 const PRUnichar
** packageList
=
276 (const PRUnichar
**)malloc( sizeof(PRUnichar
*) * numStrings
);
280 // populate the list. The list doesn't own the strings
281 for ( PRUint32 i
=0, j
=0; i
< numTriggers
; i
++ )
283 nsXPITriggerItem
*item
= mTriggers
->Get(i
);
284 packageList
[j
++] = item
->mName
.get();
285 packageList
[j
++] = item
->GetSafeURLString();
286 packageList
[j
++] = item
->mIconURL
.get();
287 packageList
[j
++] = item
->mCertName
.get();
290 //-----------------------------------------------------
291 // Get permission to install
292 //-----------------------------------------------------
294 #ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
295 if ( mChromeType
== CHROME_SKIN
)
297 // We may want to enable the simple installation UI once
298 // bug 343037 is fixed
300 // skins get a simpler/friendlier dialog
301 // XXX currently not embeddable
302 OKtoInstall
= ConfirmChromeInstall( mParentWindow
, packageList
);
307 rv
= dlgSvc
->ConfirmInstall( mParentWindow
,
312 OKtoInstall
= PR_FALSE
;
313 #ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
319 //-----------------------------------------------------
320 // Open the progress dialog
321 //-----------------------------------------------------
323 rv
= dlgSvc
->OpenProgressDialog( packageList
, numStrings
, this );
327 rv
= NS_ERROR_OUT_OF_MEMORY
;
329 //-----------------------------------------------------
330 // cleanup and signal callbacks if there were errors
331 //-----------------------------------------------------
336 PRInt32 cbstatus
= 0; // callback status
338 cbstatus
= nsInstall::UNEXPECTED_ERROR
;
339 else if (!OKtoInstall
)
340 cbstatus
= nsInstall::USER_CANCELLED
;
344 // --- must shutdown if not continuing
345 Shutdown( cbstatus
);
353 nsXPInstallManager::ConfirmInstall(nsIDOMWindow
*aParent
, const PRUnichar
**aPackageList
, PRUint32 aCount
, PRBool
*aRetval
)
357 nsCOMPtr
<nsIDOMWindowInternal
> parentWindow( do_QueryInterface(aParent
) );
358 nsCOMPtr
<nsIDialogParamBlock
> params
;
359 nsresult rv
= LoadParams( aCount
, aPackageList
, getter_AddRefs(params
) );
361 if ( NS_SUCCEEDED(rv
) && parentWindow
&& params
)
363 nsCOMPtr
<nsIDOMWindow
> newWindow
;
365 nsCOMPtr
<nsISupportsInterfacePointer
> ifptr
=
366 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID
, &rv
);
367 NS_ENSURE_SUCCESS(rv
, rv
);
369 ifptr
->SetData(params
);
370 ifptr
->SetDataIID(&NS_GET_IID(nsIDialogParamBlock
));
372 char* confirmDialogURL
;
373 nsCOMPtr
<nsIPrefBranch
> pref(do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
));
377 rv
= pref
->GetCharPref(PREF_XPINSTALL_CONFIRM_DLG
, &confirmDialogURL
);
378 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't invoke XPInstall FE without a FE URL! Set xpinstall.dialog.confirm");
382 rv
= parentWindow
->OpenDialog(NS_ConvertASCIItoUTF16(confirmDialogURL
),
383 NS_LITERAL_STRING("_blank"),
384 NS_LITERAL_STRING("chrome,centerscreen,modal,titlebar"),
386 getter_AddRefs(newWindow
));
388 if (NS_SUCCEEDED(rv
))
390 //Now get which button was pressed from the ParamBlock
391 PRInt32 buttonPressed
= 0;
392 params
->GetInt( 0, &buttonPressed
);
393 *aRetval
= buttonPressed
? PR_FALSE
: PR_TRUE
;
400 #ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
401 PRBool
nsXPInstallManager::ConfirmChromeInstall(nsIDOMWindowInternal
* aParentWindow
, const PRUnichar
**aPackage
)
403 // get the dialog strings
404 nsXPIDLString applyNowText
;
405 nsXPIDLString confirmText
;
406 nsCOMPtr
<nsIStringBundleService
> bundleSvc
=
407 do_GetService(NS_STRINGBUNDLE_CONTRACTID
);
411 nsCOMPtr
<nsIStringBundle
> xpiBundle
;
412 bundleSvc
->CreateBundle( XPINSTALL_BUNDLE_URL
,
413 getter_AddRefs(xpiBundle
) );
417 const PRUnichar
*formatStrings
[2] = { aPackage
[0], aPackage
[1] };
418 if ( mChromeType
== CHROME_LOCALE
)
420 xpiBundle
->GetStringFromName(
421 NS_LITERAL_STRING("ApplyNowLocale").get(),
422 getter_Copies(applyNowText
));
423 xpiBundle
->FormatStringFromName(
424 NS_LITERAL_STRING("ConfirmLocale").get(),
427 getter_Copies(confirmText
));
431 xpiBundle
->GetStringFromName(
432 NS_LITERAL_STRING("ApplyNowSkin").get(),
433 getter_Copies(applyNowText
));
434 xpiBundle
->FormatStringFromName(
435 NS_LITERAL_STRING("ConfirmSkin").get(),
438 getter_Copies(confirmText
));
441 if (confirmText
.IsEmpty())
444 // confirmation dialog
445 PRBool bInstall
= PR_FALSE
;
446 nsCOMPtr
<nsIPromptService
> dlgService(do_GetService(NS_PROMPTSERVICE_CONTRACTID
));
461 nsXPInstallManager::OpenProgressDialog(const PRUnichar
**aPackageList
, PRUint32 aCount
, nsIObserver
*aObserver
)
463 // --- convert parameters into nsISupportArray members
464 nsCOMPtr
<nsIDialogParamBlock
> list
;
465 nsresult rv
= LoadParams( aCount
, aPackageList
, getter_AddRefs(list
) );
469 nsCOMPtr
<nsISupportsInterfacePointer
> listwrap(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID
));
471 listwrap
->SetData(list
);
472 listwrap
->SetDataIID(&NS_GET_IID(nsIDialogParamBlock
));
475 nsCOMPtr
<nsISupportsInterfacePointer
> callbackwrap(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID
));
477 callbackwrap
->SetData(aObserver
);
478 callbackwrap
->SetDataIID(&NS_GET_IID(nsIObserver
));
481 nsCOMPtr
<nsISupportsArray
> params(do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID
));
483 if ( !params
|| !listwrap
|| !callbackwrap
)
484 return NS_ERROR_FAILURE
;
486 params
->AppendElement(listwrap
);
487 params
->AppendElement(callbackwrap
);
489 // --- open the window
490 nsCOMPtr
<nsIWindowWatcher
> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
));
494 char *statusDialogURL
, *statusDialogType
;
495 nsCOMPtr
<nsIPrefBranch
> pref(do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
));
498 const char* statusDlg
= mChromeType
== CHROME_SKIN
? PREF_XPINSTALL_STATUS_DLG_SKIN
499 : PREF_XPINSTALL_STATUS_DLG_CHROME
;
500 rv
= pref
->GetCharPref(statusDlg
, &statusDialogURL
);
501 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't invoke XPInstall FE without a FE URL! Set xpinstall.dialog.status");
505 const char* statusType
= mChromeType
== CHROME_SKIN
? PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN
506 : PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME
;
507 rv
= pref
->GetCharPref(statusType
, &statusDialogType
);
509 type
.AssignWithConversion(statusDialogType
);
510 if (NS_SUCCEEDED(rv
) && !type
.IsEmpty()) {
511 nsCOMPtr
<nsIWindowMediator
> wm
= do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
);
513 nsCOMPtr
<nsIDOMWindowInternal
> recentWindow
;
514 wm
->GetMostRecentWindow(type
.get(), getter_AddRefs(recentWindow
));
516 nsCOMPtr
<nsIObserverService
> os(do_GetService("@mozilla.org/observer-service;1"));
517 os
->NotifyObservers(params
, "xpinstall-download-started", nsnull
);
519 recentWindow
->Focus();
524 nsCOMPtr
<nsIDOMWindow
> newWindow
;
525 rv
= wwatch
->OpenWindow(0,
528 "chrome,centerscreen,titlebar,dialog=no,resizable",
530 getter_AddRefs(newWindow
));
536 NS_IMETHODIMP
nsXPInstallManager::Observe( nsISupports
*aSubject
,
538 const PRUnichar
*aData
)
540 nsresult rv
= NS_ERROR_ILLEGAL_VALUE
;
542 if ( !aTopic
|| !aData
)
545 nsDependentCString
topic( aTopic
);
546 if ( topic
.Equals( XPI_PROGRESS_TOPIC
) )
548 //------------------------------------------------------
549 // Communication from the XPInstall Progress Dialog
550 //------------------------------------------------------
552 nsDependentString
data( aData
);
554 if ( data
.Equals( NS_LITERAL_STRING("open") ) )
556 // -- The dialog has been opened
558 return NS_OK
; // We've already been opened, nothing more to do
560 mDialogOpen
= PR_TRUE
;
563 nsCOMPtr
<nsIObserverService
> os(do_GetService("@mozilla.org/observer-service;1"));
566 os
->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC
, PR_TRUE
);
567 os
->AddObserver(this, "quit-application", PR_TRUE
);
570 mDlg
= do_QueryInterface(aSubject
);
572 // -- get the ball rolling
576 else if ( data
.Equals( NS_LITERAL_STRING("cancel") ) )
578 // -- The dialog/user wants us to cancel the download
579 mCancelled
= PR_TRUE
;
582 // if we've never been opened then we can shutdown right here,
583 // otherwise we need to let mCancelled get discovered elsewhere
589 else if ( topic
.Equals( NS_IOSERVICE_GOING_OFFLINE_TOPIC
) ||
590 topic
.Equals( "quit-application" ) )
592 mCancelled
= PR_TRUE
;
600 ///////////////////////////////////////////////////////////////////////////////////////////////
601 // Function name : VerifySigning
602 // Description : Verify that the entire zip file is signed by the certificate displayed to
603 // the user during download
604 // Return type : PRInt32
605 // Argument : nsIZipReader* hZip - the zip reader
606 // Argument : nsIPrincipal* aPrincipal - a principal, if any, displayed to the user
607 // during download. Would have been retrieved from the first file in the zip
608 ///////////////////////////////////////////////////////////////////////////////////////////////
611 VerifySigning(nsIZipReader
* hZip
, nsIPrincipal
* aPrincipal
)
613 // If we didn't detect a principal from the zip file during download then
614 // we didn't suggest it was signed to the user, so just carry on.
619 aPrincipal
->GetHasCertificate(&hasCert
);
621 return NS_ERROR_FAILURE
;
623 nsCOMPtr
<nsIJAR
> jar(do_QueryInterface(hZip
));
625 return NS_ERROR_FAILURE
;
627 // See if the archive is signed at all first
628 nsCOMPtr
<nsIPrincipal
> principal
;
629 nsresult rv
= jar
->GetCertificatePrincipal(nsnull
, getter_AddRefs(principal
));
630 if (NS_FAILED(rv
) || !principal
)
631 return NS_ERROR_FAILURE
;
633 PRUint32 entryCount
= 0;
635 // first verify all files in the jar are also in the manifest.
636 nsCOMPtr
<nsIUTF8StringEnumerator
> entries
;
637 rv
= hZip
->FindEntries(nsnull
, getter_AddRefs(entries
));
643 while (NS_SUCCEEDED(entries
->HasMore(&more
)) && more
)
645 rv
= entries
->GetNext(name
);
646 if (NS_FAILED(rv
)) return rv
;
648 // Do not verify the directory entries or
649 // entries which are in the meta-inf directory
650 if ((name
.Last() == '/') ||
651 (PL_strncasecmp("META-INF/", name
.get(), 9) == 0))
654 // Count the entries to be verified
657 // Each entry must be signed
658 rv
= jar
->GetCertificatePrincipal(name
.get(), getter_AddRefs(principal
));
659 if (NS_FAILED(rv
) || !principal
) return NS_ERROR_FAILURE
;
662 rv
= principal
->Equals(aPrincipal
, &equal
);
663 if (NS_FAILED(rv
) || !equal
) return NS_ERROR_FAILURE
;
666 // next verify all files in the manifest are in the archive.
667 PRUint32 manifestEntryCount
;
668 rv
= jar
->GetManifestEntriesCount(&manifestEntryCount
);
672 if (entryCount
!= manifestEntryCount
)
673 return NS_ERROR_FAILURE
; // some files were deleted from archive
679 ///////////////////////////////////////////////////////////////////////////////////////////////
680 // Function name : OpenAndValidateArchive
681 // Description : Opens install archive and validates contents
682 // Return type : PRInt32
683 // Argument : nsIZipReader* hZip - the zip reader
684 // Argument : nsIFile* jarFile - the .xpi file
685 // Argument : nsIPrincipal* aPrincipal - a principal, if any, displayed to the user
686 // regarding the cert used to sign this install
687 ///////////////////////////////////////////////////////////////////////////////////////////////
690 OpenAndValidateArchive(nsIZipReader
* hZip
, nsIFile
* jarFile
, nsIPrincipal
* aPrincipal
)
693 return nsInstall::DOWNLOAD_ERROR
;
695 nsCOMPtr
<nsIFile
> jFile
;
696 nsresult rv
=jarFile
->Clone(getter_AddRefs(jFile
));
697 if (NS_SUCCEEDED(rv
))
698 rv
= hZip
->Open(jFile
);
701 return nsInstall::CANT_READ_ARCHIVE
;
703 // CRC check the integrity of all items in this archive
704 rv
= hZip
->Test(nsnull
);
707 NS_WARNING("CRC check of archive failed!");
708 return nsInstall::CANT_READ_ARCHIVE
;
711 rv
= VerifySigning(hZip
, aPrincipal
);
714 NS_WARNING("Signing check of archive failed!");
715 return nsInstall::INVALID_SIGNATURE
;
718 if (NS_FAILED(hZip
->Test("install.rdf")))
720 NS_WARNING("Archive did not contain an install manifest!");
721 return nsInstall::NO_INSTALL_SCRIPT
;
724 return nsInstall::SUCCESS
;
728 nsresult
nsXPInstallManager::InstallItems()
731 nsCOMPtr
<nsIZipReader
> hZip
= do_CreateInstance(kZipReaderCID
, &rv
);
732 NS_ENSURE_SUCCESS(rv
, rv
);
733 nsCOMPtr
<nsIExtensionManager
> em
= do_GetService("@mozilla.org/extensions/manager;1", &rv
);
734 NS_ENSURE_SUCCESS(rv
, rv
);
736 // can't cancel from here on cause we can't undo installs in a multitrigger
737 for (PRUint32 i
= 0; i
< mTriggers
->Size(); ++i
)
739 mItem
= (nsXPITriggerItem
*)mTriggers
->Get(i
);
740 if ( !mItem
|| !mItem
->mFile
)
742 // notification for these errors already handled
746 // If there was hash info in the trigger, but
747 // there wasn't a hash object created, then the
748 // algorithm used isn't known.
750 if (mItem
->mHashFound
&& !mItem
->mHasher
)
753 mTriggers
->SendStatus( mItem
->mURL
.get(), nsInstall::INVALID_HASH_TYPE
);
755 mDlg
->OnStateChange( i
, nsIXPIProgressDialog::INSTALL_DONE
,
756 nsInstall::INVALID_HASH_TYPE
);
760 // Don't install if we can't verify the hash (if specified)
761 if (mItem
->mHasher
&& !VerifyHash(mItem
))
764 mTriggers
->SendStatus( mItem
->mURL
.get(), nsInstall::INVALID_HASH
);
766 mDlg
->OnStateChange( i
, nsIXPIProgressDialog::INSTALL_DONE
,
767 nsInstall::INVALID_HASH
);
772 mDlg
->OnStateChange( i
, nsIXPIProgressDialog::INSTALL_START
, 0 );
774 PRInt32 finalStatus
= OpenAndValidateArchive( hZip
,
779 if (finalStatus
== nsInstall::SUCCESS
)
781 rv
= em
->InstallItemFromFile( mItem
->mFile
,
782 NS_INSTALL_LOCATION_APPPROFILE
);
784 finalStatus
= nsInstall::EXECUTION_ERROR
;
787 mTriggers
->SendStatus( mItem
->mURL
.get(), finalStatus
);
789 mDlg
->OnStateChange( i
, nsIXPIProgressDialog::INSTALL_DONE
,
795 NS_IMETHODIMP
nsXPInstallManager::DownloadNext()
802 // Don't download any more if we were cancelled
807 if ( mNextItem
< mTriggers
->Size() )
809 //-------------------------------------------------
810 // There are items to download, get the next one
811 //-------------------------------------------------
812 mItem
= (nsXPITriggerItem
*)mTriggers
->Get(mNextItem
++);
814 NS_ASSERTION( mItem
, "bogus Trigger slipped through" );
815 NS_ASSERTION( !mItem
->mURL
.IsEmpty(), "bogus trigger");
816 if ( !mItem
|| mItem
->mURL
.IsEmpty() )
818 // serious problem with trigger! Can't notify anyone of the
819 // error without the URL, just try to carry on.
820 return DownloadNext();
823 // --- Tell the dialog we're starting a download
825 mDlg
->OnStateChange( mNextItem
-1, nsIXPIProgressDialog::DOWNLOAD_START
, 0 );
827 if ( mItem
->IsFileURL() && mChromeType
== NOT_CHROME
)
829 //--------------------------------------------------
830 // Already local, we can open it where it is
831 //--------------------------------------------------
832 nsCOMPtr
<nsIURI
> pURL
;
833 rv
= NS_NewURI(getter_AddRefs(pURL
), mItem
->mURL
);
835 if (NS_SUCCEEDED(rv
))
837 nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(pURL
,&rv
);
840 nsCOMPtr
<nsIFile
> localFile
;
841 rv
= fileURL
->GetFile(getter_AddRefs(localFile
));
842 if (NS_SUCCEEDED(rv
))
844 mItem
->mFile
= do_QueryInterface(localFile
,&rv
);
849 if ( NS_FAILED(rv
) || !mItem
->mFile
)
851 // send error status back
853 mDlg
->OnStateChange( mNextItem
-1,
854 nsIXPIProgressDialog::INSTALL_DONE
,
855 nsInstall::UNEXPECTED_ERROR
);
856 mTriggers
->SendStatus( mItem
->mURL
.get(),
857 nsInstall::UNEXPECTED_ERROR
);
862 mDlg
->OnStateChange( mNextItem
-1,
863 nsIXPIProgressDialog::DOWNLOAD_DONE
, 0);
866 // --- on to the next one
867 return DownloadNext();
871 //--------------------------------------------------
872 // We have one to download
873 //--------------------------------------------------
874 rv
= GetDestinationFile(mItem
->mURL
, getter_AddRefs(mItem
->mFile
));
875 if (NS_SUCCEEDED(rv
))
877 nsCOMPtr
<nsIURI
> pURL
;
878 rv
= NS_NewURI(getter_AddRefs(pURL
), mItem
->mURL
);
879 if (NS_SUCCEEDED(rv
))
881 nsCOMPtr
<nsIChannel
> channel
;
883 rv
= NS_NewChannel(getter_AddRefs(channel
), pURL
, nsnull
, nsnull
, this);
884 if (NS_SUCCEEDED(rv
))
886 rv
= channel
->AsyncOpen(this, nsnull
);
895 mDlg
->OnStateChange( mNextItem
-1,
896 nsIXPIProgressDialog::INSTALL_DONE
,
897 nsInstall::DOWNLOAD_ERROR
);
898 mTriggers
->SendStatus( mItem
->mURL
.get(),
899 nsInstall::DOWNLOAD_ERROR
);
902 // We won't get Necko callbacks so start the next one now
903 return DownloadNext();
909 //------------------------------------------------------
910 // all downloaded, install them
911 //------------------------------------------------------
920 //-------------------------------------------------------------------
923 // Returns true if the file hash matches the expected value (or if
924 // the item has no hash value). False if we can't verify the hash
927 PRBool
nsXPInstallManager::VerifyHash(nsXPITriggerItem
* aItem
)
929 NS_ASSERTION(aItem
, "Null nsXPITriggerItem passed to VerifyHash");
935 nsCOMPtr
<nsIInputStream
> stream
;
936 rv
= NS_NewLocalFileInputStream(getter_AddRefs(stream
), aItem
->mFile
);
937 if (NS_FAILED(rv
)) return PR_FALSE
;
939 rv
= aItem
->mHasher
->UpdateFromStream(stream
, PR_UINT32_MAX
);
940 if (NS_FAILED(rv
)) return PR_FALSE
;
942 nsCAutoString binaryHash
;
943 rv
= aItem
->mHasher
->Finish(PR_FALSE
, binaryHash
);
944 if (NS_FAILED(rv
)) return PR_FALSE
;
947 for (PRUint32 i
=0; i
< binaryHash
.Length(); ++i
)
949 hash
= PR_sprintf_append(hash
,"%.2x", (PRUint8
)binaryHash
[i
]);
952 PRBool result
= aItem
->mHash
.EqualsIgnoreCase(hash
);
954 PR_smprintf_free(hash
);
959 void nsXPInstallManager::Shutdown(PRInt32 status
)
963 // tell the dialog it can go away
964 mDlg
->OnStateChange(0, nsIXPIProgressDialog::DIALOG_CLOSE
, 0 );
970 mNeedsShutdown
= PR_FALSE
;
972 // Send remaining status notifications if we were cancelled early
973 nsXPITriggerItem
* item
;
974 while ( mNextItem
< mTriggers
->Size() )
976 item
= (nsXPITriggerItem
*)mTriggers
->Get(mNextItem
++);
977 if ( item
&& !item
->mURL
.IsEmpty() )
979 mTriggers
->SendStatus( item
->mURL
.get(), status
);
983 // Clean up downloaded files (regular install only, not chrome installs)
984 for (PRUint32 i
= 0; i
< mTriggers
->Size(); i
++ )
986 item
= static_cast<nsXPITriggerItem
*>(mTriggers
->Get(i
));
987 if ( item
&& item
->mFile
&& !item
->IsFileURL() )
988 item
->mFile
->Remove(PR_FALSE
);
991 nsCOMPtr
<nsIObserverService
> os(do_GetService("@mozilla.org/observer-service;1"));
994 os
->RemoveObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC
);
995 os
->RemoveObserver(this, "quit-application");
1009 nsXPInstallManager::LoadParams(PRUint32 aCount
, const PRUnichar
** aPackageList
, nsIDialogParamBlock
** aParams
)
1012 nsCOMPtr
<nsIDialogParamBlock
> paramBlock
= do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID
, &rv
);
1013 if (NS_SUCCEEDED(rv
))
1015 // set OK and Cancel buttons
1016 paramBlock
->SetInt( 0, 2 );
1017 // pass in number of strings
1018 paramBlock
->SetInt( 1, aCount
);
1020 paramBlock
->SetNumberStrings( aCount
);
1021 for (PRUint32 i
= 0; i
< aCount
; i
++)
1022 paramBlock
->SetString( i
, aPackageList
[i
] );
1025 NS_IF_ADDREF(*aParams
= paramBlock
);
1031 nsXPInstallManager::GetDestinationFile(nsString
& url
, nsILocalFile
* *file
)
1033 NS_ENSURE_ARG_POINTER(file
);
1036 nsCOMPtr
<nsIProperties
> directoryService
=
1037 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
, &rv
);
1038 NS_ENSURE_SUCCESS(rv
, rv
);
1040 nsCOMPtr
<nsILocalFile
> temp
;
1041 rv
= directoryService
->Get(NS_OS_TEMP_DIR
,
1042 NS_GET_IID(nsIFile
),
1043 getter_AddRefs(temp
));
1044 NS_ENSURE_SUCCESS(rv
, rv
);
1046 temp
->AppendNative(NS_LITERAL_CSTRING("tmp.xpi"));
1047 temp
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
1049 NS_IF_ADDREF(*file
);
1055 nsXPInstallManager::CheckCert(nsIChannel
* aChannel
)
1057 nsCOMPtr
<nsIURI
> uri
;
1058 nsresult rv
= aChannel
->GetOriginalURI(getter_AddRefs(uri
));
1059 NS_ENSURE_SUCCESS(rv
, rv
);
1060 nsCAutoString scheme
;
1061 rv
= uri
->GetScheme(scheme
);
1062 NS_ENSURE_SUCCESS(rv
, rv
);
1063 if (!scheme
.Equals(NS_LITERAL_CSTRING("https")))
1066 nsCOMPtr
<nsISupports
> security
;
1067 rv
= aChannel
->GetSecurityInfo(getter_AddRefs(security
));
1068 NS_ENSURE_SUCCESS(rv
, rv
);
1069 nsCOMPtr
<nsISSLStatusProvider
> statusProvider(do_QueryInterface(security
));
1070 NS_ENSURE_TRUE(statusProvider
, NS_ERROR_FAILURE
);
1072 rv
= statusProvider
->GetSSLStatus(getter_AddRefs(security
));
1073 NS_ENSURE_SUCCESS(rv
, rv
);
1074 nsCOMPtr
<nsISSLStatus
> status(do_QueryInterface(security
));
1075 NS_ENSURE_TRUE(status
, NS_ERROR_FAILURE
);
1076 nsCOMPtr
<nsIX509Cert
> cert
;
1077 rv
= status
->GetServerCert(getter_AddRefs(cert
));
1078 NS_ENSURE_SUCCESS(rv
, rv
);
1080 nsCOMPtr
<nsIX509Cert
> issuer
;
1081 rv
= cert
->GetIssuer(getter_AddRefs(issuer
));
1082 NS_ENSURE_SUCCESS(rv
, rv
);
1084 while (issuer
&& NS_SUCCEEDED(cert
->Equals(issuer
, &equal
)) && !equal
) {
1086 rv
= cert
->GetIssuer(getter_AddRefs(issuer
));
1087 NS_ENSURE_SUCCESS(rv
, rv
);
1091 nsAutoString tokenName
;
1092 rv
= issuer
->GetTokenName(tokenName
);
1093 NS_ENSURE_SUCCESS(rv
,rv
);
1094 if (tokenName
.Equals(NS_LITERAL_STRING("Builtin Object Token")))
1097 return NS_ERROR_FAILURE
;
1101 nsXPInstallManager::OnStartRequest(nsIRequest
* request
, nsISupports
*ctxt
)
1103 nsresult rv
= NS_ERROR_FAILURE
;
1105 // If we are dealing with a HTTP request, then treat HTTP error pages as
1106 // download failures.
1107 nsCOMPtr
<nsIHttpChannel
> httpChan
= do_QueryInterface(request
);
1109 // If we were chrome lauched check the certificate on the request
1110 if (mFromChrome
&& NS_FAILED(CheckCert(httpChan
))) {
1111 request
->Cancel(NS_BINDING_ABORTED
);
1115 if (NS_SUCCEEDED(httpChan
->GetRequestSucceeded(&succeeded
)) && !succeeded
) {
1116 // HTTP response is not a 2xx!
1117 request
->Cancel(NS_BINDING_ABORTED
);
1122 NS_ASSERTION( mItem
&& mItem
->mFile
, "XPIMgr::OnStartRequest bad state");
1123 if ( mItem
&& mItem
->mFile
)
1125 NS_ASSERTION( !mItem
->mOutStream
, "Received double OnStartRequest from Necko");
1127 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(mItem
->mOutStream
),
1129 PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
,
1137 nsXPInstallManager::OnStopRequest(nsIRequest
*request
, nsISupports
*ctxt
,
1145 case NS_BINDING_SUCCEEDED
:
1149 case NS_BINDING_FAILED
:
1150 case NS_BINDING_ABORTED
:
1152 // XXX need to note failure, both to send back status
1153 // to the callback, and also so we don't try to install
1154 // this probably corrupt file.
1158 rv
= NS_ERROR_ILLEGAL_VALUE
;
1161 NS_ASSERTION( mItem
, "Bad state in XPIManager");
1162 NS_ASSERTION( mItem
->mOutStream
, "XPIManager: output stream doesn't exist");
1163 if ( mItem
&& mItem
->mOutStream
)
1165 mItem
->mOutStream
->Close();
1166 mItem
->mOutStream
= nsnull
;
1169 if (NS_FAILED(rv
) || mCancelled
)
1172 // -- first clean up partially downloaded file
1177 rv2
= mItem
->mFile
->Exists(&flagExists
);
1178 if (NS_SUCCEEDED(rv2
) && flagExists
)
1179 mItem
->mFile
->Remove(PR_FALSE
);
1184 // -- then notify interested parties
1185 PRInt32 errorcode
= mCancelled
? nsInstall::USER_CANCELLED
1186 : nsInstall::DOWNLOAD_ERROR
;
1188 mDlg
->OnStateChange( mNextItem
-1,
1189 nsIXPIProgressDialog::INSTALL_DONE
,
1191 mTriggers
->SendStatus( mItem
->mURL
.get(), errorcode
);
1195 mDlg
->OnStateChange( mNextItem
-1, nsIXPIProgressDialog::DOWNLOAD_DONE
, 0);
1204 nsXPInstallManager::OnDataAvailable(nsIRequest
* request
, nsISupports
*ctxt
,
1205 nsIInputStream
*pIStream
,
1206 PRUint32 sourceOffset
,
1209 #define XPI_ODA_BUFFER_SIZE 8*1024
1210 PRUint32 amt
= PR_MIN(XPI_ODA_BUFFER_SIZE
, length
);
1212 char buffer
[XPI_ODA_BUFFER_SIZE
];
1213 PRUint32 writeCount
;
1217 // We must cancel this download in progress. We may get extra
1218 // OnData calls if they were already queued so beware
1219 request
->Cancel(NS_BINDING_ABORTED
);
1220 return NS_ERROR_FAILURE
;
1225 err
= pIStream
->Read(buffer
, amt
, &amt
);
1227 if (amt
== 0) break;
1228 if (NS_FAILED(err
)) return err
;
1230 err
= mItem
->mOutStream
->Write( buffer
, amt
, &writeCount
);
1231 if (NS_FAILED(err
) || writeCount
!= amt
)
1233 return NS_ERROR_FAILURE
;
1237 amt
= PR_MIN(XPI_ODA_BUFFER_SIZE
, length
);
1239 } while (length
> 0);
1246 nsXPInstallManager::OnProgress(nsIRequest
* request
, nsISupports
*ctxt
, PRUint64 aProgress
, PRUint64 aProgressMax
)
1248 nsresult rv
= NS_OK
;
1250 if (mDlg
&& !mCancelled
)
1252 if (mContentLength
< 1) {
1253 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
,&rv
);
1254 NS_ASSERTION(channel
, "should have a channel");
1255 if (NS_FAILED(rv
)) return rv
;
1256 rv
= channel
->GetContentLength(&mContentLength
);
1257 if (NS_FAILED(rv
)) return rv
;
1259 // XXX once channels support that, use 64-bit contentlength
1260 rv
= mDlg
->OnProgress( mNextItem
-1, aProgress
, nsUint64(mContentLength
) );
1267 nsXPInstallManager::OnStatus(nsIRequest
* request
, nsISupports
*ctxt
,
1268 nsresult aStatus
, const PRUnichar
*aStatusArg
)
1270 // don't need to do anything
1274 // nsIInterfaceRequestor method
1276 nsXPInstallManager::GetInterface(const nsIID
& eventSinkIID
, void* *_retval
)
1278 if (eventSinkIID
.Equals(NS_GET_IID(nsIAuthPrompt
))) {
1282 nsCOMPtr
<nsIWindowWatcher
> ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
));
1283 NS_ENSURE_SUCCESS(rv
, rv
);
1285 nsCOMPtr
<nsIAuthPrompt
> prompt
;
1286 rv
= ww
->GetNewAuthPrompter(nsnull
, getter_AddRefs(prompt
));
1287 NS_ENSURE_SUCCESS(rv
, rv
);
1289 nsIAuthPrompt
*p
= prompt
.get();
1294 else if (eventSinkIID
.Equals(NS_GET_IID(nsIBadCertListener2
))) {
1295 // If we aren't chrome triggered fall back to the default dialogs
1297 return NS_ERROR_NO_INTERFACE
;
1299 return QueryInterface(eventSinkIID
, (void**)_retval
);
1302 // nsIChannelEventSink method
1304 nsXPInstallManager::OnChannelRedirect(nsIChannel
*oldChannel
, nsIChannel
*newChannel
, PRUint32 flags
)
1306 // Chrome triggered installs need to have their certificates checked
1308 return CheckCert(oldChannel
);
1312 // nsIBadCertListener2 methods
1314 nsXPInstallManager::NotifyCertProblem(nsIInterfaceRequestor
*socketInfo
,
1315 nsISSLStatus
*status
,
1316 const nsACString
&targetSite
,
1323 // nsISSLErrorListener methods
1325 nsXPInstallManager::NotifySSLError(nsIInterfaceRequestor
*socketInfo
,
1327 const nsACString
&targetSite
,
1335 nsXPInstallManager::OnCertAvailable(nsIURI
*aURI
,
1336 nsISupports
* context
,
1338 nsIPrincipal
*aPrincipal
)
1340 if (NS_FAILED(aStatus
) && aStatus
!= NS_BINDING_ABORTED
) {
1341 // Check for a bad status. The only acceptable failure status code we accept
1342 // is NS_BINDING_ABORTED. For all others we want to ensure that the
1343 // nsIPrincipal is nsnull.
1345 NS_ASSERTION(aPrincipal
== nsnull
, "There has been an error, but we have a principal!");
1346 aPrincipal
= nsnull
;
1349 // get the current one and assign the cert name
1350 nsXPITriggerItem
*item
= mTriggers
->Get(mOutstandingCertLoads
);
1351 item
->SetPrincipal(aPrincipal
);
1353 if (mOutstandingCertLoads
== 0) {
1354 InitManagerInternal();
1358 // get the next one to load. If there is any failure, we just go on to the
1359 // next trigger. When all triggers items are handled, we call into InitManagerInternal
1361 item
= mTriggers
->Get(--mOutstandingCertLoads
);
1363 nsCOMPtr
<nsIURI
> uri
;
1364 NS_NewURI(getter_AddRefs(uri
), NS_ConvertUTF16toUTF8(item
->mURL
.get()).get());
1366 if (!uri
|| mChromeType
!= NOT_CHROME
)
1367 return OnCertAvailable(uri
, context
, NS_ERROR_FAILURE
, nsnull
);
1369 nsIStreamListener
* listener
= new CertReader(uri
, nsnull
, this);
1371 return OnCertAvailable(uri
, context
, NS_ERROR_FAILURE
, nsnull
);
1373 NS_ADDREF(listener
);
1374 nsresult rv
= NS_OpenURI(listener
, nsnull
, uri
);
1376 NS_ASSERTION(NS_SUCCEEDED(rv
), "OpenURI failed");
1377 NS_RELEASE(listener
);
1380 return OnCertAvailable(uri
, context
, NS_ERROR_FAILURE
, nsnull
);