Mozilla/mozilla/modules/softupdt/src/nsSoftwareUpdate.cpp
dmose%mozilla.org 0efb7c174c updated xPL license boilerplate to v1.1, a=chofmann@netscape.com,r=endico@mozilla.org
git-svn-id: svn://10.0.0.236/trunk@52910 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-06 03:43:54 +00:00

2399 lines
62 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "prmem.h"
#include "prmon.h"
#include "prlog.h"
#include "fe_proto.h"
#include "xp.h"
#include "xp_str.h"
#include "prprf.h"
#include "softupdt.h"
#include "nsString.h"
#include "nsSoftwareUpdate.h"
#include "nsSoftUpdateEnums.h"
#include "nsInstallObject.h"
#include "nsInstallFile.h"
#include "nsInstallDelete.h"
#include "nsInstallExecute.h"
#include "nsInstallPatch.h"
#include "nsUninstallObject.h"
#include "nsSUError.h"
#include "nsWinProfile.h"
#include "nsWinReg.h"
#include "nsPrivilegeManager.h"
#include "nsTarget.h"
#include "nsPrincipal.h"
#include "zig.h"
#include "prefapi.h"
#include "proto.h"
#include "jsapi.h"
#include "xp_error.h"
#include "jsjava.h"
#include "xpgetstr.h"
#include "pw_public.h"
#include "VerReg.h"
#ifdef XP_MAC
#include "su_aplsn.h"
#endif
#ifdef XP_MAC
#pragma export on
#endif
extern int MK_OUT_OF_MEMORY;
extern int SU_ERROR_BAD_PACKAGE_NAME;
extern int SU_ERROR_WIN_PROFILE_MUST_CALL_START;
extern int SU_ERROR_BAD_PACKAGE_NAME_AS;
extern int SU_ERROR_EXTRACT_FAILED;
extern int SU_ERROR_NO_CERTIFICATE;
extern int SU_ERROR_TOO_MANY_CERTIFICATES;
extern int SU_ERROR_BAD_JS_ARGUMENT;
extern int SU_ERROR_SMART_UPDATE_DISABLED;
extern int SU_ERROR_UNEXPECTED;
extern int SU_ERROR_VERIFICATION_FAILED;
extern int SU_ERROR_MISSING_INSTALLER;
extern int SU_ERROR_OUT_OF_MEMORY;
extern int SU_ERROR_EXTRACT_FAILED;
/*****************************************
* SoftwareUpdate progress dialog methods
*****************************************/
#define TITLESIZE 256
extern int SU_INSTALLWIN_TITLE;
extern int SU_INSTALLWIN_UNPACKING;
extern int SU_INSTALLWIN_INSTALLING;
#ifdef XP_PC
extern char * WH_TempFileName(int type, const char * prefix,
const char * extension);
#endif
PR_BEGIN_EXTERN_C
static PRBool su_PathEndsWithSeparator(char* path, char* sep);
extern uint32 FE_DiskSpaceAvailable (MWContext *context, const char *lpszPath );
/* Public Methods */
/**
* @param env JavaScript environment (this inside the installer).
* Contains installer directives
* @param inUserPackageName Name of tha package installed.
* This name is displayed to the user
*/
nsSoftwareUpdate::nsSoftwareUpdate(void* env, char* inUserPackageName)
{
userPackageName = XP_STRDUP(inUserPackageName);
installPrincipal = NULL;
packageName = NULL;
packageFolder = NULL;
installedFiles = NULL;
confdlg = NULL;
progwin = NULL;
patchList = new nsHashtable();
zigPtr = NULL;
versionInfo = NULL;
userChoice = -1;
lastError = 0;
silent = PR_FALSE;
force = PR_FALSE;
jarName = NULL;
jarURL = NULL;
bUninstallPackage = PR_FALSE;
bRegisterPackage = PR_FALSE;
bShowProgress = PR_TRUE;
bShowFinalize = PR_TRUE;
bUserCancelled = PR_FALSE;
char *errorMsg;
/* Need to verify that this is a SoftUpdate JavaScript object */
errorMsg = VerifyJSObject(env);
/* XXX: FIX IT. How do we get data from env
jarName = (String) env.getMember("src");
jarCharset = (String) env.getMember("jarCharset");
jarURL = (String) env.getMember("srcURL");
silent = ((PRBool) env.getMember("silent")).booleanValue();
force = ((PRBool) env.getMember("force")).booleanValue();
*/
#ifdef XP_PC
filesep = "\\";
#elif defined(XP_MAC)
filesep = ":";
#else
filesep = "/";
#endif
}
nsSoftwareUpdate::~nsSoftwareUpdate()
{
if(patchList)
delete patchList;
if(packageFolder)
delete packageFolder;
if(versionInfo)
delete versionInfo;
CleanUp();
}
/* Gives its internal copy. Don't free the returned value */
nsPrincipal* nsSoftwareUpdate::GetPrincipal()
{
return installPrincipal;
}
/* Gives its internal copy. Don't free the returned value */
char* nsSoftwareUpdate::GetUserPackageName()
{
return userPackageName;
}
/* Gives its internal copy. Don't free the returned value */
char* nsSoftwareUpdate::GetRegPackageName()
{
return packageName;
}
PRBool nsSoftwareUpdate::GetSilent()
{
return silent;
}
/**
* @return a vector of InstallObjects
*/
nsVector* nsSoftwareUpdate::GetInstallQueue()
{
return installedFiles;
}
/**
* @return the most recent non-zero error code
* @see ResetError
*/
PRInt32 nsSoftwareUpdate::GetLastError()
{
return lastError;
}
/**
* resets remembered error code to zero
* @see GetLastError
*/
void nsSoftwareUpdate::ResetError()
{
lastError = 0;
}
/**
* @return the folder object suitable to be passed into AddSubcomponent
* @param folderID One of the predefined folder names
* @see AddSubcomponent
*/
nsFolderSpec* nsSoftwareUpdate::GetFolder(char* folderID, char* *errorMsg)
{
nsFolderSpec* spec = NULL;
errorMsg = NULL;
if ((XP_STRCMP(folderID, "Installed") != 0) &&
(XP_STRCMP(folderID, FOLDER_FILE_URL) != 0)) {
spec = new nsFolderSpec(folderID, packageName, userPackageName);
if (XP_STRCMP(folderID, "User Pick") == 0) {
// Force the prompt
char * ignore = spec->GetDirectoryPath();
if (ignore == NULL)
{
if(spec)
delete spec;
return NULL;
}
}
}
return spec;
}
/**
* @return the full path to a component as it is known to the
* Version Registry
* @param component Version Registry component name
*/
nsFolderSpec* nsSoftwareUpdate::GetComponentFolder(char* component)
{
int err;
char* dir;
char* qualifiedComponent;
nsFolderSpec* spec = NULL;
qualifiedComponent = GetQualifiedPackageName( component );
if (qualifiedComponent == NULL)
{
return NULL;
}
dir = (char*)XP_CALLOC(MAXREGPATHLEN, sizeof(char));
err = VR_GetDefaultDirectory( qualifiedComponent, MAXREGPATHLEN, dir );
if (err != REGERR_OK)
{
XP_FREEIF(dir);
dir = NULL;
}
if ( dir == NULL )
{
dir = (char*)XP_CALLOC(MAXREGPATHLEN, sizeof(char));
err = VR_GetPath( qualifiedComponent, MAXREGPATHLEN, dir );
if (err != REGERR_OK)
{
XP_FREEIF(dir);
dir = NULL;
}
if ( dir != NULL ) {
int i;
nsString dirStr(dir);
if ((i = dirStr.RFind(filesep)) > 0) {
XP_FREEIF(dir);
dir = (char*)XP_ALLOC(i);
dir = dirStr.ToCString(dir, i);
}
}
}
if ( dir != NULL )
{
/* We have a directory */
if (su_PathEndsWithSeparator(dir, filesep))
{
spec = new nsFolderSpec("Installed", dir, userPackageName);
}
else
{
dir = XP_AppendStr(dir, filesep);
spec = new nsFolderSpec("Installed", dir, userPackageName);
}
XP_FREEIF(dir);
}
return spec;
}
nsFolderSpec* nsSoftwareUpdate::GetComponentFolder(char* component,
char* subdir,
char* *errorMsg)
{
nsFolderSpec* spec = GetComponentFolder( component );
nsFolderSpec* ret_val = GetFolder( spec, subdir, errorMsg );
if(spec);
delete spec;
return ret_val;
}
/**
* sets the default folder for the package
* @param folder a folder object obtained through GetFolder()
*/
void nsSoftwareUpdate::SetPackageFolder(nsFolderSpec* folder)
{
packageFolder = folder;
}
/**
* Returns a Windows Profile object if you're on windows,
* null if you're not or if there's a security error
*/
void* nsSoftwareUpdate::GetWinProfile(nsFolderSpec* folder,
char* file,
char* *errorMsg)
{
#ifdef XP_PC
nsWinProfile* profile = NULL;
*errorMsg = NULL;
SanityCheck(errorMsg);
if (*errorMsg == NULL)
{
profile = new nsWinProfile(this, folder, file);
}
return profile;
#else
return NULL;
#endif
}
/**
* @return an object for manipulating the Windows Registry.
* Null if you're not on windows
*/
void* nsSoftwareUpdate::GetWinRegistry(char* *errorMsg)
{
#ifdef XP_PC
nsWinReg* registry = NULL;
SanityCheck(errorMsg);
if (*errorMsg == NULL)
{
registry = new nsWinReg(this);
}
return registry;
#else
return NULL;
#endif /* XP_PC */
}
/**
* extract the file out of the JAR directory and places it into temporary
* directory.
* two security checks:
* - the certificate of the extracted file must match the installer certificate
* - must have privileges to extract the jar file
*
* Caller should free the returned string
*
* @param inJarLocation file name inside the JAR file
*/
char* nsSoftwareUpdate::ExtractJARFile(char* inJarLocation,
char* finalFile,
char* *errorMsg)
{
if (zigPtr == NULL) {
*errorMsg = SU_GetErrorMsg3("JAR file has not been opened",
SUERR_UNKNOWN_JAR_FILE );
return NULL;
}
/* Security checks */
nsTarget* target;
nsPrivilegeManager* privMgr = nsPrivilegeManager::getPrivilegeManager();
target = nsTarget::findTarget( INSTALL_PRIV );
if (target != NULL) {
if (!privMgr->isPrivilegeEnabled(target, 1)) {
return NULL;
}
}
/* Make sure that the certificate of the extracted file matches
the installer certificate */
/* XXX this needs to be optimized, so that we do not create a principal
for every certificate */
/* XXX: Should we put up a dialog for unsigned JAR file support? */
XP_Bool unsigned_jar_enabled;
PREF_GetBoolPref("autoupdate.unsigned_jar_support", &unsigned_jar_enabled);
{
PRUint32 i;
PRBool haveMatch = PR_FALSE;
nsPrincipalArray* prinArray =
(nsPrincipalArray*)getCertificates(zigPtr, inJarLocation);
PR_ASSERT(installPrincipal != NULL);
if ((prinArray != NULL) && (prinArray->GetSize() > 0)) {
PRUint32 noOfPrins = prinArray->GetSize();
for (i=0; i < noOfPrins; i++) {
nsPrincipal* prin = (nsPrincipal*)prinArray->Get(i);
if (installPrincipal->equals( prin )) {
haveMatch = PR_TRUE;
break;
}
}
}
if ((haveMatch == PR_FALSE)) {
char *msg = NULL;
if (prinArray == NULL) {
if (!unsigned_jar_enabled) {
msg = PR_sprintf_append(msg, "Missing certificate for %s",
inJarLocation);
*errorMsg = SU_GetErrorMsg3(msg, SUERR_NO_CERTIFICATE);
}
} else {
msg = PR_sprintf_append(msg, "Missing certificate for %s", inJarLocation);
*errorMsg = SU_GetErrorMsg3(msg,
SUERR_NO_MATCHING_CERTIFICATE);
}
PR_FREEIF(msg);
}
freeIfCertificates(prinArray);
if (*errorMsg != NULL)
return NULL;
}
/* Extract the file */
char* outExtractLocation = NativeExtractJARFile(inJarLocation, finalFile,
errorMsg);
return outExtractLocation;
}
void
nsSoftwareUpdate::ParseFlags(int flags)
{
if ((flags & SU_NO_STATUS_DLG) == SU_NO_STATUS_DLG)
{
bShowProgress = PR_FALSE;
}
if ((flags & SU_NO_FINALIZE_DLG) == SU_NO_FINALIZE_DLG)
{
bShowFinalize = PR_FALSE;
}
}
/**
* Call this to initialize the update
* Opens the jar file and gets the certificate of the installer
* Opens up the gui, and asks for proper security privileges
*
* @param vrPackageName Full qualified version registry name of the package
* (ex: "/Plugins/Adobe/Acrobat")
* NULL or empty package names are errors
*
* @param inVInfo version of the package installed.
* Can be NULL, in which case package is installed
* without a version. Having a NULL version, this
* package is automatically updated in the future
* (ie. no version check is performed).
*
* @param flags Once was securityLevel(LIMITED_INSTALL or FULL_INSTALL). Now
* can be either NO_STATUS_DLG or NO_FINALIZE_DLG
*/
PRInt32 nsSoftwareUpdate::StartInstall(char* vrPackageName,
nsVersionInfo* inVInfo,
PRInt32 flags,
char* *errorMsg)
{
int errcode= SU_SUCCESS;
*errorMsg = NULL;
ResetError();
ParseFlags(flags);
bUserCancelled = PR_FALSE;
packageName = NULL;
if ( vrPackageName == NULL )
{
*errorMsg = SU_GetErrorMsg4(SU_ERROR_BAD_PACKAGE_NAME,
SUERR_INVALID_ARGUMENTS );
return SUERR_INVALID_ARGUMENTS;
}
packageName = GetQualifiedPackageName( vrPackageName );
int len = XP_STRLEN(packageName);
int last_pos = len-1;
char* tmpPackageName = new char[len+1];
XP_STRCPY(tmpPackageName, packageName);
while ((last_pos >= 0) && (tmpPackageName[last_pos] == '/'))
{
// Make sure that package name does not end with '/'
char* ptr = new char[last_pos+1];
memcpy(tmpPackageName, ptr, last_pos);
ptr[last_pos] = '\0';
if(tmpPackageName);
delete tmpPackageName;
tmpPackageName = ptr;
last_pos = last_pos - 1;
}
packageName = XP_STRDUP(tmpPackageName);
if(tmpPackageName);
delete tmpPackageName;
/* delete the old nsVersionInfo object. */
if(versionInfo);
delete versionInfo;
versionInfo = inVInfo;
installedFiles = new nsVector();
/* JAR initalization */
/* open the file, create a principal out of installer file certificate */
errcode = OpenJARFile(errorMsg);
if (*errorMsg != NULL)
return errcode;
errcode = InitializeInstallerCertificate(errorMsg);
if (*errorMsg != NULL)
return errcode;
CheckSilentPrivileges();
errcode = RequestSecurityPrivileges(errorMsg);
if (*errorMsg != NULL)
return errcode;
if (bShowProgress)
{
OpenProgressDialog();
}
// set up default package folder, if any
int err;
char* path = (char*)XP_CALLOC(MAXREGPATHLEN, sizeof(char));
err = VR_GetDefaultDirectory( packageName, MAXREGPATHLEN, path );
if (err != REGERR_OK)
{
XP_FREEIF(path);
path = NULL;
}
if ( path != NULL ) {
packageFolder = new nsFolderSpec("Installed", path, userPackageName);
XP_FREEIF(path);
}
saveError( errcode );
if (errcode != 0)
{
packageName = NULL; // Reset!
}
return errcode;
}
/*
* another forms of StartInstall()
*/
PRInt32 nsSoftwareUpdate::StartInstall(char* vrPackageName,
char* inVer,
PRInt32 flags,
char* *errorMsg)
{
return StartInstall(vrPackageName, new nsVersionInfo( inVer ), flags, errorMsg);
}
/**
* another StartInstall() simplification -- version as char*
*/
PRInt32 nsSoftwareUpdate::StartInstall(char* vrPackageName, char* inVer,
char* *errorMsg)
{
return StartInstall( vrPackageName, new nsVersionInfo( inVer ), 0, errorMsg );
}
/*
* UI feedback
*/
void nsSoftwareUpdate::UserCancelled()
{
userChoice = 0;
}
void nsSoftwareUpdate::UserApproved()
{
userChoice = 1;
}
/**
* Proper completion of the install
* Copies all the files to the right place
* returns 0 on success, <0 error code on failure
*/
PRInt32 nsSoftwareUpdate::FinalizeInstall(char* *errorMsg)
{
PRBool rebootNeeded = PR_FALSE;
int result = SU_SUCCESS;
*errorMsg = NULL;
result = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( result );
return result;
}
if ( installedFiles == NULL || installedFiles->GetSize() == 0 ) {
// no actions queued: don't register the package version
// and no need for user confirmation
CleanUp();
return result; // XXX: a different status code here?
}
// Wait for user approval
if ( !silent && UserWantsConfirm() ) {
#ifdef XXX /* XXX: RAMAN FIX IT */
confdlg = new nsProgressDetails(this);
/* XXX What is this?
while ( userChoice == -1 )
Thread.sleep(10);
*/
#endif /* XXX */
confdlg = NULL;
if (userChoice != 1) {
AbortInstall();
return saveError(SUERR_USER_CANCELLED);
}
}
// If the user passed NO_FINALIZE_DLG, we should close the progress dialog here.
// If the user passed !NO_FINALIZE_DLG, we should open the progress dialog here.
if ( bShowFinalize )
{
OpenProgressDialog();
}
else
{
CloseProgressDialog();
}
SetProgressDialogItem(""); // blank the "current item" line
SetProgressDialogRange( installedFiles->GetSize() );
/* call Complete() on all the elements */
/* If an error occurs in the middle, call Abort() on the rest */
nsVector* ve = GetInstallQueue();
nsInstallObject* ie = NULL;
// Main loop
int count = 0;
if ( bUninstallPackage )
{
VR_UninstallCreateNode( packageName, userPackageName );
}
PRUint32 i=0;
for (i=0; i < ve->GetSize(); i++) {
ie = (nsInstallObject*)ve->Get(i);
if (ie == NULL)
continue;
*errorMsg = ie->Complete();
if (*errorMsg != NULL) {
ie->Abort();
return SUERR_UNEXPECTED_ERROR;
}
SetProgressDialogThermo(++count);
}
// add overall version for package
if ( (versionInfo != NULL) && (bRegisterPackage))
{
result = VR_Install( packageName, NULL, versionInfo->toString(), PR_FALSE );
// Register default package folder if set
if ( packageFolder != NULL )
{
VR_SetDefaultDirectory( packageName, packageFolder->toString() );
}
}
CleanUp();
if (result == SU_SUCCESS && rebootNeeded)
result = SU_REBOOT_NEEDED;
saveError( result );
return result;
}
/**
* Aborts the install :), cleans up all the installed files
* XXX: This is a synchronized method. FIX it.
*/
void nsSoftwareUpdate::AbortInstall()
{
nsInstallObject* ie;
if (installedFiles != NULL) {
nsVector* ve = GetInstallQueue();
PRUint32 i=0;
for (i=0; i < ve->GetSize(); i++) {
ie = (nsInstallObject *)ve->Get(i);
if (ie == NULL)
continue;
ie->Abort();
}
}
CloseJARFile();
CleanUp();
}
/**
* ScheduleForInstall
* call this to put an InstallObject on the install queue
* Do not call installedFiles.addElement directly, because this routine also
* handles progress messages
*/
char* nsSoftwareUpdate::ScheduleForInstall(nsInstallObject* ob)
{
char *errorMsg = NULL;
char *objString = ob->toString();
// flash current item
SetProgressDialogItem( objString );
XP_FREEIF(objString);
// do any unpacking or other set-up
errorMsg = ob->Prepare();
if (errorMsg != NULL) {
return errorMsg;
}
// Add to installation list if we haven't thrown out
installedFiles->Add( ob );
// if (confdlg != NULL)
// confdlg.ScheduleForInstall( ob );
// turn on flags for creating the uninstall node and
// the package node for each InstallObject
if (ob->CanUninstall())
bUninstallPackage = PR_TRUE;
if (ob->RegisterPackageNode())
bRegisterPackage = PR_TRUE;
return NULL;
}
/**
* Extract a file from JAR archive to the disk, and update the
* version registry. Actually, keep a record of elements to be installed.
* FinalizeInstall() does the real installation. Install elements are accepted
* if they meet one of the following criteria:
* 1) There is no entry for this subcomponnet in the registry
* 2) The subcomponent version info is newer than the one installed
* 3) The subcomponent version info is NULL
*
* @param name path of the package in the registry. Can be:
* absolute: "/Plugins/Adobe/Acrobat/Drawer.exe"
* relative: "Drawer.exe". Relative paths are relative to
* main package name NULL: if NULL jarLocation is assumed
* to be the relative path
* @param version version of the subcomponent. Can be NULL
* @param jarSource location of the file to be installed inside JAR
* @param folderSpec one of the predefined folder locations
* @see GetFolder
* @param relativePath where the file should be copied to on the local disk.
* Relative to folder
* if NULL, use the path to the JAR source.
* @param forceInstall if true, file is always replaced
*/
PRInt32 nsSoftwareUpdate::AddSubcomponent(char* name,
nsVersionInfo* version,
char* jarSource,
nsFolderSpec* folderSpec,
char* relativePath,
PRBool forceInstall,
char* *errorMsg)
{
nsInstallFile* ie;
int result = SU_SUCCESS;
int err; /* does not get saved */
*errorMsg = NULL;
char *new_name;
if ( jarSource == NULL || (XP_STRLEN(jarSource) == 0) ) {
*errorMsg = SU_GetErrorMsg3("No Jar Source",
SUERR_INVALID_ARGUMENTS );
return SUERR_INVALID_ARGUMENTS;
}
if ( folderSpec == NULL ) {
*errorMsg = SU_GetErrorMsg3("folderSpec is NULL ",
SUERR_INVALID_ARGUMENTS );
return SUERR_INVALID_ARGUMENTS;
}
result = SanityCheck(errorMsg);
if (*errorMsg == NULL)
{
saveError( result );
return result;
}
if ((name == NULL) || (XP_STRLEN(name) == 0)) {
// Default subName = location in jar file
new_name = GetQualifiedRegName( jarSource, errorMsg );
} else {
new_name = GetQualifiedRegName( name, errorMsg );
}
if (*errorMsg != NULL)
{
return SUERR_BAD_PACKAGE_NAME;
}
if ( (relativePath == NULL) || (XP_STRLEN(relativePath) == 0) ) {
relativePath = jarSource;
}
/* Check for existence of the newer version */
PRBool versionNewer = PR_FALSE;
if ( (forceInstall == PR_FALSE ) &&
(version != NULL) &&
( VR_ValidateComponent( new_name ) == 0 ) )
{
VERSION versionStruct;
err = VR_GetVersion( new_name, &versionStruct );
nsVersionInfo* oldVersion = new nsVersionInfo(versionStruct.major,
versionStruct.minor,
versionStruct.release,
versionStruct.build);
if ( version->compareTo( oldVersion ) != nsVersionEnum_EQUAL )
versionNewer = PR_TRUE;
if( oldVersion )
delete oldVersion;
}
else
{
versionNewer = PR_TRUE;
}
if (versionNewer) {
ie = new nsInstallFile( this, new_name, version, jarSource,
folderSpec, relativePath, forceInstall,
errorMsg );
if (errorMsg == NULL) {
*errorMsg = ScheduleForInstall( ie );
}
}
if (*errorMsg != NULL) {
result = SUERR_UNEXPECTED_ERROR;
}
XP_FREEIF (new_name);
saveError( result );
return result;
}
/**
* executes the file
* @param jarSource name of the file to execute inside JAR archive
* @param args command-line argument string (Win/Unix only)
*/
PRInt32 nsSoftwareUpdate::Execute(char* jarSource, char* *errorMsg, char* args)
{
int errcode = SU_SUCCESS;
errcode = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( errcode );
return errcode;
}
nsInstallExecute* ie = new nsInstallExecute(this, jarSource, errorMsg, args);
if (*errorMsg != NULL) {
errcode = SUERR_ACCESS_DENIED;
} else {
*errorMsg = ScheduleForInstall( ie );
if (*errorMsg != NULL) {
errcode = SUERR_UNEXPECTED_ERROR;
}
}
saveError( errcode );
return errcode;
}
#ifdef XP_MAC
#include <Gestalt.h>
#endif
/**
* Mac-only, simulates Mac toolbox Gestalt function
* OSErr Gestalt(char* selector, long * response)
* @param selector 4-character string,
* @param os_err corresponds to OSErr
* @param errorMsg Error message
* @return an integer corresponding to response from Gestalt
*/
PRInt32 nsSoftwareUpdate::Gestalt(char* selectorStr, int* os_err,
char* *errorMsg)
{
*errorMsg = NULL;
#ifdef XP_MAC
*os_err = noErr;
long response = 0;
OSType selector;
if ((selectorStr == NULL) ||
(XP_STRLEN(selectorStr) != 4))
{
*os_err = SUERR_GESTALT_UNKNOWN_ERR; /* gestaltUnknownErr */
goto fail;
}
XP_MEMCPY(&selector, selectorStr, 4);
*os_err = (int)Gestalt(selector, response);
if (*os_err == noErr) {
return response;
}
goto fail; /* Drop through to failure */
#else
*os_err = SUERR_GESTALT_INVALID_ARGUMENT;
goto fail;
#endif
fail:
*errorMsg = SU_GetErrorMsg4(SU_ERROR_EXTRACT_FAILED, *os_err );
return 0;
}
/**
* Patch
*
* nsVersionInfo object shouldn't be free'ed. nsSoftwareUpdate object
* deletes it.
*
*/
PRInt32 nsSoftwareUpdate::Patch(char* regName,
nsVersionInfo* version,
char* patchname,
char* *errorMsg)
{
int errcode = SU_SUCCESS;
if ( regName == NULL || patchname == NULL ) {
*errorMsg = SU_GetErrorMsg3("regName or patchName is NULL ",
SUERR_INVALID_ARGUMENTS );
return saveError( SUERR_INVALID_ARGUMENTS );
}
errcode = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( errcode );
return errcode;
}
char* rname = GetQualifiedRegName( regName, errorMsg);
if (*errorMsg != NULL)
{
errcode = SUERR_BAD_PACKAGE_NAME;
saveError( errcode );
return errcode;
}
nsInstallPatch* ip = new nsInstallPatch(this, rname, version, patchname,
errorMsg);
if (*errorMsg != NULL) {
errcode = SUERR_ACCESS_DENIED;
} else {
*errorMsg = ScheduleForInstall( ip );
if (*errorMsg != NULL) {
errcode = SUERR_UNEXPECTED_ERROR;
}
}
XP_FREEIF( rname );
saveError( errcode );
return errcode;
}
/* Patch
*
* nsVersionInfo object shouldn't be free'ed. nsSoftwareUpdate object
* deletes it.
*
*/
PRInt32 nsSoftwareUpdate::Patch(char* regName,
nsVersionInfo* version,
char* patchname,
nsFolderSpec* folder,
char* filename,
char* *errorMsg)
{
if ( folder == NULL || regName == NULL ||
XP_STRLEN(regName) == 0 || patchname == NULL ) {
*errorMsg = SU_GetErrorMsg3("folder or regName or patchName is NULL ",
SUERR_INVALID_ARGUMENTS );
return saveError( SUERR_INVALID_ARGUMENTS );
}
int errcode = SU_SUCCESS;
errcode = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( errcode );
return errcode;
}
char* rname = GetQualifiedRegName( regName, errorMsg );
if (*errorMsg != NULL)
{
errcode = SUERR_BAD_PACKAGE_NAME;
saveError( errcode );
return errcode;
}
nsInstallPatch* ip = new nsInstallPatch( this, rname, version,
patchname, folder, filename,
errorMsg );
if (*errorMsg != NULL) {
errcode = SUERR_ACCESS_DENIED;
} else {
*errorMsg = ScheduleForInstall( ip );
if (*errorMsg != NULL) {
errcode = SUERR_UNEXPECTED_ERROR;
}
}
XP_FREEIF ( rname );
saveError( errcode );
return errcode;
}
/**
* This method deletes the specified file from the disk. It does not look
* for the file in the registry and is guaranteed to muddle any uninstall
* reference counting. Its main purpose is to delete files installed or
* created outside of SmartUpdate.
*/
PRInt32 nsSoftwareUpdate::DeleteFile(nsFolderSpec* folder,
char* relativeFileName,
char* *errorMsg)
{
int errcode = SU_SUCCESS;
errcode = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( errcode );
return errcode;
}
nsInstallDelete* id = new nsInstallDelete(this, folder, relativeFileName,
errorMsg);
if (*errorMsg != NULL) {
errcode = SUERR_ACCESS_DENIED;
} else {
*errorMsg = ScheduleForInstall( id );
if (*errorMsg != NULL) {
errcode = SUERR_UNEXPECTED_ERROR;
}
}
/* XXX: who set FILE_DOES_NOT_EXIST errcode ?? */
if (errcode == SUERR_FILE_DOES_NOT_EXIST) {
errcode = SU_SUCCESS;
}
saveError( errcode );
return errcode;
}
/**
* This method finds named registry component and deletes both the file and the
* entry in the Client VR. registryName is the name of the component in the
* registry. Returns usual errors codes + code to indicate item doesn't exist
* in registry, registry item wasn't a file item, or the related file doesn't
* exist. If the file is in use we will store the name and to try to delete it
* on subsequent start-ups until we're successful.
*/
PRInt32 nsSoftwareUpdate::DeleteComponent(char* registryName, char* *errorMsg)
{
int errcode = SU_SUCCESS;
errcode = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( errcode );
return errcode;
}
char* rname = GetQualifiedRegName( registryName, errorMsg );
if (*errorMsg != NULL)
{
errcode = SUERR_UNEXPECTED_ERROR;
saveError( errcode );
return errcode;
}
nsInstallDelete* id = new nsInstallDelete(this, NULL, rname, errorMsg);
if (*errorMsg != NULL) {
errcode = SUERR_ACCESS_DENIED;
} else {
*errorMsg = ScheduleForInstall( id );
if (*errorMsg != NULL) {
errcode = SUERR_UNEXPECTED_ERROR;
}
}
/* XXX: who set FILE_DOES_NOT_EXIST errcode ?? */
if (errcode == SUERR_FILE_DOES_NOT_EXIST) {
errcode = SU_SUCCESS;
}
XP_FREEIF (rname );
saveError( errcode );
return errcode;
}
static PRBool su_PathEndsWithSeparator(char* path, char* sep)
{
PRBool ends_with_filesep = PR_FALSE;
if (path != NULL) {
PRInt32 filesep_len = XP_STRLEN(sep);
PRInt32 path_len = XP_STRLEN(path);
if (path_len >= filesep_len) {
ends_with_filesep = (XP_STRSTR(&path[path_len - filesep_len], sep)
? PR_TRUE : PR_FALSE);
}
}
return ends_with_filesep;
}
nsFolderSpec* nsSoftwareUpdate::GetFolder(char* targetFolder,
char* subdirectory,
char* *errorMsg)
{
if (XP_STRCMP(targetFolder, FOLDER_FILE_URL) == 0) {
char* newPath = NULL;
char* path = NativeFileURLToNative("/", subdirectory );
if ( path != NULL ) {
nsFolderSpec *spec;
if (su_PathEndsWithSeparator(path, filesep)) {
spec = new nsFolderSpec("Installed", path, userPackageName);
} else {
path = XP_AppendStr(path, filesep);
spec = new nsFolderSpec("Installed", path, userPackageName);
}
XP_FREEIF(path);
return spec;
} else {
return NULL;
}
} else {
nsFolderSpec* spec = GetFolder(targetFolder, errorMsg);
nsFolderSpec* ret_val = GetFolder( spec, subdirectory, errorMsg );
if (spec)
{
delete spec;
}
return ret_val;
}
}
nsFolderSpec* nsSoftwareUpdate::GetFolder(nsFolderSpec* folder,
char* subdir,
char* *errorMsg)
{
nsFolderSpec* spec = NULL;
char* path = NULL;
char* newPath = NULL;
if ( subdir == NULL || (XP_STRLEN(subdir) == 0 ))
{
// no subdir, return what we were passed
return folder;
}
else if ( folder != NULL )
{
path = folder->MakeFullPath( subdir, errorMsg );
if (path != NULL) {
if (su_PathEndsWithSeparator(path, filesep)) {
spec = new nsFolderSpec("Installed", path, userPackageName);
} else {
path = XP_AppendStr(path, filesep);
spec = new nsFolderSpec("Installed", path, userPackageName);
}
XP_FREEIF(path);
}
}
return spec;
}
/**
* This method returns true if there is enough free diskspace, false if there
* isn't. The drive containg the folder is checked for # of free bytes.
*/
long nsSoftwareUpdate::DiskSpaceAvailable(nsFolderSpec* folder)
{
char *errorMsg = NULL;
char* path = folder->GetDirectoryPath();
if (path == NULL)
return 0;
return NativeDiskSpaceAvailable(path);
}
/**
* This method is used to install an entire subdirectory of the JAR.
* Any files so installed are individually entered into the registry so they
* can be uninstalled later. Like AddSubcomponent the directory is installed
* only if the version specified is greater than that already registered,
* or if the force parameter is true.The final version presented is the complete
* form, the others are for convenience and assume values for the missing
* arguments.
*/
PRInt32 nsSoftwareUpdate::AddDirectory(char* name,
nsVersionInfo* version,
char* jarSource,
nsFolderSpec* folderSpec,
char* subdir,
PRBool forceInstall,
char* *errorMsg)
{
nsInstallFile* ie = NULL;
int result = SU_SUCCESS;
char *qualified_name = NULL;
*errorMsg = NULL;
if ( jarSource == NULL || XP_STRLEN(jarSource) == 0 || folderSpec == NULL ) {
*errorMsg = SU_GetErrorMsg3("folder or Jarsource is NULL ",
SUERR_INVALID_ARGUMENTS );
return saveError(SUERR_INVALID_ARGUMENTS);
}
if (packageName == NULL) {
// probably didn't call StartInstall()
*errorMsg = SU_GetErrorMsg4(SU_ERROR_BAD_PACKAGE_NAME_AS,
SUERR_BAD_PACKAGE_NAME );
return saveError(SUERR_BAD_PACKAGE_NAME);
}
if ((name == NULL) || (XP_STRLEN(name) == 0)) {
// Default subName = location in jar file
qualified_name = GetQualifiedRegName( jarSource, errorMsg );
} else {
qualified_name = GetQualifiedRegName( name, errorMsg );
}
if (*errorMsg != NULL)
{
return SUERR_BAD_PACKAGE_NAME;
}
if ( subdir == NULL ) {
subdir = NULL;
} else if ( XP_STRLEN(subdir) != 0 ) {
subdir = XP_Cat(subdir, "/");
}
/* XXX: May be we should print the following to JS console
System.out.println("AddDirectory " + qualified_name
+ " from " + jarSource + " into " +
folderSpec.MakeFullPath(subdir));
*/
int length;
char** matchingFiles = ExtractDirEntries( jarSource, &length );
int i;
PRBool bInstall;
for (i=0; i < length; i++) {
/* XP_Cat allocates a new string and returns it */
char* fullRegName = XP_Cat(qualified_name, "/", matchingFiles[i]);
if ( (forceInstall == PR_FALSE) && (version != NULL) &&
(VR_ValidateComponent(fullRegName) == 0) )
{
// Only install if newer
VERSION versionStruct;
VR_GetVersion( fullRegName, &versionStruct );
nsVersionInfo* oldVer = new nsVersionInfo(versionStruct.major,
versionStruct.minor,
versionStruct.release,
versionStruct.build);
bInstall = ( version->compareTo(oldVer) > 0 );
if (oldVer)
delete oldVer;
} else {
// file doesn't exist or "forced" install
bInstall = PR_TRUE;
}
if ( bInstall ) {
char *newJarSource = XP_Cat(jarSource, "/", matchingFiles[i]);
char *newSubDir;
if (subdir) {
newSubDir = XP_Cat(subdir, matchingFiles[i]);
} else {
newSubDir = XP_STRDUP(matchingFiles[i]);
}
ie = new nsInstallFile( this,
fullRegName,
version,
newJarSource,
(nsFolderSpec*) folderSpec,
newSubDir,
forceInstall,
errorMsg);
if (*errorMsg == NULL) {
ScheduleForInstall( ie );
} else {
/* We have an error and we haven't scheduled,
* thus we can delete it
*/
if (ie)
delete ie;
}
XP_FREEIF(newJarSource);
XP_FREEIF(newSubDir);
}
XP_FREEIF(fullRegName);
}
XP_FREEIF(subdir);
XP_FREEIF(qualified_name);
if (errorMsg != NULL) {
result = SUERR_UNEXPECTED_ERROR;
}
saveError( result );
return (result);
}
/* Uninstall */
PRInt32 nsSoftwareUpdate::Uninstall(char* packageName, char* *errorMsg)
{
int errcode = SU_SUCCESS;
errcode = SanityCheck(errorMsg);
if (*errorMsg != NULL)
{
saveError( errcode );
return errcode;
}
char* rname = GetQualifiedPackageName( packageName );
nsUninstallObject* u = new nsUninstallObject( this, rname, errorMsg );
if (*errorMsg != NULL) {
errcode = SUERR_UNEXPECTED_ERROR;
} else {
ScheduleForInstall( u );
}
XP_FREEIF (rname);
saveError( errcode );
return errcode;
}
/*******************************
*
* progress window
*
* functions for dealing with the progress window.
* normally I'd make this an object, but since we're implementing
* it with native routines and will soon be getting rid of Java
* altogether this makes more sense for now. Creating a new object
* would only lead to more JRI hell, especially on the Mac
*******************************/
void nsSoftwareUpdate::OpenProgressDialog(void)
{
if ( !silent && progwin == 0) {
progwin = NativeOpenProgDlg( GetUserPackageName() );
}
}
void nsSoftwareUpdate::CloseProgressDialog(void)
{
if ( progwin != NULL ) {
NativeCloseProgDlg( progwin );
progwin = NULL;
}
}
void nsSoftwareUpdate::SetProgressDialogItem(char* message)
{
if ( progwin != NULL ) {
NativeSetProgDlgItem( progwin, message );
}
}
void nsSoftwareUpdate::SetProgressDialogRange(PRInt32 max)
{
if ( progwin != NULL ) {
NativeSetProgDlgRange( progwin, max );
}
}
void nsSoftwareUpdate::SetProgressDialogThermo(PRInt32 value)
{
if ( progwin != NULL ) {
NativeSetProgDlgThermo( progwin, value );
}
}
/* Private Methods */
/*
* Reads in the installer certificate, and creates its principal
*/
PRInt32 nsSoftwareUpdate::InitializeInstallerCertificate(char* *errorMsg)
{
PRInt32 errcode;
nsPrincipal *prin = NULL;
*errorMsg = NULL;
/* XXX: Should we put up a dialog for unsigned JAR file support? */
XP_Bool unsigned_jar_enabled;
PREF_GetBoolPref("autoupdate.unsigned_jar_support", &unsigned_jar_enabled);
nsPrincipalArray* prinArray =
(nsPrincipalArray*)getCertificates(zigPtr, installerJarName);
if ((prinArray == NULL) || (prinArray->GetSize() == 0)) {
if (!unsigned_jar_enabled) {
*errorMsg = SU_GetErrorMsg4(SU_ERROR_NO_CERTIFICATE,
SUERR_NO_INSTALLER_CERTIFICATE);
errcode = SUERR_NO_INSTALLER_CERTIFICATE;
}
} else if (prinArray->GetSize() > 1) {
*errorMsg = SU_GetErrorMsg4(SU_ERROR_TOO_MANY_CERTIFICATES,
SUERR_TOO_MANY_CERTIFICATES);
errcode = SUERR_TOO_MANY_CERTIFICATES;
} else {
prin = (nsPrincipal *)prinArray->Get(0);
if ((prin == NULL) && (!unsigned_jar_enabled)) {
*errorMsg = SU_GetErrorMsg4(SU_ERROR_NO_CERTIFICATE,
SUERR_NO_INSTALLER_CERTIFICATE);
errcode = SUERR_NO_INSTALLER_CERTIFICATE;
}
}
if (prin != NULL) {
/* XXX: We should have Dup method on nsPrincipal object */
nsPrincipalType prinType = prin->getType();
void* key = prin->getKey();
PRUint32 key_len = prin->getKeyLength();
installPrincipal = new nsPrincipal(prinType, key, key_len);
} else {
/* We should get the URL where we are getting the JarURL from */
PR_ASSERT(jarURL != NULL);
/* Create a codebase principal with the JAR URL */
installPrincipal = new nsPrincipal(nsPrincipalType_CodebaseExact,
jarURL, XP_STRLEN(jarURL));
}
PR_ASSERT(installPrincipal != NULL);
/* Free the allocated principals */
freeIfCertificates(prinArray);
return saveError(errcode);
}
/*
* checks if our principal has privileges for silent install
*/
PRBool nsSoftwareUpdate::CheckSilentPrivileges()
{
PRBool ret_val = PR_FALSE;
if (silent == PR_FALSE) {
return ret_val;
}
/* Request impersonation privileges */
nsTarget* impersonation = nsTarget::findTarget(IMPERSONATOR);
nsPrivilegeManager* privMgr = nsPrivilegeManager::getPrivilegeManager();
if ((privMgr != NULL) && (impersonation != NULL)) {
privMgr->enablePrivilege(impersonation, 1);
nsTarget* target = nsTarget::findTarget(SILENT_PRIV);
/* check the security permissions */
if (target != NULL) {
ret_val = privMgr->enablePrivilege(target, GetPrincipal(), 1);
}
}
if (!ret_val) {
silent = PR_FALSE;
}
return ret_val;
}
/* Request the security privileges, so that the security dialogs
* pop up
*/
PRInt32 nsSoftwareUpdate::RequestSecurityPrivileges(char* *errorMsg)
{
PRBool ret_val = PR_FALSE;
/* Request impersonation privileges */
nsTarget* impersonation = nsTarget::findTarget(IMPERSONATOR);
nsPrivilegeManager* privMgr = nsPrivilegeManager::getPrivilegeManager();
if ((privMgr != NULL) && (impersonation != NULL)) {
privMgr->enablePrivilege(impersonation, 1);
nsTarget* target = nsTarget::findTarget(INSTALL_PRIV);
/* check the security permissions */
if (target != NULL) {
ret_val = privMgr->enablePrivilege(target, GetPrincipal(), 1);
}
}
if (!ret_val) {
*errorMsg = "Permssion was denied";
return SUERR_ACCESS_DENIED;
} else {
*errorMsg = NULL;
}
return SU_SUCCESS;
}
/**
* saves non-zero error codes so they can be seen by GetLastError()
*/
PRInt32 nsSoftwareUpdate::saveError(PRInt32 errcode)
{
if ( errcode != SU_SUCCESS )
lastError = errcode;
return errcode;
}
/*
* CleanUp
* call it when done with the install
*
* XXX: This is a synchronized method. FIX it.
*/
void nsSoftwareUpdate::CleanUp()
{
nsInstallObject* ie;
CloseJARFile();
confdlg = NULL;
zigPtr = NULL;
if ( installedFiles != NULL ) {
PRUint32 i=0;
for (; i < installedFiles->GetSize(); i++) {
ie = (nsInstallObject*)installedFiles->Get(i);
XP_FREEIF (ie);
}
installedFiles->RemoveAll();
XP_FREEIF (installedFiles);
installedFiles = NULL;
}
XP_FREEIF(packageName);
packageName = NULL; // used to see if StartInstall() has been called
CloseProgressDialog();
}
/**
* GetQualifiedRegName
*
* Allocates a new string and returns it. Caller is supposed to free it
*
* This routine converts a package-relative component registry name
* into a full name that can be used in calls to the version registry.
*/
char* nsSoftwareUpdate::GetQualifiedRegName(char* name, char**errorMsg)
{
char *comm = "=COMM=/";
PRUint32 comm_len = XP_STRLEN(comm);
char *usr = "=USER=/";
PRUint32 usr_len = XP_STRLEN(usr);
if ((XP_STRLEN(name)) >= comm_len)
{
PRUint32 i;
for (i=0; i<comm_len; i++)
{
if (XP_TO_UPPER(name[i]) != comm[i])
break;
}
if (i == comm_len)
{
name = XP_STRDUP(&name[comm_len]);
}
}
else if ((XP_STRLEN(name)) >= usr_len)
{
PRUint32 i;
for (i=0; i<usr_len; i++)
{
if (XP_TO_UPPER(name[i]) != usr[i])
break;
}
if (i == usr_len)
{
name = XP_STRDUP(&name[usr_len]);
}
}
else if (name[0] != '/')
{
// Relative package path
char* packagePrefix;
if (XP_STRLEN(packageName) != 0) {
packagePrefix = XP_Cat(packageName, "/");
name = XP_AppendStr(packagePrefix, name);
} else {
name = XP_STRDUP(name);
}
}
if (BadRegName(name))
{
*errorMsg = SU_GetErrorMsg4(SUERR_BAD_PACKAGE_NAME, SUERR_BAD_PACKAGE_NAME);
name = NULL;
}
return name;
}
/* Private Native methods */
/* VerifyJSObject
* Make sure that JSObject is of type SoftUpdate.
* Since SoftUpdate JSObject can only be created by C code
* we cannot be spoofed
*/
char* nsSoftwareUpdate::VerifyJSObject(void* jsObj)
{
char *errorMsg = NULL;
/* Get the object's class, and verify that it is of class SoftUpdate
*/
#ifdef XXX
JSObject * jsobj;
JSClass * jsclass;
jsobj = (JSObject *) jsObj;
/* jsobj = JSJ_ExtractInternalJSObject(env, (HObject*)a);*/
jsclass = JS_GetClass(jsobj);
/* XXX Fix it */
if ( jsclass != &lm_softup_class ) {
errorMsg = SU_GetErrorMsg4(SU_ERROR_BAD_JS_ARGUMENT, -1);
}
#endif
return errorMsg;
}
/* Open/close the jar file
*/
PRInt32 nsSoftwareUpdate::OpenJARFile(char* *errorMsg)
{
ZIG * jarData;
char * jarFile;
PRInt32 err;
XP_Bool enabled;
*errorMsg = NULL;
/* XXX: Should we put up a dialog for unsigned JAR file support? */
XP_Bool unsigned_jar_enabled;
PREF_GetBoolPref("autoupdate.unsigned_jar_support", &unsigned_jar_enabled);
PREF_GetBoolPref( AUTOUPDATE_ENABLE_PREF, &enabled);
if (!enabled) {
*errorMsg = SU_GetErrorMsg4(SU_ERROR_BAD_JS_ARGUMENT,
SUERR_INVALID_ARGUMENTS);
return SUERR_INVALID_ARGUMENTS;
}
jarFile = jarName;
if (jarFile == NULL) {
/* error already signaled */
*errorMsg = SU_GetErrorMsg3("No Jar Source",
SUERR_INVALID_ARGUMENTS);
return SUERR_INVALID_ARGUMENTS;
}
/* Open and initialize the JAR archive */
jarData = SOB_new();
if ( jarData == NULL ) {
*errorMsg = SU_GetErrorMsg3("No Jar Source",
SUERR_UNEXPECTED_ERROR);
return SUERR_UNEXPECTED_ERROR;
}
err = SOB_pass_archive( ZIG_F_GUESS,
jarFile,
NULL, /* realStream->fURL->address, */
jarData );
if ( err != 0 ) {
if (unsigned_jar_enabled)
return SU_SUCCESS;
*errorMsg = SU_GetErrorMsg4(SU_ERROR_VERIFICATION_FAILED, err);
return err;
}
/* Get the installer file name */
installerJarName = NULL;
err = SOB_get_metainfo(jarData, NULL, INSTALLER_HEADER,
(void**)&installerJarName, &installerJarNameLength);
if (err != 0) {
if (unsigned_jar_enabled)
return SU_SUCCESS;
*errorMsg = SU_GetErrorMsg4(SU_ERROR_MISSING_INSTALLER, err);
return err;
}
zigPtr = jarData;
return SU_SUCCESS;
}
void nsSoftwareUpdate::CloseJARFile()
{
ZIG* jarData = (ZIG*)zigPtr;
if (jarData != NULL)
SOB_destroy( jarData);
zigPtr = NULL;
}
/* getCertificates
* native encapsulation that calls AppletClassLoader.getCertificates
* we cannot call this method from Java because it is private.
* The method cannot be made public because it is a security risk
*/
void* nsSoftwareUpdate::getCertificates(void* zigPtr, char* pathname)
{
return nsPrincipal::getSigners(zigPtr, pathname);
}
void nsSoftwareUpdate::freeIfCertificates(void* prins)
{
nsPrincipalArray* prinArray = (nsPrincipalArray*)prins;
if ((prinArray == NULL) || (prinArray->GetSize() == 0))
return;
PRUint32 noOfPrins = prinArray->GetSize();
/* Free all the objects */
PRUint32 i;
for (i=0; i < noOfPrins; i++) {
nsPrincipal* prin = (nsPrincipal*)prinArray->Get(i);
XP_FREEIF (prin);
prinArray->Set(i, NULL);
}
XP_FREEIF (prinArray);
}
#define APPLESINGLE_MAGIC_HACK 1 /* Hack to automatically detect applesingle files until we get tdell to do the right thing */
/* Caller should free the returned string */
char* nsSoftwareUpdate::NativeExtractJARFile(char* inJarLocation,
char* finalFile,
char* *errorMsg)
{
char * tempName = NULL;
char * target = NULL;
char * ret_value = NULL;
int result;
ZIG * jar;
char * jarPath;
/* Extract the file */
jar = (ZIG *)zigPtr;
jarPath = (char*)inJarLocation;
if (jarPath == NULL) {
/* out-of-memory error already signaled */
*errorMsg = SU_GetErrorMsg4(SU_ERROR_OUT_OF_MEMORY,
SUERR_UNEXPECTED_ERROR);
return NULL;
}
target = (char*)finalFile;
if (target) {
char *fn;
char *end;
char *URLfile = XP_PlatformFileToURL(target);
fn = URLfile+7; /* skip "file://" part */
if ((end = XP_STRRCHR(fn, '/')) != NULL )
end[1] = 0;
/* Create a temporary location */
tempName = WH_TempName( xpURL, fn );
XP_FREEIF(URLfile);
} else {
#ifdef XP_PC
char * extension = XP_STRRCHR(jarPath, '.');
if (extension)
tempName = WH_TempFileName( xpURL, NULL, extension );
else
tempName = WH_TempName( xpURL, NULL );
#else
tempName = WH_TempName( xpURL, NULL );
#endif
}
if (tempName == NULL) {
result = MK_OUT_OF_MEMORY;
goto done;
}
result = SOB_verified_extract( jar, jarPath, tempName);
if ( result == 0 ) {
/* If we have an applesingle file
* decode it to a new file
*/
char * encodingName;
unsigned long encodingNameLength;
XP_Bool isApplesingle = FALSE;
result = SOB_get_metainfo( jar, NULL, CONTENT_ENCODING_HEADER,
(void**)&encodingName, &encodingNameLength);
#ifdef APPLESINGLE_MAGIC_HACK
if (result != 0) {
XP_File f;
uint32 magic;
f = XP_FileOpen(tempName, xpURL, XP_FILE_READ_BIN);
XP_FileRead( &magic, sizeof(magic), f);
XP_FileClose(f);
isApplesingle = (magic == 0x00051600 );
result = 0;
}
#else
isApplesingle = (( result == 0 ) &&
(XP_STRNCMP(APPLESINGLE_MIME_TYPE, encodingName,
XP_STRLEN( APPLESINGLE_MIME_TYPE ) == 0)));
#endif
if ( isApplesingle ) {
/* We have an AppleSingle file */
/* Extract it to the new AppleFile, and get the URL back */
char * newTempName = NULL;
#ifdef XP_MAC
result = SU_DecodeAppleSingle(tempName, &newTempName);
if ( result == 0 ) {
XP_FileRemove( tempName, xpURL );
XP_FREEIF(tempName);
tempName = newTempName;
} else {
XP_FileRemove( tempName, xpURL );
}
#else
result = 0;
#endif
}
}
done:
XP_Bool unsigned_jar_enabled;
PREF_GetBoolPref("autoupdate.unsigned_jar_support", &unsigned_jar_enabled);
/* Create the return Java string if everything went OK */
if (tempName) {
if (result == 0) {
ret_value = tempName;
} else {
if (unsigned_jar_enabled) {
ret_value = tempName;
}
}
}
if ( (result != 0) && (!unsigned_jar_enabled) ) {
*errorMsg = SU_GetErrorMsg4(SU_ERROR_EXTRACT_FAILED, result);
return NULL;
}
if (ret_value == NULL) {
*errorMsg = SU_GetErrorMsg4(SU_ERROR_EXTRACT_FAILED, result);
return NULL;
}
return ret_value;
}
PRInt32 nsSoftwareUpdate::NativeMakeDirectory(char* dir)
{
if ( !dir )
return -1;
return XP_MakeDirectoryR ( dir, xpURL );
}
long nsSoftwareUpdate::NativeDiskSpaceAvailable(char* fileSystem)
{
PRInt32 val = 0;
if (fileSystem) {
val = FE_DiskSpaceAvailable(NULL, fileSystem);
}
return val;
}
/* Caller should free the returned string */
char* nsSoftwareUpdate::NativeFileURLToNative(char* dir, char* pathNamePlatform)
{
char * newName = NULL;
if (pathNamePlatform != NULL) {
newName = WH_FileName(pathNamePlatform, xpURL);
}
return newName;
}
char** nsSoftwareUpdate::ExtractDirEntries(char *Directory, int *length)
{
int size = 0;
int len = 0;
int dirlen;
char *pattern = NULL;
ZIG_Context *context;
SOBITEM *item;
char* *StrArray = NULL;
char *buff;
if (!zigPtr)
goto bail;
if (!Directory)
goto bail;
dirlen = XP_STRLEN(Directory);
if ( (pattern = (char *)XP_ALLOC(dirlen + 3)) == NULL)
goto bail;
XP_STRCPY(pattern, Directory);
XP_STRCPY(pattern+dirlen, "/*");
/* it's either go through the JAR twice (first time just to get a count)
* or go through once and potentially use lots of memory saving all the
* strings. In deference to Win16 and potentially large installs we're
* going to loop through the JAR twice and take the performance hit;
* no one installs very often anyway.
*/
if ((context = SOB_find ((ZIG*)zigPtr, pattern, ZIG_MF)) == NULL)
goto bail;
while (SOB_find_next (context, &item) >= 0)
size++;
SOB_find_end (context);
StrArray = (char **)XP_CALLOC(size, sizeof(char *));
if (StrArray == NULL)
goto bail;
if ((context = SOB_find ((ZIG*)zigPtr, pattern, ZIG_MF)) == NULL)
goto bail;
*length = size;
size = 0;
while (SOB_find_next (context, &item) >= 0) {
len = XP_STRLEN(item->pathname);
/* subtract length of target directory + slash */
len = len - (dirlen+1);
if (( buff = (char*)XP_ALLOC (len+1)) != NULL ) {
/* Don't copy the search directory part */
XP_STRCPY (buff, (item->pathname)+dirlen+1);
StrArray[size++] = buff;
}
}
SOB_find_end (context);
XP_FREEIF(pattern);
return StrArray;
bail:
XP_FREEIF(pattern);
if (StrArray != NULL) {
size = *length;
for (int i=0; i<size; i++) {
XP_FREEIF(StrArray[i]);
}
}
StrArray = NULL;
*length = 0;
return StrArray;
}
void* nsSoftwareUpdate::NativeOpenProgDlg(char* name)
{
pw_ptr prgwin = NULL;
char buf[TITLESIZE];
prgwin = PW_Create( XP_FindContextOfType(NULL, MWContextBookmarks),
pwStandard );
if ( prgwin != NULL ) {
PR_snprintf(buf, TITLESIZE, XP_GetString(SU_INSTALLWIN_TITLE), name);
PW_SetWindowTitle( prgwin, buf );
PW_SetLine2( prgwin, NULL );
PW_Show( prgwin );
/* for some reason must be after PW_Show() to work */
PW_SetLine1( prgwin, XP_GetString(SU_INSTALLWIN_UNPACKING) );
}
return (void *)prgwin;
}
void nsSoftwareUpdate::NativeCloseProgDlg(void* progptr)
{
PW_Destroy( (pw_ptr)progptr );
}
void nsSoftwareUpdate::NativeSetProgDlgItem(void* progptr, char* message)
{
PW_SetLine2( (pw_ptr)progptr, message );
}
void nsSoftwareUpdate::NativeSetProgDlgRange(void* progptr, PRInt32 max)
{
PW_SetLine1( (pw_ptr)progptr, XP_GetString(SU_INSTALLWIN_INSTALLING) );
PW_SetProgressRange( (pw_ptr)progptr, 0, max );
PW_SetProgressValue( (pw_ptr)progptr, max );
PW_SetProgressValue( (pw_ptr)progptr, 0 );
}
void nsSoftwareUpdate::NativeSetProgDlgThermo(void* progptr, PRInt32 value)
{
PW_SetProgressValue( (pw_ptr)progptr, value );
}
PRBool nsSoftwareUpdate::UserWantsConfirm()
{
XP_Bool bConfirm;
PREF_GetBoolPref( AUTOUPDATE_CONFIRM_PREF, &bConfirm);
if (bConfirm)
return PR_TRUE;
else
return PR_FALSE;
}
/**
* GetQualifiedPackageName
*
* This routine converts a package-relative component registry name
* into a full name that can be used in calls to the version registry.
*/
char*
nsSoftwareUpdate::GetQualifiedPackageName( char* name )
{
char* qualifedName;
if (XP_STRNCMP(name, "=USER=/", XP_STRLEN( name )) == 0)
{
char* currentUserNode;
currentUserNode = CurrentUserNode();
qualifedName = XP_STRDUP(currentUserNode);
/* Append the the the path to the CurrentUserName.
name[7], should be after the =USER=/
*/
qualifedName = XP_AppendStr(qualifedName, &name[7]);
XP_FREEIF (currentUserNode);
}
if (BadRegName(qualifedName))
{
XP_FREEIF (qualifedName);
qualifedName = NULL;
}
return qualifedName;
}
char*
nsSoftwareUpdate::CurrentUserNode()
{
char *qualifedName;
char *profileName;
qualifedName = XP_STRDUP("/Netscape/Users/");
profileName = NativeProfileName();
XP_AppendStr(qualifedName, profileName);
XP_AppendStr(qualifedName, "/");
XP_FREEIF(profileName);
return qualifedName;
}
char*
nsSoftwareUpdate::NativeProfileName( )
{
char *profname;
int len = MAXREGNAMELEN;
int err;
profname = (char*) malloc(len);
err = PREF_GetCharPref( "profile.name", profname, &len );
if ( err != PREF_OK )
{
XP_FREEIF(profname);
profname = NULL;
}
return profname;
}
/**
* SanityCheck
*
* This routine checks if the packageName is null. It also checks the flag if the user cancels
* the install progress dialog is set and acccordingly aborts the install.
*/
int
nsSoftwareUpdate::SanityCheck(char**errorMsg)
{
if ( packageName == NULL )
{
*errorMsg = SU_GetErrorMsg4(SUERR_INSTALL_NOT_STARTED, SUERR_INSTALL_NOT_STARTED);
return SUERR_INSTALL_NOT_STARTED;
}
if (bUserCancelled)
{
AbortInstall();
saveError(SUERR_USER_CANCELLED);
*errorMsg = SU_GetErrorMsg4(SUERR_USER_CANCELLED, SUERR_USER_CANCELLED);
return SUERR_USER_CANCELLED;
}
return 0;
}
// catch obvious registry name errors proactively
// rather than returning some cryptic libreg error
PRBool
nsSoftwareUpdate::BadRegName(char* regName)
{
long regNameLen;
if (regName== NULL)
return PR_TRUE;
regNameLen = XP_STRLEN(regName);
if ((regName[0] == ' ' ) || (regName[regNameLen] == ' ' ))
return PR_TRUE;
if ( XP_STRSTR(regName, "//") != NULL )
return PR_TRUE;
if ( XP_STRSTR(regName, " /") != NULL )
return PR_TRUE;
if ( XP_STRSTR(regName, "/ ") != NULL )
return PR_TRUE;
if ( XP_STRSTR(regName, "=") != NULL )
return PR_TRUE;
return PR_FALSE;
}
static JSClass stringObject = {
"global", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSObject*
nsSoftwareUpdate::LoadStringObject(const char* filename)
{
int err = 0;
XP_File fp;
XP_StatStruct stats;
long fileLength;
JSObject *stringObj = NULL;
MWContext *cx;
/* XXX FIX: We should use the context passed into nsSoftwareUpdate via the env var! dft */
cx = XP_FindContextOfType(NULL, MWContextJava);
stringObj = JS_NewObject(cx->mocha_context, &stringObject, NULL, NULL);
if (stringObj == NULL) return NULL;
/* Now lets open of the file and read it into a buffer */
stats.st_size = 0;
if ( stat(filename, (struct stat *) &stats) == -1 )
return NULL;
fileLength = stats.st_size;
if (fileLength <= 1)
return NULL;
fp = XP_FileOpen(filename, xpURL, "r");
if (fp)
{
char* readBuf = (char *) XP_ALLOC(fileLength * sizeof(char) + 1);
char* buffPtr;
char* buffEnd;
char* valuePtr;
char* tempPtr;
if (readBuf)
{
fileLength = XP_FileRead(readBuf, fileLength, fp);
readBuf[fileLength+1] = 0;
buffPtr = readBuf;
buffEnd = readBuf + fileLength;
while (buffPtr < buffEnd)
{
/* Loop until we come across a interesting char */
if (XP_IS_SPACE(*buffPtr))
{
buffPtr++;
}
else
{
/* cool we got something. lets find its value, and then add it to the js object */
valuePtr = XP_STRCHR(buffPtr, '=');
if (valuePtr != NULL)
{
/* lets check to see if we hit a new line prior to this = */
tempPtr = XP_STRCHR(buffPtr, '\n');
if (tempPtr == NULL || tempPtr > valuePtr)
{
*valuePtr = 0; /* null out the last char */
valuePtr++; /* point it pass the = sign */
/* Make sure that the Value is nullified. */
tempPtr = XP_STRCHR(valuePtr, CR);
if (tempPtr)
{
tempPtr = 0;
}
else
{
/* EOF? */
}
/* we found both the name and value, lets add it! */
JS_DefineProperty( cx->mocha_context,
stringObj,
buffPtr,
valuePtr?STRING_TO_JSVAL(JS_NewStringCopyZ(cx->mocha_context, valuePtr)):JSVAL_VOID,
NULL,
NULL,
JSPROP_ENUMERATE | JSPROP_READONLY);
/* set the buffPtr to the end of the value so that we can restart this loop */
if (tempPtr)
{
buffPtr= ++tempPtr;
}
}
else
{
/* we hit a return before hitting the =. Lets adjust the buffPtr, and continue */
buffPtr = XP_STRCHR(buffPtr, '\n');
if (buffPtr != NULL)
{
buffPtr++;
}
}
}
else
{
/* the resource file does look right - Line without an = */
/* what do we do? - lets just break*/
break;
}
}
}
XP_FREEIF(readBuf);
}
XP_FileClose(fp);
}
else
{
return NULL;
}
return stringObj;
}
PR_END_EXTERN_C