Back to index

lightning-sunbird  0.9+nobinonly
nsInstallPatch.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "zlib.h"
00039 #include "nsCRT.h"
00040 #include "prmem.h"
00041 #include "nsXPIDLString.h"
00042 #include "nsInstall.h"
00043 #include "nsInstallPatch.h"
00044 #include "nsInstallResources.h"
00045 #include "nsIDOMInstallVersion.h"
00046 #include "nsILocalFile.h"
00047 #include "nsNativeCharsetUtils.h"
00048 
00049 #include "gdiff.h"
00050 
00051 #include "VerReg.h"
00052 #include "ScheduledTasks.h"
00053 #include "plstr.h"
00054 #include "prlog.h"
00055 
00056 #ifdef XP_MAC
00057 #include "PatchableAppleSingle.h"
00058 #include "nsILocalFileMac.h"
00059 #endif
00060 
00061 #ifdef XP_WIN
00062 #include <windows.h>
00063 #endif
00064 
00065 #define BUFSIZE     32768
00066 #define OPSIZE      1
00067 #define MAXCMDSIZE  12
00068 #define SRCFILE     0
00069 #define OUTFILE     1
00070 
00071 #define getshort(s) (uint16)( ((uchar)*(s) << 8) + ((uchar)*((s)+1)) )
00072 
00073 #define getlong(s)  \
00074             (uint32)( ((uchar)*(s) << 24) + ((uchar)*((s)+1) << 16 ) + \
00075                       ((uchar)*((s)+2) << 8) + ((uchar)*((s)+3)) )
00076 
00077 
00078 
00079 static int32   gdiff_parseHeader( pDIFFDATA dd );
00080 static int32   gdiff_validateFile( pDIFFDATA dd, int file );
00081 static int32   gdiff_valCRC32( pDIFFDATA dd, PRFileDesc* fh, uint32 chksum );
00082 static int32   gdiff_ApplyPatch( pDIFFDATA dd );
00083 static int32   gdiff_getdiff( pDIFFDATA dd, uchar *buffer, uint32 length );
00084 static int32   gdiff_add( pDIFFDATA dd, uint32 count );
00085 static int32   gdiff_copy( pDIFFDATA dd, uint32 position, uint32 count );
00086 static int32   gdiff_validateFile( pDIFFDATA dd, int file );
00087 #ifdef WIN32
00088 static PRBool  su_unbind(char* oldsrc, char* newsrc);
00089 #endif
00090 
00091 MOZ_DECL_CTOR_COUNTER(nsInstallPatch)
00092 
00093 nsInstallPatch::nsInstallPatch( nsInstall* inInstall,
00094                                 const nsString& inVRName,
00095                                 const nsString& inVInfo,
00096                                 const nsString& inJarLocation,
00097                                 PRInt32 *error)
00098 
00099 : nsInstallObject(inInstall)
00100 {
00101     MOZ_COUNT_CTOR(nsInstallPatch);
00102 
00103     char tempTargetFile[MAXREGPATHLEN];
00104 
00105     PRInt32 err = VR_GetPath( NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(inVRName).get()),
00106                               sizeof(tempTargetFile), tempTargetFile );
00107     
00108     if (err != REGERR_OK)
00109     {
00110         *error = nsInstall::NO_SUCH_COMPONENT;
00111         return;
00112     }
00113        
00114     nsCOMPtr<nsILocalFile> tmp;
00115     NS_NewNativeLocalFile(nsDependentCString(tempTargetFile), PR_TRUE, getter_AddRefs(tmp));
00116 
00117     mPatchFile      =   nsnull;
00118     mTargetFile     =   nsnull;
00119     mPatchedFile    =   nsnull;
00120     mRegistryName   =   new nsString(inVRName);
00121     mJarLocation    =   new nsString(inJarLocation);
00122     mVersionInfo    =   new nsInstallVersion();
00123     tmp->Clone(getter_AddRefs(mTargetFile));
00124     
00125     if (mRegistryName == nsnull ||
00126         mJarLocation  == nsnull ||
00127         mTargetFile   == nsnull ||
00128         mVersionInfo  == nsnull )
00129     {
00130         *error = nsInstall::OUT_OF_MEMORY;
00131         return;
00132     }
00133 
00134     mVersionInfo->Init(inVInfo);
00135 }
00136 
00137 
00138 nsInstallPatch::nsInstallPatch( nsInstall* inInstall,
00139                                 const nsString& inVRName,
00140                                 const nsString& inVInfo,
00141                                 const nsString& inJarLocation,
00142                                 nsInstallFolder* folderSpec,
00143                                 const nsString& inPartialPath,
00144                                 PRInt32 *error)
00145 
00146 : nsInstallObject(inInstall)
00147 {
00148     MOZ_COUNT_CTOR(nsInstallPatch);
00149 
00150     if ((inInstall == nsnull) || (inVRName.IsEmpty()) || (inJarLocation.IsEmpty())) 
00151     {
00152         *error = nsInstall::INVALID_ARGUMENTS;
00153         return;
00154     }
00155     
00156     nsCOMPtr<nsIFile> tmp = folderSpec->GetFileSpec();
00157     if (!tmp)
00158     {
00159         *error = nsInstall::INVALID_ARGUMENTS;
00160         return;
00161     }
00162 
00163     mPatchFile      =   nsnull;
00164     mTargetFile     =   nsnull;
00165     mPatchedFile    =   nsnull;
00166     mRegistryName   =   new nsString(inVRName);
00167     mJarLocation    =   new nsString(inJarLocation);
00168     mVersionInfo    =   new nsInstallVersion();
00169     tmp->Clone(getter_AddRefs(mTargetFile));
00170 
00171     if (mRegistryName == nsnull ||
00172         mJarLocation  == nsnull ||
00173         mTargetFile   == nsnull ||
00174         mVersionInfo  == nsnull )
00175     {
00176         *error = nsInstall::OUT_OF_MEMORY;
00177         return;
00178     }
00179     
00180     mVersionInfo->Init(inVInfo);
00181     
00182     if(! inPartialPath.IsEmpty())
00183         mTargetFile->Append(inPartialPath);
00184 }
00185 
00186 nsInstallPatch::~nsInstallPatch()
00187 {
00188     if (mVersionInfo)
00189         delete mVersionInfo;
00190 
00191     //if (mTargetFile)
00192     //    delete mTargetFile;
00193 
00194     if (mJarLocation)
00195         delete mJarLocation;
00196     
00197     if (mRegistryName)
00198         delete mRegistryName;
00199 
00200     //if (mPatchedFile)
00201     //    delete mPatchedFile;
00202     
00203     //if (mPatchFile)
00204     //    delete mPatchFile;
00205 
00206     MOZ_COUNT_DTOR(nsInstallPatch);
00207 }
00208 
00209 
00210 PRInt32 nsInstallPatch::Prepare()
00211 {
00212     PRInt32 err;
00213     PRBool deleteOldSrc, flagExists, flagIsFile;
00214     
00215     if (mTargetFile == nsnull)
00216         return  nsInstall::INVALID_ARGUMENTS;
00217 
00218     mTargetFile->Exists(&flagExists);
00219     if (flagExists)
00220     {
00221         mTargetFile->IsFile(&flagIsFile);
00222         if (flagIsFile)
00223         {
00224             err = nsInstall::SUCCESS;
00225         }
00226         else
00227         {
00228             err = nsInstall::IS_DIRECTORY;
00229         }
00230     }
00231     else
00232     {
00233         err = nsInstall::DOES_NOT_EXIST;
00234     }
00235 
00236     if (err != nsInstall::SUCCESS)
00237     {   
00238         return err;
00239     }
00240 
00241     err =  mInstall->ExtractFileFromJar(*mJarLocation, mTargetFile, getter_AddRefs(mPatchFile));
00242    
00243     
00244     nsCOMPtr<nsIFile> fileName = nsnull;
00245     //nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );//nsIFileXXX: nsFilePath?
00246     nsVoidKey ikey( HashFilePath( mTargetFile ));
00247 
00248     mInstall->GetPatch(&ikey, getter_AddRefs(fileName));
00249 
00250     if (fileName != nsnull) 
00251     {
00252         deleteOldSrc = PR_TRUE;
00253     } 
00254     else 
00255     {
00256         fileName     = mTargetFile;
00257         deleteOldSrc = PR_FALSE;
00258     }
00259 
00260     err = NativePatch(  fileName,           // the file to patch
00261                         mPatchFile,         // the patch that was extracted from the jarfile
00262                         getter_AddRefs(mPatchedFile));     // the new patched file
00263     
00264     // clean up extracted diff data file
00265     mPatchFile->Exists(&flagExists);
00266     if ( (mPatchFile != nsnull) && (flagExists) )
00267     {
00268         mPatchFile->Remove(PR_FALSE);
00269     }
00270 
00271 
00272     if (err != nsInstall::SUCCESS)
00273     {   
00274         // clean up tmp patched file since patching failed
00275       mPatchFile->Exists(&flagExists);
00276                 if ((mPatchedFile != nsnull) && (flagExists))
00277       {
00278                          mPatchedFile->Remove(PR_FALSE);
00279       }
00280               return err;
00281     }
00282 
00283     PR_ASSERT(mPatchedFile != nsnull);
00284     mInstall->AddPatch(&ikey, mPatchedFile );
00285 
00286     if ( deleteOldSrc ) 
00287     {
00288     DeleteFileNowOrSchedule(fileName );
00289     }
00290   
00291     return err;
00292 }
00293 
00294 PRInt32 nsInstallPatch::Complete()
00295 {  
00296     PRBool flagEquals;
00297 
00298     if ((mInstall == nsnull) || (mVersionInfo == nsnull) || (mPatchedFile == nsnull) || (mTargetFile == nsnull)) 
00299     {
00300         return nsInstall::INVALID_ARGUMENTS;
00301     }
00302     
00303     PRInt32 err = nsInstall::SUCCESS;
00304 
00305     nsCOMPtr<nsIFile> fileName = nsnull;
00306     //nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) )  );//nsIFileXXX: nsFilePath?
00307     nsVoidKey ikey( HashFilePath( mTargetFile ));
00308     
00309     mInstall->GetPatch(&ikey, getter_AddRefs(fileName));
00310     
00311     if (fileName == nsnull)
00312     {
00313         err = nsInstall::UNEXPECTED_ERROR;
00314     }
00315     else
00316     {
00317       fileName->Equals(mPatchedFile, &flagEquals);
00318       if (flagEquals) 
00319       {
00320         // the patch has not been superceded--do final replacement
00321         err = ReplaceFileNowOrSchedule( mPatchedFile, mTargetFile, 0);
00322         if ( 0 == err || nsInstall::REBOOT_NEEDED == err ) 
00323         {
00324             nsString tempVersionString;
00325             mVersionInfo->ToString(tempVersionString);
00326             
00327             nsCAutoString tempPath;
00328             mTargetFile->GetNativePath(tempPath);
00329 
00330             // DO NOT propogate version registry errors, it will abort 
00331             // FinalizeInstall() leaving things hosed. These piddly errors
00332             // aren't worth that.
00333             VR_Install( NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(*mRegistryName).get()),
00334                         NS_CONST_CAST(char *, tempPath.get()),
00335                         NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(tempVersionString).get()),
00336                         PR_FALSE );
00337         }
00338         else
00339         {
00340             err = nsInstall::UNEXPECTED_ERROR;
00341         }
00342       }
00343       else
00344       {
00345         // nothing -- old intermediate patched file was
00346         // deleted by a superceding patch
00347       }
00348     }
00349     return err;
00350 }
00351 
00352 void nsInstallPatch::Abort()
00353 {
00354     PRBool flagEquals;
00355     nsCOMPtr<nsIFile> fileName = nsnull;
00356     //nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) ); //nsIFileXXX: nsFilePath?
00357     nsVoidKey ikey( HashFilePath( mTargetFile ));
00358 
00359     mInstall->GetPatch(&ikey, getter_AddRefs(fileName));
00360 
00361     fileName->Equals(mPatchedFile, &flagEquals);
00362     if (fileName != nsnull && (flagEquals) )
00363     {
00364         DeleteFileNowOrSchedule( mPatchedFile );
00365     }
00366 }
00367 
00368 char* nsInstallPatch::toString()
00369 {
00370          char* buffer = new char[1024];
00371     char* rsrcVal = nsnull;
00372 
00373     if (buffer == nsnull || !mInstall)
00374         return buffer;
00375 
00376     if (mTargetFile != nsnull)
00377     {
00378         rsrcVal = mInstall->GetResourcedString(NS_LITERAL_STRING("Patch"));
00379 
00380         if (rsrcVal)
00381         {
00382             nsCAutoString temp;
00383             mTargetFile->GetNativePath(temp);
00384             sprintf( buffer, rsrcVal, temp.get()); 
00385             nsCRT::free(rsrcVal);
00386         }
00387     }
00388 
00389        return buffer;
00390 }
00391 
00392 
00393 PRBool
00394 nsInstallPatch::CanUninstall()
00395 {
00396     return PR_FALSE;
00397 }
00398 
00399 PRBool
00400 nsInstallPatch::RegisterPackageNode()
00401 {
00402     return PR_TRUE;
00403 }
00404 
00405 
00406 PRInt32
00407 nsInstallPatch::NativePatch(nsIFile *sourceFile, nsIFile *patchFile, nsIFile **newFile)  //nsIFileXXX: changed & to *
00408 {
00409 
00410        PRBool flagExists;
00411   nsresult rv;
00412   DIFFDATA      *dd;
00413        PRInt32                status                = GDIFF_ERR_MEM;
00414        char              *tmpurl             = NULL;
00415        //nsFileSpec  *outFileSpec = new nsFileSpec; 
00416   //nsFileSpec  *tempSrcFile = new nsFileSpec;   // TODO: do you need to free?
00417   nsCOMPtr<nsIFile> outFileSpec;
00418   nsCOMPtr<nsIFile> tempSrcFile;
00419   nsCOMPtr<nsILocalFile> uniqueSrcFile;
00420   nsCOMPtr<nsILocalFile> patchFileLocal = do_QueryInterface(patchFile, &rv);
00421   
00422   nsCAutoString realfile;
00423   sourceFile->GetNativePath(realfile);
00424 
00425   sourceFile->Clone(getter_AddRefs(outFileSpec));
00426 
00427        dd = (DIFFDATA *)PR_Calloc( 1, sizeof(DIFFDATA));
00428        if (dd != NULL)
00429        {
00430               dd->databuf = (uchar*)PR_Malloc(BUFSIZE);
00431               if (dd->databuf == NULL) 
00432               {
00433                      status = GDIFF_ERR_MEM;
00434                      goto cleanup;
00435               }
00436 
00437 
00438               dd->bufsize = BUFSIZE;
00439 
00440               // validate patch header & check for special instructions
00441     // we're just reading, the 0400 is an annotation.
00442     patchFileLocal->OpenNSPRFileDesc(PR_RDONLY, 0400, &dd->fDiff);
00443 
00444               if (dd->fDiff != NULL)
00445               {
00446                      status = gdiff_parseHeader(dd);
00447               } 
00448     else 
00449     {
00450                      status = GDIFF_ERR_ACCESS;
00451               }
00452 
00453 
00454     // in case we need to unbind Win32 images OR encode Mac file
00455     if (( dd->bWin32BoundImage || dd->bMacAppleSingle) && (status == GDIFF_OK ))
00456     {
00457         // make an unique tmp file  (FILENAME-src.EXT)
00458         nsAutoString leafName;
00459         rv = sourceFile->GetLeafName(leafName);
00460 
00461         NS_NAMED_LITERAL_STRING(tmpName, "-src");
00462 
00463         PRInt32 i;
00464         if ((i = leafName.RFindChar('.')) > 0)
00465         {
00466             // build the temp filename for which to unbind the file to.
00467             // eg: if the filename is bind.dll, the temp src filename would
00468             //     be bind-src.dll
00469             nsAutoString ext;
00470             nsAutoString fName;
00471             // get the extension
00472             leafName.Right(ext, (leafName.Length() - i) );
00473             // get the filename - extension
00474             leafName.Left(fName, (leafName.Length() - (leafName.Length() - i)));
00475             // build the temp filename with '-src' at the end of filename,
00476             // but before extension
00477             leafName.Assign(fName);
00478             leafName.Append(tmpName);
00479             leafName.Append(ext);
00480         } else {
00481             // no extension found, just append '-src' to the end of filename
00482             leafName.Append(tmpName);
00483         }
00484         
00485     
00486         rv = sourceFile->Clone(getter_AddRefs(tempSrcFile));  //Clone the sourceFile
00487         tempSrcFile->SetLeafName(leafName); //Append the new leafname
00488         uniqueSrcFile = do_QueryInterface(tempSrcFile, &rv);
00489         uniqueSrcFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
00490 
00491 #ifdef WIN32
00492         // unbind Win32 images
00493 
00494         nsCAutoString unboundFile;
00495         uniqueSrcFile->GetNativePath(unboundFile);
00496 
00497         if (su_unbind(NS_CONST_CAST(char*, realfile.get()), NS_CONST_CAST(char*,unboundFile.get())))  //
00498         {
00499             // un-binding worked, save the tmp name for later
00500             uniqueSrcFile->GetNativePath(realfile);
00501         }
00502         else
00503         {
00504             status = GDIFF_ERR_MEM;
00505         }
00506         unboundFile.Truncate();
00507 #endif
00508 #ifdef XP_MAC
00509    // Encode src file, and put into temp file
00510         FSSpec sourceSpec, tempSpec;
00511         nsCOMPtr<nsILocalFileMac> tempSourceFile;
00512         tempSourceFile = do_QueryInterface(sourceFile, &rv);
00513         tempSourceFile->GetFSSpec(&sourceSpec);
00514     
00515         status = PAS_EncodeFile(&sourceSpec, &tempSpec);   
00516 
00517         if (status == noErr)
00518         {
00519             // set
00520             tempSrcFile->GetNativePath(realfile);
00521         }
00522 #endif
00523     }
00524 
00525     if (status != NS_OK)
00526     goto cleanup;
00527 
00528     // make a unique file at the same location of our source file  (FILENAME-ptch.EXT)
00529     NS_NAMED_LITERAL_STRING(patchFileName, "-ptch");
00530     nsAutoString newFileName;
00531     sourceFile->GetLeafName(newFileName);
00532 
00533     PRInt32 index;
00534     if ((index = newFileName.RFindChar('.')) > 0)
00535     {
00536         nsAutoString extention;
00537         nsAutoString fileName;
00538         newFileName.Right(extention, (newFileName.Length() - index) );        
00539         newFileName.Left(fileName, (newFileName.Length() - (newFileName.Length() - index)));
00540         newFileName = fileName + patchFileName + extention;
00541 
00542     }
00543     else
00544     {
00545         newFileName += patchFileName;
00546     }
00547 
00548 
00549     outFileSpec->SetLeafName(newFileName);  //Set new leafname
00550     nsCOMPtr<nsILocalFile> outFileLocal = do_QueryInterface(outFileSpec, &rv); 
00551     outFileLocal->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
00552 
00553     // apply patch to the source file
00554     //dd->fSrc = PR_Open ( realfile, PR_RDONLY, 0666);
00555     //dd->fOut = PR_Open ( outFile, PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0666);
00556     nsCOMPtr<nsILocalFile> realFileLocal = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);;
00557     realFileLocal->InitWithNativePath(realfile);
00558 
00559     // it's ok for people in the group to modify these files later, but it wouldn't be good for just anyone.
00560     realFileLocal->OpenNSPRFileDesc(PR_RDONLY, 0664, &dd->fSrc);
00561     outFileLocal->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0664, &dd->fOut);
00562 
00563     if (dd->fSrc != NULL && dd->fOut != NULL)
00564     {
00565         status = gdiff_validateFile (dd, SRCFILE);
00566 
00567         // specify why diff failed
00568         if (status == GDIFF_ERR_CHECKSUM)
00569             status = GDIFF_ERR_CHECKSUM_TARGET;
00570 
00571         if (status == GDIFF_OK)
00572             status = gdiff_ApplyPatch(dd);
00573 
00574         if (status == GDIFF_OK)
00575             status = gdiff_validateFile (dd, OUTFILE);
00576 
00577         if (status == GDIFF_ERR_CHECKSUM)
00578             status = GDIFF_ERR_CHECKSUM_RESULT;
00579 
00580         rv = outFileSpec->Clone(newFile);
00581     } 
00582     else 
00583     {
00584         status = GDIFF_ERR_ACCESS;
00585     }
00586   }
00587 
00588 
00589 
00590 #ifdef XP_MAC
00591   if ( dd->bMacAppleSingle && status == GDIFF_OK ) 
00592  {
00593         // create another file, so that we can decode somewhere
00594         //nsFileSpec anotherName = *outFileSpec;
00595         nsCOMPtr<nsILocalFile> anotherName;
00596         nsCOMPtr<nsIFile> bsTemp;
00597         
00598         outFileSpec->Clone(getter_AddRefs(bsTemp));   //Clone because we'll be changing the name
00599         anotherName = do_QueryInterface(bsTemp, &rv); //Set the old name
00600         anotherName->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
00601 
00602               // Close the out file so that we can read it            
00603               PR_Close( dd->fOut );
00604               dd->fOut = NULL;
00605               
00606               FSSpec outSpec;
00607               FSSpec anotherSpec;
00608               nsCOMPtr<nsILocalFileMac> outSpecMacSpecific;
00609               nsCOMPtr<nsILocalFileMac> anotherNameMacSpecific;
00610               
00611               anotherNameMacSpecific = do_QueryInterface(anotherName, &rv); //set value to nsILocalFileMac (sheesh)
00612               outSpecMacSpecific = do_QueryInterface(outFileSpec, &rv); //ditto 
00613               
00614               anotherNameMacSpecific->GetFSSpec(&anotherSpec);
00615               outSpecMacSpecific->GetFSSpec(&outSpec);
00616               
00617               outFileSpec->Exists(&flagExists);
00618               if ( flagExists )
00619               {
00620                   PRInt64 fileSize;
00621                   outFileSpec->GetFileSize(&fileSize);
00622               }
00623               
00624                      
00625         status =  PAS_DecodeFile(&outSpec, &anotherSpec);
00626               if (status != noErr)
00627               {
00628                      goto cleanup;
00629         }
00630               
00631         nsCOMPtr<nsIFile> parent;
00632         
00633         outFileSpec->GetParent(getter_AddRefs(parent));
00634         outFileSpec->Remove(PR_FALSE);
00635 
00636         nsAutoString leaf;
00637         anotherName->GetLeafName(leaf);
00638         anotherName->CopyTo(parent, leaf);
00639         anotherName->Clone(newFile);
00640        }
00641        
00642 #endif 
00643 
00644 
00645 cleanup:
00646     if ( dd != NULL ) 
00647     {
00648         if ( dd->fSrc != nsnull )
00649             PR_Close( dd->fSrc );
00650 
00651         if ( dd->fDiff != nsnull )
00652             PR_Close( dd->fDiff );
00653 
00654         if ( dd->fOut != nsnull )
00655             PR_Close( dd->fOut );
00656 
00657         if ( status != GDIFF_OK )
00658         //XP_FileRemove( outfile, outtype );
00659             newFile = NULL;
00660 
00661         PR_FREEIF( dd->databuf );
00662         PR_FREEIF( dd->oldChecksum );
00663         PR_FREEIF( dd->newChecksum );
00664         PR_DELETE(dd);
00665     }
00666 
00667     if ( tmpurl != NULL ) {
00668         //XP_FileRemove( tmpurl, xpURL );
00669         tmpurl = NULL;
00670         PR_DELETE( tmpurl );
00671     }
00672 
00673     if (tempSrcFile)
00674     {
00675         tempSrcFile->Exists(&flagExists);
00676         if (flagExists)
00677             tempSrcFile->Remove(PR_FALSE);
00678     }
00679 
00680     /* lets map any GDIFF error to nice SU errors */
00681 
00682     switch (status)
00683     {
00684         case GDIFF_OK:
00685                 break;
00686         case GDIFF_ERR_HEADER:
00687         case GDIFF_ERR_BADDIFF:
00688         case GDIFF_ERR_OPCODE:
00689         case GDIFF_ERR_CHKSUMTYPE:
00690             status = nsInstall::PATCH_BAD_DIFF;
00691             break;
00692         case GDIFF_ERR_CHECKSUM_TARGET:
00693             status = nsInstall::PATCH_BAD_CHECKSUM_TARGET;
00694             break;
00695         case GDIFF_ERR_CHECKSUM_RESULT:
00696             status = nsInstall::PATCH_BAD_CHECKSUM_RESULT;
00697             break;
00698         case GDIFF_ERR_OLDFILE:
00699         case GDIFF_ERR_ACCESS:
00700         case GDIFF_ERR_MEM:
00701         case GDIFF_ERR_UNKNOWN:
00702         default:
00703             status = nsInstall::UNEXPECTED_ERROR;
00704             break;
00705     }
00706 
00707     return status;
00708 
00709     // return -1;    //old return value
00710 }
00711 
00712 
00713 void* 
00714 nsInstallPatch::HashFilePath(nsIFile* aPath)
00715 {
00716     PRUint32 rv = 0;
00717 
00718     nsCAutoString cPath;
00719     aPath->GetNativePath(cPath);
00720     
00721     if (!cPath.IsEmpty())
00722     {
00723         char  ch;
00724         const char* pathIndex = cPath.get();
00725 
00726         while ((ch = *pathIndex++) != 0) 
00727         {
00728             // FYI: rv = rv*37 + ch
00729             rv = ((rv << 5) + (rv << 2) + rv) + ch;
00730         }
00731     }
00732 
00733     return (void*)rv;
00734 }
00735 
00736 
00737 
00738 
00739 /*---------------------------------------------------------
00740  *  gdiff_parseHeader()
00741  *
00742  *  reads and validates the GDIFF header info
00743  *---------------------------------------------------------
00744  */
00745 static
00746 int32 gdiff_parseHeader( pDIFFDATA dd )
00747 {
00748     int32   err = GDIFF_OK;
00749     uint8   cslen;
00750     uint8   oldcslen;
00751     uint8   newcslen;
00752     uint32  nRead;
00753     uchar   header[GDIFF_HEADERSIZE];
00754 
00755     /* Read the fixed-size part of the header */
00756 
00757        nRead = PR_Read (dd->fDiff, header, GDIFF_HEADERSIZE);
00758     if ( nRead != GDIFF_HEADERSIZE ||
00759          memcmp( header, GDIFF_MAGIC, GDIFF_MAGIC_LEN ) != 0  ||
00760          header[GDIFF_VER_POS] != GDIFF_VER )
00761     {
00762         err = GDIFF_ERR_HEADER;
00763     }
00764     else
00765     {
00766         /* get the checksum information */
00767 
00768         dd->checksumType = header[GDIFF_CS_POS];
00769         cslen = header[GDIFF_CSLEN_POS];
00770 
00771         if ( cslen > 0 )
00772         {
00773             oldcslen = cslen / 2;
00774             newcslen = cslen - oldcslen;
00775             PR_ASSERT( newcslen == oldcslen );
00776 
00777             dd->checksumLength = oldcslen;
00778             dd->oldChecksum = (uchar*)PR_MALLOC(oldcslen);
00779             dd->newChecksum = (uchar*)PR_MALLOC(newcslen);
00780 
00781             if ( dd->oldChecksum != NULL && dd->newChecksum != NULL )
00782             {
00783                             nRead = PR_Read (dd->fDiff, dd->oldChecksum, oldcslen);
00784                 if ( nRead == oldcslen )
00785                 {
00786                                    nRead = PR_Read (dd->fDiff, dd->newChecksum, newcslen);
00787                     if ( nRead != newcslen ) {
00788                         err = GDIFF_ERR_HEADER;
00789                     }
00790                 }
00791                 else {
00792                     err = GDIFF_ERR_HEADER;
00793                 }
00794             }
00795             else {
00796                 err = GDIFF_ERR_MEM;
00797             }
00798         }
00799 
00800 
00801         /* get application data, if any */
00802 
00803         if ( err == GDIFF_OK )
00804         {
00805             uint32  appdataSize;
00806             uchar   *buf;
00807             uchar   lenbuf[GDIFF_APPDATALEN];
00808 
00809                      nRead = PR_Read(dd->fDiff, lenbuf, GDIFF_APPDATALEN);
00810             if ( nRead == GDIFF_APPDATALEN ) 
00811             {
00812                 appdataSize = getlong(lenbuf);
00813 
00814                 if ( appdataSize > 0 ) 
00815                 {
00816                     buf = (uchar *)PR_MALLOC( appdataSize );
00817 
00818                     if ( buf != NULL )
00819                     {
00820                                           nRead = PR_Read (dd->fDiff, buf, appdataSize);
00821                         if ( nRead == appdataSize ) 
00822                         {
00823                             if ( 0 == memcmp( buf, APPFLAG_W32BOUND, appdataSize ) )
00824                                 dd->bWin32BoundImage = TRUE;
00825 
00826                             if ( 0 == memcmp( buf, APPFLAG_APPLESINGLE, appdataSize ) )
00827                                 dd->bMacAppleSingle = TRUE;
00828                         }
00829                         else {
00830                             err = GDIFF_ERR_HEADER;
00831                         }
00832 
00833                         PR_DELETE( buf );
00834                     }
00835                     else {
00836                         err = GDIFF_ERR_MEM;
00837                     }
00838                 }
00839             }
00840             else {
00841                 err = GDIFF_ERR_HEADER;
00842             }
00843         }
00844     }
00845 
00846     return (err);
00847 }
00848 
00849 
00850 /*---------------------------------------------------------
00851  *  gdiff_validateFile()
00852  *
00853  *  computes the checksum of the file and compares it to 
00854  *  the value stored in the GDIFF header
00855  *---------------------------------------------------------
00856  */
00857 static 
00858 int32 gdiff_validateFile( pDIFFDATA dd, int file )
00859 {
00860     int32            result;
00861     PRFileDesc*      fh;
00862     uchar*           chksum;
00863 
00864     /* which file are we dealing with? */
00865     if ( file == SRCFILE ) {
00866         fh = dd->fSrc;
00867         chksum = dd->oldChecksum;
00868     }
00869     else { /* OUTFILE */
00870         fh = dd->fOut;
00871         chksum = dd->newChecksum;
00872     }
00873 
00874     /* make sure file's at beginning */
00875     PR_Seek( fh, 0, PR_SEEK_SET );
00876 
00877     /* calculate appropriate checksum */
00878     switch (dd->checksumType)
00879     {
00880         case GDIFF_CS_NONE:
00881             result = GDIFF_OK;
00882             break;
00883 
00884 
00885         case GDIFF_CS_CRC32:
00886             if ( dd->checksumLength == CRC32_LEN )
00887                 result = gdiff_valCRC32( dd, fh, getlong(chksum) );
00888             else
00889                 result = GDIFF_ERR_HEADER;
00890             break;
00891 
00892 
00893         case GDIFF_CS_MD5:
00894 
00895 
00896         case GDIFF_CS_SHA:
00897 
00898 
00899         default:
00900             /* unsupported checksum type */
00901             result = GDIFF_ERR_CHKSUMTYPE;
00902             break;
00903     }
00904 
00905     /* reset file position to beginning and return status */
00906     PR_Seek( fh, 0, PR_SEEK_SET );
00907     return (result);
00908 }
00909 
00910 
00911 /*---------------------------------------------------------
00912  *  gdiff_valCRC32()
00913  *
00914  *  computes the checksum of the file and compares it to 
00915  *  the passed in checksum.  Assumes file is positioned at
00916  *  beginning.
00917  *---------------------------------------------------------
00918  */
00919 static 
00920 int32 gdiff_valCRC32( pDIFFDATA dd, PRFileDesc* fh, uint32 chksum )
00921 {
00922     uint32 crc;
00923     uint32 nRead;
00924 
00925     crc = crc32(0L, Z_NULL, 0);
00926 
00927        nRead = PR_Read (fh, dd->databuf, dd->bufsize);
00928     while ( nRead > 0 ) 
00929     {
00930         crc = crc32( crc, dd->databuf, nRead );
00931               nRead = PR_Read (fh, dd->databuf, dd->bufsize);
00932     }
00933 
00934     if ( crc == chksum )
00935         return GDIFF_OK;
00936     else
00937         return GDIFF_ERR_CHECKSUM;
00938 }
00939 
00940 
00941 /*---------------------------------------------------------
00942  *  gdiff_ApplyPatch()
00943  *
00944  *  Combines patch data with source file to produce the
00945  *  new target file.  Assumes all three files have been
00946  *  opened, GDIFF header read, and all other setup complete
00947  *
00948  *  The GDIFF patch is processed sequentially which random
00949  *  access is necessary for the source file.
00950  *---------------------------------------------------------
00951  */
00952 static
00953 int32 gdiff_ApplyPatch( pDIFFDATA dd )
00954 {
00955     int32   err;
00956     PRBool done;
00957     uint32  position;
00958     uint32  count;
00959     uchar   opcode;
00960     uchar   cmdbuf[MAXCMDSIZE];
00961 
00962     done = FALSE;
00963     while ( !done ) {
00964         err = gdiff_getdiff( dd, &opcode, OPSIZE );
00965         if ( err != GDIFF_OK )
00966             break;
00967 
00968         switch (opcode)
00969         {
00970             case ENDDIFF:
00971                 done = TRUE;
00972                 break;
00973 
00974             case ADD16:
00975                 err = gdiff_getdiff( dd, cmdbuf, ADD16SIZE );
00976                 if ( err == GDIFF_OK ) {
00977                     err = gdiff_add( dd, getshort( cmdbuf ) );
00978                 }
00979                 break;
00980 
00981             case ADD32:
00982                 err = gdiff_getdiff( dd, cmdbuf, ADD32SIZE );
00983                 if ( err == GDIFF_OK ) {
00984                     err = gdiff_add( dd, getlong( cmdbuf ) );
00985                 }
00986                 break;
00987 
00988             case COPY16BYTE:
00989                 err = gdiff_getdiff( dd, cmdbuf, COPY16BYTESIZE );
00990                 if ( err == GDIFF_OK ) {
00991                     position = getshort( cmdbuf );
00992                     count = *(cmdbuf + sizeof(short));
00993                     err = gdiff_copy( dd, position, count );
00994                 }
00995                 break;
00996 
00997             case COPY16SHORT:
00998                 err = gdiff_getdiff( dd, cmdbuf, COPY16SHORTSIZE );
00999                 if ( err == GDIFF_OK ) {
01000                     position = getshort( cmdbuf );
01001                     count = getshort(cmdbuf + sizeof(short));
01002                     err = gdiff_copy( dd, position, count );
01003                 }
01004                 break;
01005 
01006             case COPY16LONG:
01007                 err = gdiff_getdiff( dd, cmdbuf, COPY16LONGSIZE );
01008                 if ( err == GDIFF_OK ) {
01009                     position = getshort( cmdbuf );
01010                     count =  getlong(cmdbuf + sizeof(short));
01011                     err = gdiff_copy( dd, position, count );
01012                 }
01013                 break;
01014 
01015             case COPY32BYTE:
01016                 err = gdiff_getdiff( dd, cmdbuf, COPY32BYTESIZE );
01017                 if ( err == GDIFF_OK ) {
01018                     position = getlong( cmdbuf );
01019                     count = *(cmdbuf + sizeof(long));
01020                     err = gdiff_copy( dd, position, count );
01021                 }
01022                 break;
01023 
01024             case COPY32SHORT:
01025                 err = gdiff_getdiff( dd, cmdbuf, COPY32SHORTSIZE );
01026                 if ( err == GDIFF_OK ) {
01027                     position = getlong( cmdbuf );
01028                     count = getshort(cmdbuf + sizeof(long));
01029                     err = gdiff_copy( dd, position, count );
01030                 }
01031                 break;
01032 
01033             case COPY32LONG:
01034                 err = gdiff_getdiff( dd, cmdbuf, COPY32LONGSIZE );
01035                 if ( err == GDIFF_OK ) {
01036                     position = getlong( cmdbuf );
01037                     count = getlong(cmdbuf + sizeof(long));
01038                     err = gdiff_copy( dd, position, count );
01039                 }
01040                 break;
01041 
01042             case COPY64:
01043                 /* we don't support 64-bit file positioning yet */
01044                 err = GDIFF_ERR_OPCODE;
01045                 break;
01046 
01047             default:
01048                 err = gdiff_add( dd, opcode );
01049                 break;
01050         }
01051 
01052         if ( err != GDIFF_OK )
01053             done = TRUE;
01054     }
01055 
01056     /* return status */
01057     return (err);
01058 }
01059 
01060 
01061 /*---------------------------------------------------------
01062  *  gdiff_getdiff()
01063  *
01064  *  reads the next "length" bytes of the diff into "buffer"
01065  *
01066  *  XXX: need a diff buffer to optimize reads!
01067  *---------------------------------------------------------
01068  */
01069 static
01070 int32 gdiff_getdiff( pDIFFDATA dd, uchar *buffer, uint32 length )
01071 {
01072     uint32 bytesRead;
01073 
01074        bytesRead = PR_Read (dd->fDiff, buffer, length);
01075     if ( bytesRead != length )
01076         return GDIFF_ERR_BADDIFF;
01077 
01078     return GDIFF_OK;
01079 }
01080 
01081 
01082 /*---------------------------------------------------------
01083  *  gdiff_add()
01084  *
01085  *  append "count" bytes from diff file to new file
01086  *---------------------------------------------------------
01087  */
01088 static
01089 int32 gdiff_add( pDIFFDATA dd, uint32 count )
01090 {
01091     int32   err = GDIFF_OK;
01092     uint32  nRead;
01093     uint32  chunksize;
01094 
01095     while ( count > 0 ) {
01096         chunksize = ( count > dd->bufsize) ? dd->bufsize : count;
01097               nRead = PR_Read (dd->fDiff, dd->databuf, chunksize);
01098         if ( nRead != chunksize ) {
01099             err = GDIFF_ERR_BADDIFF;
01100             break;
01101         }
01102 
01103               PR_Write (dd->fOut, dd->databuf, chunksize);
01104 
01105         count -= chunksize;
01106     }
01107 
01108     return (err);
01109 }
01110 
01111 
01112 
01113 /*---------------------------------------------------------
01114  *  gdiff_copy()
01115  *
01116  *  copy "count" bytes from "position" in source file
01117  *---------------------------------------------------------
01118  */
01119 static
01120 int32 gdiff_copy( pDIFFDATA dd, uint32 position, uint32 count )
01121 {
01122     int32 err = GDIFF_OK;
01123     uint32 nRead;
01124     uint32 chunksize;
01125 
01126        PR_Seek (dd->fSrc, position, PR_SEEK_SET);
01127 
01128     while ( count > 0 ) {
01129         chunksize = (count > dd->bufsize) ? dd->bufsize : count;
01130 
01131               nRead = PR_Read (dd->fSrc, dd->databuf, chunksize);
01132         if ( nRead != chunksize ) {
01133             err = GDIFF_ERR_OLDFILE;
01134             break;
01135         }
01136 
01137               PR_Write (dd->fOut, dd->databuf, chunksize);
01138 
01139         count -= chunksize;
01140     }
01141 
01142     return (err);
01143 }
01144 
01145 
01146 #ifdef WIN32
01147 /*---------------------------------------------------------
01148  *  su_unbind()
01149  *
01150  *  strips import binding information from Win32
01151  *  executables and .DLL's
01152  *---------------------------------------------------------
01153  */
01154 static 
01155 PRBool su_unbind(char* oldfile, char* newfile)
01156 {
01157     PRBool bSuccess = FALSE;
01158 
01159     int     i;
01160     DWORD   nRead;
01161     PDWORD  pOrigThunk;
01162     PDWORD  pBoundThunk;
01163     FILE    *fh = NULL;
01164     char    *buf;
01165     BOOL    bModified = FALSE;
01166     BOOL    bImports = FALSE;
01167 
01168     IMAGE_DOS_HEADER            mz;
01169     IMAGE_NT_HEADERS            nt;
01170     IMAGE_SECTION_HEADER        sec;
01171 
01172     PIMAGE_DATA_DIRECTORY       pDir;
01173     PIMAGE_IMPORT_DESCRIPTOR    pImp;
01174 
01175     typedef BOOL (__stdcall *BINDIMAGEEX)(DWORD Flags,
01176                                                                  LPSTR ImageName,
01177                                                                  LPSTR DllPath,
01178                                                                  LPSTR SymbolPath,
01179                                                                  PVOID StatusRoutine);
01180     HINSTANCE   hImageHelp;
01181     BINDIMAGEEX pfnBindImageEx;
01182 
01183     if ( oldfile != NULL && newfile != NULL &&
01184          CopyFile( oldfile, newfile, FALSE ) )
01185     {   
01186         /* call BindImage() first to make maximum room for a possible
01187          * NT-style Bound Import Descriptors which can change various
01188          * offsets in the file */
01189            hImageHelp = LoadLibrary("IMAGEHLP.DLL");
01190            if ( hImageHelp > (HINSTANCE)HINSTANCE_ERROR ) {
01191               pfnBindImageEx = (BINDIMAGEEX)GetProcAddress(hImageHelp, "BindImageEx");
01192            if (pfnBindImageEx) {
01193                 pfnBindImageEx(0, newfile, NULL, NULL, NULL);
01194             }
01195                   FreeLibrary(hImageHelp);
01196            }
01197         
01198         
01199         fh = fopen( newfile, "r+b" );
01200         if ( fh == NULL )
01201             goto bail;
01202 
01203 
01204         /* read and validate the MZ header */
01205 
01206         nRead = fread( &mz, 1, sizeof(mz), fh );
01207         if ( nRead != sizeof(mz) || mz.e_magic != IMAGE_DOS_SIGNATURE )
01208             goto bail;
01209 
01210 
01211         /* read and validate the NT header */
01212         fseek( fh, mz.e_lfanew, SEEK_SET );
01213         nRead = fread( &nt, 1, sizeof(nt), fh );
01214         if ( nRead != sizeof(nt) || 
01215              nt.Signature != IMAGE_NT_SIGNATURE ||
01216              nt.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ) 
01217         {
01218             goto bail;
01219         }
01220 
01221 
01222         /* find .idata section */
01223         for (i = nt.FileHeader.NumberOfSections; i > 0; i--)
01224         {
01225             nRead = fread( &sec, 1, sizeof(sec), fh );
01226             if ( nRead != sizeof(sec) ) 
01227                 goto bail;
01228 
01229             if ( memcmp( sec.Name, ".idata", 6 ) == 0 ) {
01230                 bImports = TRUE;
01231                 break;
01232             }
01233         }
01234 
01235 
01236         /* Zap any binding in the imports section */
01237         if ( bImports ) 
01238         {
01239             buf = (char*)malloc( sec.SizeOfRawData );
01240             if ( buf == NULL )
01241                 goto bail;
01242 
01243             fseek( fh, sec.PointerToRawData, SEEK_SET );
01244             nRead = fread( buf, 1, sec.SizeOfRawData, fh );
01245             if ( nRead != sec.SizeOfRawData ) {
01246                 free( buf );
01247                 goto bail;
01248             }
01249             
01250             pImp = (PIMAGE_IMPORT_DESCRIPTOR)buf;
01251             while ( pImp->OriginalFirstThunk != 0 )
01252             {
01253                 if ( pImp->TimeDateStamp != 0 || pImp->ForwarderChain != 0 )
01254                 {
01255                     /* found a bound .DLL */
01256                     pImp->TimeDateStamp = 0;
01257                     pImp->ForwarderChain = 0;
01258                     bModified = TRUE;
01259     
01260                     pOrigThunk = (PDWORD)(buf + (DWORD)(pImp->OriginalFirstThunk) - sec.VirtualAddress);
01261                     pBoundThunk = (PDWORD)(buf + (DWORD)(pImp->FirstThunk) - sec.VirtualAddress);
01262     
01263                     for ( ; *pOrigThunk != 0; pOrigThunk++, pBoundThunk++ ) {
01264                         *pBoundThunk = *pOrigThunk;
01265                     }
01266                 }
01267                 pImp++;
01268             }
01269 
01270             if ( bModified ) 
01271             {
01272                 /* it's been changed, write out the section */
01273                 fseek( fh, sec.PointerToRawData, SEEK_SET );
01274                 fwrite( buf, 1, sec.SizeOfRawData, fh );
01275             }
01276     
01277             free( buf );
01278         }
01279 
01280 
01281         /* Check for a Bound Import Directory in the headers */
01282         pDir = &nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT];
01283         if ( pDir->VirtualAddress != 0 ) 
01284         {
01285             /* we've got one, so stomp it */
01286             buf = (char*)calloc( pDir->Size, 1 );
01287             if ( buf == NULL )
01288                 goto bail;
01289         
01290             fseek( fh, pDir->VirtualAddress, SEEK_SET );
01291             fwrite( buf, pDir->Size, 1, fh );
01292             free( buf );
01293 
01294             pDir->VirtualAddress = 0;
01295             pDir->Size = 0;
01296             bModified = TRUE;
01297         }
01298 
01299 
01300         /* Write out changed headers if necessary */
01301         if ( bModified )
01302         {
01303             /* zap checksum since it's now invalid */
01304             nt.OptionalHeader.CheckSum = 0;
01305 
01306             fseek( fh, mz.e_lfanew, SEEK_SET );
01307             fwrite( &nt, 1, sizeof(nt), fh );
01308         }
01309 
01310         bSuccess = TRUE;
01311     }
01312 
01313 bail:
01314     if ( fh != NULL ) 
01315         fclose(fh);
01316 
01317     return bSuccess;
01318 }
01319 #endif
01320