Changeset 273

Show
Ignore:
Timestamp:
01/08/07 12:36:33 (2 years ago)
Author:
gbooker
Message:

Since perian can be installed for the system or the user, check for the other installation, and attempt to remove it.
Also, an error string is created during the install process. We should display this somewhere if its length is not 0.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/CPFPerianPrefPaneController.h

    r247 r273  
    33#import <Cocoa/Cocoa.h> 
    44#import <PreferencePanes/NSPreferencePane.h> 
     5#import <Security/Security.h> 
    56 
    67#define ComponentInfoDictionaryKey      @"Components" 
     
    1213typedef enum 
    1314{ 
    14         InstallStatusNotInstalled, 
    15         InstallStatusOutdated, 
    16         InstallStatusInstalled 
     15        InstallStatusInstalledInWrongLocation = 0, 
     16        InstallStatusNotInstalled = 1, 
     17        InstallStatusOutdatedWithAnotherInWrongLocation = 2, 
     18        InstallStatusOutdated = 3, 
     19        InstallStatusInstalledInBothLocations = 4, 
     20        InstallStatusInstalled = 5 
    1721} InstallStatus; 
     22 
     23InstallStatus inline currentInstallStatus(InstallStatus status) 
     24{ 
     25        return (status | 1); 
     26} 
     27 
     28BOOL inline isWrongLocationInstalled(InstallStatus status) 
     29{ 
     30        return ((status & 1) == 0); 
     31} 
     32 
     33InstallStatus inline setWrongLocationInstalled(InstallStatus status) 
     34{ 
     35        return (status & ~1); 
     36} 
    1837 
    1938typedef enum 
     
    4766         
    4867        InstallStatus                                           installStatus; //This is only marked as installed if everything is installed 
     68        BOOL                                                            userInstalled; 
     69        AuthorizationRef                                        auth; 
     70        NSMutableString                                         *errorString; 
    4971         
    5072        NSURL                                                           *perianForumURL; 
  • trunk/CPFPerianPrefPaneController.m

    r250 r273  
    11#import "CPFPerianPrefPaneController.h" 
    2 #import <Security/Security.h> 
    32#include <sys/stat.h> 
    43 
     
    3130} 
    3231 
    33 - (BOOL)systemInstalled 
    34 
    35         NSString *myPath = [[self bundle] bundlePath]; 
    36          
    37         if([myPath hasPrefix:NSHomeDirectory()]) 
    38                 return NO; 
    39         return YES; 
    40 
    41  
    42 - (NSString *)quickTimeComponentDir 
    43 
    44         NSString *basePath = nil; 
    45          
    46         if(![self systemInstalled]) 
    47                 basePath = NSHomeDirectory(); 
    48         else 
    49                 basePath = [NSString stringWithString:@"/"]; 
    50          
    51         return [basePath stringByAppendingPathComponent:@"Library/QuickTime"]; 
    52 
    53  
    54 - (NSString *)coreAudioComponentDir 
    55 
    56         NSString *basePath = nil; 
    57          
    58         if(![self systemInstalled]) 
    59                 basePath = NSHomeDirectory(); 
    60         else 
    61                 basePath = [NSString stringWithString:@"/"]; 
    62          
    63         return [basePath stringByAppendingPathComponent:@"Library/Audio/Plug-Ins/Components"]; 
    64 
    65  
    66 - (NSString *)frameworkComponentDir 
    67 
    68         NSString *basePath = nil; 
    69          
    70         if(![self systemInstalled]) 
    71                 basePath = NSHomeDirectory(); 
    72         else 
    73                 basePath = [NSString stringWithString:@"/"]; 
    74          
    75         return [basePath stringByAppendingPathComponent:@"Library/Frameworks"]; 
    76 
    77  
    78 - (InstallStatus)installStatusForComponent:(NSString *)component type:(ComponentType)type withMyVersion:(NSString *)myVersion 
     32- (NSString *)installationBasePath:(BOOL)userInstallation 
     33
     34        if(userInstallation) 
     35                return NSHomeDirectory(); 
     36        return [NSString stringWithString:@"/"]; 
     37
     38 
     39- (NSString *)quickTimeComponentDir:(BOOL)userInstallation 
     40
     41        return [[self installationBasePath:userInstallation] stringByAppendingPathComponent:@"Library/QuickTime"]; 
     42
     43 
     44- (NSString *)coreAudioComponentDir:(BOOL)userInstallation 
     45
     46        return [[self installationBasePath:userInstallation] stringByAppendingPathComponent:@"Library/Audio/Plug-Ins/Components"]; 
     47
     48 
     49- (NSString *)frameworkComponentDir:(BOOL)userInstallation 
     50
     51        return [[self installationBasePath:userInstallation] stringByAppendingPathComponent:@"Library/Frameworks"]; 
     52
     53 
     54- (NSString *)basePathForType:(ComponentType)type user:(BOOL)userInstallation 
    7955{ 
    8056        NSString *path = nil; 
     
    8359        { 
    8460                case ComponentTypeCoreAudio: 
    85                         path = [self coreAudioComponentDir]; 
     61                        path = [self coreAudioComponentDir:userInstallation]; 
    8662                        break; 
    8763                case ComponentTypeQuickTime: 
    88                         path = [self quickTimeComponentDir]; 
     64                        path = [self quickTimeComponentDir:userInstallation]; 
    8965                        break; 
    9066                case ComponentTypeFramework: 
    91                         path = [self frameworkComponentDir]; 
     67                        path = [self frameworkComponentDir:userInstallation]; 
    9268                        break; 
    9369        } 
    94         path = [path stringByAppendingPathComponent:component]; 
     70        return path; 
     71
     72 
     73- (InstallStatus)installStatusForComponent:(NSString *)component type:(ComponentType)type withMyVersion:(NSString *)myVersion 
     74
     75        NSString *path = nil; 
     76        InstallStatus ret = InstallStatusNotInstalled; 
     77         
     78        path = [[self basePathForType:type user:userInstalled] stringByAppendingPathComponent:component]; 
    9579         
    9680        NSDictionary *infoDict = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Contents/Info.plist"]]; 
     81        if(infoDict != nil) 
     82        { 
     83                NSString *currentVersion = [infoDict objectForKey:BundleVersionKey]; 
     84                if([currentVersion compare:myVersion] == NSOrderedAscending) 
     85                        ret = InstallStatusOutdated; 
     86                 
     87                ret = InstallStatusInstalled;            
     88        } 
     89         
     90        /* Check other installation type */ 
     91        path = [[self basePathForType:type user:!userInstalled] stringByAppendingPathComponent:component]; 
     92         
     93        infoDict = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Contents/Info.plist"]]; 
    9794        if(infoDict == nil) 
    98                 return InstallStatusNotInstalled; 
    99          
    100         NSString *currentVersion = [infoDict objectForKey:BundleVersionKey]; 
    101         if([currentVersion compare:myVersion] == NSOrderedAscending) 
    102                 return InstallStatusOutdated; 
    103          
    104         return InstallStatusInstalled; 
     95                /* Above result is all there is */ 
     96                return ret; 
     97         
     98        return setWrongLocationInstalled(ret); 
    10599} 
    106100 
     
    116110                perianAppID = CFSTR("org.perian.perian"); 
    117111                a52AppID = CFSTR("com.cod3r.a52codec"); 
     112                 
     113                NSString *myPath = [[self bundle] bundlePath]; 
     114                 
     115                if([myPath hasPrefix:@"/Library"]) 
     116                        userInstalled = NO; 
     117                else 
     118                        userInstalled = YES; 
    118119    } 
    119120     
     
    125126        NSDictionary *infoDict = [[self bundle] infoDictionary]; 
    126127        installStatus = [self installStatusForComponent:@"Perian.component" type:ComponentTypeQuickTime withMyVersion:[infoDict objectForKey:BundleVersionKey]]; 
    127         if(installStatus == InstallStatusNotInstalled) 
     128        if(currentInstallStatus(installStatus) == InstallStatusNotInstalled) 
    128129        { 
    129130                [textField_installStatus setStringValue:NSLocalizedString(@"Perian is not Installed", @"")]; 
    130131                [button_install setTitle:NSLocalizedString(@"Install Perian", @"")]; 
    131132        } 
    132         else if(installStatus == InstallStatusOutdated) 
     133        else if(currentInstallStatus(installStatus) == InstallStatusOutdated) 
    133134        { 
    134135                [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed, but Outdated", @"")]; 
     
    151152                        switch(installStatus) 
    152153                        { 
     154                                case InstallStatusInstalledInWrongLocation: 
    153155                                case InstallStatusNotInstalled: 
    154156                                        [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed, but parts are Not Installed", @"")]; 
    155157                                        [button_install setTitle:NSLocalizedString(@"Install Perian", @"")]; 
    156158                                        break; 
     159                                case InstallStatusOutdatedWithAnotherInWrongLocation: 
    157160                                case InstallStatusOutdated: 
    158161                                        [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed, but parts are Outdated", @"")]; 
    159162                                        [button_install setTitle:NSLocalizedString(@"Update Perian", @"")]; 
     163                                        break; 
     164                                case InstallStatusInstalledInBothLocations: 
     165                                        [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed Twice", @"")]; 
     166                                        [button_install setTitle:NSLocalizedString(@"Correct Installation", @"")]; 
    160167                                        break; 
    161168                                case InstallStatusInstalled: 
     
    165172                        } 
    166173                } 
     174                else if(isWrongLocationInstalled(installStatus)) 
     175                { 
     176                        [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed Twice", @"")]; 
     177                        [button_install setTitle:NSLocalizedString(@"Correct Installation", @"")]; 
     178                } 
    167179                else 
    168180                { 
     
    194206        [perianDonateURL release]; 
    195207        [perianWebSiteURL release]; 
     208        if(auth != nil) 
     209                AuthorizationFree(auth, 0); 
     210        [errorString release]; 
    196211        [super dealloc]; 
    197212} 
     
    205220        struct stat sb; 
    206221        if(stat([destination fileSystemRepresentation], &sb) != 0) 
     222        { 
     223                [errorString appendFormat:NSLocalizedString(@"No such directory %@\n", @""), destination]; 
    207224                return FALSE; 
     225        } 
    208226         
    209227        char *buf = NULL; 
     
    211229                         "ditto -x -k --rsrc \"$SRC_ARCHIVE\" \"$DST_PATH\""); 
    212230        if(!buf) 
     231        { 
     232                [errorString appendFormat:NSLocalizedString(@"Could not allocate memory for extraction command\n", @"")]; 
    213233                return FALSE; 
     234        } 
    214235         
    215236        setenv("SRC_ARCHIVE", [archivePath fileSystemRepresentation], 1); 
     
    219240        if(WIFEXITED(status) && WEXITSTATUS(status) == 0) 
    220241                ret = YES; 
     242        else 
     243                [errorString appendFormat:NSLocalizedString(@"Extraction for %@ failed\n", @""), archivePath]; 
    221244 
    222245        free(buf); 
     
    226249} 
    227250 
    228 - (BOOL)_authenticatedExtractArchivePath:(NSString *)archivePath toDestination:(NSString *)destination finalPath:(NSString *)finalPath authorization:(AuthorizationRef)auth 
     251- (BOOL)_authenticatedExtractArchivePath:(NSString *)archivePath toDestination:(NSString *)destination finalPath:(NSString *)finalPath 
    229252{ 
    230253        BOOL ret = NO, oldExist = NO; 
     
    234257         
    235258        if(stat([destination fileSystemRepresentation], &sb) != 0) 
     259        { 
     260                [errorString appendFormat:NSLocalizedString(@"No such directory %@\n", @""), destination]; 
    236261                return FALSE; 
     262        } 
    237263         
    238264        char *buf = NULL; 
     
    250276                                 sb.st_uid, sb.st_gid); 
    251277        if(!buf) 
     278        { 
     279                [errorString appendFormat:NSLocalizedString(@"Could not allocate memory for extraction command\n", @"")]; 
    252280                return FALSE; 
     281        } 
    253282         
    254283        setenv("SRC_ARCHIVE", [archivePath fileSystemRepresentation], 1); 
     
    264293                if(pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) 
    265294                        ret = YES; 
    266         } 
     295                else 
     296                        [errorString appendFormat:NSLocalizedString(@"Extraction for %@ failed\n", @""), archivePath]; 
     297        } 
     298        else 
     299                [errorString appendFormat:NSLocalizedString(@"Authentication failed for extraction for %@\n", @""), archivePath]; 
     300         
    267301        free(buf); 
    268302        unsetenv("SRC_ARCHIVE"); 
     
    273307} 
    274308 
    275 - (BOOL)_authenticatedRemove:(NSString *)componentPath authorization:(AuthorizationRef)auth 
     309- (BOOL)_authenticatedRemove:(NSString *)componentPath 
    276310{ 
    277311        BOOL ret = NO; 
    278312        struct stat sb; 
    279313        if(stat([componentPath fileSystemRepresentation], &sb) != 0) 
     314                /* No error, just forget it */ 
    280315                return FALSE; 
    281316         
     
    284319                         "rm -rf \"$COMP_PATH\""); 
    285320        if(!buf) 
     321        { 
     322                [errorString appendFormat:NSLocalizedString(@"Could not allocate memory for removal command\n", @"")]; 
    286323                return FALSE; 
     324        } 
    287325         
    288326        setenv("COMP_PATH", [componentPath fileSystemRepresentation], 1); 
     
    295333                if(pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) 
    296334                        ret = YES; 
    297         } 
     335                else 
     336                        [errorString appendFormat:NSLocalizedString(@"Removal for %@ failed\n", @""), componentPath]; 
     337        } 
     338        else 
     339                [errorString appendFormat:NSLocalizedString(@"Authentication failed for removal for %@\n", @""), componentPath]; 
    298340        free(buf); 
    299341        unsetenv("COMP_PATH"); 
     
    302344 
    303345 
    304 - (BOOL)installArchive:(NSString *)archivePath forPiece:(NSString *)component type:(ComponentType)type withMyVersion:(NSString *)myVersion andAuthorization:(AuthorizationRef)auth 
    305 
    306         NSString *containingDir = nil; 
    307         switch(type) 
    308         { 
    309                 case ComponentTypeCoreAudio: 
    310                         containingDir = [self coreAudioComponentDir]; 
    311                         break; 
    312                 case ComponentTypeQuickTime: 
    313                         containingDir = [self quickTimeComponentDir]; 
    314                         break; 
    315                 case ComponentTypeFramework: 
    316                         containingDir = [self frameworkComponentDir]; 
    317                         break; 
    318         } 
     346- (BOOL)installArchive:(NSString *)archivePath forPiece:(NSString *)component type:(ComponentType)type withMyVersion:(NSString *)myVersion 
     347
     348        NSString *containingDir = [self basePathForType:type user:userInstalled]; 
     349        BOOL ret = YES; 
     350 
    319351        InstallStatus pieceStatus = [self installStatusForComponent:component type:type withMyVersion:myVersion]; 
    320         if(auth != nil && pieceStatus != InstallStatusInstalled) 
    321         { 
    322                 BOOL result = [self _authenticatedExtractArchivePath:archivePath toDestination:containingDir finalPath:[containingDir stringByAppendingPathComponent:component] authorization:auth]; 
     352        if(!userInstalled && currentInstallStatus(pieceStatus) != InstallStatusInstalled) 
     353        { 
     354                BOOL result = [self _authenticatedExtractArchivePath:archivePath toDestination:containingDir finalPath:[containingDir stringByAppendingPathComponent:component]]; 
    323355                if(result == NO) 
    324                         return NO; 
     356                        ret = NO; 
    325357        } 
    326358        else 
    327359        { 
    328360                //Not authenticated 
    329                 if(pieceStatus == InstallStatusOutdated) 
     361                if(currentInstallStatus(pieceStatus) == InstallStatusOutdated) 
    330362                { 
    331363                        //Remove the old one here 
     
    333365                        BOOL result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:containingDir destination:@"" files:[NSArray arrayWithObject:component] tag:&tag]; 
    334366                        if(result == NO) 
    335                                 return NO; 
     367                                ret = NO; 
    336368                } 
    337                 if(pieceStatus != InstallStatusInstalled) 
     369                if(currentInstallStatus(pieceStatus) != InstallStatusInstalled) 
    338370                { 
    339371                        //Decompress and install new one 
    340372                        BOOL result = [self _extractArchivePath:archivePath toDestination:containingDir]; 
    341373                        if(result == NO) 
    342                                 return NO; 
     374                                ret = NO; 
    343375                }                
    344376        } 
    345         return YES; 
     377        if(ret != NO && isWrongLocationInstalled(pieceStatus) != 0) 
     378        { 
     379                /* Let's try and remove the wrong one, if we can, but only if install succeeded */ 
     380                containingDir = [self basePathForType:type user:!userInstalled]; 
     381 
     382                if(userInstalled) 
     383                        [self _authenticatedRemove:[containingDir stringByAppendingPathComponent:component]]; 
     384                else 
     385                { 
     386                        int tag = 0; 
     387                        [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:containingDir destination:@"" files:[NSArray arrayWithObject:component] tag:&tag]; 
     388                } 
     389        } 
     390        return ret; 
    346391} 
    347392 
     
    355400        NSString *quickTimeComponentPath = [componentPath stringByAppendingPathComponent:@"QuickTime"]; 
    356401        NSString *frameworkComponentPath = [componentPath stringByAppendingPathComponent:@"Frameworks"]; 
    357         AuthorizationRef auth = nil; 
    358          
    359         if([self systemInstalled]) 
    360         { 
    361                 if(!AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) == errAuthorizationSuccess) 
    362                         // Try it anyway, it will likely fail, but who knows what kind of screwed up systems people have 
    363                         auth = nil; 
    364         } 
    365          
    366         [self installArchive:[componentPath stringByAppendingPathComponent:@"Perian.zip"] forPiece:@"Perian.component" type:ComponentTypeQuickTime withMyVersion:[infoDict objectForKey:BundleVersionKey] andAuthorization:auth]; 
     402 
     403        [errorString release]; 
     404        errorString = [[NSMutableString alloc] init]; 
     405        /* This doesn't ask the user, so create it anyway.  If we don't need it, no problem */ 
     406        if(AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) != errAuthorizationSuccess) 
     407                /* Oh well, hope we don't need it */ 
     408                auth = nil; 
     409         
     410        [self installArchive:[componentPath stringByAppendingPathComponent:@"Perian.zip"] forPiece:@"Perian.component" type:ComponentTypeQuickTime withMyVersion:[infoDict objectForKey:BundleVersionKey]]; 
    367411         
    368412        NSEnumerator *componentEnum = [myComponentsInfo objectEnumerator]; 
     
    384428                                break; 
    385429                } 
    386                 [self installArchive:archivePath forPiece:[myComponent objectForKey:ComponentNameKey] type:type withMyVersion:[myComponent objectForKey:BundleVersionKey] andAuthorization:auth]; 
     430                [self installArchive:archivePath forPiece:[myComponent objectForKey:ComponentNameKey] type:type withMyVersion:[myComponent objectForKey:BundleVersionKey]]; 
    387431        } 
    388432        if(auth != nil) 
     433        { 
    389434                AuthorizationFree(auth, 0); 
     435                auth = nil; 
     436        } 
    390437        [self performSelectorOnMainThread:@selector(installComplete:) withObject:nil waitUntilDone:NO]; 
    391438        [pool release]; 
     
    397444        NSDictionary *infoDict = [[self bundle] infoDictionary]; 
    398445        NSDictionary *myComponentsInfo = [infoDict objectForKey:ComponentInfoDictionaryKey]; 
    399         AuthorizationRef auth = nil; 
    400          
    401         if([self systemInstalled]) 
    402         { 
    403                 if(!AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) == errAuthorizationSuccess) 
    404                         // Try it anyway, it will likely fail, but who knows what kind of screwed up systems people have 
    405                         auth = nil; 
    406         } 
     446 
     447        [errorString release]; 
     448        errorString = [[NSMutableString alloc] init]; 
     449        /* This doesn't ask the user, so create it anyway.  If we don't need it, no problem */ 
     450        if(AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) != errAuthorizationSuccess) 
     451                /* Oh well, hope we don't need it */ 
     452                auth = nil; 
    407453         
    408454        int tag = 0; 
    409455        BOOL result = NO; 
    410456        if(auth != nil) 
    411                 [self _authenticatedRemove:[[self quickTimeComponentDir] stringByAppendingPathComponent:@"Perian.component"] authorization:auth]; 
    412         else 
    413                 result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[self quickTimeComponentDir] destination:@"" files:[NSArray arrayWithObject:@"Perian.component"] tag:&tag]; 
     457                [self _authenticatedRemove:[[self quickTimeComponentDir:userInstalled] stringByAppendingPathComponent:@"Perian.component"]]; 
     458        else 
     459                result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[self quickTimeComponentDir:userInstalled] destination:@"" files:[NSArray arrayWithObject:@"Perian.component"] tag:&tag]; 
    414460         
    415461        NSEnumerator *componentEnum = [myComponentsInfo objectEnumerator]; 
     
    418464        { 
    419465                ComponentType type = [[myComponent objectForKey:ComponentTypeKey] intValue]; 
    420                 NSString *directory = nil; 
    421                 switch(type) 
    422                 { 
    423                         case ComponentTypeCoreAudio: 
    424                                 directory = [self coreAudioComponentDir]; 
    425                                 break; 
    426                         case ComponentTypeQuickTime: 
    427                                 directory = [self quickTimeComponentDir]; 
    428                                 break; 
    429                         case ComponentTypeFramework: 
    430                                 directory = [self frameworkComponentDir]; 
    431                                 break; 
    432                 } 
     466                NSString *directory = [self basePathForType:type user:userInstalled]; 
    433467                if(auth != nil) 
    434                         [self _authenticatedRemove:[directory stringByAppendingPathComponent:[myComponent objectForKey:ComponentNameKey]] authorization:auth]; 
     468                        [self _authenticatedRemove:[directory stringByAppendingPathComponent:[myComponent objectForKey:ComponentNameKey]]]; 
    435469                else 
    436470                        result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:directory destination:@"" files:[NSArray arrayWithObject:[myComponent objectForKey:ComponentNameKey]] tag:&tag]; 
    437471        } 
    438472        if(auth != nil) 
     473        { 
    439474                AuthorizationFree(auth, 0); 
     475                auth = nil; 
     476        } 
    440477         
    441478        [self performSelectorOnMainThread:@selector(installComplete:) withObject:nil waitUntilDone:NO];