/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Daniel Veditz * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nscore.h" #include "pratom.h" #include "prmem.h" #include "nsInt64.h" #include "nsISupports.h" #include "nsIServiceManager.h" #include "nsIURL.h" #include "nsIFileURL.h" #include "nsITransport.h" #include "nsIOutputStream.h" #include "nsNetUtil.h" #include "nsIInputStream.h" #include "nsIFileStreams.h" #include "nsIStreamListener.h" #include "nsISoftwareUpdate.h" #include "nsSoftwareUpdateIIDs.h" #include "nsXPITriggerInfo.h" #include "nsXPInstallManager.h" #include "nsInstallTrigger.h" #include "nsInstallResources.h" #include "nsIProxyObjectManager.h" #include "nsIWindowWatcher.h" #include "nsIWindowMediator.h" #include "nsIDOMWindowInternal.h" #include "nsPIDOMWindow.h" #include "nsDirectoryService.h" #include "nsDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h" #include "nsReadableUtils.h" #include "nsProxiedService.h" #include "nsIPromptService.h" #include "nsIScriptGlobalObject.h" #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" #include "nsIObserverService.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "CertReader.h" #include "nsEmbedCID.h" static NS_DEFINE_IID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); static NS_DEFINE_IID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); #include "nsIEventQueueService.h" #define PREF_XPINSTALL_ENABLED "xpinstall.enabled" #define PREF_XPINSTALL_CONFIRM_DLG "xpinstall.dialog.confirm" #define PREF_XPINSTALL_STATUS_DLG_SKIN "xpinstall.dialog.progress.skin" #define PREF_XPINSTALL_STATUS_DLG_CHROME "xpinstall.dialog.progress.chrome" #define PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN "xpinstall.dialog.progress.type.skin" #define PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME "xpinstall.dialog.progress.type.chrome" #define UPDATE_DLG(x) (((x) - mLastUpdate) > 250000) // Mac can't handle PRTime as integers, must go through this hell inline PRBool nsXPInstallManager::TimeToUpdate(PRTime now) { // XXX lets revisit this when dveditz gets back return PR_TRUE; } nsXPInstallManager::nsXPInstallManager() : mTriggers(0), mItem(0), mNextItem(0), mNumJars(0), mChromeType(NOT_CHROME), mContentLength(0), mDialogOpen(PR_FALSE), mCancelled(PR_FALSE), mSelectChrome(PR_FALSE), mNeedsShutdown(PR_FALSE) { // we need to own ourself because we have a longer // lifetime than the scriptlet that created us. NS_ADDREF_THIS(); // initialize mLastUpdate to the current time mLastUpdate = PR_Now(); nsCOMPtr os(do_GetService("@mozilla.org/observer-service;1")); if (os) os->AddObserver(this, XPI_PROGRESS_TOPIC, PR_TRUE); } nsXPInstallManager::~nsXPInstallManager() { if (mTriggers) delete mTriggers; } NS_IMPL_THREADSAFE_ISUPPORTS9( nsXPInstallManager, nsIXPIListener, nsIXPIDialogService, nsIXPInstallManager, nsIObserver, nsIStreamListener, nsIProgressEventSink, nsIInterfaceRequestor, nsPICertNotification, nsISupportsWeakReference) NS_IMETHODIMP nsXPInstallManager::InitManagerFromChrome(const PRUnichar **aURLs, PRUint32 aURLCount, nsIXPIProgressDialog* aListener) { // If Software Installation is not enabled, we don't want to proceed with // update. PRBool xpinstallEnabled = PR_TRUE; nsCOMPtr pref(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pref) pref->GetBoolPref(PREF_XPINSTALL_ENABLED, &xpinstallEnabled); if (!xpinstallEnabled) return NS_OK; mTriggers = new nsXPITriggerInfo(); if (!mTriggers) return NS_ERROR_OUT_OF_MEMORY; for (PRInt32 i = 0; i < aURLCount; ++i) { nsXPITriggerItem* item = new nsXPITriggerItem(0, aURLs[i], nsnull); if (!item) { delete mTriggers; // nsXPITriggerInfo frees any alloc'ed nsXPITriggerItems mTriggers = nsnull; return NS_ERROR_OUT_OF_MEMORY; } mTriggers->Add(item); } nsresult rv; mInstallSvc = do_GetService(nsSoftwareUpdate::GetCID(), &rv); if (NS_FAILED(rv)) { delete mTriggers; mTriggers = nsnull; return rv; } return Observe(aListener, XPI_PROGRESS_TOPIC, NS_LITERAL_STRING("open").get()); } NS_IMETHODIMP nsXPInstallManager::InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITriggerInfo* aTriggers, PRUint32 aChromeType) { if ( !aTriggers || aTriggers->Size() == 0 ) { NS_WARNING("XPInstallManager called with no trigger info!"); NS_RELEASE_THIS(); return NS_ERROR_INVALID_POINTER; } nsresult rv = NS_OK; mTriggers = aTriggers; mChromeType = aChromeType; mNeedsShutdown = PR_TRUE; mParentWindow = do_QueryInterface(aGlobalObject); // Don't launch installs while page is still loading PRBool isPageLoading = PR_FALSE; nsCOMPtr piWindow = do_QueryInterface(mParentWindow); if (piWindow) isPageLoading = piWindow->IsLoadingOrRunningTimeout(); if (isPageLoading) rv = NS_ERROR_FAILURE; else { // Start downloading initial chunks looking for signatures, mOutstandingCertLoads = mTriggers->Size(); nsXPITriggerItem *item = mTriggers->Get(--mOutstandingCertLoads); nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), NS_ConvertUCS2toUTF8(item->mURL)); nsCOMPtr listener = new CertReader(uri, nsnull, this); if (listener) rv = NS_OpenURI(listener, nsnull, uri); else rv = NS_ERROR_OUT_OF_MEMORY; } if (NS_FAILED(rv)) { Shutdown(); } return rv; } nsresult nsXPInstallManager::InitManagerInternal() { nsresult rv; PRBool OKtoInstall = PR_FALSE; // initialize to secure state //----------------------------------------------------- // *** Do not return early after this point *** // // We have to clean up the triggers in case of error //----------------------------------------------------- // --- use embedding dialogs if any registered nsCOMPtr dlgSvc(do_CreateInstance(NS_XPIDIALOGSERVICE_CONTRACTID)); if ( !dlgSvc ) dlgSvc = this; // provide our own dialogs // --- make sure we can get the install service mInstallSvc = do_GetService(nsSoftwareUpdate::GetCID(), &rv); // --- prepare dialog params PRUint32 numTriggers = mTriggers->Size(); PRUint32 numStrings = 4 * numTriggers; const PRUnichar** packageList = (const PRUnichar**)malloc( sizeof(PRUnichar*) * numStrings ); if ( packageList && NS_SUCCEEDED(rv) ) { // populate the list. The list doesn't own the strings for ( PRUint32 i=0, j=0; i < numTriggers; i++ ) { nsXPITriggerItem *item = mTriggers->Get(i); packageList[j++] = item->mName.get(); packageList[j++] = item->GetSafeURLString(); packageList[j++] = item->mIconURL.get(); packageList[j++] = item->mCertName.get(); } //----------------------------------------------------- // Get permission to install //----------------------------------------------------- if ( mChromeType == CHROME_SKIN ) { // skins get a simpler/friendlier dialog // XXX currently not embeddable OKtoInstall = ConfirmChromeInstall( mParentWindow, packageList ); } else { rv = dlgSvc->ConfirmInstall( mParentWindow, packageList, numStrings, &OKtoInstall ); if (NS_FAILED(rv)) OKtoInstall = PR_FALSE; } if (OKtoInstall) { //----------------------------------------------------- // Open the progress dialog //----------------------------------------------------- rv = dlgSvc->OpenProgressDialog( packageList, numStrings, this ); } } else rv = NS_ERROR_OUT_OF_MEMORY; //----------------------------------------------------- // cleanup and signal callbacks if there were errors //----------------------------------------------------- if (packageList) free(packageList); PRInt32 cbstatus = 0; // callback status if (NS_FAILED(rv)) cbstatus = nsInstall::UNEXPECTED_ERROR; else if (!OKtoInstall) cbstatus = nsInstall::USER_CANCELLED; if ( cbstatus != 0 ) { // --- inform callbacks of error for (PRUint32 i = 0; i < mTriggers->Size(); i++) { mTriggers->SendStatus( mTriggers->Get(i)->mURL.get(), cbstatus ); } // --- must delete ourselves if not continuing NS_RELEASE_THIS(); } return rv; } NS_IMETHODIMP nsXPInstallManager::ConfirmInstall(nsIDOMWindow *aParent, const PRUnichar **aPackageList, PRUint32 aCount, PRBool *aRetval) { *aRetval = PR_FALSE; nsCOMPtr parentWindow( do_QueryInterface(aParent) ); nsCOMPtr params; nsresult rv = LoadParams( aCount, aPackageList, getter_AddRefs(params) ); if ( NS_SUCCEEDED(rv) && parentWindow && params) { nsCOMPtr newWindow; nsCOMPtr ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); ifptr->SetData(params); ifptr->SetDataIID(&NS_GET_IID(nsIDialogParamBlock)); char* confirmDialogURL; nsCOMPtr pref(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pref) { rv = pref->GetCharPref(PREF_XPINSTALL_CONFIRM_DLG, &confirmDialogURL); NS_ASSERTION(NS_SUCCEEDED(rv), "Can't invoke XPInstall FE without a FE URL! Set xpinstall.dialog.confirm"); if (NS_FAILED(rv)) return rv; } rv = parentWindow->OpenDialog(NS_ConvertASCIItoUCS2(confirmDialogURL), NS_LITERAL_STRING("_blank"), NS_LITERAL_STRING("chrome,centerscreen,modal,titlebar"), ifptr, getter_AddRefs(newWindow)); if (NS_SUCCEEDED(rv)) { //Now get which button was pressed from the ParamBlock PRInt32 buttonPressed = 0; params->GetInt( 0, &buttonPressed ); *aRetval = buttonPressed ? PR_FALSE : PR_TRUE; } } return rv; } PRBool nsXPInstallManager::ConfirmChromeInstall(nsIDOMWindowInternal* aParentWindow, const PRUnichar **aPackage) { // get the dialog strings nsXPIDLString applyNowText; nsXPIDLString confirmText; nsCOMPtr bundleSvc = do_GetService( kStringBundleServiceCID ); if (!bundleSvc) return PR_FALSE; nsCOMPtr xpiBundle; bundleSvc->CreateBundle( XPINSTALL_BUNDLE_URL, getter_AddRefs(xpiBundle) ); if (!xpiBundle) return PR_FALSE; const PRUnichar *formatStrings[2] = { aPackage[0], aPackage[1] }; if ( mChromeType == CHROME_LOCALE ) { xpiBundle->GetStringFromName( NS_LITERAL_STRING("ApplyNowLocale").get(), getter_Copies(applyNowText)); xpiBundle->FormatStringFromName( NS_LITERAL_STRING("ConfirmLocale").get(), formatStrings, 2, getter_Copies(confirmText)); } else { xpiBundle->GetStringFromName( NS_LITERAL_STRING("ApplyNowSkin").get(), getter_Copies(applyNowText)); xpiBundle->FormatStringFromName( NS_LITERAL_STRING("ConfirmSkin").get(), formatStrings, 2, getter_Copies(confirmText)); } if (confirmText.IsEmpty()) return PR_FALSE; // confirmation dialog PRBool bInstall = PR_FALSE; nsCOMPtr dlgService(do_GetService(NS_PROMPTSERVICE_CONTRACTID)); if (dlgService) { dlgService->Confirm( aParentWindow, nsnull, confirmText, &bInstall ); } return bInstall; } NS_IMETHODIMP nsXPInstallManager::OpenProgressDialog(const PRUnichar **aPackageList, PRUint32 aCount, nsIObserver *aObserver) { // --- convert parameters into nsISupportArray members nsCOMPtr list; nsresult rv = LoadParams( aCount, aPackageList, getter_AddRefs(list) ); if (NS_FAILED(rv)) return rv; nsCOMPtr listwrap(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID)); if (listwrap) { listwrap->SetData(list); listwrap->SetDataIID(&NS_GET_IID(nsIDialogParamBlock)); } nsCOMPtr callbackwrap(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID)); if (callbackwrap) { callbackwrap->SetData(aObserver); callbackwrap->SetDataIID(&NS_GET_IID(nsIObserver)); } nsCOMPtr params(do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID)); if ( !params || !listwrap || !callbackwrap ) return NS_ERROR_FAILURE; params->AppendElement(listwrap); params->AppendElement(callbackwrap); // --- open the window nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); if (wwatch) { char *statusDialogURL, *statusDialogType; nsCOMPtr pref(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pref) { const char* statusDlg = mChromeType == CHROME_SKIN ? PREF_XPINSTALL_STATUS_DLG_SKIN : PREF_XPINSTALL_STATUS_DLG_CHROME; rv = pref->GetCharPref(statusDlg, &statusDialogURL); NS_ASSERTION(NS_SUCCEEDED(rv), "Can't invoke XPInstall FE without a FE URL! Set xpinstall.dialog.status"); if (NS_FAILED(rv)) return rv; const char* statusType = mChromeType == CHROME_SKIN ? PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN : PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME; rv = pref->GetCharPref(statusType, &statusDialogType); nsAutoString type; type.AssignWithConversion(statusDialogType); if (NS_SUCCEEDED(rv) && !type.IsEmpty()) { nsCOMPtr wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); nsCOMPtr recentWindow; wm->GetMostRecentWindow(type.get(), getter_AddRefs(recentWindow)); if (recentWindow) { nsCOMPtr os(do_GetService("@mozilla.org/observer-service;1")); os->NotifyObservers(params, "xpinstall-download-started", nsnull); recentWindow->Focus(); return NS_OK; } } } nsCOMPtr newWindow; rv = wwatch->OpenWindow(0, statusDialogURL, "_blank", "chrome,centerscreen,titlebar,dialog=no,resizable", params, getter_AddRefs(newWindow)); } return rv; } NS_IMETHODIMP nsXPInstallManager::Observe( nsISupports *aSubject, const char *aTopic, const PRUnichar *aData ) { nsresult rv = NS_ERROR_ILLEGAL_VALUE; if ( !aTopic || !aData ) return rv; if ( nsDependentCString( aTopic ).Equals( XPI_PROGRESS_TOPIC ) ) { //------------------------------------------------------ // Communication from the XPInstall Progress Dialog //------------------------------------------------------ nsDependentString data( aData ); if ( data.Equals( NS_LITERAL_STRING("open") ) ) { // -- The dialog has been opened if (mDialogOpen) return NS_OK; // We've already been opened, nothing more to do mDialogOpen = PR_TRUE; rv = NS_OK; nsCOMPtr dlg( do_QueryInterface(aSubject) ); if (dlg) { // --- create and save a proxy for the dialog nsCOMPtr pmgr = do_GetService(kProxyObjectManagerCID, &rv); if (pmgr) { rv = pmgr->GetProxyForObject( NS_UI_THREAD_EVENTQ, NS_GET_IID(nsIXPIProgressDialog), dlg, PROXY_SYNC | PROXY_ALWAYS, getter_AddRefs(mDlg) ); } } // -- get the ball rolling DownloadNext(); } else if ( data.Equals( NS_LITERAL_STRING("cancel") ) ) { // -- The dialog/user wants us to cancel the download mCancelled = PR_TRUE; if ( !mDialogOpen ) { // if we've never been opened then we can shutdown right here, // otherwise we need to let mCancelled get discovered elsewhere Shutdown(); } rv = NS_OK; } } return rv; } NS_IMETHODIMP nsXPInstallManager::DownloadNext() { nsresult rv; mContentLength = 0; if (mCancelled) { // Don't download any more if we were cancelled Shutdown(); return NS_OK; } if ( mNextItem < mTriggers->Size() ) { //------------------------------------------------- // There are items to download, get the next one //------------------------------------------------- mItem = (nsXPITriggerItem*)mTriggers->Get(mNextItem++); NS_ASSERTION( mItem, "bogus Trigger slipped through" ); NS_ASSERTION( !mItem->mURL.IsEmpty(), "bogus trigger"); if ( !mItem || mItem->mURL.IsEmpty() ) { // serious problem with trigger! Can't notify anyone of the // error without the URL, just try to carry on. return DownloadNext(); } // --- Tell the dialog we're starting a download if (mDlg) mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::DOWNLOAD_START, 0 ); if ( mItem->IsFileURL() && mChromeType == NOT_CHROME ) { //-------------------------------------------------- // Already local, we can open it where it is //-------------------------------------------------- nsCOMPtr pURL; rv = NS_NewURI(getter_AddRefs(pURL), mItem->mURL); if (NS_SUCCEEDED(rv)) { nsCOMPtr fileURL = do_QueryInterface(pURL,&rv); if (fileURL) { nsCOMPtr localFile; rv = fileURL->GetFile(getter_AddRefs(localFile)); if (NS_SUCCEEDED(rv)) { mItem->mFile = do_QueryInterface(localFile,&rv); } } } if ( NS_FAILED(rv) || !mItem->mFile ) { // send error status back if (mDlg) mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::INSTALL_DONE, nsInstall::UNEXPECTED_ERROR ); mTriggers->SendStatus( mItem->mURL.get(), nsInstall::UNEXPECTED_ERROR ); mItem->mFile = 0; } else if (mDlg) { mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::DOWNLOAD_DONE, 0); } // --- on to the next one return DownloadNext(); } else { //-------------------------------------------------- // We have one to download //-------------------------------------------------- rv = GetDestinationFile(mItem->mURL, getter_AddRefs(mItem->mFile)); if (NS_SUCCEEDED(rv)) { nsCOMPtr pURL; rv = NS_NewURI(getter_AddRefs(pURL), mItem->mURL); if (NS_SUCCEEDED(rv)) { nsCOMPtr channel; rv = NS_NewChannel(getter_AddRefs(channel), pURL, nsnull, nsnull, this); if (NS_SUCCEEDED(rv)) { rv = channel->AsyncOpen(this, nsnull); } } } if (NS_FAILED(rv)) { // announce failure if (mDlg) mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::INSTALL_DONE, nsInstall::DOWNLOAD_ERROR ); mTriggers->SendStatus( mItem->mURL.get(), nsInstall::DOWNLOAD_ERROR ); mItem->mFile = 0; // We won't get Necko callbacks so start the next one now return DownloadNext(); } } } else { //------------------------------------------------------ // all downloaded, queue them for installation //------------------------------------------------------ // can't cancel from here on cause we can't undo installs in a multitrigger for (PRUint32 i = 0; i < mTriggers->Size(); ++i) { mItem = (nsXPITriggerItem*)mTriggers->Get(i); if ( !mItem || !mItem->mFile ) { // notification for these errors already handled continue; } // We've got one to install; increment count first so we // don't have to worry about thread timing. PR_AtomicIncrement(&mNumJars); if ( mChromeType == NOT_CHROME ) { rv = mInstallSvc->InstallJar( mItem->mFile, mItem->mURL.get(), mItem->mArguments.get(), mItem->mPrincipal, mItem->mFlags, this ); } else { rv = mInstallSvc->InstallChrome(mChromeType, mItem->mFile, mItem->mURL.get(), mItem->mName.get(), mSelectChrome, this ); } if (NS_FAILED(rv)) { // it failed so remove it from the count PR_AtomicDecrement(&mNumJars); // send the error status to any trigger callback mTriggers->SendStatus( mItem->mURL.get(), nsInstall::UNEXPECTED_ERROR ); if (mDlg) mDlg->OnStateChange( i, nsIXPIProgressDialog::INSTALL_DONE, nsInstall::UNEXPECTED_ERROR ); } } if ( mNumJars == 0 ) { // We must clean ourself up now -- we won't be called back Shutdown(); } } return rv; } void nsXPInstallManager::Shutdown() { if (mDlg) { // tell the dialog it can go away mDlg->OnStateChange(0, nsIXPIProgressDialog::DIALOG_CLOSE, 0 ); mDlg = nsnull; mDialogOpen = PR_FALSE; } if (mNeedsShutdown) { mNeedsShutdown = PR_FALSE; // Send remaining status notifications if we were cancelled early nsXPITriggerItem* item; while ( mNextItem < mTriggers->Size() ) { item = (nsXPITriggerItem*)mTriggers->Get(mNextItem++); if ( item && !item->mURL.IsEmpty() ) { mTriggers->SendStatus( item->mURL.get(), nsInstall::USER_CANCELLED ); } } // Clean up downloaded files (regular install only, not chrome installs) nsCOMPtr tmpSpec; if ( mChromeType == NOT_CHROME ) { for (PRUint32 i = 0; i < mTriggers->Size(); i++ ) { item = NS_STATIC_CAST(nsXPITriggerItem*, mTriggers->Get(i)); if ( item && item->mFile && !item->IsFileURL() ) item->mFile->Remove(PR_FALSE); } } nsCOMPtr os(do_GetService("@mozilla.org/observer-service;1")); if (os) os->RemoveObserver(this, XPI_PROGRESS_TOPIC); NS_RELEASE_THIS(); } } NS_IMETHODIMP nsXPInstallManager::LoadParams(PRUint32 aCount, const PRUnichar** aPackageList, nsIDialogParamBlock** aParams) { nsresult rv; nsCOMPtr paramBlock = do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { // set OK and Cancel buttons paramBlock->SetInt( 0, 2 ); // pass in number of strings paramBlock->SetInt( 1, aCount ); // add strings paramBlock->SetNumberStrings( aCount ); for (PRUint32 i = 0; i < aCount; i++) paramBlock->SetString( i, aPackageList[i] ); } NS_IF_ADDREF(*aParams = paramBlock); return rv; } NS_IMETHODIMP nsXPInstallManager::GetDestinationFile(nsString& url, nsILocalFile* *file) { NS_ENSURE_ARG_POINTER(file); nsresult rv; nsAutoString leaf; PRInt32 pos = url.RFindChar('/'); url.Mid( leaf, pos+1, url.Length() ); nsCOMPtr directoryService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); if (mChromeType == NOT_CHROME ) { // a regular XPInstall, not chrome if (NS_SUCCEEDED(rv)) { nsCOMPtr temp; rv = directoryService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(temp)); if (NS_SUCCEEDED(rv)) { temp->AppendNative(NS_LITERAL_CSTRING("tmp.xpi")); temp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); *file = temp; NS_IF_ADDREF(*file); } } } else { // a chrome install, download straight to final destination if (NS_SUCCEEDED(rv)) // Getting directoryService { nsCOMPtr userChrome; // Get the user's Chrome directory, create if necessary rv = directoryService->Get(NS_APP_USER_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(userChrome)); NS_ASSERTION(NS_SUCCEEDED(rv) && userChrome, "App_UserChromeDirectory not defined!"); if (NS_SUCCEEDED(rv)) { PRBool exists; rv = userChrome->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) { rv = userChrome->Create(nsIFile::DIRECTORY_TYPE, 0775); } if (NS_SUCCEEDED(rv)) { userChrome->Append(leaf); userChrome->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); *file = userChrome; NS_IF_ADDREF(*file); } } } } return rv; } NS_IMETHODIMP nsXPInstallManager::OnStartRequest(nsIRequest* request, nsISupports *ctxt) { nsresult rv = NS_ERROR_FAILURE; NS_ASSERTION( mItem && mItem->mFile, "XPIMgr::OnStartRequest bad state"); if ( mItem && mItem->mFile ) { NS_ASSERTION( !mItem->mOutStream, "Received double OnStartRequest from Necko"); rv = NS_NewLocalFileOutputStream(getter_AddRefs(mItem->mOutStream), mItem->mFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0664); } return rv; } NS_IMETHODIMP nsXPInstallManager::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) { nsresult rv; switch( status ) { case NS_BINDING_SUCCEEDED: rv = NS_OK; break; case NS_BINDING_FAILED: case NS_BINDING_ABORTED: rv = status; // XXX need to note failure, both to send back status // to the callback, and also so we don't try to install // this probably corrupt file. break; default: rv = NS_ERROR_ILLEGAL_VALUE; } NS_ASSERTION( mItem, "Bad state in XPIManager"); NS_ASSERTION( mItem->mOutStream, "XPIManager: output stream doesn't exist"); if ( mItem && mItem->mOutStream ) { mItem->mOutStream->Close(); mItem->mOutStream = nsnull; } if (NS_FAILED(rv) || mCancelled) { // Download error! // -- first clean up partially downloaded file if ( mItem->mFile ) { PRBool flagExists; nsresult rv2 ; rv2 = mItem->mFile->Exists(&flagExists); if (NS_SUCCEEDED(rv2) && flagExists) mItem->mFile->Remove(PR_FALSE); mItem->mFile = 0; } // -- then notify interested parties PRInt32 errorcode = mCancelled ? nsInstall::USER_CANCELLED : nsInstall::DOWNLOAD_ERROR; if (mDlg) mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::INSTALL_DONE, errorcode ); mTriggers->SendStatus( mItem->mURL.get(), errorcode ); } else if (mDlg) { mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::DOWNLOAD_DONE, 0); } DownloadNext(); return rv; } NS_IMETHODIMP nsXPInstallManager::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, nsIInputStream *pIStream, PRUint32 sourceOffset, PRUint32 length) { PRUint32 amt; nsresult err; char buffer[8*1024]; PRUint32 writeCount; if (mCancelled) { // We must cancel this download in progress. We may get extra // OnData calls if they were already queued so beware request->Cancel(NS_BINDING_ABORTED); return NS_ERROR_FAILURE; } do { err = pIStream->Read(buffer, sizeof(buffer), &amt); if (amt == 0) break; if (NS_FAILED(err)) { //printf("pIStream->Read Failed! %d", err); return err; } err = mItem->mOutStream->Write( buffer, amt, &writeCount); if (NS_FAILED(err) || writeCount != amt) { return NS_ERROR_FAILURE; } length -= amt; } while (length > 0); return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnProgress(nsIRequest* request, nsISupports *ctxt, PRUint64 aProgress, PRUint64 aProgressMax) { nsresult rv = NS_OK; PRTime now = PR_Now(); if (mDlg && !mCancelled && TimeToUpdate(now)) { if (mContentLength < 1) { nsCOMPtr channel = do_QueryInterface(request,&rv); NS_ASSERTION(channel, "should have a channel"); if (NS_FAILED(rv)) return rv; rv = channel->GetContentLength(&mContentLength); if (NS_FAILED(rv)) return rv; } mLastUpdate = now; // XXX once channels support that, use 64-bit contentlength rv = mDlg->OnProgress( mNextItem-1, aProgress, nsUint64(mContentLength) ); } return rv; } NS_IMETHODIMP nsXPInstallManager::OnStatus(nsIRequest* request, nsISupports *ctxt, nsresult aStatus, const PRUnichar *aStatusArg) { // don't need to do anything return NS_OK; } // nsIInterfaceRequestor method NS_IMETHODIMP nsXPInstallManager::GetInterface(const nsIID & eventSinkIID, void* *_retval) { return QueryInterface(eventSinkIID, (void**)_retval); } // IXPIListener methods PRInt32 nsXPInstallManager::GetIndexFromURL(const PRUnichar* aUrl) { // --- figure out which index corresponds to this URL PRUint32 i; for (i=0; i < mTriggers->Size(); i++) { if ( (nsXPITriggerInfo*)mTriggers->Get(i)->mURL.Equals(aUrl) ) break; } NS_ASSERTION( i < mTriggers->Size(), "invalid result URL!" ); return i; } NS_IMETHODIMP nsXPInstallManager::OnInstallStart(const PRUnichar *URL) { if (mDlg) mDlg->OnStateChange( GetIndexFromURL( URL ), nsIXPIProgressDialog::INSTALL_START, 0 ); return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnPackageNameSet(const PRUnichar *URL, const PRUnichar *UIPackageName, const PRUnichar *aVersion) { // Don't need to do anything return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnItemScheduled(const PRUnichar *message) { // Don't need to do anything return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnFinalizeProgress(const PRUnichar *message, PRInt32 itemNum, PRInt32 totNum) { // Don't need to do anything return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnInstallDone(const PRUnichar *URL, PRInt32 status) { // --- send the final success/error status on to listeners mTriggers->SendStatus( URL, status ); if (mDlg) mDlg->OnStateChange( GetIndexFromURL( URL ), nsIXPIProgressDialog::INSTALL_DONE, status ); PR_AtomicDecrement( &mNumJars ); if ( mNumJars == 0 ) Shutdown(); return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnLogComment(const PRUnichar* comment) { // Don't need to do anything return NS_OK; } NS_IMETHODIMP nsXPInstallManager::OnCertAvailable(nsIURI *aURI, nsISupports* context, nsresult aStatus, nsIPrincipal *aPrincipal) { if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED) { // Check for a bad status. The only acceptable failure status code we accept // is NS_BINDING_ABORTED. For all others we want to ensure that the // nsIPrincipal is nsnull. NS_ASSERTION(aPrincipal == nsnull, "There has been an error, but we have a principal!"); aPrincipal = nsnull; } // get the current one and assign the cert name nsXPITriggerItem *item = mTriggers->Get(mOutstandingCertLoads); item->SetPrincipal(aPrincipal); if (mOutstandingCertLoads == 0) { InitManagerInternal(); return NS_OK; } // get the next one to load. If there is any failure, we just go on to the // next trigger. When all triggers items are handled, we call into InitManagerInternal item = mTriggers->Get(--mOutstandingCertLoads); nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), NS_ConvertUCS2toUTF8(item->mURL.get()).get()); if (!uri || mChromeType != NOT_CHROME) return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull); nsIStreamListener* listener = new CertReader(uri, nsnull, this); if (!listener) return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull); NS_ADDREF(listener); nsresult rv = NS_OpenURI(listener, nsnull, uri); NS_ASSERTION(NS_SUCCEEDED(rv), "OpenURI failed"); NS_RELEASE(listener); if (NS_FAILED(rv)) return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull); return NS_OK; }