/* -*- Mode: C++; tab-width: 4; tabs-indent-mode: nil -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* su_win.c * Win specific softupdate routines */ #ifdef XP_OS2 #include os2updt.h" #endif #include #include "xp_mcom.h" #include "su_folderspec.h" #include "su_instl.h" #include "xp_str.h" #include "npapi.h" #include "libevent.h" #include "fe_proto.h" #include "xpgetstr.h" #include #include "NSReg.h" #ifdef WIN32 #include #elif !defined (XP_OS2) #include #endif extern NPError wfe_GetPluginsDirectory(CString& csDirname); extern "C" char * FE_GetProgramDirectory(char *buffer, int length); extern "C" int SU_NEED_TO_REBOOT; XP_Bool fe_FileNeedsUpdate(char *sourceFile, char *targetFile); #if defined(WIN32) || defined(XP_OS2) #define su_FileRemove(file,type) XP_FileRemove((file),(type)) #else int su_FileRemove(char *fname, XP_FileType type); #endif char * FE_GetDirectoryPath( su_DirSpecID folderID) { char * directory = NULL; char path[_MAX_PATH]; DWORD dwVersion; UINT len; switch( folderID) { case ePluginFolder: { NPError err; CString c; err = wfe_GetPluginsDirectory(c); if (err == 0) { c += (TCHAR)'\\'; directory = XP_STRDUP( c ); } } break; case eProgramFolder: FE_GetProgramDirectory( path, _MAX_PATH ); directory = XP_STRDUP( path ); break; case ePackageFolder: break; case eTemporaryFolder: { char* tmpdir = XP_TempDirName(); int slen; XP_STRNCPY_SAFE( path, tmpdir, _MAX_PATH ); XP_FREEIF(tmpdir); slen = XP_STRLEN(path); if ( slen < _MAX_PATH - 1) { path[slen] = '\\'; path[slen+1] = '\0'; } else break; directory = XP_STRDUP( path ); } break; case eCommunicatorFolder: { char* p; FE_GetProgramDirectory( path, _MAX_PATH ); if ( (p = XP_STRRCHR( path, '\\' )) ) *p = '\0'; if ( (p = XP_STRRCHR( path, '\\' )) ) p[1] = '\0'; directory = XP_STRDUP( path ); } break; case eWin_System16Folder: len = GetSystemDirectory( path, _MAX_PATH ); #ifndef XP_OS2 // If Windows NT dwVersion = GetVersion(); if ( dwVersion < 0x80000000 ) { // and the last two chars of the system dir are "32" if ( XP_STRCMP( path+len-2, "32" ) == 0 ) { // 16-bit system dir is just "System" len = len - 2; } } #endif // Need enough space to add the trailing backslash if(len > _MAX_PATH-2) break; path[len] = '\\'; path[len+1] = '\0'; directory = XP_STRDUP( path ); break; case eWin_SystemFolder: len = GetSystemDirectory( path, _MAX_PATH ); // Need enough space to add the trailing backslash if(len > _MAX_PATH-2) break; path[len] = '\\'; path[len+1] = '\0'; directory = XP_STRDUP( path ); break; case eWin_WindowsFolder: len = GetWindowsDirectory( path, _MAX_PATH ); // Need enough space to add the trailing backslash if(len > _MAX_PATH-2) break; path[len] = '\\'; path[len+1] = '\0'; directory = XP_STRDUP( path ); break; case eJavaBinFolder: FE_GetProgramDirectory( path, _MAX_PATH ); directory = PR_smprintf("%sjava\\bin\\", path); break; case eJavaClassesFolder: FE_GetProgramDirectory( path, _MAX_PATH ); directory = PR_smprintf("%sjava\\classes\\", path); break; case eJavaDownloadFolder: FE_GetProgramDirectory( path, _MAX_PATH ); directory = PR_smprintf("%sjava\\download\\", path); break; case eNetHelpFolder: { char* tmpdir = FE_GetNetHelpDir(); if ('/' != *tmpdir ) { /* we got a valid file url from FE_GetNetHelpDir() */ directory = WH_FileName(tmpdir+7, xpURL); } else { /* windows FE couldn't find registry setting */ FE_GetProgramDirectory( path, _MAX_PATH ); if(strlen(path)+strlen("NetHelp\\")+1 > _MAX_PATH) break; XP_STRCAT( path, "NetHelp\\" ); directory = XP_STRDUP( path ); } XP_FREEIF(tmpdir); } break; case eOSDriveFolder: len = GetWindowsDirectory( path, _MAX_PATH ); if (len) { if ( path[1] == ':' && path[2] == '\\' ) { path[3] = 0; directory = XP_STRDUP( path ); } } break; // inapplicable platform specific directories case eMac_SystemFolder: case eMac_DesktopFolder: case eMac_TrashFolder: case eMac_StartupFolder: case eMac_ShutdownFolder: case eMac_AppleMenuFolder: case eMac_ControlPanelFolder: case eMac_ExtensionFolder: case eMac_FontsFolder: case eMac_PreferencesFolder: case eUnix_LocalFolder: case eUnix_LibFolder: break; case eFileURLFolder: // should never be handled in this routine default: // someone added a new one and didn't take care of it XP_ASSERT(0); break; } return directory; } int FE_ExecuteFile( const char * filename, const char * cmdline ) { if ( cmdline == NULL || filename == NULL ) return -1; DWORD hInst = WinExec( cmdline, SW_NORMAL ); su_DeleteOldFileLater( (char*)filename ); if ( hInst < 32 ) return -1; else return 0; } /* Template for a delayed "rename" of a file on windows platform, need to: - Convert filenames to native form - Add error checking */ #ifndef NO_ERROR #define NO_ERROR 0 #endif int FE_ReplaceExistingFile(char *CurrentFname, XP_FileType ctype, char *FinalFname, XP_FileType ftype, XP_Bool force) { int err; char * currentName; char * finalName; /* Convert file names to their native format */ currentName = WH_FileName (CurrentFname, ctype); finalName = WH_FileName (FinalFname, ftype); if ( currentName == NULL || finalName == NULL ) { err = -1; goto cleanup; } // replace file if forced or if windows version info is newer if (force || fe_FileNeedsUpdate( currentName, finalName ) ) { /* try to delete the existing file */ err = su_FileRemove( FinalFname, ftype ); if ( NO_ERROR == err ) { err = XP_FileRename(CurrentFname, ctype, FinalFname, ftype); } else { /* couldn't delete, probably in use. Schedule for later */ DWORD dwVersion, dwWindowsMajorVersion; char* final = finalName; char* current = currentName; #if !defined(XP_OS2) #if !defined(XP_WIN16) /* Get OS version info */ dwVersion = GetVersion(); dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); /* Get build numbers for Windows NT or Win32s */ if (dwVersion < 0x80000000) // Windows NT { /* On Windows NT */ if ( WFE_IsMoveFileExBroken() ) { /* the MOVEFILE_DELAY_UNTIL_REBOOT option doesn't work on * NT 3.51 SP4 or on NT 4.0 until SP2 */ struct stat statbuf; BOOL nameFound = FALSE; char tmpname[_MAX_PATH]; XP_STRNCPY_SAFE( tmpname, finalName, _MAX_PATH ); int len = strlen(tmpname); while (!nameFound && len < _MAX_PATH ) { tmpname[len-1] = '~'; tmpname[len] = '\0'; if ( stat(tmpname, &statbuf) != 0 ) nameFound = TRUE; else len++; } if ( nameFound ) { if ( MoveFile( finalName, tmpname ) ) { if ( MoveFile( currentName, finalName ) ) { err = su_DeleteOldFileLater( tmpname ); } else { /* 2nd move failed, put old file back */ MoveFile( tmpname, finalName ); } } else { /* non-executable in use; schedule for later */ err = su_ReplaceOldFileLater( CurrentFname, FinalFname ); } } } else if ( MoveFileEx(currentName, finalName, MOVEFILE_DELAY_UNTIL_REBOOT) ) { err = NO_ERROR; } } else // Windows 95 or Win16 { /* * Place an entry in the WININIT.INI file in the Windows directory * to delete finalName and rename currentName to be finalName at reboot */ int strlen; char Src[_MAX_PATH]; // 8.3 name char Dest[_MAX_PATH]; // 8.3 name strlen = GetShortPathName( (LPCTSTR)currentName, (LPTSTR)Src, (DWORD)sizeof(Src) ); if ( strlen > 0 ) { current = Src; } strlen = GetShortPathName( (LPCTSTR) finalName, (LPTSTR) Dest, (DWORD) sizeof(Dest)); if ( strlen > 0 ) { final = Dest; } #endif /* !XP_WIN16 */ /* NOTE: use OEM filenames! Even though it looks like a Windows * .INI file, WININIT.INI is processed under DOS */ AnsiToOem( final, final ); AnsiToOem( current, current ); if ( WritePrivateProfileString( "Rename", final, current, "WININIT.INI" ) ) err = NO_ERROR; #if !defined(XP_WIN16) } #endif /* XP_WIN16 */ #else /* XP_OS2 */ ULONG usRetCode; usRetCode = WriteLockFileRenameEntry(final,current,"SOFTUPDT.LST"); if (usRetCode == NO_ERROR) { err = NO_ERROR; } #endif /* XP_OS2 */ if ( NO_ERROR == err ) err = SU_REBOOT_NEEDED; } } // (force || fe_FileNeedsUpdate( currentName, finalName )) else { // file to be installed was older than existing file--clean up err = XP_FileRemove( CurrentFname, ctype ); } cleanup: XP_FREEIF(finalName); XP_FREEIF(currentName); return err; } void PR_CALLBACK SU_AlertCallback(void * dummy) { MWContext * cx; cx = XP_FindSomeContext(); FE_Alert(cx, XP_GetString(SU_NEED_TO_REBOOT)); } XP_Bool fe_FileNeedsUpdate(char *sourceFile, char *targetFile) { XP_Bool needUpdate; #if defined XP_OS2 needUpdate = TRUE; //Do not have internal version control so //assume that files should be replaced as //in unix version #else DWORD targetInfoSize; DWORD sourceInfoSize; DWORD bogusHandle; void *sourceData; void *targetData; VS_FIXEDFILEINFO *sourceInfo; VS_FIXEDFILEINFO *targetInfo; UINT targIBSize; UINT srcIBSize; // always replace old file if it doesn't have version info targetInfoSize = GetFileVersionInfoSize( targetFile, &bogusHandle ); if (targetInfoSize == 0) return TRUE; // if new file doesn't have a version assume it's older than one that does sourceInfoSize = GetFileVersionInfoSize( sourceFile, &bogusHandle ); if (sourceInfoSize == 0) return FALSE; // both files have version info blocks; we must compare them needUpdate = TRUE; sourceData = XP_ALLOC(sourceInfoSize); targetData = XP_ALLOC(targetInfoSize); if ( (sourceData != NULL) && (targetData != NULL) && GetFileVersionInfo(targetFile, NULL, targetInfoSize, targetData) && GetFileVersionInfo(sourceFile, NULL, sourceInfoSize, sourceData) && VerQueryValue(targetData, "\\", (void**)&targetInfo, &targIBSize) && VerQueryValue(sourceData, "\\", (void**)&sourceInfo, &srcIBSize) ) { DWORD targetMajorVer = targetInfo->dwFileVersionMS; DWORD targetMinorVer = targetInfo->dwFileVersionLS; DWORD sourceMajorVer = sourceInfo->dwFileVersionMS; DWORD sourceMinorVer = sourceInfo->dwFileVersionLS; // don't replace a file with a newer version if (targetMajorVer > sourceMajorVer) { needUpdate = FALSE; } else if (targetMajorVer == sourceMajorVer && targetMinorVer > sourceMinorVer) { needUpdate = FALSE; } else needUpdate = TRUE; } XP_FREEIF(sourceData); XP_FREEIF(targetData); #endif //XP_OS2 return needUpdate; } #if !defined(WIN32) && !defined(XP_OS2) #define BUFLEN 1024 void FE_ScheduleRenameUtility(void) { int cmdlength; char *folder; char * buf = (char*)XP_ALLOC(BUFLEN); if ( buf != NULL ) { FE_GetProgramDirectory(buf, BUFLEN); XP_STRCAT(buf, "NSINIT.EXE,"); cmdlength = XP_STRLEN(buf); if (!GetProfileString("Windows","Run","",(buf+cmdlength), (BUFLEN-cmdlength))) { // no other RUN commands so don't need trailing comma buf[cmdlength-1] = '\0'; } WriteProfileString("Windows","Run",buf); XP_FREE(buf); } } // WIN16 only, Win32 is macro'd to usual XP_FileRemove() int su_FileRemove(char *fname, XP_FileType type) { HMODULE hinst; char *pFile; int err = NO_ERROR; // convert to native filename and see if it's currently running pFile = WH_FileName( fname, type ); if (pFile != NULL ) { // Don't try to delete if Windows says it's in use hinst = GetModuleHandle(pFile); if ( hinst != NULL ) { // GetModuleHandle() accepts the full path but matches only on the // root name. Use GetModuleFileName() to verify a true match char module[_MAX_PATH]; GetModuleFileName( hinst, module, _MAX_PATH ); if ( stricmp( pFile, module ) == 0 ) { // they match err = -1; } } XP_FREE(pFile); } if ( err == NO_ERROR ) { // wasn't in use (or couldn't convert to native filename) // try to remove the old way err = XP_FileRemove( fname, type ); } return err; } #endif /* !WIN32 */ #ifdef WIN32 BOOL WFE_IsMoveFileExBroken() { /* the NT option MOVEFILE_DELAY_UNTIL_REBOOT is broken on * Windows NT 3.51 Service Pack 4 and NT 4.0 before Service Pack 2 */ BOOL broken = FALSE; OSVERSIONINFO osinfo; // they *all* appear broken--better to have one way that works. return TRUE; osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osinfo) && osinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { if ( osinfo.dwMajorVersion == 3 && osinfo.dwMinorVersion == 51 ) { if ( 0 == stricmp(osinfo.szCSDVersion,"Service Pack 4")) { broken = TRUE; } } else if ( osinfo.dwMajorVersion == 4 ) { if (osinfo.szCSDVersion[0] == '\0' || (0 == stricmp(osinfo.szCSDVersion,"Service Pack 1"))) { broken = TRUE; } } } return broken; } #endif /* WIN32 */