1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Mozilla Communicator client code,
14
* released March 31, 1998.
16
* The Initial Developer of the Original Code is Netscape Communications
17
* Corporation. Portions created by Netscape are
18
* Copyright (C) 1998 Netscape Communications Corporation. All
22
* Daniel Veditz <dveditz@netscape.com>
23
* Douglas Turner <dougt@netscape.com>
27
#include "nsXPIDLString.h"
28
#include "nsInstall.h" // for error codes
30
#include "ScheduledTasks.h"
31
#include "InstallCleanupDefines.h"
33
#include "nsDirectoryService.h"
34
#include "nsDirectoryServiceDefs.h"
35
#include "nsAppDirectoryServiceDefs.h"
38
GetPersistentStringFromSpec(nsIFile* inSpec, nsACString &string)
42
nsCOMPtr<nsILocalFile> LocalFile = do_QueryInterface(inSpec, &rv);
44
if (NS_SUCCEEDED(rv)) {
45
rv = LocalFile->GetNativePath(string);
61
PRInt32 ReplaceWindowsSystemFile(nsIFile* currentSpec, nsIFile* finalSpec)
65
// Get OS version info
66
DWORD dwVersion = GetVersion();
69
nsCAutoString current;
71
finalSpec->GetNativePath(final);
72
currentSpec->GetNativePath(current);
74
// Get build numbers for Windows NT or Win32s
76
if (dwVersion > 0x80000000)
78
// Windows 95 or Win16
80
// Place an entry in the WININIT.INI file in the Windows directory
81
// to delete finalName and rename currentName to be finalName at reboot
84
char Src[_MAX_PATH]; // 8.3 name
85
char Dest[_MAX_PATH]; // 8.3 name
87
strlen = GetShortPathName( (LPCTSTR)current.get(), (LPTSTR)Src, (DWORD)sizeof(Src) );
93
strlen = GetShortPathName( (LPCTSTR) final.get(), (LPTSTR) Dest, (DWORD) sizeof(Dest));
99
// NOTE: use OEM filenames! Even though it looks like a Windows
100
// .INI file, WININIT.INI is processed under DOS
102
AnsiToOem( final.BeginWriting(), final.BeginWriting() );
103
AnsiToOem( current.BeginWriting(), current.BeginWriting() );
105
if ( WritePrivateProfileString( "Rename", final.get(), current.get(), "WININIT.INI" ) )
111
if ( MoveFileEx(final.get(), current.get(), MOVEFILE_DELAY_UNTIL_REBOOT) )
119
nsresult GetRegFilePath(nsACString ®FilePath)
122
nsCOMPtr<nsILocalFile> iFileUtilityPath;
123
//Get the program directory
124
nsCOMPtr<nsIProperties> directoryService =
125
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
129
if (nsSoftwareUpdate::GetProgramDirectory()) // In the stub installer
131
nsCOMPtr<nsIFile> tmp;
132
rv = nsSoftwareUpdate::GetProgramDirectory()->Clone(getter_AddRefs(tmp));
134
if (NS_FAILED(rv) || !tmp)
138
tmp->AppendNative(ESSENTIAL_FILES);
140
iFileUtilityPath = do_QueryInterface(tmp);
144
rv = directoryService->Get(NS_APP_INSTALL_CLEANUP_DIR,
146
getter_AddRefs(iFileUtilityPath));
148
if (NS_FAILED(rv) || !iFileUtilityPath)
151
iFileUtilityPath->AppendNative(CLEANUP_REGISTRY);
153
//Yes, we know using GetPath is buggy on the Mac.
154
//When libreg is fixed to accept nsIFiles we'll change this to match.
155
return iFileUtilityPath->GetNativePath(regFilePath);
159
PRInt32 DeleteFileNowOrSchedule(nsIFile* filename)
162
PRInt32 result = nsInstall::SUCCESS;
164
filename->Remove(PR_FALSE);
165
filename->Exists(&flagExists);
167
result = ScheduleFileForDeletion(filename);
172
PRInt32 ScheduleFileForDeletion(nsIFile *filename)
174
// could not delete, schedule it for later
179
PRInt32 result = nsInstall::UNEXPECTED_ERROR;
182
GetRegFilePath(path);
183
err = NR_RegOpen(NS_CONST_CAST(char*, path.get()), ®);
185
if ( err == REGERR_OK )
187
err = NR_RegAddKey(reg,ROOTKEY_PRIVATE,REG_DELETE_LIST_KEY,&newkey);
188
if ( err == REGERR_OK )
192
err = NR_RegGetUniqueName( reg, valname, sizeof(valname) );
193
if ( err == REGERR_OK )
195
nsCAutoString nameowner;
196
nsresult rv = GetPersistentStringFromSpec(filename, nameowner);
197
if ( NS_SUCCEEDED(rv) && !nameowner.IsEmpty() )
199
const char *fnamestr = nameowner.get();
200
err = NR_RegSetEntry( reg, newkey, valname,
203
strlen(fnamestr)+sizeof('\0'));
205
if ( err == REGERR_OK )
207
result = nsInstall::REBOOT_NEEDED;
208
nsSoftwareUpdate::NeedCleanup();
223
PRInt32 ReplaceFileNow(nsIFile* aReplacementFile, nsIFile* aDoomedFile )
225
PRBool flagExists, flagRenamedDoomedFileExists, flagIsEqual;
226
nsCOMPtr<nsIFile> replacementFile;
229
// make a clone of aReplacement file so we touch affect callers
230
aReplacementFile->Clone(getter_AddRefs(replacementFile));
232
// replacement file must exist, doomed file doesn't have to
233
replacementFile->Exists(&flagExists);
235
return nsInstall::DOES_NOT_EXIST;
237
// don't have to do anything if the files are the same
238
replacementFile->Equals(aDoomedFile, &flagIsEqual);
240
return nsInstall::SUCCESS;
243
PRInt32 result = nsInstall::ACCESS_DENIED;
245
// first try to rename the doomed file out of the way (if it exists)
246
nsCOMPtr<nsIFile> renamedDoomedFile;
247
nsCOMPtr<nsILocalFile> tmpLocalFile;
249
aDoomedFile->Clone(getter_AddRefs(renamedDoomedFile));
250
renamedDoomedFile->Exists(&flagRenamedDoomedFileExists);
251
if ( flagRenamedDoomedFileExists )
254
// If we clone an nsIFile, and move the clone, the FSRef of the *original*
255
// file is not what you would expect - it points to the moved file. This
256
// is despite the fact that the two FSRefs are independent objects. Until
257
// the OS X file impl is changed to not use FSRefs, need to do this (see
259
nsCOMPtr<nsILocalFile> doomedFileLocal = do_QueryInterface(aDoomedFile);
260
nsCAutoString doomedFilePath;
261
rv = doomedFileLocal->GetNativePath(doomedFilePath);
263
return nsInstall::UNEXPECTED_ERROR;
266
tmpLocalFile = do_QueryInterface(renamedDoomedFile, &rv); // Convert to an nsILocalFile
268
//get the doomedLeafname so we can convert its extension to .old
269
nsAutoString doomedLeafname;
270
nsCAutoString uniqueLeafName;
271
tmpLocalFile->GetLeafName(doomedLeafname);
273
// do not RFind on the native charset! UTF8 or Unicode are OK
274
PRInt32 extpos = doomedLeafname.RFindChar('.');
277
// We found the extension;
278
doomedLeafname.Truncate(extpos + 1); //strip off the old extension
280
doomedLeafname.Append(NS_LITERAL_STRING("old"));
282
//Now reset the doomedLeafname
283
tmpLocalFile->SetLeafName(doomedLeafname);
284
tmpLocalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
285
tmpLocalFile->GetNativeLeafName(uniqueLeafName);//this is the new "unique" doomedLeafname
287
rv = aDoomedFile->Clone(getter_AddRefs(renamedDoomedFile));// Reset renamedDoomed file so aDoomedfile
288
// isn't changed during the MoveTo call
290
result = nsInstall::UNEXPECTED_ERROR;
293
rv = renamedDoomedFile->MoveToNative(nsnull, uniqueLeafName);
296
// MoveToNative() failing is OK. It simply means that the file
297
// was locked in memory and needs to be replaced on browser
298
// shutdown or system reboot.
300
// Since renamedDoomedFile->MoveToNative() failed, it created a
301
// 0 byte '-old' file that needs to be cleaned up.
302
tmpLocalFile->Remove(PR_FALSE);
306
// The implementation of MoveToNative() on some platforms (osx and win32) resets
307
// the object to the 'moved to' filename. This is incorrect behavior. This
308
// implementation will be fixed in the future. We need to take into account that
309
// fix by setting renamedDoomedFile to the filename that it was moved to above.
312
// renamedDoomedFile needs to be reset because it's used later on in this
314
rv = renamedDoomedFile->SetNativeLeafName(uniqueLeafName);
316
result = nsInstall::UNEXPECTED_ERROR;
321
rv = doomedFileLocal->InitWithNativePath(doomedFilePath);
323
result = nsInstall::UNEXPECTED_ERROR;
326
if (result == nsInstall::UNEXPECTED_ERROR)
331
// if aDoomedFile is still in the way, give up and return result.
332
aDoomedFile->Exists(&flagExists);
336
nsCOMPtr<nsIFile> parentofDoomedFile;
337
nsCAutoString doomedLeafname;
339
rv = aDoomedFile->GetParent(getter_AddRefs(parentofDoomedFile));
340
if ( NS_SUCCEEDED(rv) )
341
rv = aDoomedFile->GetNativeLeafName(doomedLeafname);
342
if ( NS_SUCCEEDED(rv) )
344
rv = replacementFile->MoveToNative(parentofDoomedFile, doomedLeafname);
345
// The implementation of MoveToNative() on some platforms (osx and win32) resets
346
// the object to the 'moved to' filename. This is incorrect behavior. This
347
// implementation will be fixed in the future. We need to take into account that
348
// fix by setting replacementFile to the filename that it was moved to above.
351
// However, since replacementFile is a clone of aReplacementFile and is also
352
// not used beyond here, there's no need to set the path+leafname to what
353
// it was MoveToNative()'ed to.
356
if (NS_SUCCEEDED(rv))
358
if (flagRenamedDoomedFileExists)
360
// we replaced the old file OK, now we have to
361
// get rid of it if it was renamed out of the way
362
result = DeleteFileNowOrSchedule( renamedDoomedFile );
367
// couldn't rename file, try to put old file back
368
renamedDoomedFile->MoveToNative(nsnull, doomedLeafname);
369
// No need to reset remanedDoomedFile after a MoveToNative() call
370
// because renamedDoomedFile is not used beyond here.
380
PRInt32 ReplaceFileNowOrSchedule(nsIFile* aReplacementFile, nsIFile* aDoomedFile, PRInt32 aMode)
382
PRInt32 result = ReplaceFileNow( aReplacementFile, aDoomedFile );
384
if ( result == nsInstall::ACCESS_DENIED )
386
// if we couldn't replace the file schedule it for later
388
if ( (aMode & WIN_SYSTEM_FILE) &&
389
(ReplaceWindowsSystemFile(aReplacementFile, aDoomedFile) == 0) )
390
return nsInstall::REBOOT_NEEDED;
398
nsCAutoString regFilePath;
399
GetRegFilePath(regFilePath);
400
if ( REGERR_OK == NR_RegOpen(NS_CONST_CAST(char*, regFilePath.get()), ®) )
402
err = NR_RegAddKey( reg, ROOTKEY_PRIVATE, REG_REPLACE_LIST_KEY, &listkey );
403
if ( err == REGERR_OK )
408
err = NR_RegGetUniqueName( reg, valname, sizeof(valname) );
409
if ( err == REGERR_OK )
411
err = NR_RegAddKey( reg, listkey, valname, &filekey );
412
if ( REGERR_OK == err )
414
nsCAutoString srcowner;
415
nsCAutoString destowner;
416
nsresult rv = GetPersistentStringFromSpec(aReplacementFile, srcowner);
417
nsresult rv2 = GetPersistentStringFromSpec(aDoomedFile, destowner);
418
if ( NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2) )
420
const char *fsrc = srcowner.get();
421
const char *fdest = destowner.get();
422
err = NR_RegSetEntry( reg, filekey,
426
strlen(fsrc)+sizeof('\0'));
428
err2 = NR_RegSetEntry(reg, filekey,
429
REG_REPLACE_DESTFILE,
432
strlen(fdest)+sizeof('\0'));
434
if ( err == REGERR_OK && err2 == REGERR_OK )
436
result = nsInstall::REBOOT_NEEDED;
437
nsSoftwareUpdate::NeedCleanup();
440
NR_RegDeleteKey( reg, listkey, valname );
455
//-----------------------------------------------------------------------------
457
// STARTUP: DO SCHEDULED ACTIONS
459
//-----------------------------------------------------------------------------
461
void DeleteScheduledFiles(HREG);
462
void ReplaceScheduledFiles(HREG);
464
void PerformScheduledTasks(HREG reg)
466
DeleteScheduledFiles( reg );
467
ReplaceScheduledFiles( reg );
472
void DeleteScheduledFiles( HREG reg )
479
// perform scheduled file deletions
480
if (REGERR_OK == NR_RegGetKey(reg,ROOTKEY_PRIVATE,REG_DELETE_LIST_KEY,&key))
482
// the delete key exists, so we loop through its children
483
// and try to delete all the listed files
485
char namebuf[MAXREGNAMELEN];
486
char valbuf[MAXREGPATHLEN];
488
nsCOMPtr<nsIFile> doomedFile;
489
nsCOMPtr<nsILocalFile> spec;
491
if (NS_SUCCEEDED(rv))
493
while (REGERR_OK == NR_RegEnumEntries( reg, key, &state, namebuf,
494
sizeof(namebuf), 0 ) )
496
uint32 bufsize = sizeof(valbuf); // gets changed, must reset
497
err = NR_RegGetEntry( reg, key, namebuf, valbuf, &bufsize );
498
if ( err == REGERR_OK )
500
// no need to check return value of
501
// SetPersistentDescriptorString, it's always NS_OK
502
//spec->SetPersistentDescriptorString(valbuf);
503
//nsIFileXXX: Do we still need this instead of InitWithPath?
504
NS_NewNativeLocalFile(nsDependentCString(valbuf), PR_TRUE, getter_AddRefs(spec));
505
spec->Clone(getter_AddRefs(doomedFile));
506
if (NS_SUCCEEDED(rv))
509
doomedFile->Remove(PR_FALSE);
510
doomedFile->Exists(&flagExists);
513
// deletion successful, don't have to retry
514
NR_RegDeleteEntry( reg, key, namebuf );
520
// delete list node if empty
522
err = NR_RegEnumEntries(reg, key, &state, namebuf, sizeof(namebuf), 0);
523
if ( err == REGERR_NOMORE )
525
NR_RegDeleteKey(reg, ROOTKEY_PRIVATE, REG_DELETE_LIST_KEY);
533
void ReplaceScheduledFiles( HREG reg )
537
// replace files if any listed
538
if (REGERR_OK == NR_RegGetKey(reg,ROOTKEY_PRIVATE,REG_REPLACE_LIST_KEY,&key))
540
char keyname[MAXREGNAMELEN];
541
char doomedFile[MAXREGPATHLEN];
542
char srcFile[MAXREGPATHLEN];
544
nsCOMPtr<nsIFile> doomedSpec;
545
nsCOMPtr<nsIFile> srcSpec;
546
nsCOMPtr<nsILocalFile> src;
547
nsCOMPtr<nsILocalFile> dest;
552
while (REGERR_OK == NR_RegEnumSubkeys( reg, key, &state,
553
keyname, sizeof(keyname), REGENUM_CHILDREN))
555
bufsize = sizeof(srcFile);
556
REGERR err1 = NR_RegGetEntry( reg, (RKEY)state,
557
REG_REPLACE_SRCFILE, srcFile, &bufsize);
559
bufsize = sizeof(doomedFile);
560
REGERR err2 = NR_RegGetEntry( reg, (RKEY)state,
561
REG_REPLACE_DESTFILE, doomedFile, &bufsize);
563
if ( err1 == REGERR_OK && err2 == REGERR_OK )
565
rv1 = NS_NewNativeLocalFile(nsDependentCString(srcFile), PR_TRUE, getter_AddRefs(src));
566
rv1 = src->Clone(getter_AddRefs(srcSpec));
568
rv2 = NS_NewNativeLocalFile(nsDependentCString(doomedFile), PR_TRUE, getter_AddRefs(dest));
569
rv2 = dest->Clone(getter_AddRefs(doomedSpec));
571
if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2))
573
// finally now try to do the replace
574
PRInt32 result = ReplaceFileNow( srcSpec, doomedSpec );
576
if ( result == nsInstall::DOES_NOT_EXIST ||
577
result == nsInstall::SUCCESS )
580
NR_RegDeleteKey( reg, key, keyname );
587
// delete list node if empty
589
if (REGERR_NOMORE == NR_RegEnumSubkeys( reg, key, &state, keyname,
590
sizeof(keyname), REGENUM_CHILDREN ))
592
NR_RegDeleteKey(reg, ROOTKEY_PRIVATE, REG_REPLACE_LIST_KEY);