michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "install.h" michael@0: #include "install-ds.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifdef XP_UNIX michael@0: /* for chmod */ michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: /*extern "C" {*/ michael@0: #include michael@0: /*}*/ michael@0: michael@0: extern /*"C"*/ michael@0: int Pk11Install_AddNewModule(char* moduleName, char* dllPath, michael@0: unsigned long defaultMechanismFlags, michael@0: unsigned long cipherEnableFlags); michael@0: extern /*"C"*/ michael@0: short Pk11Install_UserVerifyJar(JAR *jar, PRFileDesc *out, michael@0: PRBool query); michael@0: extern /*"C"*/ michael@0: const char* mySECU_ErrorString(PRErrorCode errnum); michael@0: extern michael@0: int Pk11Install_yyparse(); michael@0: michael@0: #define INSTALL_METAINFO_TAG "Pkcs11_install_script" michael@0: #define SCRIPT_TEMP_FILE "pkcs11inst.tmp" michael@0: #define ROOT_MARKER "%root%" michael@0: #define TEMP_MARKER "%temp%" michael@0: #define PRINTF_ROOT_MARKER "%%root%%" michael@0: #define TEMPORARY_DIRECTORY_NAME "pk11inst.dir" michael@0: #define JAR_BASE_END (JAR_BASE+100) michael@0: michael@0: static PRLock* errorHandlerLock=NULL; michael@0: static Pk11Install_ErrorHandler errorHandler=NULL; michael@0: static char* PR_Strdup(const char* str); michael@0: static int rm_dash_r (char *path); michael@0: static int make_dirs(char *path, int file_perms); michael@0: static int dir_perms(int perms); michael@0: michael@0: static Pk11Install_Error DoInstall(JAR *jar, const char *installDir, michael@0: const char* tempDir, Pk11Install_Platform *platform, michael@0: PRFileDesc *feedback, PRBool noverify); michael@0: michael@0: static char *errorString[]= { michael@0: "Operation was successful", /* PK11_INSTALL_NO_ERROR */ michael@0: "Directory \"%s\" does not exist", /* PK11_INSTALL_DIR_DOESNT_EXIST */ michael@0: "File \"%s\" does not exist", /* PK11_INSTALL_FILE_DOESNT_EXIST */ michael@0: "File \"%s\" is not readable", /* PK11_INSTALL_FILE_NOT_READABLE */ michael@0: "%s", /* PK11_INSTALL_ERROR_STRING */ michael@0: "Error in JAR file %s: %s", /* PK11_INSTALL_JAR_ERROR */ michael@0: "No Pkcs11_install_script specified in JAR metainfo file", michael@0: /* PK11_INSTALL_NO_INSTALLER_SCRIPT */ michael@0: "Could not delete temporary file \"%s\"", michael@0: /*PK11_INSTALL_DELETE_TEMP_FILE */ michael@0: "Could not open temporary file \"%s\"", /*PK11_INSTALL_OPEN_SCRIPT_FILE*/ michael@0: "%s: %s", /* PK11_INSTALL_SCRIPT_PARSE */ michael@0: "Error in script: %s", michael@0: "Unable to obtain system platform information", michael@0: "Installer script has no information about the current platform (%s)", michael@0: "Relative directory \"%s\" does not contain "PRINTF_ROOT_MARKER, michael@0: "Module File \"%s\" not found", michael@0: "Error occurred installing module \"%s\" into database", michael@0: "Error extracting \"%s\" from JAR file: %s", michael@0: "Directory \"%s\" is not writeable", michael@0: "Could not create directory \"%s\"", michael@0: "Could not remove directory \"%s\"", michael@0: "Unable to execute \"%s\"", michael@0: "Unable to wait for process \"%s\"", michael@0: "\"%s\" returned error code %d", michael@0: "User aborted operation", michael@0: "Unspecified error" michael@0: }; michael@0: michael@0: enum { michael@0: INSTALLED_FILE_MSG=0, michael@0: INSTALLED_MODULE_MSG, michael@0: INSTALLER_SCRIPT_NAME, michael@0: MY_PLATFORM_IS, michael@0: USING_PLATFORM, michael@0: PARSED_INSTALL_SCRIPT, michael@0: EXEC_FILE_MSG, michael@0: EXEC_SUCCESS, michael@0: INSTALLATION_COMPLETE_MSG, michael@0: USER_ABORT michael@0: }; michael@0: michael@0: static char *msgStrings[] = { michael@0: "Installed file %s to %s\n", michael@0: "Installed module \"%s\" into module database\n", michael@0: "Using installer script \"%s\"\n", michael@0: "Current platform is %s\n", michael@0: "Using installation parameters for platform %s\n", michael@0: "Successfully parsed installation script\n", michael@0: "Executing \"%s\"...\n", michael@0: "\"%s\" executed successfully\n", michael@0: "\nInstallation completed successfully\n", michael@0: "\nAborting...\n" michael@0: }; michael@0: michael@0: /************************************************************************** michael@0: * S t r i n g N o d e michael@0: */ michael@0: typedef struct StringNode_str { michael@0: char *str; michael@0: struct StringNode_str* next; michael@0: } StringNode; michael@0: michael@0: StringNode* StringNode_new() michael@0: { michael@0: StringNode* new_this; michael@0: new_this = (StringNode*)malloc(sizeof(StringNode)); michael@0: new_this->str=NULL; michael@0: new_this->next=NULL; michael@0: return new_this; michael@0: } michael@0: michael@0: void StringNode_delete(StringNode* s) michael@0: { michael@0: if(s->str) { michael@0: PR_Free(s->str); michael@0: s->str=NULL; michael@0: } michael@0: } michael@0: michael@0: /************************************************************************* michael@0: * S t r i n g L i s t michael@0: */ michael@0: typedef struct StringList_str { michael@0: StringNode* head; michael@0: StringNode* tail; michael@0: } StringList; michael@0: michael@0: void StringList_new(StringList* list) michael@0: { michael@0: list->head=NULL; michael@0: list->tail=NULL; michael@0: } michael@0: michael@0: void StringList_delete(StringList* list) michael@0: { michael@0: StringNode *tmp; michael@0: while(list->head) { michael@0: tmp = list->head; michael@0: list->head = list->head->next; michael@0: StringNode_delete(tmp); michael@0: } michael@0: } michael@0: michael@0: void michael@0: StringList_Append(StringList* list, char* str) michael@0: { michael@0: if(!str) { michael@0: return; michael@0: } michael@0: michael@0: if(!list->tail) { michael@0: /* This is the first element */ michael@0: list->head = list->tail = StringNode_new(); michael@0: } else { michael@0: list->tail->next = StringNode_new(); michael@0: list->tail = list->tail->next; michael@0: } michael@0: michael@0: list->tail->str = PR_Strdup(str); michael@0: list->tail->next = NULL; /* just to be sure */ michael@0: } michael@0: michael@0: /************************************************************************** michael@0: * michael@0: * P k 1 1 I n s t a l l _ S e t E r r o r H a n d l e r michael@0: * michael@0: * Sets the error handler to be used by the library. Returns the current michael@0: * error handler function. michael@0: */ michael@0: Pk11Install_ErrorHandler michael@0: Pk11Install_SetErrorHandler(Pk11Install_ErrorHandler handler) michael@0: { michael@0: Pk11Install_ErrorHandler old; michael@0: michael@0: if(!errorHandlerLock) { michael@0: errorHandlerLock = PR_NewLock(); michael@0: } michael@0: michael@0: PR_Lock(errorHandlerLock); michael@0: michael@0: old = errorHandler; michael@0: errorHandler = handler; michael@0: michael@0: PR_Unlock(errorHandlerLock); michael@0: michael@0: return old; michael@0: } michael@0: michael@0: /************************************************************************** michael@0: * michael@0: * P k 1 1 I n s t a l l _ I n i t michael@0: * michael@0: * Does initialization that otherwise would be done on the fly. Only michael@0: * needs to be called by multithreaded apps, before they make any calls michael@0: * to this library. michael@0: */ michael@0: void michael@0: Pk11Install_Init() michael@0: { michael@0: if(!errorHandlerLock) { michael@0: errorHandlerLock = PR_NewLock(); michael@0: } michael@0: } michael@0: michael@0: /************************************************************************** michael@0: * michael@0: * P k 1 1 I n s t a l l _ R e l e a s e michael@0: * michael@0: * Releases static data structures used by the library. Don't use the michael@0: * library after calling this, unless you call Pk11Install_Init() michael@0: * first. This function doesn't have to be called at all unless you're michael@0: * really anal about freeing memory before your program exits. michael@0: */ michael@0: void michael@0: Pk11Install_Release() michael@0: { michael@0: if(errorHandlerLock) { michael@0: PR_Free(errorHandlerLock); michael@0: errorHandlerLock = NULL; michael@0: } michael@0: } michael@0: michael@0: /************************************************************************* michael@0: * michael@0: * e r r o r michael@0: * michael@0: * Takes an error code and its arguments, creates the error string, michael@0: * and sends the string to the handler function if it exists. michael@0: */ michael@0: michael@0: #ifdef OSF1 michael@0: /* stdarg has already been pulled in from NSPR */ michael@0: #undef va_start michael@0: #undef va_end michael@0: #undef va_arg michael@0: #include michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef OSF1 michael@0: static void michael@0: error(long va_alist, ...) michael@0: #else michael@0: static void michael@0: error(Pk11Install_Error errcode, ...) michael@0: #endif michael@0: { michael@0: michael@0: va_list ap; michael@0: char *errstr; michael@0: Pk11Install_ErrorHandler handler; michael@0: michael@0: if(!errorHandlerLock) { michael@0: errorHandlerLock = PR_NewLock(); michael@0: } michael@0: michael@0: PR_Lock(errorHandlerLock); michael@0: michael@0: handler = errorHandler; michael@0: michael@0: PR_Unlock(errorHandlerLock); michael@0: michael@0: if(handler) { michael@0: #ifdef OSF1 michael@0: va_start(ap); michael@0: errstr = PR_vsmprintf(errorString[va_arg(ap, Pk11Install_Error)], ap); michael@0: #else michael@0: va_start(ap, errcode); michael@0: errstr = PR_vsmprintf(errorString[errcode], ap); michael@0: #endif michael@0: handler(errstr); michael@0: PR_smprintf_free(errstr); michael@0: va_end(ap); michael@0: } michael@0: } michael@0: michael@0: /************************************************************************* michael@0: * michael@0: * j a r _ c a l l b a c k michael@0: */ michael@0: static int michael@0: jar_callback(int status, JAR *foo, const char *bar, char *pathname, michael@0: char *errortext) { michael@0: char *string; michael@0: michael@0: string = PR_smprintf("JAR error %d: %s in file %s\n", status, errortext, michael@0: pathname); michael@0: error(PK11_INSTALL_ERROR_STRING, string); michael@0: PR_smprintf_free(string); michael@0: return 0; michael@0: } michael@0: michael@0: /************************************************************************* michael@0: * michael@0: * P k 1 1 I n s t a l l _ D o I n s t a l l michael@0: * michael@0: * jarFile is the path of a JAR in the PKCS #11 module JAR format. michael@0: * installDir is the directory relative to which files will be michael@0: * installed. michael@0: */ michael@0: Pk11Install_Error michael@0: Pk11Install_DoInstall(char *jarFile, const char *installDir, michael@0: const char *tempDir, PRFileDesc *feedback, short force, PRBool noverify) michael@0: { michael@0: JAR *jar; michael@0: char *installer; michael@0: unsigned long installer_len; michael@0: int status; michael@0: Pk11Install_Error ret; michael@0: PRBool made_temp_file; michael@0: Pk11Install_Info installInfo; michael@0: Pk11Install_Platform *platform; michael@0: char* errMsg; michael@0: char sysname[SYS_INFO_BUFFER_LENGTH], release[SYS_INFO_BUFFER_LENGTH], michael@0: arch[SYS_INFO_BUFFER_LENGTH]; michael@0: char *myPlatform; michael@0: michael@0: jar=NULL; michael@0: ret = PK11_INSTALL_UNSPECIFIED; michael@0: made_temp_file=PR_FALSE; michael@0: errMsg=NULL; michael@0: Pk11Install_Info_init(&installInfo); michael@0: michael@0: /* michael@0: printf("Inside DoInstall, jarFile=%s, installDir=%s, tempDir=%s\n", michael@0: jarFile, installDir, tempDir); michael@0: */ michael@0: michael@0: /* michael@0: * Check out jarFile and installDir for validity michael@0: */ michael@0: if( PR_Access(installDir, PR_ACCESS_EXISTS) != PR_SUCCESS ) { michael@0: error(PK11_INSTALL_DIR_DOESNT_EXIST, installDir); michael@0: return PK11_INSTALL_DIR_DOESNT_EXIST; michael@0: } michael@0: if(!tempDir) { michael@0: tempDir = "."; michael@0: } michael@0: if( PR_Access(tempDir, PR_ACCESS_EXISTS) != PR_SUCCESS ) { michael@0: error(PK11_INSTALL_DIR_DOESNT_EXIST, tempDir); michael@0: return PK11_INSTALL_DIR_DOESNT_EXIST; michael@0: } michael@0: if( PR_Access(tempDir, PR_ACCESS_WRITE_OK) != PR_SUCCESS ) { michael@0: error(PK11_INSTALL_DIR_NOT_WRITEABLE, tempDir); michael@0: return PK11_INSTALL_DIR_NOT_WRITEABLE; michael@0: } michael@0: if( (PR_Access(jarFile, PR_ACCESS_EXISTS) != PR_SUCCESS) ) { michael@0: error(PK11_INSTALL_FILE_DOESNT_EXIST, jarFile); michael@0: return PK11_INSTALL_FILE_DOESNT_EXIST; michael@0: } michael@0: if( PR_Access(jarFile, PR_ACCESS_READ_OK) != PR_SUCCESS ) { michael@0: error(PK11_INSTALL_FILE_NOT_READABLE, jarFile); michael@0: return PK11_INSTALL_FILE_NOT_READABLE; michael@0: } michael@0: michael@0: /* michael@0: * Extract the JAR file michael@0: */ michael@0: jar = JAR_new(); michael@0: JAR_set_callback(JAR_CB_SIGNAL, jar, jar_callback); michael@0: michael@0: if(noverify) { michael@0: status = JAR_pass_archive_unverified(jar, jarArchGuess, jarFile, "url"); michael@0: } else { michael@0: status = JAR_pass_archive(jar, jarArchGuess, jarFile, "url"); michael@0: } michael@0: if( (status < 0) || (jar->valid < 0) ) { michael@0: if (status >= JAR_BASE && status <= JAR_BASE_END) { michael@0: error(PK11_INSTALL_JAR_ERROR, jarFile, JAR_get_error(status)); michael@0: } else { michael@0: error(PK11_INSTALL_JAR_ERROR, jarFile, michael@0: mySECU_ErrorString(PORT_GetError())); michael@0: } michael@0: ret=PK11_INSTALL_JAR_ERROR; michael@0: goto loser; michael@0: } michael@0: /*printf("passed the archive\n");*/ michael@0: michael@0: /* michael@0: * Show the user security information, allow them to abort or continue michael@0: */ michael@0: if( Pk11Install_UserVerifyJar(jar, PR_STDOUT, michael@0: force?PR_FALSE:PR_TRUE) && !force) { michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[USER_ABORT]); michael@0: } michael@0: ret=PK11_INSTALL_USER_ABORT; michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * Get the name of the installation file michael@0: */ michael@0: if( JAR_get_metainfo(jar, NULL, INSTALL_METAINFO_TAG, (void**)&installer, michael@0: (unsigned long*)&installer_len) ) { michael@0: error(PK11_INSTALL_NO_INSTALLER_SCRIPT); michael@0: ret=PK11_INSTALL_NO_INSTALLER_SCRIPT; michael@0: goto loser; michael@0: } michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[INSTALLER_SCRIPT_NAME], installer); michael@0: } michael@0: michael@0: /* michael@0: * Extract the installation file michael@0: */ michael@0: if( PR_Access(SCRIPT_TEMP_FILE, PR_ACCESS_EXISTS) == PR_SUCCESS) { michael@0: if( PR_Delete(SCRIPT_TEMP_FILE) != PR_SUCCESS) { michael@0: error(PK11_INSTALL_DELETE_TEMP_FILE, SCRIPT_TEMP_FILE); michael@0: ret=PK11_INSTALL_DELETE_TEMP_FILE; michael@0: goto loser; michael@0: } michael@0: } michael@0: if(noverify) { michael@0: status = JAR_extract(jar, installer, SCRIPT_TEMP_FILE); michael@0: } else { michael@0: status = JAR_verified_extract(jar, installer, SCRIPT_TEMP_FILE); michael@0: } michael@0: if(status) { michael@0: if (status >= JAR_BASE && status <= JAR_BASE_END) { michael@0: error(PK11_INSTALL_JAR_EXTRACT, installer, JAR_get_error(status)); michael@0: } else { michael@0: error(PK11_INSTALL_JAR_EXTRACT, installer, michael@0: mySECU_ErrorString(PORT_GetError())); michael@0: } michael@0: ret = PK11_INSTALL_JAR_EXTRACT; michael@0: goto loser; michael@0: } else { michael@0: made_temp_file = PR_TRUE; michael@0: } michael@0: michael@0: /* michael@0: * Parse the installation file into a syntax tree michael@0: */ michael@0: Pk11Install_FD = PR_Open(SCRIPT_TEMP_FILE, PR_RDONLY, 0); michael@0: if(!Pk11Install_FD) { michael@0: error(PK11_INSTALL_OPEN_SCRIPT_FILE, SCRIPT_TEMP_FILE); michael@0: ret=PK11_INSTALL_OPEN_SCRIPT_FILE; michael@0: goto loser; michael@0: } michael@0: if(Pk11Install_yyparse()) { michael@0: error(PK11_INSTALL_SCRIPT_PARSE, installer, michael@0: Pk11Install_yyerrstr ? Pk11Install_yyerrstr : ""); michael@0: ret=PK11_INSTALL_SCRIPT_PARSE; michael@0: goto loser; michael@0: } michael@0: michael@0: #if 0 michael@0: /* for debugging */ michael@0: Pk11Install_valueList->Print(0); michael@0: #endif michael@0: michael@0: /* michael@0: * From the syntax tree, build a semantic structure michael@0: */ michael@0: errMsg = Pk11Install_Info_Generate(&installInfo,Pk11Install_valueList); michael@0: if(errMsg) { michael@0: error(PK11_INSTALL_SEMANTIC, errMsg); michael@0: ret=PK11_INSTALL_SEMANTIC; michael@0: goto loser; michael@0: } michael@0: #if 0 michael@0: installInfo.Print(0); michael@0: #endif michael@0: michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[PARSED_INSTALL_SCRIPT]); michael@0: } michael@0: michael@0: /* michael@0: * Figure out which platform to use michael@0: */ michael@0: { michael@0: sysname[0] = release[0] = arch[0] = '\0'; michael@0: michael@0: if( (PR_GetSystemInfo(PR_SI_SYSNAME, sysname, SYS_INFO_BUFFER_LENGTH) michael@0: != PR_SUCCESS) || michael@0: (PR_GetSystemInfo(PR_SI_RELEASE, release, SYS_INFO_BUFFER_LENGTH) michael@0: != PR_SUCCESS) || michael@0: (PR_GetSystemInfo(PR_SI_ARCHITECTURE, arch, SYS_INFO_BUFFER_LENGTH) michael@0: != PR_SUCCESS) ) { michael@0: error(PK11_INSTALL_SYSINFO); michael@0: ret=PK11_INSTALL_SYSINFO; michael@0: goto loser; michael@0: } michael@0: myPlatform = PR_smprintf("%s:%s:%s", sysname, release, arch); michael@0: platform = Pk11Install_Info_GetBestPlatform(&installInfo,myPlatform); michael@0: if(!platform) { michael@0: error(PK11_INSTALL_NO_PLATFORM, myPlatform); michael@0: PR_smprintf_free(myPlatform); michael@0: ret=PK11_INSTALL_NO_PLATFORM; michael@0: goto loser; michael@0: } michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[MY_PLATFORM_IS], myPlatform); michael@0: PR_fprintf(feedback, msgStrings[USING_PLATFORM], michael@0: Pk11Install_PlatformName_GetString(&platform->name)); michael@0: } michael@0: PR_smprintf_free(myPlatform); michael@0: } michael@0: michael@0: /* Run the install for that platform */ michael@0: ret = DoInstall(jar, installDir, tempDir, platform, feedback, noverify); michael@0: if(ret) { michael@0: goto loser; michael@0: } michael@0: michael@0: ret = PK11_INSTALL_SUCCESS; michael@0: loser: michael@0: if(Pk11Install_valueList) { michael@0: Pk11Install_ValueList_delete(Pk11Install_valueList); michael@0: PR_Free(Pk11Install_valueList); michael@0: Pk11Install_valueList = NULL; michael@0: } michael@0: if(jar) { michael@0: JAR_destroy(jar); michael@0: } michael@0: if(made_temp_file) { michael@0: PR_Delete(SCRIPT_TEMP_FILE); michael@0: } michael@0: if(errMsg) { michael@0: PR_smprintf_free(errMsg); michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: /* michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: // actually run the installation, copying files to and fro michael@0: */ michael@0: static Pk11Install_Error michael@0: DoInstall(JAR *jar, const char *installDir, const char *tempDir, michael@0: Pk11Install_Platform *platform, PRFileDesc *feedback, PRBool noverify) michael@0: { michael@0: Pk11Install_File *file; michael@0: Pk11Install_Error ret; michael@0: char *reldir; michael@0: char *dest; michael@0: char *modDest; michael@0: char *cp; michael@0: int i; michael@0: int status; michael@0: char *tempname, *temp; michael@0: StringList executables; michael@0: StringNode *execNode; michael@0: PRProcessAttr *attr; michael@0: PRProcess *proc; michael@0: char *argv[2]; michael@0: char *envp[1]; michael@0: int errcode; michael@0: michael@0: ret=PK11_INSTALL_UNSPECIFIED; michael@0: reldir=NULL; michael@0: dest=NULL; michael@0: modDest=NULL; michael@0: tempname=NULL; michael@0: michael@0: StringList_new(&executables); michael@0: /* michael@0: // Create Temporary directory michael@0: */ michael@0: tempname = PR_smprintf("%s/%s", tempDir, TEMPORARY_DIRECTORY_NAME); michael@0: if( PR_Access(tempname, PR_ACCESS_EXISTS)==PR_SUCCESS ) { michael@0: /* Left over from previous run? Delete it. */ michael@0: rm_dash_r(tempname); michael@0: } michael@0: if(PR_MkDir(tempname, 0700) != PR_SUCCESS) { michael@0: error(PK11_INSTALL_CREATE_DIR, tempname); michael@0: ret = PK11_INSTALL_CREATE_DIR; michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: // Install all the files michael@0: */ michael@0: for(i=0; i < platform->numFiles; i++) { michael@0: file = &platform->files[i]; michael@0: michael@0: if(file->relativePath) { michael@0: PRBool foundMarker = PR_FALSE; michael@0: reldir = PR_Strdup(file->relativePath); michael@0: michael@0: /* Replace all the markers with the directories for which they stand */ michael@0: while(1) { michael@0: if( (cp=PL_strcasestr(reldir, ROOT_MARKER)) ) { michael@0: /* Has a %root% marker */ michael@0: *cp = '\0'; michael@0: temp = PR_smprintf("%s%s%s", reldir, installDir, michael@0: cp+strlen(ROOT_MARKER)); michael@0: PR_Free(reldir); michael@0: reldir = temp; michael@0: foundMarker = PR_TRUE; michael@0: } else if( (cp = PL_strcasestr(reldir, TEMP_MARKER)) ) { michael@0: /* Has a %temp% marker */ michael@0: *cp = '\0'; michael@0: temp = PR_smprintf("%s%s%s", reldir, tempname, michael@0: cp+strlen(TEMP_MARKER)); michael@0: PR_Free(reldir); michael@0: reldir = temp; michael@0: foundMarker = PR_TRUE; michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: if(!foundMarker) { michael@0: /* Has no markers...this isn't really a relative directory */ michael@0: error(PK11_INSTALL_BOGUS_REL_DIR, file->relativePath); michael@0: ret = PK11_INSTALL_BOGUS_REL_DIR; michael@0: goto loser; michael@0: } michael@0: dest = reldir; michael@0: reldir = NULL; michael@0: } else if(file->absolutePath) { michael@0: dest = PR_Strdup(file->absolutePath); michael@0: } michael@0: michael@0: /* Remember if this is the module file, we'll need to add it later */ michael@0: if(i == platform->modFile) { michael@0: modDest = PR_Strdup(dest); michael@0: } michael@0: michael@0: /* Remember is this is an executable, we'll need to run it later */ michael@0: if(file->executable) { michael@0: StringList_Append(&executables,dest); michael@0: /*executables.Append(dest);*/ michael@0: } michael@0: michael@0: /* Make sure the directory we are targetting exists */ michael@0: if( make_dirs(dest, file->permissions) ) { michael@0: ret=PK11_INSTALL_CREATE_DIR; michael@0: goto loser; michael@0: } michael@0: michael@0: /* Actually extract the file onto the filesystem */ michael@0: if(noverify) { michael@0: status = JAR_extract(jar, (char*)file->jarPath, dest); michael@0: } else { michael@0: status = JAR_verified_extract(jar, (char*)file->jarPath, dest); michael@0: } michael@0: if(status) { michael@0: if (status >= JAR_BASE && status <= JAR_BASE_END) { michael@0: error(PK11_INSTALL_JAR_EXTRACT, file->jarPath, michael@0: JAR_get_error(status)); michael@0: } else { michael@0: error(PK11_INSTALL_JAR_EXTRACT, file->jarPath, michael@0: mySECU_ErrorString(PORT_GetError())); michael@0: } michael@0: ret=PK11_INSTALL_JAR_EXTRACT; michael@0: goto loser; michael@0: } michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[INSTALLED_FILE_MSG], michael@0: file->jarPath, dest); michael@0: } michael@0: michael@0: /* no NSPR command to change permissions? */ michael@0: #ifdef XP_UNIX michael@0: chmod(dest, file->permissions); michael@0: #endif michael@0: michael@0: /* Memory clean-up tasks */ michael@0: if(reldir) { michael@0: PR_Free(reldir); michael@0: reldir = NULL; michael@0: } michael@0: if(dest) { michael@0: PR_Free(dest); michael@0: dest = NULL; michael@0: } michael@0: } michael@0: /* Make sure we found the module file */ michael@0: if(!modDest) { michael@0: /* Internal problem here, since every platform is supposed to have michael@0: a module file */ michael@0: error(PK11_INSTALL_NO_MOD_FILE, platform->moduleName); michael@0: ret=PK11_INSTALL_NO_MOD_FILE; michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: // Execute any executable files michael@0: */ michael@0: { michael@0: argv[1] = NULL; michael@0: envp[0] = NULL; michael@0: for(execNode = executables.head; execNode; execNode = execNode->next) { michael@0: attr = PR_NewProcessAttr(); michael@0: argv[0] = PR_Strdup(execNode->str); michael@0: michael@0: /* Announce our intentions */ michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[EXEC_FILE_MSG], execNode->str); michael@0: } michael@0: michael@0: /* start the process */ michael@0: if( !(proc=PR_CreateProcess(execNode->str, argv, envp, attr)) ) { michael@0: PR_Free(argv[0]); michael@0: PR_DestroyProcessAttr(attr); michael@0: error(PK11_INSTALL_EXEC_FILE, execNode->str); michael@0: ret=PK11_INSTALL_EXEC_FILE; michael@0: goto loser; michael@0: } michael@0: michael@0: /* wait for it to finish */ michael@0: if( PR_WaitProcess(proc, &errcode) != PR_SUCCESS) { michael@0: PR_Free(argv[0]); michael@0: PR_DestroyProcessAttr(attr); michael@0: error(PK11_INSTALL_WAIT_PROCESS, execNode->str); michael@0: ret=PK11_INSTALL_WAIT_PROCESS; michael@0: goto loser; michael@0: } michael@0: michael@0: /* What happened? */ michael@0: if(errcode) { michael@0: /* process returned an error */ michael@0: error(PK11_INSTALL_PROC_ERROR, execNode->str, errcode); michael@0: } else if(feedback) { michael@0: /* process ran successfully */ michael@0: PR_fprintf(feedback, msgStrings[EXEC_SUCCESS], execNode->str); michael@0: } michael@0: michael@0: PR_Free(argv[0]); michael@0: PR_DestroyProcessAttr(attr); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: // Add the module michael@0: */ michael@0: status = Pk11Install_AddNewModule((char*)platform->moduleName, michael@0: (char*)modDest, platform->mechFlags, platform->cipherFlags ); michael@0: michael@0: if(status != SECSuccess) { michael@0: error(PK11_INSTALL_ADD_MODULE, platform->moduleName); michael@0: ret=PK11_INSTALL_ADD_MODULE; michael@0: goto loser; michael@0: } michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[INSTALLED_MODULE_MSG], michael@0: platform->moduleName); michael@0: } michael@0: michael@0: if(feedback) { michael@0: PR_fprintf(feedback, msgStrings[INSTALLATION_COMPLETE_MSG]); michael@0: } michael@0: michael@0: ret = PK11_INSTALL_SUCCESS; michael@0: michael@0: loser: michael@0: if(reldir) { michael@0: PR_Free(reldir); michael@0: } michael@0: if(dest) { michael@0: PR_Free(dest); michael@0: } michael@0: if(modDest) { michael@0: PR_Free(modDest); michael@0: } michael@0: if(tempname) { michael@0: PRFileInfo info; michael@0: if(PR_GetFileInfo(tempname, &info) == PR_SUCCESS) { michael@0: if(info.type == PR_FILE_DIRECTORY) { michael@0: /* Recursively remove temporary directory */ michael@0: if(rm_dash_r(tempname)) { michael@0: error(PK11_INSTALL_REMOVE_DIR, michael@0: tempname); michael@0: ret=PK11_INSTALL_REMOVE_DIR; michael@0: } michael@0: michael@0: } michael@0: } michael@0: PR_Free(tempname); michael@0: } michael@0: StringList_delete(&executables); michael@0: return ret; michael@0: } michael@0: michael@0: /* michael@0: ////////////////////////////////////////////////////////////////////////// michael@0: */ michael@0: static char* michael@0: PR_Strdup(const char* str) michael@0: { michael@0: char *tmp = (char*) PR_Malloc(strlen(str)+1); michael@0: strcpy(tmp, str); michael@0: return tmp; michael@0: } michael@0: michael@0: /* michael@0: * r m _ d a s h _ r michael@0: * michael@0: * Remove a file, or a directory recursively. michael@0: * michael@0: */ michael@0: static int michael@0: rm_dash_r (char *path) michael@0: { michael@0: PRDir *dir; michael@0: PRDirEntry *entry; michael@0: PRFileInfo fileinfo; michael@0: char filename[240]; michael@0: michael@0: if(PR_GetFileInfo(path, &fileinfo) != PR_SUCCESS) { michael@0: /*fprintf(stderr, "Error: Unable to access %s\n", filename);*/ michael@0: return -1; michael@0: } michael@0: if(fileinfo.type == PR_FILE_DIRECTORY) { michael@0: michael@0: dir = PR_OpenDir(path); michael@0: if(!dir) { michael@0: return -1; michael@0: } michael@0: michael@0: /* Recursively delete all entries in the directory */ michael@0: while((entry = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) { michael@0: sprintf(filename, "%s/%s", path, entry->name); michael@0: if(rm_dash_r(filename)) return -1; michael@0: } michael@0: michael@0: if(PR_CloseDir(dir) != PR_SUCCESS) { michael@0: return -1; michael@0: } michael@0: michael@0: /* Delete the directory itself */ michael@0: if(PR_RmDir(path) != PR_SUCCESS) { michael@0: return -1; michael@0: } michael@0: } else { michael@0: if(PR_Delete(path) != PR_SUCCESS) { michael@0: return -1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: * michael@0: * m a k e _ d i r s michael@0: * michael@0: * Ensure that the directory portion of the path exists. This may require michael@0: * making the directory, and its parent, and its parent's parent, etc. michael@0: */ michael@0: static int michael@0: make_dirs(char *path, int file_perms) michael@0: { michael@0: char *Path; michael@0: char *start; michael@0: char *sep; michael@0: int ret = 0; michael@0: PRFileInfo info; michael@0: michael@0: if(!path) { michael@0: return 0; michael@0: } michael@0: michael@0: Path = PR_Strdup(path); michael@0: start = strpbrk(Path, "/\\"); michael@0: if(!start) { michael@0: return 0; michael@0: } michael@0: start++; /* start right after first slash */ michael@0: michael@0: /* Each time through the loop add one more directory. */ michael@0: while( (sep=strpbrk(start, "/\\")) ) { michael@0: *sep = '\0'; michael@0: michael@0: if( PR_GetFileInfo(Path, &info) != PR_SUCCESS) { michael@0: /* No such dir, we have to create it */ michael@0: if( PR_MkDir(Path, dir_perms(file_perms)) != PR_SUCCESS) { michael@0: error(PK11_INSTALL_CREATE_DIR, Path); michael@0: ret = PK11_INSTALL_CREATE_DIR; michael@0: goto loser; michael@0: } michael@0: } else { michael@0: /* something exists by this name, make sure it's a directory */ michael@0: if( info.type != PR_FILE_DIRECTORY ) { michael@0: error(PK11_INSTALL_CREATE_DIR, Path); michael@0: ret = PK11_INSTALL_CREATE_DIR; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: /* If this is the lowest directory level, make sure it is writeable */ michael@0: if(!strpbrk(sep+1, "/\\")) { michael@0: if( PR_Access(Path, PR_ACCESS_WRITE_OK)!=PR_SUCCESS) { michael@0: error(PK11_INSTALL_DIR_NOT_WRITEABLE, Path); michael@0: ret = PK11_INSTALL_DIR_NOT_WRITEABLE; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: start = sep+1; /* start after the next slash */ michael@0: *sep = '/'; michael@0: } michael@0: michael@0: loser: michael@0: PR_Free(Path); michael@0: return ret; michael@0: } michael@0: michael@0: /************************************************************************* michael@0: * d i r _ p e r m s michael@0: * michael@0: * Guesses the desired permissions on a directory based on the permissions michael@0: * of a file that will be stored in it. Give read, write, and michael@0: * execute to the owner (so we can create the file), read and michael@0: * execute to anyone who has read permissions on the file, and write michael@0: * to anyone who has write permissions on the file. michael@0: */ michael@0: static int michael@0: dir_perms(int perms) michael@0: { michael@0: int ret = 0; michael@0: michael@0: /* owner */ michael@0: ret |= 0700; michael@0: michael@0: /* group */ michael@0: if(perms & 0040) { michael@0: /* read on the file -> read and execute on the directory */ michael@0: ret |= 0050; michael@0: } michael@0: if(perms & 0020) { michael@0: /* write on the file -> write on the directory */ michael@0: ret |= 0020; michael@0: } michael@0: michael@0: /* others */ michael@0: if(perms & 0004) { michael@0: /* read on the file -> read and execute on the directory */ michael@0: ret |= 0005; michael@0: } michael@0: if(perms & 0002) { michael@0: /* write on the file -> write on the directory */ michael@0: ret |= 0002; michael@0: } michael@0: michael@0: return ret; michael@0: }