596 lines
16 KiB
C++
596 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** 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.org code.
|
|
*
|
|
* 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):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either 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 "nsCRT.h"
|
|
#include "xp.h"
|
|
#include "su_folderspec.h"
|
|
#include "su_instl.h"
|
|
|
|
#include "nsSoftUpdateEnums.h"
|
|
#include "VerReg.h"
|
|
#include "nsInstallFile.h"
|
|
#include "nsSUError.h"
|
|
|
|
#include "nsPrivilegeManager.h"
|
|
#include "nsTarget.h"
|
|
#include "jvmmgr.h"
|
|
|
|
extern int SU_ERROR_INSTALL_FILE_UNEXPECTED;
|
|
extern int SU_DETAILS_REPLACE_FILE_MSG_ID;
|
|
extern int SU_DETAILS_INSTALL_FILE_MSG_ID;
|
|
|
|
#ifdef XP_WIN16
|
|
XP_Bool utilityScheduled = FALSE;
|
|
#endif
|
|
|
|
PR_BEGIN_EXTERN_C
|
|
|
|
static PRBool endsWith(nsString* str, char* string_to_find);
|
|
|
|
static PRBool endsWith(nsString* str, char* string_to_find)
|
|
{
|
|
PRBool found = PR_FALSE;
|
|
if (str) {
|
|
int len = strlen(".zip");
|
|
int size = str->Length();
|
|
int offset = str->RFind(string_to_find, PR_FALSE);
|
|
if (offset == (size - len))
|
|
found = PR_TRUE;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/* Public Methods */
|
|
|
|
/* Constructor
|
|
inSoftUpdate - softUpdate object we belong to
|
|
inComponentName - full path of the registry component
|
|
inVInfo - full version info
|
|
inJarLocation - location inside the JAR file
|
|
inFinalFileSpec - final location on disk
|
|
*/
|
|
nsInstallFile::nsInstallFile(nsSoftwareUpdate* inSoftUpdate,
|
|
char* inVRName,
|
|
nsVersionInfo* inVInfo,
|
|
char* inJarLocation,
|
|
nsFolderSpec* folderSpec,
|
|
char* inPartialPath,
|
|
PRBool forceInstall,
|
|
char* *errorMsg) : nsInstallObject(inSoftUpdate)
|
|
{
|
|
tempFile = NULL;
|
|
vrName = NULL;
|
|
jarLocation = NULL;
|
|
versionInfo = NULL;
|
|
finalFile = NULL;
|
|
regPackageName = NULL;
|
|
userPackageName = NULL;
|
|
force = PR_FALSE;
|
|
bJavaDir = PR_FALSE;
|
|
replace = PR_FALSE;
|
|
bChild = PR_FALSE;
|
|
bUpgrade = PR_FALSE;
|
|
|
|
if ((inVRName == NULL) || (inJarLocation == NULL) ||
|
|
(folderSpec == NULL) || (inSoftUpdate == NULL) ||
|
|
(inVInfo == NULL)) {
|
|
*errorMsg = SU_GetErrorMsg3("Invalid arguments to the constructor",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
return;
|
|
}
|
|
vrName = new nsString(inVRName);
|
|
versionInfo = inVInfo; /* XXX: Who owns and who free's this object. Is it nsSoftwareUpdate?? */
|
|
jarLocation = new nsString(inJarLocation);
|
|
force = forceInstall;
|
|
char* temp = folderSpec->MakeFullPath( inPartialPath, errorMsg );
|
|
if (temp != NULL) {
|
|
finalFile = new nsString(temp);
|
|
XP_FREE(temp);
|
|
}
|
|
bJavaDir = folderSpec->IsJavaCapable();
|
|
|
|
/* Request impersonation privileges */
|
|
nsTarget* impersonation = nsTarget::findTarget(IMPERSONATOR);
|
|
nsPrivilegeManager* privMgr = nsPrivilegeManager::getPrivilegeManager();
|
|
|
|
if ((privMgr != NULL) && (impersonation != NULL)) {
|
|
privMgr->enablePrivilege(impersonation, 1);
|
|
|
|
/* check the security permissions */
|
|
nsTarget* install_target = nsTarget::findTarget(INSTALL_PRIV);
|
|
if (install_target != NULL) {
|
|
if (!privMgr->enablePrivilege(install_target, softUpdate->GetPrincipal(), 1)) {
|
|
*errorMsg = SU_GetErrorMsg3("Permssion was denied", SUERR_ACCESS_DENIED);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
temp = inSoftUpdate->GetUserPackageName();
|
|
if (temp != NULL) {
|
|
userPackageName = new nsString(temp);
|
|
}
|
|
temp = inSoftUpdate->GetRegPackageName();
|
|
if (temp != NULL) {
|
|
regPackageName = new nsString(temp);
|
|
}
|
|
|
|
// determine Child status
|
|
if ( regPackageName == NULL ) {
|
|
// in the "current communicator package" absolute pathnames (start
|
|
// with slash) indicate shared files -- all others are children
|
|
bChild = ( vrName->CharAt(0) != '/' );
|
|
} else {
|
|
//bChild = vrName.startsWith(regPackageName);
|
|
/* Because nsString doesn't support startWith, implemented the following. Waiting for approval */
|
|
bChild = (nsCRT::strncmp((PRUnichar*)vrName, (PRUnichar*)regPackageName,
|
|
nsCRT::strlen((PRUnichar*)regPackageName)) == 0);
|
|
}
|
|
|
|
replace = NativeDoesFileExist();
|
|
|
|
}
|
|
|
|
|
|
nsInstallFile::~nsInstallFile()
|
|
{
|
|
delete vrName;
|
|
delete jarLocation;
|
|
if (tempFile)
|
|
delete tempFile;
|
|
if (finalFile)
|
|
delete finalFile;
|
|
if (regPackageName)
|
|
delete regPackageName;
|
|
if (userPackageName)
|
|
delete userPackageName;
|
|
}
|
|
|
|
/* Prepare
|
|
* Extracts file out of the JAR archive into the temp directory
|
|
*/
|
|
char* nsInstallFile::Prepare()
|
|
{
|
|
char *errorMsg = NULL;
|
|
|
|
if (softUpdate == NULL) {
|
|
errorMsg = SU_GetErrorMsg3("nsSoftwareUpdate object is null",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
return errorMsg;
|
|
}
|
|
if (jarLocation == NULL) {
|
|
errorMsg = SU_GetErrorMsg3("JAR file is null",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
return errorMsg;
|
|
}
|
|
if (finalFile == NULL) {
|
|
errorMsg = SU_GetErrorMsg3("folderSpec's full path (finalFile) was null",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
return errorMsg;
|
|
}
|
|
|
|
// XXX: Make the following security code into a function.
|
|
|
|
/* Request impersonation privileges */
|
|
nsTarget* impersonation = nsTarget::findTarget(IMPERSONATOR);
|
|
nsPrivilegeManager* privMgr = nsPrivilegeManager::getPrivilegeManager();
|
|
|
|
if ((privMgr != NULL) && (impersonation != NULL)) {
|
|
PRBool allowed = privMgr->enablePrivilege(impersonation, 1);
|
|
if (allowed == PR_FALSE) {
|
|
errorMsg = SU_GetErrorMsg3("Permssion was denied", SUERR_ACCESS_DENIED);
|
|
return errorMsg;
|
|
}
|
|
|
|
/* check the security permissions */
|
|
nsTarget* install_target = nsTarget::findTarget(INSTALL_PRIV);
|
|
if (install_target != NULL) {
|
|
PRBool allowed = privMgr->enablePrivilege(install_target, softUpdate->GetPrincipal(), 1);
|
|
if (allowed == PR_FALSE) {
|
|
errorMsg = SU_GetErrorMsg3("Permssion was denied", SUERR_ACCESS_DENIED);
|
|
return errorMsg;
|
|
}
|
|
}
|
|
}
|
|
|
|
char* jarLocationCharPtr = jarLocation->ToNewCString();
|
|
char* finalFileCharPtr = finalFile->ToNewCString();
|
|
char* temp = softUpdate->ExtractJARFile(jarLocationCharPtr, finalFileCharPtr, &errorMsg);
|
|
delete jarLocationCharPtr;
|
|
delete finalFileCharPtr;
|
|
if (errorMsg != NULL) {
|
|
PR_ASSERT(temp == NULL);
|
|
return errorMsg;
|
|
}
|
|
if (temp != NULL) {
|
|
tempFile = new nsString(temp);
|
|
free(temp);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Complete
|
|
* Completes the install:
|
|
* - move the downloaded file to the final location
|
|
* - updates the registry
|
|
*/
|
|
char* nsInstallFile::Complete()
|
|
{
|
|
int err;
|
|
int refCount;
|
|
int rc;
|
|
|
|
if (softUpdate == NULL) {
|
|
return SU_GetErrorMsg3("nsSoftwareUpdate object is null",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
}
|
|
if (vrName == NULL) {
|
|
return SU_GetErrorMsg3("version registry name is null",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
}
|
|
if (finalFile == NULL) {
|
|
return SU_GetErrorMsg3("folderSpec's full path (finalFile) is null",
|
|
SUERR_INVALID_ARGUMENTS);
|
|
}
|
|
|
|
/* Check the security for our target */
|
|
// XXX: Make the following security code into a function.
|
|
|
|
/* Request impersonation privileges */
|
|
nsTarget* impersonation = nsTarget::findTarget(IMPERSONATOR);
|
|
nsPrivilegeManager* privMgr = nsPrivilegeManager::getPrivilegeManager();
|
|
nsTarget* install_target = NULL;
|
|
|
|
if ((privMgr != NULL) && (impersonation != NULL)) {
|
|
privMgr->enablePrivilege(impersonation, 1);
|
|
|
|
/* check the security permissions */
|
|
install_target = nsTarget::findTarget(INSTALL_PRIV);
|
|
if (install_target != NULL) {
|
|
if (!privMgr->enablePrivilege(install_target,
|
|
softUpdate->GetPrincipal(), 1)) {
|
|
return SU_GetErrorMsg3("Permssion was denied",
|
|
SUERR_ACCESS_DENIED);
|
|
}
|
|
}
|
|
}
|
|
|
|
err = NativeComplete();
|
|
|
|
if ((privMgr != NULL) && (install_target != NULL)) {
|
|
privMgr->revertPrivilege(install_target, 1);
|
|
}
|
|
|
|
char *vr_name = vrName->ToNewCString();
|
|
char *final_file = finalFile->ToNewCString();
|
|
|
|
// Add java archives to the classpath. Don't add if we're
|
|
// replacing an existing file -- it'll already be there.
|
|
|
|
if ( bJavaDir && !replace ) {
|
|
PRBool found_zip = endsWith(finalFile, ".zip");
|
|
PRBool found_jar = endsWith(finalFile, ".jar");;
|
|
if (found_zip || found_jar) {
|
|
AddToClasspath( finalFile );
|
|
}
|
|
}
|
|
|
|
|
|
// Register file and log for Uninstall
|
|
|
|
if ( 0 == err || SU_REBOOT_NEEDED == err ) {
|
|
// we ignore all registry errors because they're not
|
|
// important enough to abort an otherwise OK install.
|
|
if (!bChild) {
|
|
int found;
|
|
if (regPackageName) {
|
|
char *reg_package_name = regPackageName->ToNewCString();
|
|
found = VR_UninstallFileExistsInList( reg_package_name, vr_name );
|
|
delete reg_package_name;
|
|
} else {
|
|
found = VR_UninstallFileExistsInList( "", vr_name );
|
|
}
|
|
if (found != REGERR_OK)
|
|
bUpgrade = PR_FALSE;
|
|
else
|
|
bUpgrade = PR_TRUE;
|
|
} else if (REGERR_OK == VR_InRegistry(vr_name)) {
|
|
bUpgrade = PR_TRUE;
|
|
} else {
|
|
bUpgrade = PR_FALSE;
|
|
}
|
|
|
|
err = VR_GetRefCount( vr_name, &refCount );
|
|
if ( err != REGERR_OK )
|
|
{
|
|
refCount = 0;
|
|
}
|
|
|
|
|
|
if (!bUpgrade) {
|
|
if (refCount != 0)
|
|
{
|
|
rc = 1 + refCount;
|
|
VR_Install( vr_name, final_file, versionInfo->toString(), PR_FALSE );
|
|
VR_SetRefCount( vr_name, rc );
|
|
}
|
|
else
|
|
{
|
|
if (replace)
|
|
{
|
|
VR_Install( vr_name, final_file, versionInfo->toString(), PR_FALSE);
|
|
VR_SetRefCount( vr_name, 2 );
|
|
}
|
|
else
|
|
{
|
|
VR_Install( vr_name, final_file, versionInfo->toString(), PR_FALSE );
|
|
VR_SetRefCount( vr_name, 1 );
|
|
|
|
}
|
|
}
|
|
}
|
|
else if (bUpgrade)
|
|
{
|
|
if (refCount == 0)
|
|
{
|
|
VR_Install( vr_name, final_file, versionInfo->toString(), PR_FALSE );
|
|
VR_SetRefCount( vr_name, 1 );
|
|
}
|
|
else
|
|
{
|
|
VR_Install( vr_name, final_file, versionInfo->toString(), PR_FALSE );
|
|
VR_SetRefCount( vr_name, 0 );
|
|
}
|
|
}
|
|
|
|
if ( !bChild && !bUpgrade ) {
|
|
if (regPackageName) {
|
|
char *reg_package_name = regPackageName->ToNewCString();
|
|
VR_UninstallAddFileToList( reg_package_name, vr_name );
|
|
delete reg_package_name;
|
|
} else {
|
|
VR_UninstallAddFileToList( "", vr_name );
|
|
}
|
|
}
|
|
}
|
|
delete vr_name;
|
|
delete final_file;
|
|
|
|
if ( err != 0 ) {
|
|
return SU_GetErrorMsg2(SU_ERROR_INSTALL_FILE_UNEXPECTED, finalFile, err);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void nsInstallFile::Abort()
|
|
{
|
|
NativeAbort();
|
|
}
|
|
|
|
char* nsInstallFile::toString()
|
|
{
|
|
if (replace) {
|
|
return SU_GetString2(SU_DETAILS_REPLACE_FILE_MSG_ID, finalFile);
|
|
} else {
|
|
return SU_GetString2(SU_DETAILS_INSTALL_FILE_MSG_ID, finalFile);
|
|
}
|
|
}
|
|
|
|
|
|
/* Private Methods */
|
|
|
|
/* Private Native Methods */
|
|
void nsInstallFile::NativeAbort()
|
|
{
|
|
char* currentName;
|
|
int result;
|
|
|
|
/* Get the names */
|
|
if (tempFile == NULL)
|
|
return;
|
|
currentName = tempFile->ToNewCString();
|
|
|
|
result = XP_FileRemove(currentName, xpURL);
|
|
XP_ASSERT(result == 0); /* XXX: need to fe_deletefilelater() or something */
|
|
delete currentName;
|
|
}
|
|
|
|
/* NativeComplete
|
|
* copies the file to its final location
|
|
* Tricky, we need to create the directories
|
|
*/
|
|
int nsInstallFile::NativeComplete()
|
|
{
|
|
char* currentName = NULL;
|
|
char* finalName = NULL;
|
|
char* finalNamePlatform;
|
|
int result = 0;
|
|
|
|
if (tempFile == NULL) {
|
|
return -1;
|
|
}
|
|
/* Get the names */
|
|
currentName = tempFile->ToNewCString();
|
|
|
|
PR_ASSERT(finalFile != NULL);
|
|
finalNamePlatform = finalFile->ToNewCString();
|
|
finalName = XP_PlatformFileToURL(finalNamePlatform);
|
|
|
|
if ( finalName == NULL || currentName == NULL ) {
|
|
/* memory or JRI problems */
|
|
result = -1;
|
|
goto end;
|
|
} else {
|
|
/* convert finalName name to xpURL form by stripping "file://" */
|
|
char *temp = XP_STRDUP(&finalName[7]);
|
|
XP_FREE(finalName);
|
|
finalName = temp;
|
|
}
|
|
|
|
if (finalName != NULL) {
|
|
if ( XP_STRCMP(finalName, currentName) == 0 ) {
|
|
/* No need to rename, they are the same */
|
|
result = 0;
|
|
} else {
|
|
XP_StatStruct s;
|
|
if ( XP_Stat( finalName, &s, xpURL ) != 0 ) {
|
|
/* Target file doesn't exist, try to rename file */
|
|
result = XP_FileRename(currentName, xpURL, finalName, xpURL);
|
|
} else {
|
|
/* Target exists, can't trust XP_FileRename--do platform
|
|
* specific stuff in FE_ReplaceExistingFile()
|
|
*/
|
|
result = -1;
|
|
}
|
|
}
|
|
} else {
|
|
/* memory problem */
|
|
result = -1;
|
|
}
|
|
|
|
if (result != 0) {
|
|
XP_StatStruct s;
|
|
if ( XP_Stat( finalName, &s, xpURL ) == 0 ) {
|
|
/* File already exists, need to remove the original */
|
|
result = FE_ReplaceExistingFile(currentName, xpURL, finalName, xpURL, force);
|
|
|
|
if ( result == SU_REBOOT_NEEDED ) {
|
|
#ifdef XP_WIN16
|
|
if (!utilityScheduled) {
|
|
utilityScheduled = PR_TRUE;
|
|
FE_ScheduleRenameUtility();
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
/* Directory might not exist, check and create if necessary */
|
|
char separator;
|
|
char * end;
|
|
separator = '/';
|
|
end = XP_STRRCHR(finalName, separator);
|
|
if (end) {
|
|
end[0] = 0;
|
|
result = XP_MakeDirectoryR( finalName, xpURL);
|
|
end[0] = separator;
|
|
if ( 0 == result )
|
|
result = XP_FileRename(currentName, xpURL, finalName, xpURL);
|
|
}
|
|
}
|
|
#ifdef XP_UNIX
|
|
/* Last try, can't rename() across file systems on UNIX */
|
|
if ( -1 == result ) {
|
|
result = FE_CopyFile(currentName, finalName);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
end:
|
|
XP_FREEIF(finalName);
|
|
delete currentName;
|
|
delete finalNamePlatform;
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
void nsInstallFile::AddToClasspath(nsString* file)
|
|
{
|
|
if ( file != NULL ) {
|
|
char *final_file = file->ToNewCString();
|
|
JVM_AddToClassPath(final_file);
|
|
delete final_file;
|
|
}
|
|
}
|
|
|
|
|
|
/* Finds out if the file exists
|
|
*/
|
|
PRBool nsInstallFile::NativeDoesFileExist()
|
|
{
|
|
char* fileName;
|
|
char* fileNamePlatform;
|
|
int32 err;
|
|
XP_StatStruct statinfo;
|
|
XP_Bool exists = FALSE;
|
|
|
|
PR_ASSERT(finalFile != NULL);
|
|
fileNamePlatform = finalFile->ToNewCString();
|
|
fileName = XP_PlatformFileToURL(fileNamePlatform);
|
|
if (fileName != NULL) {
|
|
char * temp = XP_STRDUP(&fileName[7]);
|
|
XP_FREEIF(fileName);
|
|
fileName = temp;
|
|
|
|
if (fileName) {
|
|
err = XP_Stat(fileName, &statinfo, xpURL);
|
|
if (err != -1) {
|
|
exists = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
XP_FREEIF(fileName);
|
|
delete fileNamePlatform;
|
|
return exists;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* CanUninstall
|
|
* InstallFile() installs files which can be uninstalled,
|
|
* hence this function returns true.
|
|
*/
|
|
PRBool
|
|
nsInstallFile::CanUninstall()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* RegisterPackageNode
|
|
* InstallFile() installs files which need to be registered,
|
|
* hence this function returns true.
|
|
*/
|
|
PRBool
|
|
nsInstallFile::RegisterPackageNode()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
PR_END_EXTERN_C
|