/* -*- 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 #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= usr_len) { PRUint32 i; for (i=0; ifURL->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; imocha_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